我在过去的 CMU 考试中发现了这个问题,但我无法理解输出是如何可能的。
基本上,它背后的想法是有一个阻止用户定义信号的父进程,然后父进程派生一个子进程。并且基于首先运行的进程(又名:赢得比赛)可能会有不同的输出。 Here is the question that is being asked in the exam (请阅读)
这是考试的代码:
int i = 1;
void handler (int sig) {
i++;
}
int main() {
pid_t pid;
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGUSR1);
signal(SIGUSR1, handler);
sigprocmask(SIG_BLOCK, &s, 0);
pid = fork();
<LINE A>
if (pid != 0) {
i = 2;
<LINE B>
} else {
i = 3;
<LINE C>
}
sigprocmask(SIG_UNBLOCK, &s, 0);
pause(); /* pause to allow all signals to arrive */
printf("%d\n", i);
exit(0);
}
因为我们需要放置函数,所以有3种情况需要测试:
kill(pid,USRSIG1);
在 LINE A 或 LINE B 或 LINE C 中找到可能的输出。
现在这是我所做的,我将函数放在 LINE A 中。
假设我们运行程序,然后父级将创建一个空集 s,将信号 SIGSUR1 添加到其中,然后它将为 SIGUSR1 信号分配一个自定义处理程序,并阻塞集 s 中的信号。这些是哪几行
sigset_t s;
sigemptyset(&s);
sigaddset(&s, SIGUSR1);
signal(SIGUSR1, handler);
sigprocmask(SIG_BLOCK, &s, 0);
然后 parent 会跑线
pid = fork();
这将从进程中创建一个新的子进程。
现在有 2 种情况将决定输出。操作系统安排父级或子级先运行。
假设父进程先运行。然后会执行LINE A(也就是kill函数)
因为它是父进程,所以 pid 值将是子进程的进程 ID。所以它会将 USRSIG1 发送给 child ,但由于它被阻止了,所以它什么都不做
if 语句为全局变量 i 赋值。如果进程是父进程则 i = 2,否则 i = 3。因此在我们的父进程中,我们将有 i = 2。
if (pid != 0) { //if i am a parent then i = 2
i = 2;
<LINE B>
} else { //if i am a child then i = 3
i = 3;
<LINE C>
}
下一行将在父级中执行,它将解除对 SIGUSR1 信号的阻塞
sigprocmask(SIG_UNBLOCK, &s, 0);
并且父进程将暂停,直到它收到信号
现在子进程将运行,它会向进程组中的所有进程(包括它自己)发送一个 kill(0,SIGUSR1) 信号。但由于它在 child 体内被阻塞,所以什么也不会发生。 parent 将收到信号,它会将我增加 1(所以现在我在 parent 中 = 3)。它(父级)将从函数 pause 恢复以打印 I 的值(即 3)并退出。
child 现在从 kill 函数中恢复,因为它是 child ,if 语句将不为真(因此 child 中 i 的值 = 3)。 child 解锁来自 set 和 pause() 的信号。
由于没有其他进程向子进程发送信号,它将永远暂停,父进程的输出仅为 3。如果我们走另一条路( child 跑在 parent 之前),那么输出将只有 4。
让我感到困惑的是,考试的解决方案说每次运行有 2 个输出?我不明白这怎么可能,因为其中一个进程将停留在 pause() 中。
解决方案的关键是 LINE A 的可能输出是:
3 4, 4 3, 3 5, or 5 3
这就是我从问题中所能理解的全部内容。任何帮助或提示将不胜感激。
最佳答案
如果 child 先跑,输出将为 5,因为它会收到来自自身和 parent 的信号。如果两个进程在进入 pause()
之前都完成了 kill(pid,USRSIG1)
,则两者都不会终止或打印。
POSIX 还允许进程因延迟信号而终止和打印(例如,如果使用网络消息而不是共享内存),并允许子进程将任何 int
值打印为 i
不是 volatile sig_atomic_t
类型。
从评论中可以看出,考试作者错误地认为 pause()
会神奇地等待所有已发送或将要发送到进程的信号被接收到。
如果 sigprocmask(SIG_UNBLOCK, &s, 0); pause();
被替换为对 sigsuspend
的适当调用,它将充当考试作者状态。 parent 将收到 1 个信号, child 将收到 1 或 2 个信号,因为来自 parent 的信号可能为时已晚或与来自其自身的信号相结合。
关于子信号和父信号之间的并发竞争,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46744792/