我试图了解阻塞和解除阻塞信号是如何工作的,我正在尝试理解下面的代码。具体来说,我正在看第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
但是,为什么我们要解除孩子的信号?这不是通过立即解除阻塞来消除整个阻塞点,允许孩子在父母添加之前删除吗?
不。每个过程都有自己的信号掩码。一个新的进程继承了它的父进程的信号掩码,但只是在它继承父内存的内容的同一意义上 - 子进程相当于一个独立的副本。它对该副本的修改不会反映在父母的副本中,也不会反映在孩子的副本之后。如果不是这种情况,那么系统中的所有进程将共享一个信号掩码。
只有父母不能太快收到SIGCLD
,因此只有父母需要阻止该信号。
[...]教科书指出:
“请注意,孩子们继承了他们父母的被阻止的一套,因此我们必须小心在调用exec之前解除孩子的SIGCHLD信号。”
但在我看来,解锁会导致处理程序被调用。
再次,“继承”在继承副本的意义上,而不是在共享相同掩码的意义上。
这条线到底是做什么的?
它解除了孩子们的SIGCLD
- 再次对父母没有影响 - 如果它被阻止会干扰孩子即将执行的/bin/date
的行为。