当有多个子进程的SIGCHLD信号到达父进程的时候,如果父进程用wait等待,那么父进程在处理第一个达到的SIGCHLD信号的时候,其他的SIGCHLD信号被堵塞,而且信号不被缓存,这样就会导致信号丢失,这样会产生很多的僵尸进程。。解决办法是父进程用waitpid来等待子进程信号。。。
正好看到有人问这样一个问题
看unix网络编程第一卷的时候,碰到书上这样一个例子:
一个并发服务器, 每一个客户端连接服务器就fork一个子进程.书上讲到当同时有n多个客户端断开连接时,
服务器端同时有n多个子进程终止, 这时候内核同时向父进程发送n多个sigchld信号.它的sigchld信号处理
函数如下:
void sig_chld(int signo)
{
pid_t pid;
int stat;
while((pid = waitpid(-1, &stat, WNOHANG)) > 0){
printf("child %d terminated\n", pid);
}
return;
}
我的问题是:既然sigchld是不可靠的信号,进程就不可能对sigchld进行排队,
直接丢弃了sigchld信号(当进程注册信号的时候,发现已有sigchld注册进未决信号,
因为内核同时发送多个sigchld).请问大家上面的代码是如何保证不产生僵尸进程的.谢谢!
超清晰的解答,来自与chinaunix论坛的flw大版主。。。:
- 根本就不需要找回来!
- 好比有五个进程,
- 不妨分别称为 p1 p2 p3 p4 p5,
- 一开始 p1 结束了,发了一个 SIGCHLD(s1),
- 这时父进程可能空闲了,于是开始处理这个信号,假设处理的过程中 p2 又结束了,又发了一个 SIGCHLD(s2),
- 这时候已经有两个信号了(一个正在处理,一个待处理),这时如果 p3 又结束了,那么它发的那个 SIGCHLD(s3) 势必会丢失,
- 丢失了怎么办?
- 没关系,因为那个信号处理函数是个循环嘛,
- 所以 while(waitpid()) 的时候,会把 p1 p2 p3 都处理的。
- 即使是很不幸,因为十分凑巧的原因,p3 没有被回收,导致变成僵尸进程了,也没关系,
- 因为还有 p4 p5 嘛,等到 p4 或者 p5 结束的时候,
- 又会再一次调用 while(waitpid()),到时候虽说这个 while(waitpid()) 是由 p4/p5 引起的,但是它也会一并把 p3 也处理的,因为它是个循环嘛!
- 如果还搞不懂,你就再看看 waitpid 的 man。
- 记住一点:
- waitpid 和 SIGCHLD 没关系,即使是某个子进程对应的 SIGCHLD 丢失了,只要父进程在任何一个时刻调用了 waitpid,那么这个进程还是可以被回收的。
- 哎呀呀,简直费劲死了,其实说白了,就是一个“生产者-消费者”问题。
- 子进程结束的时候,系统“生产”出一个僵尸进程,
- 同时用 SIGCHLD 通知父进程来“消费”这个僵尸进程,
- 即使是 SIGCHLD 丢失了,没有来得及消费,
- 但是只要有一次消费,就会把所有的僵尸进程都处理光光!
- (我再说一遍:因为,while(waitpid()) 是个循环嘛!)
我试了一下,wait好像也可以哦
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
- #include <stdio.h>
-
- int main()
- {
- pid_t pc, pr;
- pc = fork();
- if(pc<0){
- printf("fork error\n");
- }else if(pc == 0){
- printf("child pid = %d\n", getpid());
- }else{
- pr = fork();
- if(pr == 0)
- printf("child pid = %d\n", getpid());
- else{
- printf("parent pid = %d\n", getpid());
- sleep(20);
- while((pr = wait(NULL)) != -1)
- ;
- printf("this is parent process.\n");
- sleep(20);
- }
- }
-
- exit(0);
- }
运行后,
程序先输出
child pid = 14478
parent pid = 14477
child pid = 14479
然后进入sleep(20); 这时候 开另一个terminal, 运行 ps auxw | grep 1447, 得到
1000 14477 0.0 0.0 4124 316 pts/3 S+ 05:36 0:00 ./wait
1000 14478 0.0 0.0 0 0 pts/3 Z+ 05:36 0:00 [wait]
1000 14479 0.0 0.0 0 0 pts/3 Z+ 05:36 0:00 [wait] 1000 14499 0.0 0.0 109244 868 pts/5 S+ 05:36 0:00 grep --color=auto 1447
可以看到,两个子进程已经编程僵尸进程
然后20秒后,父进程执行了wait() ,输出
child pid = 14478
parent pid = 14477
child pid = 14479
this is parent process.这时候,到另一个终端里,再次运行ps
1000 14477 0.0 0.0 4124 316 pts/3 S+ 05:36 0:00 ./wait
1000 14619 0.0 0.0 109244 872 pts/5 S+ 05:37 0:00 grep --color=auto 1447
可以看到两个僵尸子进程已经被回收。。
阅读(5354) | 评论(1) | 转发(3) |