免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2585 | 回复: 0
打印 上一主题 下一主题

阻塞SIGCHLD信号 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-02-29 11:29 |只看该作者 |倒序浏览
阻塞SIGCHLD信号

我在看APUE的10.18节——system函数和在分析linux的/sbin/init程序的源代码时遇到了一个相同的、让我迷惑了好一阵的问题,相关的代码如下:

代码1:APUE10.18节的system函数源代码
int system(const char *cmdstring) /* with appropriate signal handling */
{
    pid_t               pid;
    int                 status;
    struct sigaction    ignore, saveintr, savequit;
    sigset_t            chldmask, savemask;
    ……
    sigemptyset(&chldmask);         /* now block SIGCHLD */
    sigaddset(&chldmask, SIGCHLD);
    if (sigprocmask(SIG_BLOCK, &chldmask, &savemask)
        return(-1);

    if ((pid = fork())
        status = -1;    /* probably out of processes */
    } else if (pid == 0) {          /* child */
        ……
        sigprocmask(SIG_SETMASK, &savemask, NULL);

        execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
        _exit(127);     /* exec error */
    } else {                        /* parent */
       while (waitpid(pid, &status, 0)
           if (errno != EINTR) {
               status = -1; /* error other than EINTR from waitpid() */
               break;
           }
    }
    ……
    if (sigprocmask(SIG_SETMASK, &savemask, NULL)
        return(-1);

    return(status);}
如果该函数的调用者在其SIGCHLD信号处理函数中调用了waitpid函数来获得任意子进程的终止状态,且在fork之前没有阻塞SIGCHLD信号,则子进程结束,父进程执行SIGCHLD信号处理函数且得到了子进程的终止状态。而该函数中的waitpid函数由于没有得到其创建的子进程的终止状态而出错。


代码2:sysvinit/src/init.c文件中的spawn函数—创建一个子进程并执行一个新程序

int spawn(CHILD *ch, int *res)
{
  pid_t pid, pgrp;      /* child, console process group. */
  sigset_t nmask, omask;    /* For blocking SIGCHLD */
  ……
  *res = -1;
   ……
  while(1) {
    sigemptyset(&nmask);
    sigaddset(&nmask, SIGCHLD);
    sigprocmask(SIG_BLOCK, &nmask, &omask);

    if ((pid = fork()) == 0) {
        ……
        sigprocmask(SIG_SETMASK, &omask, NULL);
        ……
        exit(1);
    }
    *res = pid;
    sigprocmask(SIG_SETMASK, &omask, NULL);
    ……
    return(pid);
  }
}

该函数的调用者,即init进程注册的SIGCHLD信号处理函数如下所示:
void chld_handler()
{
    CHILD       *ch;
    int     pid, st;
   
    while((pid = waitpid(-1, &st, WNOHANG)) != 0) {
        if (errno == ECHILD) break;
        for( ch = family; ch; ch = ch->next )
            if ( ch->pid == pid && (ch->flags & RUNNING) ) {
                ……
            }
        if (ch == NULL)
            ……
    }
……
}

对spawn函数的调用方式是:spawn(ch, &(ch->pid)),ch->pid参数是个值结果参数,在spawn函数里实际就是*res,而*res=pid是在fork之后进行的,即ch->pid是在fork之后才被赋值的。如果在fork之前没有阻塞SIGCHLD信号,且在而*res=pid之前子进程结束,父进程执行上面的SIGCHLD信号处理函数chld_handler(),此时ch->pid没有被赋值,因此ch->pid!=pid,出现了错误。


由上述两段代码我们可以看到他们的一个共同的特点:
定义了一个函数,函数中fork了一个子进程,并且定义的函数要得到关于子进程的一些信息,例如子进程ID、子进程终止状态等,而该函数的调用者所注册的SIGCHLD信号处理函数会影响定义的这个函数获取这些信息,因此为了避免该函数在获取这些信息之前,由于子进程终止触发SIGCHLD信号处理函数先执行,在fork之前都将SIGCHLD信号阻塞了,在函数获取了相关信息后,才对SIGCHLD信号接触阻塞。



接下来我们以代码1为例完整的解释一下阻塞SIGCHLD信号的原因:

在一个进程终止或停止时,SIGCHLD信号被送给其父进程。按系统默认,将忽略此信号。如果父进程希望了解其子进程的这种状态改变,则应捕捉此信号。信号捕捉函数中通常要调用wait函数以取得子进程ID和其终止状态。

一般的,父进程在生成子进程之后会有两种情况:一是父进程继续去做别的事情;另一是父进程什么都不做,一直在wait子进程退出。

SIGCHLD信号就是为第一种情况准备的,它让父进程去做别的事情,而只要父进程注册了处理该信号的函数,在子进程退出时就会调用该函数,在函数中wait子进程得到终止状态之后再继续做父进程的事情。


我们先来写一个function_model函数模型,如下所示:
int function_model(…) {    pid_t               pid;    int                 status;    ……    if ((pid = fork())         status = -1;        } else if (pid == 0) {          /* child */       ……    } else {                        /* parent */       while (waitpid(pid, &status, 0)            if (errno != EINTR) {               status = -1;                break;           }    }        ……    return(status);}


当我们定义了一个具有function_model函数模型结构的函数时,应该在fork之前将SIGCHLD信号阻塞,fork之后,父子进程的SIGCHLD信号都是被阻塞的。子进程在进行任何的操作之前,应该将SIGCHLD恢复至原来的设置。父进程则应该在waitpid函数之后,将SIGCHLD恢复至原来的设置。


即这种类型的函数的正确的格式为:

int function_model(…)
{
    pid_t               pid;
    int                 status;
    sigset_t            chldmask, savemask;
    ……
    sigemptyset(&chldmask);         /* now block SIGCHLD */
    sigaddset(&chldmask, SIGCHLD);
    if(sigprocmask(SIG_BLOCK, &chldmask, &savemask)
        return(-1);

    if ((pid = fork())
        status = -1;   
} else if (pid == 0) {          /* child */

sigprocmask(SIG_SETMASK, &savemask, NULL);
       ……
    } else {                        /* parent */
       while (waitpid(pid, &status, 0)
           if (errno != EINTR) {
               status = -1;
               break;
           }
    }
    ……
if(sigprocmask(SIG_SETMASK, &savemask, NULL)
    return(-1);
    return(status);
}

父进程阻塞SIGCHLD信号的原因在于:如果function_model函数的调用者caller在调用function_model之前,注册了SIGCHLD信号的处理函数sigchld_handler,且sigchld_handler函数中调用了waitpid函数等待任意子进程结束,如下所示:

void sigchld_handler(int signo)
{
    pid_t pid;
    int status;
    while((pid=waitpid(-1,&status,WNOHANG))>0)
    {
        ……
}
return ;
}

    若我们没有阻塞SIGCHLD信号,则当function_model函数中fork的子进程结束时,会向父进程发送SIGCHLD信号,则其信号处理函数sigchld_handler被调用,并在sigchld_handler函数中调用了waitpid得到了这个子进程的退出状态,然后释放掉其占用的进程表等其它资源。返回到function_model函数中继续执行,由于其创建的子进程的退出状态已经被获取,内核不再保存其退出状态,所以其中调用的waitpid函数由于无法得到其退出状态而出错,并将status设置为-1。
    若我们阻塞了SIGCHLD信号,则当function_model函数中fork的子进程结束时,内核会向父进程发送SIGCHLD信号,但SIGCHLD信号被阻塞而不递送给父进程,即处于未决状态,因此function_model函数中的waitpid就能正确得到这个子进程的退出状态。当解除对SIGCHLD信号的阻塞时,SIGCHLD信号会被递交给父进程,引发父进程调用sigchld_handler函数,由于在function_model函数中以调用waitpid获取了function_model函数创建的子进程的退出状态,内核不再保存其终止信息,所以sigchld_handler中的waitpid也不会的到其退出状态。

    下面是Advanced Programming in the UNIX® Environment: Second Edition(APUE中的解释:
POSIX.1 states that if wait or waitpid returns the status of a child process while SIGCHLD is pending, then SIGCHLD should not be delivered to the process unless the status of another child process is also available. None of the four implementations discussed in this book implements this semantic. Instead, SIGCHLD remains pending after the system function calls waitpid; when the signal is unblocked, it is delivered to the caller. If we called wait in the sig_chld function in
Figure 10.26
, it would return 1 with errno set to ECHILD, since the system function already retrieved the termination status of the child.


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/59740/showart_486730.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP