我在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();
这将从过程中创建一个新的孩子 .
现在有2个案例将确定输出 . 操作系统会安排父或子项首先运行 .
让我们说父母先跑 . 然后它将执行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(所以现在我父母的身份是3) . 并且它(父)将从函数暂停中恢复以打印I的值(即3)并退出 .
现在,孩子从kill函数恢复,因为它是一个孩子,if语句不会为真(所以child中的i值= 3) . 孩子解锁来自set和pause()的信号 .
由于没有其他进程向子节点发送信号,因此它将永远保持暂停状态,仅父节点输出为3 . 如果我们走另一条路(孩子在父母之前跑),那么输出将只有4 .
令我困惑的是,考试的解决方案是每次运行有2个输出?我不知道这是怎么回事,因为其中一个进程将保持暂停() .
解决方案密钥表示LINE A的可能输出是:
3 4, 4 3, 3 5, or 5 3
这就是我从这个问题中可以理解的一切 . 任何帮助或提示将不胜感激 .
1 回答
如果孩子先跑,输出将是5,因为它将从他们自己和父母接收信号 . 如果两个进程在输入_2651187之前完成
kill(pid,USRSIG1)
,则不会终止或打印 .POSIX还允许两个进程由于信号延迟而终止和打印(例如,如果使用网络消息而不是共享内存),并且子进程打印任何
int
值,因为i
不是volatile sig_atomic_t
类型 .从评论中可以看出,考试作者错误地认为
pause()
会神奇地等待,直到收到所有发送或将被发送到过程的信号 .如果
sigprocmask(SIG_UNBLOCK, &s, 0); pause();
被适当调用sigsuspend
替换,它将作为考试作者状态 . 父母将收到1个信号,孩子将收到1或2个信号,因为来自父母的信号可能太晚或与来自其自身的信号结合 .