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

问题描述 投票:1回答:1

我试图了解阻塞和解除阻塞信号是如何工作的,我正在尝试理解下面的代码。具体来说,我正在看第28行(在代码中注释):int a = sigprocmask(SIG_UNBLOCK, &mask, NULL);,也就是孩子中信号被解除阻塞的地方。

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

但是,输出(在代码之后描述)是相同的,无论我是否注释掉了这条线,这意味着显然不会发生什么。教科书指出:

“请注意,孩子们继承了他们父母的被阻挡的一套,所以在打电话给SIGCHLD之前,我们必须小心打开孩子的execve信号。”

但在我看来,解锁会导致处理程序被调用。这条线到底是做什么的?

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
c signals multiple-processes sigprocmask
1个回答
1
投票

但是,为什么我们要解除孩子的信号?这不是通过立即解除阻塞来消除整个阻塞点,允许孩子在父母添加之前删除吗?

不。每个过程都有自己的信号掩码。一个新的进程继承了它的父进程的信号掩码,但只是在它继承父内存的内容的同一意义上 - 子进程相当于一个独立的副本。它对该副本的修改不会反映在父母的副本中,也不会反映在孩子的副本之后。如果不是这种情况,那么系统中的所有进程将共享一个信号掩码。

只有父母不能太快收到SIGCLD,因此只有父母需要阻止该信号。

[...]教科书指出:

“请注意,孩子们继承了他们父母的被阻止的一套,因此我们必须小心在调用exec之前解除孩子的SIGCHLD信号。”

但在我看来,解锁会导致处理程序被调用。

再次,“继承”在继承副本的意义上,而不是在共享相同掩码的意义上。

这条线到底是做什么的?

它解除了孩子们的SIGCLD - 再次对父母没有影响 - 如果它被阻止会干扰孩子即将执行的/bin/date的行为。

© www.soinside.com 2019 - 2024. All rights reserved.