您的位置:首页 > 其它

僵尸进程(zombie)以及避免方法

2012-01-21 12:55 337 查看
僵尸进程在多进程环境下,父进程要长时间运行 ,期间可能创建子进程,然后子进程有
退出时,但是父进程还在运行,这时就产生了zombie进程。

子进程退出后,在进程表中还要占一项,并且子进程的一些资源等待父进程回收。如果父进
程没有显示地调用wait来为子进程回收资源的话,在父进程退出之前子进程就变成了僵尸进
程。如果父进程退出了,僵尸子进程也消失了。

还有一种避免出现僵尸的方法就是调用signal(SIGCHLD, SIG_IGN), 忽略掉SIG_IGN信号。
忽略掉以后退出的子进程会自动交给init进程处理,这个时候没有必要再调用wait或waitpid了。
如果调用,这个两个函数也会执行失败,出错信息是No child process

当然还有其他办法,比如两次fork创建子进程,使孙子进程的父进程为init,就不一一列举了。

以下是从xiaosuo的blog转载过来的,绝对经典,放在这里方便以后查阅吧,站在巨人的肩上向前辈致敬!

POSIX线程库的API中有分离线程的API:

#include
<pthread.h>

int pthread_detach(pthread_t thread);
int pthread_attr_setdetachstate (pthread_attr_t *__attr,

int __detachstate)

如果一个线程调用了这个函数,那么当这个线程终止的时候,和它相关的系统资源将被自动释放,系统不用也不能用pthread_join()等待其退出。有的时候分离线程更好些,因为它潜在地减少了一个线程回收的同步点,并且pthread_join()这个API确实也是相当地难用。

一般情况下进程终止的时候,和它相关的系统资源也并不是主动释放的,而是进入一种通常称为“僵尸”(zombie)的状态。它所占有的资源一直被系统保留,直到它的父进程(如果它直接的父进程先于它去世,那么它将被init进程所收养,这个时候init就是它的父进程)显式地调用wait系列函数为其“收尸”。为了让父进程尽快知道它去世的消息,它会在它死去的时候通过向父进程发送SIGCHLD信号的方式向其“报丧”,父进程通常安装有类似下面的SIGCHLD信号处理函数:

void reap_zombie(int signo)

{

int errno_old
= errno;

while (waitpid(-1,
NULL, WNOHANG)
> 0)

;

errno = errno_old;

}
如果只是为了为子进程“收尸”,以上代码足矣!另一方面,如果父进程对子进程的终止状态没有任何兴趣,那么以上代码无疑又会引入无谓的复杂度,并且还有潜在的同步瓶颈。如果进程能够像处于分离状态的线程那样死得干干净净,那就完美了!

POSIX标准为我们考虑地可谓十分周到,下面是sigaction(2)的手册页中的一段话:

POSIX.1-1990 disallowed setting the action for SIGCHLD to SIG_IGN. POSIX.1-2001 allows this possibility,

so that ignoring SIGCHLD can be used to prevent the creation of zombies (see wait(2)). Nevertheless, the

historical BSD and System V behaviours for ignoring SIGCHLD differ, so that the only completely portable

method of ensuring that terminated children do not become zombies is to catch the SIGCHLD signal and per-

form a wait(2) or similar.
从中我们可以得知POSIX.1-2001允许将SIGCHLD的信号处理函数设置为SIG_IGN,这时子进程退出的时候将不会进入僵尸状态,并且它所占用的系统资源将被操作系统自动回收:

signal(SIGCHLD,
SIG_IGN);
除此之外,你还可以通过将sigaction调用中的flags与上SA_NOCLDWAIT来分离子进程:

struct
sigaction act;

memset(&act, 0,
sizeof(act));

act.sa_flags
|= SA_NOCLDWAIT;

sigaction(SIGCHLD,
&act,
NULL);
以下是我为此所作的测试的代码:

#include
<sys/types.h>

#include
<sys/wait.h>

#include
<errno.h>

#include
<unistd.h>

#include
<signal.h>

#include
<stdlib.h>

#include
<stdio.h>

#include
<string.h>

void reap_zombie(int signo)

{

int errno_old
= errno;

while (waitpid(-1,
NULL, WNOHANG)
> 0)

;

errno = errno_old;

}

int main(int argc,
char *argv[])

{

pid_t pid;

if (argc
== 2)
{

if (strcmp(argv[1],
"signal")
== 0)

signal(SIGCHLD,
SIG_IGN);

else if
(strcmp(argv[1],
"handle")
== 0)

signal(SIGCHLD, reap_zombie);

else {

struct
sigaction act;

memset(&act, 0,
sizeof(act));

act.sa_flags
|= SA_NOCLDWAIT;

sigaction(SIGCHLD,
&act,
NULL);

}

}

pid = fork();

if (pid
== 0)
{

fprintf(stderr,
"I am child\n");

sleep(1);

fprintf(stderr,
"Child exits\n");

return
EXIT_SUCCESS;

}

sleep(2);

system("ps -el | grep t_process");

wait(NULL);

perror("wait");

return EXIT_SUCCESS;

}
代码的实际运行情况如下所示:

[xiaosuo@Ulard-iolo ~]$ uname -a

Linux Ulard-iolo 2.6.20-1.2316.fc5 #1 Fri Apr 27 19:49:38 EDT 2007 i686 i686 i386 GNU/Linux

[xiaosuo@Ulard-iolo ~]$ gcc t_process_detach.c -o t_process_detach

[xiaosuo@Ulard-iolo ~]$ ./t_process_detach

I am child

Child exits

0 S 506 17595 16594 0 78 0 - 361 wait pts/1 00:00:00 t_process_detac

1 Z 506 17596 17595 0 78 0 - 0 exit pts/1 00:00:00 t_process_detac <defunct>

wait: Success

[xiaosuo@Ulard-iolo ~]$ ./t_process_detach signal

I am child

Child exits

0 S 506 17602 16594 0 75 0 - 360 wait pts/1 00:00:00 t_process_detac

wait: No child processes

[xiaosuo@Ulard-iolo ~]$ ./t_process_detach handle

I am child

Child exits

0 S 506 17609 16594 0 75 0 - 361 wait pts/1 00:00:00 t_process_detac

wait: No child processes

[xiaosuo@Ulard-iolo ~]$ ./t_process_detach sigaction

I am child

Child exits

0 S 506 17616 16594 0 75 0 - 360 wait pts/1 00:00:00 t_process_detac

wait: No child processes
可见,我们的目的达到了。

注意:在比较老的系统上,以上技巧可能并不可用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: