遇到信号量的问题?
signal(SIGCLD,SIG_IGN)
SIGCHLD的语义为:子进程状态改变后产生此信号,父进程需要调用一个wait函数以确定发生了什么。
对于SIGCLD的早期处理方式如下:如果进程特地设置该信号的配置为SIG_IGN,则调用进程的子进程将不产生僵死进程。
如果将SIGCLD的配置设置为捕捉,则内核立即检查是否有子进程准备好被等待,如果是这样,则调用SIGCLD处理程序。
APUE上SIGCLD语义写的有点不清楚,到底我们的系统是如何来处理SIGCLD信号呢?
1.SIG_DFL :默认的处理方式是不理会这个信号,但是也不会丢弃子进行状态,
所以如果不用wait,waitpid 对其子进行进行状态信息回收,会产生僵尸进程。
2.SIG_IGN :忽略的处理方式,这个方式和默认的忽略是不一样的语意,暂且我们把忽略定义为SIG_IGN,
在这种方式下,子进程状态信息会被丢弃,也就是自动回收了,所以不会产生僵尸进程,
但是问题也就来了,wait,waitpid却无法捕捉到子进程状态信息了,
如果你随后调用了wait,那么会阻塞到所有的子进程结束,并返回错误ECHILD,也就是没有子进程等待。
APUE中P248叙述SIGCHLD如果配置成SIG_IGN也不会产生僵尸进程。
是否系统SIG_IGN配置下,对SIGCLD,SIGCHLD做出的处理方式是相同的。
3.自定义处理方式:SIGCLD会立即检查是否有子进程准好被等待,这便是SIGCLD最大漏洞了,
一旦在信号处理函数中加入了信号处理方式重建的步骤,那么每次设置SIGCLD处理方式时,都会去检查是否有信号
到来,如果此时信号的确到来了,先是调用自定义信号处理函数,然后是调用信号处理方式重建函数,
在重建配置的时候,会去检查信号是否到来,此时信号未被处理,会再次触发自定义信号处理函数,一直循环。
所以在处理SIGCLD时,应该先wait处理掉了信号信息后,再进行信号处理方式重建。
SIGCHLD在配置信号处理方式时,是不会立即检查是否有子进程准备好被扽带,也不会在此时调用信号处理函数。
有朋友疑惑“为什么有了 wait 函数族还需要使用 SIGCHLD 信号?”。本文详细地阐述UNIX系统中wait函数族和SIGCHLD信号的关系。
一、unix中僵尸进程的含义
关于unix中僵尸进程的含义:
凡是父进程没有调用wait函数获得子进程终止状态的子进程在终止之后都是僵尸进程,这个概念的关键一点就是父进程是否调用了wait函数。
二、SIGCHLD信号
SIGCHLD信号的含义:
简单的说,子进程退出时父进程会收到一个SIGCHLD信号,默认的处理是忽略这个信号,而常规的做法是在这个信号处理函数中调用wait函数获取子进程的退出状态。
三、既然在SIGCHLD信号的处理函数中要调用wait函数族,为什么有了wait函数族还需要使用SIGCHLD信号?
我们知道,unix中信号是采用异步处理某事的机制,好比说你准备去做某事,去之前跟邻居张三说如果李四来找你的话就通知他一声,这让你可以抽身出来去做这件事,而李四真正来访时会有人通知你,这个就是异步信号一个较为形象的比喻。
一般的,父进程在生成子进程之后会有两种情况:一是父进程继续去做别的事情,类似上面举的例子;另一是父进程啥都不做,一直在wait子进程退出。
SIGCHLD信号就是为这第一种情况准备的,它让父进程去做别的事情,而只要父进程注册了处理该信号的函数,在子进程退出时就会调用该函数,在函数中wait子进程得到终止状态之后再继续做父进程的事情。
最后,我们来明确以下二点:
1)凡父进程不调用wait函数族获得子进程终止状态的子进程在退出时都会变成僵尸进程。
2)SIGCHLD信号可以异步的通知父进程有子进程退出。
四、僵尸进程有啥危害
所谓僵尸进程,形象来说,进程已死,但其尸体还在,没人收尸啊,冤魂不散,仍然占用一个进程号,如果主进程不妥善处理,当僵尸进程数量巨大之后,就没法再次fork了,所以对于大型并发服务器来说,当建立了进程池,一定要想办法处理掉所有僵尸进程。
五,讨论一个稍微高级点话题,如果在signal和waitpid同时出现会怎样,看如下
# include <stdio.h>
# include <stdlib.h>
# include <sys/types.h>
# include <sys/wait.h>
# include <unistd.h>
void sigchld_handler( int signo ){
printf("sigchld_handler\n");
sleep(3);
return;
}
int main()
{
int i;
pid_t pid;
pid_t cpid;
signal( SIGCHLD, sigchld_handler );
for( i=0; i<5; i++ ){
pid = fork();
if ( pid == 0 ){
sleep(2);
exit(0); // child exit.
}
else if ( pid == -1 ){
perror( "fork" ); // fork failed.
exit(-1);
}
}
printf("before wait\n");
system( "ps -ef | grep ttt" );
// while((cpid = waitpid(-1,NULL,0)!=-1)) {
while ((cpid = wait(NULL)) != -1) {
printf("wait pid is %d\n", cpid);
continue;
}
printf("after wait\n");
system( "ps -ef | grep ttt" );
}
gcc -g -o ttt chil.c
./ttt
before wait
root 3094 2800 0 18:29 pts/0 00:00:00 ./ttt
root 3095 3094 0 18:29 pts/0 00:00:00 ./ttt
root 3096 3094 0 18:29 pts/0 00:00:00 ./ttt
root 3097 3094 0 18:29 pts/0 00:00:00 ./ttt
root 3098 3094 0 18:29 pts/0 00:00:00 ./ttt
root 3099 3094 0 18:29 pts/0 00:00:00 ./ttt
root 3100 3094 0 18:29 pts/0 00:00:00 sh -c ps -ef | grep ttt
root 3102 3100 0 18:29 pts/0 00:00:00 grep ttt
sigchld_handler
sigchld_handler
wait pid is 3095
wait pid is 3096
wait pid is 3097
wait pid is 3098
wait pid is 3099
after wait
root 3094 2800 0 18:29 pts/0 00:00:00 ./ttt
root 3103 3094 0 18:29 pts/0 00:00:00 sh -c ps -ef | grep ttt
root 3105 3103 0 18:29 pts/0 00:00:00 grep ttt
sigchld_handler
这是一个sig和wait联合处理到例子(当然通常做法有2种,1是在sig 处理函数中调用wait处理,2是不要sig 处理函数,单独调用wait处理)。但是程序结果,让人很意外,为什么当产生SIGCHLD信号时,wait阻塞没有被中断,而是处理正常,所有子进程都正确退出,没有产生僵尸进程呢。按理说。wait阻塞时,当产生SIGCHLD信号,应该中断,同时返回-1,error no为EINTR,