Chinaunix首页 | 论坛 | 博客
  • 博客访问: 802727
  • 博文数量: 186
  • 博客积分: 10075
  • 博客等级: 上将
  • 技术积分: 2776
  • 用 户 组: 普通用户
  • 注册时间: 2006-04-06 13:18
文章存档

2012年(2)

2011年(1)

2010年(15)

2009年(56)

2008年(112)

分类:

2008-09-27 16:13:06

很老的话题了,最近用到了,所以记下来。
 
所谓僵尸进程,即Zombie进程。当一个进程已经结束,但是系统没有把它的进程的数据结构完全释放,此时用ps 察看它的状态是defunt。 僵尸进程占据进程表的空间,而且不能被kill掉因为它已经死了,所以在开发多进程尤其是守护进程时注意要避免产生僵尸进程。
 
僵尸进程的产生原因是什么呢?
当UNIX系统中1个进程结束,init即1号进程会检查该进程有没有子进程,如果有,init就会接管这些子进程,把这些子进程的ppid改成1。
同时init会用信号SIGCHLD通知该进程的父进程。如果父进程用wait()或者waitpid()来获取子进程的状态的话,那么进程的相关状态数据返给父进程之后,该进程自身将被销毁。
那么考虑一种情况,如果父进程没有使用wait或waitpid呢? 此时因为子进程的状态数据没有被取走,所以该子进程即变成了僵尸进程。
 
解决方法通常有3种
1)2次fork
父进程
    pid_t pid;
    switch ( pid = fork() ) {
       case -1:           break;
       case 0:
         execl("xxx", "xxx", (char*)0,(char*)0);
         exit(-1);
       default:
         waitpid(pid, NULL, 0); //we wait to avoid zombie
         break;
子进程 xxx
    pid_t pid;
    switch ( pid = fork() ) {
       case -1:           break;
       case 0:
         /* 做子进程自己的动作 */
         sleep(1); // sleep 1 second to let the parent go firstly
         exit(-1);
       default:
         break;
     }
 
可以看到,子进程中又fork了一次,产生的孙进程做实际的操作,而子进程迅速结束返回,这样父进程可以快速回收。而孙进程将被init进程接管。
 
2)捕获SIGCHLD,然后waitpid
使用sigaction()为SIGCHLD安装一个handler,然后在handler里使用waitpid
static void sig_handler(int signo) {
  int stat;
  while(waitpid(-1,&stat,WNOHANG)>0)
  ;
  return;
}
为什么用while是因为可能有多个子进程,而多个SIGCHLD并不会排队。
不要用signal()函数来设handler,因为那不可靠。
 
当然如果父进程可以阻塞执行,等待子进程完成,那么不用安装信号,直接waitpid即可。通常不会这么做。
 
3)设置SIGCHLD的动作为 SA_NOCLDWAIT
使用sigaction()对SIGCHLD的flag设置 SA_NOCLDWAIT,通知系统不产生僵尸。
该方法没有试过。
阅读(826) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~