使用 SIGCHLD 处理僵尸
Handling zombies with SIGCHLD
在我的程序中,我正在监听传入的 SIGCHLD 信号以避免僵尸。
代码:
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;
}
问题是,forked childs 的数量和 printf("test") 的输出数量有时不匹配。分叉的数量 child 大于数字 printf("test").
这个代码段是不是保证没有僵尸?如果是,它如何实现这一目标?它没有打印正确数量的 "test"。 waitpid() 是否在 while 内多次清除死机 childs?
会发生什么,当这个信号处理程序调用时,同时另一个 child 可能会死掉。默认情况下信号将被阻止。(当处理程序为 运行 时,另一个 child 可能会死亡)。当信号处理程序为运行时,waitpid是否清除其信号发送的进程?
此外,计数器不相等。 (static volatile int) 或者我尝试了原子整数。
两件不同的事情正在发生:
printf
不是异步信号安全的,所以你不应该从信号处理程序中调用它。将其替换为 write
.
- 您只在信号处理程序中递增
count
一次。如果您想知道有多少进程死亡,则需要在 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
的数量正确,count
中的值正确。然而,还有一个竞争条件:如果在最终调用 waitpid
和信号处理程序结束之间,另一个子进程死亡,那么将不会收到 SIGCHLD
,因此它将成为一个僵尸进程,直到它死后的那个也是。这种边缘情况的解决方案要复杂得多,并且取决于应用程序其余部分的结构。
在我的程序中,我正在监听传入的 SIGCHLD 信号以避免僵尸。
代码:
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;
}
问题是,forked childs 的数量和 printf("test") 的输出数量有时不匹配。分叉的数量 child 大于数字 printf("test").
这个代码段是不是保证没有僵尸?如果是,它如何实现这一目标?它没有打印正确数量的 "test"。 waitpid() 是否在 while 内多次清除死机 childs?
会发生什么,当这个信号处理程序调用时,同时另一个 child 可能会死掉。默认情况下信号将被阻止。(当处理程序为 运行 时,另一个 child 可能会死亡)。当信号处理程序为运行时,waitpid是否清除其信号发送的进程?
此外,计数器不相等。 (static volatile int) 或者我尝试了原子整数。
两件不同的事情正在发生:
printf
不是异步信号安全的,所以你不应该从信号处理程序中调用它。将其替换为write
.- 您只在信号处理程序中递增
count
一次。如果您想知道有多少进程死亡,则需要在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
的数量正确,count
中的值正确。然而,还有一个竞争条件:如果在最终调用 waitpid
和信号处理程序结束之间,另一个子进程死亡,那么将不会收到 SIGCHLD
,因此它将成为一个僵尸进程,直到它死后的那个也是。这种边缘情况的解决方案要复杂得多,并且取决于应用程序其余部分的结构。