signal(SIGCLD,SIG_IGN)

20 篇文章 0 订阅

遇到信号量的问题?

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,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值