考虑:
int main()
{
if (fork() == 0){
printf("a");
}
else{
printf("b");
waitpid(-1, NULL, 0);
}
printf("c");
exit(0);
}
(来自Computer Systems,Bryant - O'Hallaron)。
我们被要求提供所有可能的输出序列。
我回答:acbc,abcc,bacc。 但是,与解决方案(bcac)相比,我缺少一个输出。我认为这个输出是不可能的,因为父进程在打印c(waitpid)之前等待它的子进程返回。这不是真的吗?为什么?并且,在这种情况下,上面的代码和没有waitpid行的代码有什么区别?
答案 0 :(得分:3)
我没有看到任何方式bcac
是可能的。起初我期望一些基于stdio缓冲区以意外顺序刷新的技巧。但即便如此:
在输出c
之后,孩子不会输出a
。因此,c
中的第一个bcac
必须来自父级。
在c
完成之后,父母不会输出waitpid
。但这种情况在孩子完成之后才会发生,包括在exit()
期间发生的最终stdio同花。因此,第一个c
总是来自孩子。
已经实现了矛盾证明......输出不能是bcac
。
嗯,你可以做一件事来弄乱订单。您可以在已经有一个即将退出的子进程的进程内执行该程序。如果预先存在的子项在新子项打印a
之前退出,则主进程将检测 退出waitpid
,并继续打印其内容并可能退出在孩子打印之前。
这在setuid程序中需要注意:不要假设因为您的程序只创建了一个子进程,它只有有一个子进程。如果您处于高级防御代码学习环境中,这个答案是有道理的。在unix-newbie语境中,它看起来并不相关,而且只说bcac
是不可能的,即使它在技术上并不正确,也可能更好。
答案 1 :(得分:2)
这很棘手,但可以中断对waitpid
的调用(返回-1
而错误号为EINTR
)。在这种情况下,父级可以在子级输出任何内容之前输出c
,并且bcac
是可能的。
为了防止发生bcac
,需要设置信号掩码,或者更好的是,检查waitpid
返回值,如果中断则再次调用。