用SIGCHLD处理僵尸
在我的程序中,我正在收听传入的信号以避免僵尸 代码:用SIGCHLD处理僵尸,c,unix,operating-system,signals,C,Unix,Operating System,Signals,在我的程序中,我正在收听传入的信号以避免僵尸 代码: void myhandler(int signo) { printf("test"); int status; pid_t pid; while((pid = waitpid(-1, &status, WNOHANG)) > 0) ++count; } int main(int argc, char const *argv[]) { struct sigacti
void myhandler(int signo)
{
printf("test");
int status;
pid_t pid;
while((pid = waitpid(-1, &status, WNOHANG)) > 0)
++count;
}
int main(int argc, char const *argv[])
{
struct sigaction sigchld_action;
memset(&sigchld_action,0,sizeof(sigchld_action));
sigchld_action.sa_handler = &myhandler;
sigaction(SIGCHLD,&sigchld_action,NULL);
if(fork() == 0){
exit(0);
}
if(fork()==0){
exit(0);
}
if(fork()==0){
exit(0);
}
while(wait(NULL) > 0)
++count;
return 0;
}
问题是,分叉子对象的数量和printf(“test”)的输出数量有时不匹配。分叉儿童的数量大于数字printf(“测试”)
这个代码段是否保证不会有僵尸?如果是,如何做到这一点??它没有打印正确的“测试”编号。waitpid()是否会在一段时间内多次清除死去的孩子
当这个信号处理程序调用时,会发生什么,同时另一个孩子可能会死亡。默认情况下,信号将被阻止。(当处理程序运行时,另一个子项可能死亡)。waitpid是否清除在信号处理程序运行时发送其信号的进程
而且,计数器是不相等的。(静态volatile int)或我尝试了原子整数。发生了两件不同的事情:
printf
不是异步信号安全的,所以不应该从信号处理程序调用它。将其替换为write
while
循环中增加它void handler(int signo)
{
int status;
pid_t pid;
while((pid = waitpid(-1, &status, WNOHANG)) > 0) {
write(1, "test", 4); /* technically this may result in a partial write and you should loop it, but in practice I think this'll be fine for this example */
++count;
}
}
有了这些代码,您可能就没有僵尸了,并且拥有正确数量的
test
s和正确的count
值。但是,还有一个竞争条件:如果在最后一次调用waitpid
和信号处理程序结束之间,另一个子进程死亡,那么将不会收到SIGCHLD
,因此它将是一个僵尸,直到它死后的一个子进程。这种边缘情况的解决方案要复杂得多,这取决于应用程序其余部分的结构。在信号处理程序中不能安全地使用stdio函数,例如printf()
,顺便说一句。@Shawn是的,我知道,但如果使用原子计数器,结果是一样的。他只是意味着相同类型的挂起信号不排队,它们被丢弃了。此外,信号处理程序在处理程序的持续时间内屏蔽信号(除非您明确阻止它这样做)。所以他们到了,但他及时得到了处理。你不会以这种方式收获所有的孩子——僵尸会出现。事实上,我上一句话是错误的,可能所有的孩子都会在while循环中收获。即使处理程序只被调用一次。在这里,检查调用处理程序的次数不是有效的指标。您的代码通过这种方式毕竟是安全的,很抱歉在一些statements@Root2A我就是这么想的,是的。若并没有更多的子进程可以收获,它将退出处理程序,但当SIGCHLD再次出现时,您只需再次输入处理程序。结果相同,在5个进程中,计数在2-3-5之间变化。但是我在父级的末尾添加了一个无限循环,以避免终止父级,然后我运行ps,我没有看到任何僵尸。如果计数小于进程数,怎么就没有僵尸?@Root2A你能编辑你的问题,使之包含完整的程序,而不仅仅是一个信号处理程序吗?然后可能会更清楚发生了什么。@Root2A两件有趣的事情:1<代码>处理程序vs.myhandler
。2.如果希望信号处理程序执行等待,那么为什么在main
中有wait
?@Root2A从来没有孤立进程。当一个进程在其子进程尚未收获的情况下死亡时,子进程将被收养(通常通过init
)。您没有看到僵尸,因为init
在父级退出后正在捕获它们。@Root2A您在main
末尾使用while(wait(NULL)>0)
的最新版本将永远不会在其所有子级退出之前退出。(当然,它仍然可能是kill-9
d或其他东西。)