作者:wuxinliulei
链接:https://www.zhihu.com/question/29378056/answer/70645585
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
#include <stdio.h>
#include <unistd.h>
int main()
{
int pid = fork();
if ( pid > 0 )
{
while ( 1 )
{
sleep(3);
}
}
else if ( pid == 0 )
{
printf("i die\n");
}
}
上述程序中,fork了一个子进程,并且子进程很快就退出。父进程持续进行sleep,这样子进程就变成了僵尸进程。利用ps命令可以清晰的看到这一点。
[leconte@localhost ~]$ps axu |greptest
leconte 32600 0.0 0.0 1516 268 pts/1 S+ 20:43 0:00 ./test
leconte 32601 0.0 0.0 0 0 pts/1 Z+ 20:43 0:00 [test] <defunct>
leconte 32603 0.0 0.1 5024 668 pts/2 R+ 20:43 0:00 grep test
Z+代表进程32601是僵尸进程。
僵尸进程和孤儿进程的区别是,孤儿进程是子进程还在运行,而父进程挂了,子进程被init进程收养。僵尸进程是父进程还在运行但是子进程挂了,但是父进程却没有使用wait来清理子进程的进程信息,导致子进程虽然运行实体已经消失,但是仍然在内核的进程表中占据一条记录,这样长期下去对于系统资源是一个浪费。
这时候杀掉父进程即可清理该僵尸进程。当然这并非长久之计,根本的办法是从代码上进行控制,防止类似的事情再发生。常见的方法无非是处理父进程的SIGCHLD信号,在信号处理函数里用wait或者waitpid等函数进行处理。更改后的代码如下:
#include <stdio.h>
#include <unistd.h>
#include <wait.h>
void* handler(int sig)
{
int status;
if (waitpid(-1, &status, WNOHANG) >= 0)
{
printf("child is die,i know\n");
}
}
int main()
{
signal(SIGCHLD,handler);
int pid = fork();
if ( pid > 0 )
{
while ( 1 )
{
sleep(3);
}
}
else if ( pid == 0 )
{
printf("i die\n");
}
}
这样更改后执行的输出如下:
[leconte@localhost test]$ ./test
i die
child is die,i know
可见父进程已经成功地处理了子进程的退出信号,子进程彻彻底底的消失了。