python进程之僵尸进程和孤儿进程

僵尸与孤儿进程解析
本文详细解释了僵尸进程和孤儿进程的概念,僵尸进程是父进程未处理已结束子进程的状态导致的问题;孤儿进程则是指父进程先于子进程结束的情况,这类进程会被init进程接管。文章还介绍了僵尸进程可能带来的危害以及如何避免。

概念

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

1. 僵尸进程

僵尸进程:(父进程没结束,子进程提前结束,父进程没有处理子进程的状态)(有害,应当避免)
一个进程使用fork创建子进程,如果子进程退出,而父进程没有调用wait或waitpid获取进程的状态信息,那么子进程的进程描述符仍保存在系统中,这种进程称为僵死进程。

2. 孤儿进程

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

详解

僵尸进程的危害

任何一个子进程(init除外)在exit()之后,并不是马上就消失掉,而是留下一个称为僵尸进程(Zombie)的数据结构,等待父进程去处理。这是每个子进程结束时都必须经过的阶段。如果子进程在exit()之后,父进程没有来的及处理,此时用ps命令查看状态的话就时Z状态。如果父进程能及时处理掉,则用ps就查看不到子进程的状态。

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

python中fork函数生产子进程

Python中os模块中有fork函数生成子进程,生成的子进程和父进程有各自的地址空间,子进程复制一份父进程内存给自己,其执行顺序可以是不确定的,随机的,不可预测的,这点和多线程执行顺序相似。


import os  

def child():  
    print 'A new child:', os.getpid()  
    print 'Parent id is:', os.getppid()  
    os._exit(0)  

def parent():  
    while True:  
        newpid=os.fork()  
        print newpid  
        if newpid==0:  
            child()  
        else:  
            pids=(os.getpid(),newpid)  
            print "parent:%d,child:%d"%pids  
            print "parent parent:",os.getppid()         
        if raw_input()=='q':  
            break  

parent()  

newpid = os.fork()
fork()函数生成了一个子进程,返回值newpid有俩个,一个为0,用以表示子进程,一个是大于0的整数,用以表示父进程,这个常数正是子进程的pid。如果返回一个负值,则表明子进程创建失败。if和else的执行顺序是不确定的,子、父进程的执行顺序由操作系统的调度算法来决定。

参考链接:
1. http://www.cnblogs.com/Anker/p/3271773.html
2. https://blog.youkuaiyun.com/caikehe/article/details/18770071

<think>嗯,用户这次的问题聚焦在Linux进程管理的三个核心概念:孤儿进程僵尸进程守护进程。从引用内容看,用户已经掌握了一些基础进程知识(如进程隔离、协程),但需要更系统的对比实际场景说明。 用户可能正在学习Linux系统编程或运维,需要理解这些特殊进程的成因处理逻辑。尤其关注“处理方法”,说明ta更倾向实用型知识。引用中提到的python进程示例协程特性,暗示用户有编程背景,回答时可适当关联编程场景。 孤儿进程的关键点在于init接管机制。引用[2]提到父进程无法预测子进程结束时间,这正是孤儿进程产生的根源——父进程先退出时,子进程失去父进程。需要强调Linux的init(或systemd)会自动收养这些孤儿进程,避免资源泄漏。处理上,用户只需确保父进程正常退出逻辑即可。 僵尸进程的核心问题是父进程未回收。引用[2]明确提到父进程必须调用wait/waitpid获取子进程终止状态,否则子进程残留的退出码会占据内核进程表slot。这里要区分“僵尸”孤儿”:僵尸进程的父进程还在运行但未尽责回收,孤儿进程的父进程已消失。处理方法除wait外,还可考虑信号机制(SIGCHLD)或双fork技巧。 守护进程的设计目标完全不同。引用[1]提到后台服务的概念,守护进程本质上是通过两次fork脱离终端控制的特殊孤儿进程。重点在于如何通过setsid创建新会话组、关闭文件描述符等步骤实现完全后台化。应用场景如httpd或sshd这类长期服务。 最后用表格对比三者的特征差异,并补充真实命令示例(如ps aux查看守护进程、kill处理僵尸进程)。用户引用过python进程,可提一句“在Python中创建守护进程需用daemon属性”,但避免展开代码以免偏离主题。 相关问题设计要延续深度:init如何回收孤儿进程的具体流程、僵尸进程占用资源的量化影响、systemd对守护进程管理的改进等。这些既能解答用户潜在疑问,也符合ta系统学习的意图。</think>### Linux 孤儿进程僵尸进程与守护进程详解 #### 一、核心概念与区别 | **进程类型** | **产生原因** | **特征** | **资源占用** | |--------------|------------------------------------------------------------------------------|--------------------------------------------------------------------------|--------------------| | **孤儿进程** | 父进程先于子进程终止 | 被 init/systemd 进程(PID=1)接管<sup>[1][2]</sup> | 正常回收 | | **僵尸进程** | 子进程终止后,父进程未调用 `wait()`/`waitpid()` 回收退出状态<sup>[2]</sup> | 进程描述符滞留内核进程表(`<defunct>` 状态) | 占用进程表项 | | **守护进程** | 主动脱离终端控制(通过两次 `fork()`) | 无控制终端、会话首进程、后台运行、生命周期与系统一致<sup>[1]</sup> | 独立运行 | > 📌 **关键区别**: > - 孤儿进程是**活进程**(仍在运行),僵尸进程是**已终止但未回收**的进程 > - 守护进程是**人为设计**的后台服务进程 #### 二、处理方法与命令 1. **孤儿进程处理** - **系统自动处理**: ```bash # 示例:父进程退出后子进程被 init 接管 $ ps -ef | grep "孤儿进程名" # 查看 PPID=1 ``` - **无需人工干预**:init 进程会自动回收资源<sup>[1]</sup> 2. **僵尸进程清除** - **方法1**:通知父进程回收 ```bash $ kill -SIGCHLD <父进程PID> # 发送信号触发 wait() ``` - **方法2**:终止父进程(极端情况) ```bash $ kill -9 <父进程PID> # 父进程终止后僵尸由 init 回收 ``` - **检测命令**: ```bash $ ps aux | grep 'Z' # 状态列为 Z 的是僵尸进程 ``` 3. **守护进程创建** **标准步骤**(C语言示例): ```c pid_t pid = fork(); if (pid > 0) exit(0); // 1. 父进程退出 setsid(); // 2. 创建新会话 chdir("/"); // 3. 切换根目录 umask(0); // 4. 重置文件权限掩码 close(STDIN_FILENO); // 5. 关闭标准流 close(STDOUT_FILENO); close(STDERR_FILENO); ``` > Python 实现更简单: > ```python > from daemon import DaemonContext > with DaemonContext(): > run_background_service() > ``` #### 三、应用场景对比 | **进程类型** | **典型场景** | **案例** | |--------------|-----------------------------------------------------------------------------|------------------------------| | **孤儿进程** | 父进程意外崩溃(如服务端断连) | SSH 会话断开后子进程由 init 接管 | | **僵尸进程** | 父进程未正确处理子进程退出(常见于缺陷代码) | 未实现 SIGCHLD 信号处理的服务器 | | **守护进程** | 系统后台服务<sup>[1]</sup> | httpd, mysqld, systemd-logind | #### 四、最佳实践 - **避免僵尸进程**: ```c // 父进程需添加 SIGCHLD 处理器 signal(SIGCHLD, [](int sig) { while (waitpid(-1, NULL, WNOHANG) > 0); // 非阻塞回收所有僵尸 }); ``` - **创建健壮守护进程**: - 使用 systemd 托管服务(通过 `.service` 文件) - 日志重定向到 syslog 或专用文件 - **孤儿进程监控**: ```bash $ auditctl -a exit,always -F arch=b64 -S exit -F ppid=1 # 审计孤儿进程行为 ``` > ⚠️ **风险提示**:僵尸进程过多会导致 `fork()` 失败(进程表满),需及时清理。 --- ### 总结 - **孤儿进程**:无害的系统托管机制 - **僵尸进程**:编程缺陷导致的资源泄漏 - **守护进程**:系统服务的标准实现方式 **核心原则**:父进程必须负责子进程回收(`wait()` 或信号处理),后台服务应通过标准守护化流程实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值