子进程是否也应该解除阻塞的SIGCHLD信号?

子进程是否也应该解除阻塞的SIGCHLD信号?,c,signals,multiple-processes,sigprocmask,C,Signals,Multiple Processes,Sigprocmask,我试图理解阻塞和取消阻塞信号是如何工作的,我试图理解下面的代码。具体地说,我在看第28行(在代码中注释):inta=sigprocmask(SIG_UNBLOCK,&mask,NULL),亦称信号在子系统中被解锁的位置 我从教科书上得到的代码说,代码使用信号阻塞是为了确保程序在其删除功能(简化为printf(“添加%d\n”,pid);)之前执行其添加功能(简化为printf(“删除%d\n,pid);)。这对我来说很有意义;通过阻塞SIGCHLD信号,然后在执行add函数后解除阻塞,我们确保在

我试图理解阻塞和取消阻塞信号是如何工作的,我试图理解下面的代码。具体地说,我在看第28行(在代码中注释):
inta=sigprocmask(SIG_UNBLOCK,&mask,NULL),亦称信号在子系统中被解锁的位置

我从教科书上得到的代码说,代码使用信号阻塞是为了确保程序在其删除功能(简化为
printf(“添加%d\n”,pid);
)之前执行其添加功能(简化为
printf(“删除%d\n,pid);
)。这对我来说很有意义;通过阻塞
SIGCHLD
信号,然后在执行add函数后解除阻塞,我们确保在执行add函数之前不会调用处理程序。然而,为什么我们要解除孩子体内的信号?这不就是通过立即解除阻塞消除了整个阻塞点,允许子级在父级添加之前删除吗

然而,无论我是否注释掉了行,输出(在代码后面描述)都是相同的,这意味着这显然不是发生的事情。教科书说:

请注意,子项继承其父项的阻止集,因此在调用
execve
之前,必须小心解除子项中的
SIGCHLD
信号的阻止

但在我看来,解除阻塞仍然会导致调用处理程序。这条线到底是干什么的

void handler(int sig) {
    pid_t pid;
    printf("here\n");
    while ((pid = waitpid(-1, NULL, 0)) > 0); /* Reap a zombie child */
    printf("deleting %d\n", pid); /* Delete the child from the job list */
}

int main(int argc, char **argv) {
    int pid;
    sigset_t mask;
    signal(SIGCHLD, handler);
    sigemptyset(&mask);
    sigaddset(&mask, SIGCHLD);
    sigprocmask(SIG_BLOCK, &mask, NULL); /* Block SIGCHLD */

    pid = fork();
    if (pid == 0) {
        printf("in child\n");

        int a = sigprocmask(SIG_UNBLOCK, &mask, NULL); // LINE 28

        printf("a is %d\n",a);
        execve("/bin/date", argv, NULL);
        exit(0);
    }

    printf("adding %d\n", pid);/* Add the child to the job list */
    sleep(5);
    printf("awake\n");

    int b = sigprocmask(SIG_UNBLOCK, &mask, NULL);
    printf("b is %d\n", b);
    sleep(3);

    exit(0);
}
产出:

adding 652

in child

a is 0

Wed Apr 24 20:18:04 UTC 2019

awake

here

deleting -1

b is 0
然而,为什么我们要解除孩子体内的信号?不是吗 只要立即解除阻塞,就可以消除整个阻塞点 是否允许在父项添加之前删除子项

不是。每个进程都有自己的信号掩码。一个新进程继承其父进程的信号掩码,但其意义与它继承父进程内存的内容相同——子进程将获得相当于一个独立副本的内容。它对该副本的修改不会反映在父副本中,子副本启动后也不会反映在父副本中。如果不是这样,那么系统中的所有进程将共享一个信号掩码

只有父级不能过早接收到
SIGCLD
,因此只有父级需要阻止该信号

[……]教科书指出:

“请注意,孩子继承了他们父母的阻塞集,因此我们必须小心地在之前解除孩子中的SIGCHLD信号的阻塞 正在调用Exeve。“

但在我看来,这似乎仍然会导致 正在调用的处理程序

同样,“继承”是指继承副本,而不是共享同一掩码

这条线到底是干什么的


它在子对象中取消阻止
SIGCLD
,同样对父对象没有影响,以防被阻止会干扰子对象即将执行的
/bin/date
的行为。

在信号处理程序中不能安全地调用
printf()
。Per:标准库中的函数不保证可重入,可能会修改具有静态或线程存储持续时间的对象。and:因此,信号处理程序通常不能调用标准库函数。POSIX允许从信号处理程序调用“异步信号安全”函数,并提供一个列表
printf()
不在该列表中。在信号处理程序中阻塞调用
waitpid()
,例如使用
waitpid(-1,NULL,0)
,也是一个坏主意<代码>while((pid=waitpid(-1,NULL,WNOHANG))>0)
要好得多-这将收获所有已退出的子进程,而不会无限期地等待任何尚未退出的子进程。如果存在尚未退出的子进程,则信号处理程序将挂起,直到它们全部退出。在子进程中取消阻止对父进程没有影响。