linux僵尸进程和孤儿进程

2024-09-01 09:32
文章标签 linux 进程 僵尸 孤儿

本文主要是介绍linux僵尸进程和孤儿进程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

孤儿进程与僵尸进程[总结]

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 }
复制代码

测试结果如下图所示:

这篇关于linux僵尸进程和孤儿进程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1126557

相关文章

Linux使用fdisk进行磁盘的相关操作

《Linux使用fdisk进行磁盘的相关操作》fdisk命令是Linux中用于管理磁盘分区的强大文本实用程序,这篇文章主要为大家详细介绍了如何使用fdisk进行磁盘的相关操作,需要的可以了解下... 目录简介基本语法示例用法列出所有分区查看指定磁盘的区分管理指定的磁盘进入交互式模式创建一个新的分区删除一个存

Linux使用dd命令来复制和转换数据的操作方法

《Linux使用dd命令来复制和转换数据的操作方法》Linux中的dd命令是一个功能强大的数据复制和转换实用程序,它以较低级别运行,通常用于创建可启动的USB驱动器、克隆磁盘和生成随机数据等任务,本文... 目录简介功能和能力语法常用选项示例用法基础用法创建可启动www.chinasem.cn的 USB 驱动

高效管理你的Linux系统: Debian操作系统常用命令指南

《高效管理你的Linux系统:Debian操作系统常用命令指南》在Debian操作系统中,了解和掌握常用命令对于提高工作效率和系统管理至关重要,本文将详细介绍Debian的常用命令,帮助读者更好地使... Debian是一个流行的linux发行版,它以其稳定性、强大的软件包管理和丰富的社区资源而闻名。在使用

Linux Mint Xia 22.1重磅发布: 重要更新一览

《LinuxMintXia22.1重磅发布:重要更新一览》Beta版LinuxMint“Xia”22.1发布,新版本基于Ubuntu24.04,内核版本为Linux6.8,这... linux Mint 22.1「Xia」正式发布啦!这次更新带来了诸多优化和改进,进一步巩固了 Mint 在 Linux 桌面

LinuxMint怎么安装? Linux Mint22下载安装图文教程

《LinuxMint怎么安装?LinuxMint22下载安装图文教程》LinuxMint22发布以后,有很多新功能,很多朋友想要下载并安装,该怎么操作呢?下面我们就来看看详细安装指南... linux Mint 是一款基于 Ubuntu 的流行发行版,凭借其现代、精致、易于使用的特性,深受小伙伴们所喜爱。对

什么是 Linux Mint? 适合初学者体验的桌面操作系统

《什么是LinuxMint?适合初学者体验的桌面操作系统》今天带你全面了解LinuxMint,包括它的历史、功能、版本以及独特亮点,话不多说,马上开始吧... linux Mint 是一款基于 Ubuntu 和 Debian 的知名发行版,它的用户体验非常友好,深受广大 Linux 爱好者和日常用户的青睐,

python多进程实现数据共享的示例代码

《python多进程实现数据共享的示例代码》本文介绍了Python中多进程实现数据共享的方法,包括使用multiprocessing模块和manager模块这两种方法,具有一定的参考价值,感兴趣的可以... 目录背景进程、进程创建进程间通信 进程间共享数据共享list实践背景 安卓ui自动化框架,使用的是

Linux(Centos7)安装Mysql/Redis/MinIO方式

《Linux(Centos7)安装Mysql/Redis/MinIO方式》文章总结:介绍了如何安装MySQL和Redis,以及如何配置它们为开机自启,还详细讲解了如何安装MinIO,包括配置Syste... 目录安装mysql安装Redis安装MinIO总结安装Mysql安装Redis搜索Red

Linux中Curl参数详解实践应用

《Linux中Curl参数详解实践应用》在现代网络开发和运维工作中,curl命令是一个不可或缺的工具,它是一个利用URL语法在命令行下工作的文件传输工具,支持多种协议,如HTTP、HTTPS、FTP等... 目录引言一、基础请求参数1. -X 或 --request2. -d 或 --data3. -H 或

Linux磁盘分区、格式化和挂载方式

《Linux磁盘分区、格式化和挂载方式》本文详细介绍了Linux系统中磁盘分区、格式化和挂载的基本操作步骤和命令,包括MBR和GPT分区表的区别、fdisk和gdisk命令的使用、常见的文件系统格式以... 目录一、磁盘分区表分类二、fdisk命令创建分区1、交互式的命令2、分区主分区3、创建扩展分区,然后