僵尸进程与孤儿进程学习

原文:http://www.cnblogs.com/Anker/p/3271773.html

  先总结一下吧,详情见看下面的文章。

概念:

   孤儿进程,就是父进程先退出,子进程必然会成为孤儿,孤儿会被init收养。

   僵尸进程,子进程死后,父进程没有调用wait或waitpid为子进程进程善后,因而子进程将成为僵尸。

僵尸进程危害:

    僵尸进程将会占用一定的系统资源、进程号,大量的僵尸进程将会导致系统进程号用光,不能再创建新的进程。

解决方法:

   1、使用信号量,当子进程退出后,使用信号量通知父进程,释放进程资源。

   2、使用fork两次,父进程  A   fork出子进程B     B fork出子进程C  , 然后B进程退出,使C成为孤儿进程,因而进程C将不会成为僵尸进程,C将会有init领养,在C退出后,init将会进行善后工作。   



1、前言

  之前在看《unix环境高级编程》第八章进程时候,提到孤儿进程和僵尸进程,一直对这两个概念比较模糊。今天被人问到什么是孤儿进程和僵尸进程,会带来什么问题,怎么解决,我只停留在概念上面,没有深入,倍感惭愧。晚上回来google了一下,再次参考APUE,认真总结一下,加深理解。

2、基本概念

  我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。

  孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

  僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。



3、问题及危害

  unix提供了一种机制可以保证只要父进程想知道子进程结束时的状态信息, 就可以得到。这种机制就是: 在每个进程退出的时候,内核释放该进程所有的资源,包括打开的文件,占用的内存等。 但是仍然为其保留一定的信息(包括进程号the process ID,退出状态the termination status of the process,运行时间the amount of CPU time taken by the process等)。直到父进程通过wait / waitpid来取时才释放。 但这样就导致了问题,如果进程不调用wait / waitpid的话, 么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵死进程,将因为没有可用的进程号而导致系统不能产生新的进程. 此即为僵尸进程的危害,应当避免。

  孤儿进程是没有父进程的进程,孤儿进程这个重任就落到了init进程身上,init进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤 儿进程的父进程设置为init,而init进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,init进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。

  任何一个子进程(init除外)在exit()之后,并非马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程处理。这是每个 子进程在结束时都要经过的阶段。如果子进程在exit()之后,父进程没有来得及处理,这时用ps命令就能看到子进程的状态是“Z”。如果父进程能及时 处理,可能用ps命令就来不及看到子进程的僵尸状态,但这并不等于子进程不经过僵尸状态。  如果父进程在子进程结束之前退出,则子进程将由init接管。init将会以父进程的身份对僵尸状态的子进程进行处理。

  僵尸进程危害场景:

  例如有个进程,它定期的产 生一个子进程,这个子进程需要做的事情很少,做完它该做的事情之后就退出了,因此这个子进程的生命周期很短,但是,父进程只管生成新的子进程,至于子进程 退出之后的事情,则一概不闻不问,这样,系统运行上一段时间之后,系统中就会存在很多的僵死进程,倘若用ps命令查看的话,就会看到很多状态为Z的进程。 严格地来说,僵死进程并不是问题的根源,罪魁祸首是产生出大量僵死进程的那个父进程。因此,当我们寻求如何消灭系统中大量的僵死进程时,答案就是把产生大 量僵死进程的那个元凶枪毙掉(也就是通过kill发送SIGTERM或者SIGKILL信号啦)。枪毙了元凶进程之后,它产生的僵死进程就变成了孤儿进 程,这些孤儿进程会被init进程接管,init进程会wait()这些孤儿进程,释放它们占用的系统进程表中的资源,这样,这些已经僵死的孤儿进程 就能瞑目而去了。

3、孤儿进程和僵尸进程测试

孤儿进程测试程序如下所示:

复制代码
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <errno.h>
 4 #include <unistd.h>
 5 
 6 int main()
 7 {
 8     pid_t pid;
 9     //创建一个进程
10     pid = fork();
11     //创建失败
12     if (pid < 0)
13     {
14         perror("fork error:");
15         exit(1);
16     }
17     //子进程
18     if (pid == 0)
19     {
20         printf("I am the child process.\n");
21         //输出进程ID和父进程ID
22         printf("pid: %d\tppid:%d\n",getpid(),getppid());
23         printf("I will sleep five seconds.\n");
24         //睡眠5s,保证父进程先退出
25         sleep(5);
26         printf("pid: %d\tppid:%d\n",getpid(),getppid());
27         printf("child process is exited.\n");
28     }
29     //父进程
30     else
31     {
32         printf("I am father process.\n");
33         //父进程睡眠1s,保证子进程输出进程id
34         sleep(1);
35         printf("father process is  exited.\n");
36     }
37     return 0;
38 }
复制代码

测试结果如下:

僵尸进程测试程序如下所示:

复制代码
 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <errno.h>
 4 #include <stdlib.h>
 5 
 6 int main()
 7 {
 8     pid_t pid;
 9     pid = fork();
10     if (pid < 0)
11     {
12         perror("fork error:");
13         exit(1);
14     }
15     else if (pid == 0)
16     {
17         printf("I am child process.I am exiting.\n");
18         exit(0);
19     }
20     printf("I am father process.I will sleep two seconds\n");
21     //等待子进程先退出
22     sleep(2);
23     //输出进程信息
24     system("ps -o pid,ppid,state,tty,command");
25     printf("father process is exiting.\n");
26     return 0;
27 }
复制代码

测试结果如下所示:

僵尸进程测试2:父进程循环创建子进程,子进程退出,造成多个僵尸进程,程序如下所示:

复制代码
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <errno.h>
 5 
 6 int main()
 7 {
 8     pid_t  pid;
 9     //循环创建子进程
10     while(1)
11     {
12         pid = fork();
13         if (pid < 0)
14         {
15             perror("fork error:");
16             exit(1);
17         }
18         else if (pid == 0)
19         {
20             printf("I am a child process.\nI am exiting.\n");
21             //子进程退出,成为僵尸进程
22             exit(0);
23         }
24         else
25         {
26             //父进程休眠20s继续创建子进程
27             sleep(20);
28             continue;
29         }
30     }
31     return 0;
32 }
复制代码

程序测试结果如下所示:

4、僵尸进程解决办法

(1)通过信号机制

  子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵尸进程。测试程序如下所示:

复制代码
 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <errno.h>
 4 #include <stdlib.h>
 5 #include <signal.h>
 6 
 7 static void sig_child(int signo);
 8 
 9 int main()
10 {
11     pid_t pid;
12     //创建捕捉子进程退出信号
13     signal(SIGCHLD,sig_child);
14     pid = fork();
15     if (pid < 0)
16     {
17         perror("fork error:");
18         exit(1);
19     }
20     else if (pid == 0)
21     {
22         printf("I am child process,pid id %d.I am exiting.\n",getpid());
23         exit(0);
24     }
25     printf("I am father process.I will sleep two seconds\n");
26     //等待子进程先退出
27     sleep(2);
28     //输出进程信息
29     system("ps -o pid,ppid,state,tty,command");
30     printf("father process is exiting.\n");
31     return 0;
32 }
33 
34 static void sig_child(int signo)
35 {
36      pid_t        pid;
37      int        stat;
38      //处理僵尸进程
39      while ((pid = waitpid(-1, &stat, WNOHANG)) >0)
40             printf("child %d terminated.\n", pid);
41 }
复制代码

测试结果如下所示:

(2)fork两次
  《Unix 环境高级编程》8.6节说的非常详细。原理是将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵尸进程。测试程序如下所示:

复制代码
 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <errno.h>
 5 
 6 int main()
 7 {
 8     pid_t  pid;
 9     //创建第一个子进程
10     pid = fork();
11     if (pid < 0)
12     {
13         perror("fork error:");
14         exit(1);
15     }
16     //第一个子进程
17     else if (pid == 0)
18     {
19         //子进程再创建子进程
20         printf("I am the first child process.pid:%d\tppid:%d\n",getpid(),getppid());
21         pid = fork();
22         if (pid < 0)
23         {
24             perror("fork error:");
25             exit(1);
26         }
27         //第一个子进程退出
28         else if (pid >0)
29         {
30             printf("first procee is exited.\n");
31             exit(0);
32         }
33         //第二个子进程
34         //睡眠3s保证第一个子进程退出,这样第二个子进程的父亲就是init进程里
35         sleep(3);
36         printf("I am the second child process.pid: %d\tppid:%d\n",getpid(),getppid());
37         exit(0);
38     }
39     //父进程处理第一个子进程退出
40     if (waitpid(pid, NULL, 0) != pid)
41     {
42         perror("waitepid error:");
43         exit(1);
44     }
45     exit(0);
46     return 0;
47 }
复制代码

测试结果如下图所示:

<think>好的,用户想了解Linux或Unix系统中僵尸进程孤儿进程的区别。首先,我需要回忆这两个概念的基本定义。 僵尸进程是已经终止但父进程尚未通过wait()系统调用获取其退出状态的进程。这时候,进程描述符仍然保留在系统中,导致资源泄漏。记得引用中的说明,如果父进程不调用wait(),就会产生僵尸进程[^2]。僵尸进程无法被kill命令终止,因为它们已经死了,只能等待父进程处理。 孤儿进程则是父进程已经终止或退出,而子进程仍在运行的进程。这时候,init进程(PID 1)会接管这些子进程,成为它们的新父进程。这样,孤儿进程最终会被init回收,不会长期占用资源。例如,用户提到的引用[1]中的例子,父进程先于子进程退出,子进程就变成了孤儿进程,由init接管[^1]。 接下来需要比较两者的生命周期和影响。僵尸进程会一直存在直到父进程调用wait(),如果父进程不处理,就会一直残留,占用系统资源。而孤儿进程只是暂时存在,被init接管后会被正常回收,不会造成长期问题。 另外,处理方式也不同。解决僵尸进程需要父进程正确处理子进程的退出状态,或者终止父进程,让init来回收。而孤儿进程则由系统自动处理,无需用户干预。 可能还需要提到如何检测这两种进程。使用ps命令查看状态,僵尸进程显示为Z,而孤儿进程的父进程ID会变为1。 用户可能还会关心实际应用中的例子,比如编写代码时,如果父进程没有正确等待子进程,就会产生僵尸进程。而如果父进程意外终止,子进程变成孤儿进程,由init接管。 最后,总结两者的主要区别:产生原因、处理方式、对系统的影响以及如何预防和解决。需要确保回答清晰,结构合理,符合用户的需求。</think>### 僵尸进程孤儿进程的区别 #### 1. **僵尸进程(Zombie Process)** - **定义**: 子进程已终止但未被父进程通过`wait()`系统调用回收退出状态,此时进程描述符仍保留在系统中。 - **特征**: - 状态为`Z`(通过`ps`命令查看) - 无法被终止(已处于终止状态) - 占用少量系统资源(如进程ID) - **产生原因**: 父进程未调用`wait()`或`waitpid()`,导致操作系统无法释放子进程资源。 - **解决方法**: - 修改父进程代码,添加`wait()`调用 - 终止父进程僵尸进程会被`init`进程接管并回收) --- #### 2. **孤儿进程(Orphan Process)** - **定义**: 父进程先于子进程终止,子进程被`init`进程(PID=1)接管。 - **特征**: - 仍在运行中 - 父进程ID变为`1` - **产生原因**: 父进程意外终止(如崩溃)或主动退出但未等待子进程[^1]。 - **系统处理**: `init`进程自动回收孤儿进程,无需人工干预。 --- #### 3. **核心区别** | 特征 | 僵尸进程 | 孤儿进程 | |--------------|------------------------------|------------------------------| | **状态** | 已终止(`Z`状态) | 仍在运行 | | **父进程** | 原父进程未调用`wait()` | 原父进程终止,由`init`接管 | | **资源占用** | 少量资源泄漏 | 无长期影响 | | **解决方案** | 需父进程处理或终止父进程 | 系统自动处理 | --- #### 4. **代码示例** ```c // 产生僵尸进程的示例 #include <unistd.h> int main() { if (fork() == 0) { // 子进程立即退出 return 0; } else { // 父进程不调用wait(),睡眠30秒 sleep(30); } return 0; } ``` 运行后通过`ps aux | grep Z`可观察到僵尸进程。 --- #### 5. **实际影响** - **僵尸进程**: 长期存在会导致进程ID耗尽(极端情况下)[^2]。 - **孤儿进程**: 对系统无危害,常用于守护进程的实现[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值