UNIX系统中wait函数簇和SIGCLD信号的关系

有朋友疑惑“为什么有了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,

那么产生这个现象一个合理的解释就是,wait对于信号SIGCHLD中断,自动恢复了


当然这只是我的猜测,如果有高人看到这点,有异议,欢迎留言讨论

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值