今天在做一个监视程序:创建一个进程,在父进程中死循环的监视着多个事件,而在子进程中只是打开一个输入、输出、错误的句柄后就退出了。呵呵,程序运行后,用ps -aux查看,居然出现好几个僵尸进程。后来打开程序一看,竟然发现子进程退出后,父进程没有给进程收尸。我们再看看下面这段话,会让我们更好的理解僵尸进程的产生及防范:
在fork()/execve()过程中,假设子进程结束时父进程仍存在,而父进程fork()之前既没安装SIGCHLD 信号处理函数调用waitpid()等待子进程结束,又没有显式忽略该信号,则子进程成为僵尸进程,无法正常结束,此时即使是root身份kill-9也 不能杀死僵尸进程。补救办法是杀死僵尸进程的父进程(僵尸进程的父进程必然存在),僵尸进程成为"孤儿进程",过继给1号进程init,init始终会负 责清理僵尸进程。
怎样来清除僵尸进程:
1.改写父进程,在子进程死后要为它收尸。具体做法是接管SIGCHLD信号。子进程死后,会发送SIGCHLD信号给父进程,父进程收到此信号后,执行waitpid()函数为子进程收尸。这是基于这样的原理:就算父进程没有调用wait,内核也会向它发送SIGCHLD消息,尽管对的默认处理是忽略,如果想响应这个消息,可以设置一个处理函数。
2.把父进程杀掉。父进程死后,僵尸进程成为"孤儿进程",过继给1号进程init,init始终会负责清理僵尸进程.它产生的所有僵尸进程也跟着消失。
3.调用fork两次。
从上面两段话,我们基本上能明白了僵尸进程的产生及消除了,下面我们来模拟一下僵尸进程的产生:
代码如下:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
if (0 == fork()) {
printf("In the child process:%d\n", getpid());
} else {
printf("In the parent process:%d\n", getpid());
sleep(10);
exit(0);
}
printf("Pid:%d\n", getpid());
return 0;
}
运行结果:
In the child process:3682
Pid:3682
In the parent process:3681
在程序没有结束之前,我们可以开另一个终端运行ps -aux命令查看,我们可以发现在该程序的旁边还会产生一个其STAT为Z+的僵尸进程,这就是在父进程没有结束之前,子进程已经结束了,且父进程没有回收子进程就会产生僵尸进程了。
下面我们看看父进程怎么接收子进程,即在上面的情况下怎么消除僵尸进程:
代码如下:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include<sys/wait.h>
void proc_child(int SIGNO)
{
int pid = -1;
int stat;
pid = waitpid(-1, &stat, WNOHANG);
}
int main(int argc, char **argv)
{
signal(SIGCHLD, proc_child);
if (0 == fork()) {
printf("In the child process:%d\n", getpid());
} else {
printf("In the parent process:%d\n", getpid());
sleep(10);
exit(0);
}
printf("Pid:%d\n", getpid());
return 0;
}
运行结果:
In the child process:3682
Pid:3682
In the parent process:3681
从上面的代码中看出就前一份代码多了一个signal()函数,signal()调用proc_child()函数就是为了等待子进程结束后发送SIGCHLD给父进程,并接收。这采用的是第1种方法。当然,还有这种方法:
代码如下:
int main(int argc, char **argv)
{
signal(SIGCHLD, SIG_IGN); //加入此句即可,忽略SIGCHLD信号
if (0 == fork()) {
printf("In the child process:%d\n", getpid());
} else {
printf("In the parent process:%d\n", getpid());
sleep(10);
exit(0);
}
printf("Pid:%d\n", getpid());
return 0;
}
网上有网友做过测试,有下面的说法:
在比较繁忙的应用中使用WAIT也无法解决僵尸进程。以前有经历在涉及数据库的程序中,当数据库异常时会出现不正常的僵尸进程,而程序中实际上已经使用了wait命令,并且捕捉了SIGCLD信号。系统很忙时估计SIGCLD信号传递不正常。
--
/**************************************/
Name: Xiong Feng
E-mail:linux0818@gmail.com
MSN:linux0818@hotmail.com
QQ:23562033
Address: GuangZhou.China
/**************************************/
没有评论:
发表评论