【发布时间】:2018-03-26 11:14:07
【问题描述】:
我在 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);
然后父级将运行该行
pid = fork();
这将从进程中创建一个新的子进程。
现在有两种情况将决定输出。操作系统安排父或子先运行。
假设父母先运行。然后它会执行LINE A(也就是kill函数)
因为它是父进程,所以 pid 值将是子进程的进程 ID。所以它会将 USRSIG1 发送给孩子,但由于它被阻止它不会做任何事情
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) 信号,包括它自己。但是由于它被阻止在孩子身上,所以什么都不会发生。父级将收到信号,它会将我增加 1(所以现在 i = 3 在父级中)。它(父级)将从函数 pause 中恢复以打印 I 的值(即 3)并退出。
孩子现在从 kill 函数中恢复,因为它是一个孩子,所以 if 语句将不正确(所以孩子中 i 的值 = 3)。子进程解除对 set 和 pause() 信号的阻塞。
由于没有其他进程向子进程发送信号,因此它将永远暂停,并且仅父进程的输出为 3。如果我们走另一条路(孩子在父母之前跑),那么输出将只有 4。
什么让我感到困惑的是,考试的解决方案说每次运行有 2 个输出?我不明白这怎么可能,因为其中一个进程将停留在 pause() 中。
解决方案键表示 LINE A 的可能输出是:
3 4, 4 3, 3 5, or 5 3
这就是我从问题中所能理解的全部内容。任何帮助或提示将不胜感激。
【问题讨论】:
标签: c linux concurrency signals fork