第十九篇:处理僵尸进程的两种经典方法

前言

如果父进程没有结束,而子进程终止了。那么在父进程调用 wait 函数回收这个子进程或者父进程终止以前,这个子进程将一直是僵尸进程。

本文将提供两种方法处理这个问题。

方法一:父进程回收法

wait函数将使其调用者阻塞,直到其某个子进程终止。故父进程可调用wait函数回收其僵尸子进程。除此之外,waitpid函数提供更为详尽的功能( 增加了非阻塞功能以及指定等待功能 ),请读者自行查阅相关资料。

代码实现

 1 #include <unistd.h>
 2 #include <sys/wait.h>
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5
 6 int main()
 7 {
 8     int pid;
 9     int *status;
10
11     printf("%s\n", "启动父进程");
12
13     if ((pid = fork()) < 0) {
14         printf("%s\n", "创建子进程失败");
15         exit(1);
16     }
17     else
18         if (pid ==0) {
19             printf("%s\n", "进入子进程");
20             sleep(4);
21             // 终止子进程
22             exit(0);
23         }
24     else {
25         // 进入父进程
26         // 回收僵尸子子进程
27         wait(status);
28         printf("%s\n", "回收完毕");
29     }
30
31     exit(0);
32 }

运行测试

结果分析

第三行的“回收完毕”是在程序执行四秒后才显示的。这说明尽管我将子进程阻塞了4秒,父进程并不会先于子进程终止。因为它调用了wait函数,故需要等待一个子进程结束并将其回收,否则就一直阻塞在那里。

方法二:init进程回收法

上面的这种解决方案需要父进程去等待子进程,但在很多情况下,这并不合适,因为父进程也许还有其他任务要做,不能阻塞在这里。在讲述下面这种不用父进程等待就能完成回收子进程的方法之前,先请明白以下两个概念:

1. 如果父进程先于子进程结束,那么子进程的父进程自动改为 init 进程。

2. 如果 init 的子进程结束,则 init 进程会自动回收其子进程的资源而不是让它变成僵尸进程。

代码实现

 1 #include "apue.h"
 2 #include <sys/wait.h>
 3
 4 int
 5 main(void)
 6 {
 7     pid_t    pid;
 8
 9     if ((pid = fork()) < 0) {    // 创建第一个子进程
10         err_sys("fork error");
11     } else if (pid == 0) {    // 进入第一个子进程
12         if ((pid = fork()) < 0)    // 创建第二个子进程
13             err_sys("fork error");
14         else if (pid > 0) // 进入第一个子进程
15             exit(0);    // 终止第一个子进程
16         // 第二个子进程在睡眠2S后才执行,这样一般情况下第一个子进程会先终止。
17         sleep(2);
18         // 这时,第一个子进程肯定终止了,那么它的父进程就自动变成了init。
19         printf("second child, parent pid = %d\n", getppid());
20         exit(0);
21     }
22
23     // 父进程等待并回收第一个子进程
24     if (waitpid(pid, NULL, 0) != pid)
25         err_sys("waitpid error");
26
27     // 父进程执行到这里以后,可以退出,也可以执行其他的任务。
28     // 对于刚才那第二个子进程,它继承了父进程的资源,同时它终止后也会被init进程回收,
29     // 不会成为僵尸进程。
30     exit(0);
31 }

说明

1. fork创建子进程以后,子进程拥有的是父进程的一个资源副本,而不是和它共享资源。

2. 子进程终止后变成僵尸进程并不是系统BUG,而是因为子进程终止后,其一些信息操作系统或者用户以后还可能会用到。

时间: 2024-10-10 13:17:17

第十九篇:处理僵尸进程的两种经典方法的相关文章

Python之路(第三十九篇)管道、进程间数据共享Manager

一.管道 概念 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信. 先画一幅图帮助大家理解下管道的基本原理 现有2个进程A和B,他们都在内存中开辟了空间,那么我们在内存中再开辟一个空间C,作用是连接这两个进程的.对于进程来说内存空间是可以共享的(任何一个进程都可以使用内存,内存当中的空间是用地址来标记的,我们通过查找某一个地址就能找到这个内存)A进程可以不断的向C空间输送东西,B进程可以不断的从C空间读取东西,这

面试题----僵尸进程的四种处理方法

l  父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起 l  如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler,因为子进程结束后,父进程会收到该信号,可以在handler中调用wait回收 l  如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCHLD, SIG_IGN) 通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号 l  还有一些技巧,就是fork两次,父进程fork一个子进程

Python之路【第十九篇】:爬虫

Python之路[第十九篇]:爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁.自动索引.模拟程序或者蠕虫. Requests Python标准库中提供了:urllib.urllib2.httplib等模块以供Http请求,但是,它的 API 太渣了.它是为另一个时代.另一个互联网所创建的.它需要巨量的工作,甚至包括各种方法覆盖,来完成最简单的任务. import

Gradle 1.12翻译——第十九章. Gradle 守护进程

有关其他已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或访问:http://gradledoc.qiniudn.com/1.12/userguide/userguide.html 本文原创,转载请注明出处:http://blog.csdn.net/maosidiaoxian/article/details/41343615 关于我对Gradle的翻译,以Github上的项目及http://gradledoc.qin

第十九篇:USB Audio/Video Class设备协议

在Spring 3 中,对表单提交的校验方式比较常见的有两种 (1)基于注解:对于简单的属性校验,例如,长度,非空等,可以使用Spring,或是Hibernate自带的校验注解 (2)自定义校验类:对于复杂的业务校验可以自定义校验类,该类继承自org.springframework.validation.Validator (3)前台JS校验 增加表单校验功能大概分一下几个步骤 定义校验规则 基于注解的验证 只需要在相关的实体字段上添加校验注解即可,下面的例子基于hibernate的校验标签 i

Python开发【第十九篇】:Python操作MySQL

Python开发[第十九篇]:Python操作MySQL 本篇对于Python操作MySQL主要使用两种方式: 原生模块 pymsql ORM框架 SQLAchemy pymsql pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb几乎相同. 下载安装 ? 1 pip3 install pymysql 使用操作 1.执行SQL + ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

关于如何有效避免僵尸进程的一种探索。(动手写自己的http服务器)

关于如何避免僵尸进程.其实方法很多.有很多现成的解决方案.这里给出一个简单的方案.以及背后思考的过程. 设想一种最简单的http服务器: 伪代码: 当接收到一个tcp请求的时候. fork 一个子进程去做工作. 主进程依旧在监听下面要来的请求. 这个模型实际上会导致.因为fork出来的子进程没有回收,并且父进程一直存在没有结束.导致越来越多的僵尸进程出现. 都知道子进程结束的时候会发一个sigchld信号给父进程.于是改进成增加一行代码: signal(SIGCHLD,deal_signal);

python全栈脱产第34天------开启进程的两种方式、join方法、进程对象其他相关的属性和方法、僵尸进程、孤儿进程、守护进程、互斥锁

一.开启进程的两种方式 方式一: from multiprocessing import Processimport time def task(name): print('%s is running' %name) time.sleep(3) print('%s is done' %name) # 在windows系统上,开启子进程的操作必须放到if __name__ == '__main__'的子代码中if __name__ == '__main__': p=Process(target=t

AIR打开创建进程的两种方式

写在这里,方便查阅 NativeApplication.nativeApplication.autoExit = true;// 主窗体关闭也跟着关闭 Debug.trace('ToursLocalConnection :: appExePath = ' + appExePath);currFile = new File(appExePath);//currFile = new File("C:/Users/lenovo/Desktop/LZPC_Test/LZPC/uninstall.exe&