wordpress建立个人网站,深圳设计公司招聘网站,平面广告设计培训学校,做受网站#xff08;1#xff09;SIGCHLD信号产生的条件
1.子进程终止时会向父进程发送SIGCHLD信号#xff0c;告知父进程回收自己#xff0c;但该信号的默认处理动作为忽略#xff0c;因此父进程仍然不会去回收子进程#xff0c;需要捕捉处理实现子进程的回收#xff1b;
2.子…1SIGCHLD信号产生的条件
1.子进程终止时会向父进程发送SIGCHLD信号告知父进程回收自己但该信号的默认处理动作为忽略因此父进程仍然不会去回收子进程需要捕捉处理实现子进程的回收
2.子进程接收到SIGSTOP19信号停止时
3.子进程处在停止态接受到SIGCONT后唤醒时。
综上子进程结束、接收到SIGSTOP停止挂起和接收到SIGCONT唤醒时都会向父进程发送SIGCHLD信号。父进程可以捕捉该信号来实现对子进程的回收或者了解子进程所处的状态。
2借助SIGCHLD信号回收子进程
子进程结束运行其父进程会收到SIGCHLD信号。该信号的默认处理动作是忽略。可以捕捉该信号在捕捉函数中完成子进程状态的回收。
//分析例子结合 17)SIGCHLD 信号默认动作掌握父使用捕捉函数回收子进程的方法
#include stdio.h
#include stdlib.h
#include unistd.h
#include errno.h
#include sys/types.h
#include sys/wait.h
#include signal.hvoid sys_err(char *str)
{perror(str);exit(1);
}void do_sig_child(int signo)
{int status;pid_t pid;// if ((pid waitpid(0, status, WNOHANG)) 0) {while ((pid waitpid(0, status, WNOHANG)) 0) {if (WIFEXITED(status))printf(------------child %d exit with %d\n, pid, WEXITSTATUS(status));else if (WIFSIGNALED(status))printf(child %d killed by the %dth signal\n, pid, WTERMSIG(status));}
}int main(void)
{pid_t pid;int i;//阻塞SIGCHLDfor (i 0; i 10; i) {if ((pid fork()) 0)break;else if (pid 0)sys_err(fork);}if (pid 0) { //10个子进程int n 1;while (n--) {printf(child ID %d\n, getpid());sleep(1);}return i1; //子进程结束状态依次为1、2、••••••、10} else if (pid 0) { struct sigaction act;act.sa_handler do_sig_child;sigemptyset(act.sa_mask);act.sa_flags 0;sigaction(SIGCHLD, act, NULL);//解除对SIGCHLD的阻塞while (1) {printf(Parent ID %d\n, getpid());sleep(1);}}return 0;
}
[rootlocalhost 01_signal_test]# ./sigchild
child ID 11129
child ID 11130
child ID 11131
child ID 11132
child ID 11133
child ID 11134
child ID 11135
child ID 11136
child ID 11137
Parent ID 11128 //表明在此时父进程肯定完成了对信号的注册工作
child ID 11138
------------child 11129 exit with 1
------------child 11130 exit with 2
------------child 11132 exit with 4
------------child 11133 exit with 5
------------child 11131 exit with 3
Parent ID 11128
------------child 11134 exit with 6
------------child 11137 exit with 9
Parent ID 11128
------------child 11136 exit with 8
Parent ID 11128
------------child 11135 exit with 7
Parent ID 11128
------------child 11138 exit with 10 //可见父进程的确完成了对全部子进程的回收
Parent ID 11128
Parent ID 11128
分析如下
1.在上述程序中每个子进程在结束之前都会睡眠1s这是为了留出足够的时间保证在所有子进程结束之前父进程能够完成对SIGCHLD信号的注册。否则所有子进程都结束了父进程还没有完成注册则此时信号都被忽略从而子进程不能被父进程回收。但是只要有一个子进程在信号注册后结束所有子进程都可以被回收因为使用了while循环回收子进程。除了使用sleep函数来实现这一点外其实更加高效而又精确的办法其实在负载较大的 情况下sleep函数也不能确保能够做到就是在父进程注册函数之前就将SIGCHLD信号设置为阻塞通过信号集操作函数加入到屏蔽字中在注册完成时立即解除对该信号的阻塞即可。
2.不可以将捕捉函数内部的while替换为if。因为在执行捕捉函数期间发送了多次SIGCHLD信号未决信号集只是记录了一次因此下一次再调用捕捉函数时if只能完成对一个子进程的回收即使有多个子进程都发了信号但是只是调用一次捕捉函数。而while循环则可以对所有结束了的子进程都完成回收。因此对于多个子进程的回收最好采用循环的方式不采用if。
3.之前在管道进程间通信父子进程实现ls | wc -l时父进程exec函数族执行ls子进程exec执行wc -l父进程无法完成对子进程的回收因为父进程执行exec函数族就离开了不能回来。但是现在可以利用信号捕捉实现在父进程调用exec函数族之前就注册SIGCHLD的捕捉函数但是需要注意以下几点1.仍然要考虑对SIGCHLD信号的屏蔽和解除屏蔽操作2.要考虑子进程向父进程发送信号时父进程已经结束了此时父进程仍然无法对子进程回收可以让父进程睡眠等待让其后结束但这在实际中是没有意义的父进程结束了子进程变为孤儿进程会被init进程回收因此实际编程不需要考虑这一点3.在ls | wc -l这个例子中由于使用了标准输出的重定向dup2因此用户处理函数输出的回收状态信息不能显示在屏幕上因此在用户处理函数中还需要再次进行重定向恢复输出重定向为屏幕。
总结
1.子进程继承了父进程的信号屏蔽字和信号处理动作但子进程没有继承未决信号集spending。
2.注意注册信号捕捉函数的位置。
3.应该在fork之前阻塞SIGCHLD信号。注册完捕捉函数后解除阻塞。