SIGCHLD信號回收子進程
- 代碼
- 問題
- 注意點
代碼
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>void handler(int signo)
{int status; pid_t pid;while ((pid = waitpid(0, &status, WNOHANG)) > 0) {if (WIFEXITED(status))printf("child %d exit %d\n", pid, WEXITSTATUS(status));else if (WIFSIGNALED(status))printf("child %d cancel signal %d\n", pid, WTERMSIG(status));}}int main()
{pid_t pid;int i = 0;//循環創建子進程for(i = 0; i < 10; i++){pid = fork();if (pid == 0){break;}else if (pid < 0){perror("pid error:");exit(1);}}if (pid > 0){sigset_t set,oldmask;sigemptyset(&set);sigemptyset(&oldmask);sigaddset(&set,SIGCHLD);//屏蔽sigprocmask(SIG_BLOCK,&set,NULL);struct sigaction act;//安裝信號act.sa_flags = 0;act.sa_handler = handler;sigemptyset(&act.sa_mask);//父進程sigaction(SIGCHLD,&act,NULL);//開啟sigprocmask(SIG_SETMASK,&oldmask,NULL);while (1);}else if (pid == 0){int n = 1;while (n--) {printf("child ID %d\n", getpid());sleep(1);}return i + 1;}return 0;
}
問題
如果我們把while循環改成if,相當于每個子進程運行完后就會發送SIGCHLD信號,父進程通過捕獲,用waitpid來回收子進程,可以嗎?
void handler(int signo)
{int status; pid_t pid;if ((pid = waitpid(0, &status, WNOHANG)) > 0) {if (WIFEXITED(status))printf("child %d exit %d\n", pid, WEXITSTATUS(status));else if (WIFSIGNALED(status))printf("child %d cancel signal %d\n", pid, WTERMSIG(status));}}
但是,運行的效果卻并不如我們所想象的。這是為什么呢?
當父進程收到第一個信號的時候,將會執行handler這個函數,在執行函數期間,子進程仍會發出信號,在Linux中1-31號信號是非實時信號,不支持排隊,信號丟失了,造成了這個原因。
注意點
代碼如下(示例):
sigset_t set,oldmask;sigemptyset(&set);sigemptyset(&oldmask);sigaddset(&set,SIGCHLD);//屏蔽sigprocmask(SIG_BLOCK,&set,NULL);struct sigaction act;//安裝信號act.sa_flags = 0;act.sa_handler = handler;sigemptyset(&act.sa_mask);//父進程sigaction(SIGCHLD,&act,NULL);//開啟sigprocmask(SIG_SETMASK,&oldmask,NULL);
為什么要采用屏蔽和開啟信號的方式呢?
原因在于在程序運行中,為了避免父進程還沒注冊完信號,子進程就發送信號的情況。信號阻塞時發送,信號的遞送會等到信號解除后。