孩子和父母信号之间的并发竞争
我从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信号分配一个自定义处理程序,并阻止该集合中的信号。这是这些线路
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分配一个值。如果这个过程是父母的话,我= 2,否则的话,我= 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);
与父进程将暂停,直到它接收到的信号
现在孩子会跑,它会包括自己的过程组中的杀(0,SIGUSR1)信号发送给所有的进程。但是由于它在孩子身上受到阻碍,什么都不会发生。父母会收到信号,它会使我增加1(所以现在我在父母中= 3)。并且它将从函数暂停中恢复,以打印I(它是3)的值并退出。
孩子现在从kill函数恢复,因为它是一个孩子,if语句不会是真的(所以我的孩子的值= 3)。孩子解除设置和暂停()的信号。
由于没有其他进程向小孩发送信号,它将永远保持暂停状态,并且仅由父级输出3。如果我们按照其他方式(孩子在父母之前跑步),那么输出将仅为4。
什么令我困惑的是,考试的解决方案说每次运行有2个输出?我不明白这是怎么可能的,因为其中一个进程将停留在()。
该解决方案的关键认为,对于A线的可能的输出是:
3 4, 4 3, 3 5, or 5 3
这是所有我可以从问题的理解。任何帮助或暗示将不胜感激。
如果孩子先跑步,输出将是5,因为它会接收来自自身和父母的信号。如果两个进程在输入pause()
之前完成kill(pid,USRSIG1)
,则两个进程都不会终止或打印。
POSIX还允许两个进程终止并打印由于到延迟信号(例如,如果网络消息来代替共享存储器)和为孩子打印任何int
值作为i
是volatile sig_atomic_t
类型不是。
从评论中可以看出,考试作者错误地认为pause()
会奇迹般地等待,直到收到所有发送或将发送给过程的信号。
如果sigprocmask(SIG_UNBLOCK, &s, 0); pause();
被替换为sigsuspend
的适当调用,它将充当考试作者状态。父母会收到1个信号,孩子会收到1或2个信号,因为来自父母的信号可能太迟或与自己的信号结合在一起。