python学习笔记—— 多进程中的 孤儿进程和僵尸进程
1 基本概述
1.1 孤儿进程和僵尸进程
父进程创建子进程后,较为理想状态是子进程结束,父进程回收子进程并释放子进程占有的资源;而实际上,父子进程是异步过程,两者谁先结束是无顺的,一般可以通过父进程调用wait()或waitpid()语句来等待子进程结束再退出。
孤儿进程:父进程结束后还有基于该父进程创建的子进程(一个或多个)尚没有结束,此时的子进程称之为孤儿进程;孤儿进程将被init进程(进程树中除了init都有父进程)接受,也就意味着init进程负责孤儿进程完成状态收集工作。一般而言,init进程的pid为1,有资料显示init有三种形式,其pid并不为1。
僵尸进程:在Linux进程状态及转换关系中有一种进程状态是僵尸状态(zombie),此时该进程称之为僵尸进程;当使用fork创建子进程后,子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程中的进程描述仍然保存在系统中,这种进程称之为僵尸进程,有时也称为僵死进程。
1.2 孤儿进程和僵尸进程危害
孤儿进程:孤儿进程不会占用系统资源,系统会将父进程回收处理孤儿进程,所以孤儿进程不占用系统资源;有时还会利用孤儿进程的这一原理进程程序逻辑设计。
僵尸进程:子进程先于父进程退出,父进程没有处理子进程的退出状态,此时子进程就会成为僵尸进程;进程已经结束,但是会占有一定的计算机资源。所以,我们应该尽量避免僵尸进程的产生。
Lunix提供了父进程获取子进程状态信息的机制;在每一个进程退出的时候,内核会释放该进程所有的资源,包括打开的文件、占用的内存等;但是仍然为其保留一定的信息(这些信息涵盖有进程号the process ID、退出状态the termination status of the process、运行时间the amount of CPU time taken by the process等),直到父进程调用wait() / waitpid()时才释放。
一个进程在调用exit命令结束自己的生命时,其实它并没有真正被销毁,而是留下一个称之为僵尸进程(Zombie)的数据结构(系统调用exit,它的作用是使进程退出,但也仅仅限于讲一个正常进程变成了一个僵尸进程,并不能将其完全销毁)。在Linux进程的状态中,僵尸进程是非常特殊的一种,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。它需要它的父进程来为它收尸,如果他的父进程没有安装SIGCHLD信号处理函数调用wait或waitpid等待子进程结束,又没有显式忽略该信号,那么它就一直保持僵尸状态,如果这时父进程结束了,那么init进程自动会接手这个子进程,为它收尸,它还是能被清除的。但是如果父进程是一个循环而不结束,那么子进程就会一直保持僵尸状态,如果如此进行下去,系统中就会遗留大量僵尸进程。
如何避免僵尸进程的产生?
1. 合理安排进程任务,尽量让父进程先结束
2. 父进程使用os.wait() os.waitpid() 来处理僵尸进程
3. 创建二级子进程,让一级子进程退出,从而形成两个完全独立的进程
4. 使用信号处理的方式
2 阻塞函数os.wait 与 os.waitpid 的基本语法
阻塞函数:当程序运行到该函数则进程处于阻塞状态不再继续运行 。一般在达到某些条件后结束阻塞
有大量IO操作或者可以给子进程操作,尽量让父进程先结束
阻塞函数不是不运行,而是等待某种事件的发生,如sleep就是阻塞等待函数
内核判断子进程是否结束,当子进程结束时,会通知应用层进行反馈
详情参考Python::OS 模块 -- 进程管理,里面详细阐述了os模块的属性
2.1 os.wait()
功能 : 阻塞等待子进程的退出,只要该父进程的任意子进程退出则终止阻塞。
参数:无
返回值:包含两个元素的元组,第一个 元素为退出的子进程的PID,第二个元素为子进程的退出码。
等待任何一个子进程结束,返回一个tuple,包括子进程的进程ID和退出状态信息:一个16位的数字,低8位是杀死该子进程的信号编号,而高8位是退出状态(如果信号编号是0),其中低8位的最高位如果被置位,则表示产生了一个core文件。
import os import sys from time import sleep pid = os.fork() if pid<0: print("create process failed") elif pid==0: print("this is chaild process:",os.getpid()) sleep(2) sys.exit(2) else: p,status = os.wait() print("this is parena process") print("p=",p,"status=",status) print(os.WEXITSTATUS(status))
运行
this is chaild process: 4088 this is parena process p= 4088 status= 512 2
注
这里status=512 而sys.exit(2)中的值是2,其实他们是256倍数关系,这是计算机本身的一种算法;可以使用os.WEXITSTATUS(status)函数来将其计算成为想要的数值,也可以使用256倍数关系进行直接运算;但是不同系统存在有不同的算法规则,所以尽量采用函数调用来计算数值
2.2 os.waitpid(pid, options)
功能: 阻塞等待子进程的退出
参数: pid -1 表示任意子进程退出都可以处理
>0 表示指定pid的子进程退出才能处理
options : 0 表示一直阻塞等待
WNOHANG: 非阻塞等待
返回值 : 同wait
os.wait() ====> os.waitpid(-1,0)
等待进程id为pid的进程结束,返回一个tuple,包括进程的进程ID和退出信息(和os.wait()一样),参数options会影响该函数的行为。在默认情况下,options的值为0。
如果pid是一个正数,waitpid()请求获取一个pid指定的进程的退出信息;
如果pid为0,则等待并获取当前进程组中的任何子进程的值;
如果pid为-1,则等待当前进程的任何子进程;
如果pid小于-1,则获取进程组id为pid的绝对值的任何一个进程;
当系统调用返回-1时,抛出一个OSError异常。
import os import sys from time import sleep pid = os.fork() if pid<0: print("create process failed") elif pid==0: print("this is chaild process:",os.getpid()) sleep(2) sys.exit(2) else: p,status = os.waitpid(-1,0)#与刚才等价 print("this is parena process") print("p=",p,"status=",status) print(os.WEXITSTATUS(status))
运行
this is chaild process: 4296 this is parena process p= 4296 status= 512 2
创建子子进程
import os pid = os.fork() if pid<0: print("create process failed") elif pid==0: p = os.fork() if p<0: pass elif p==0: print("child--->child") else: os._exit(0) else: os.wait() print("this is parena process")
运行
this is parena process child--->child
用的相关资源。
python学习笔记—— 多进程中的 孤儿进程和僵尸进程的更多相关文章
- python学习笔记——多进程中共享内存Value & Array
1 共享内存 基本特点: (1)共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝. (2)为了在多个进程间交换信息,内核专门留出了一块内存区,可以由需要访问的进程将 ...
- python学习笔记——多进程中的锁Lock
1 进程锁 python编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性. 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一线程访问对象. 在python中我 ...
- Python 学习笔记 多进程 multiprocessing--转载
本文链接地址 http://quqiuzhu.com/2016/python-multiprocessing/ Python 解释器有一个全局解释器锁(PIL),导致每个 Python 进程中最多同时 ...
- python 学习笔记 多进程
要让python程序实现多进程,我们先了解操作系统的相关知识 Unix/Linux操作系统提供了一个fork()系统调用,他非常特殊,普通的函数调用,调用一次,返回一次,但是fork调用一次, 返回两 ...
- python学习笔记-多进程
multiprocessing from multiprocessing import Process import time def f(name): time.sleep(2) print('he ...
- python学习笔记——多进程一 基础概念
1 进程 进程:程序的一次(从开始到结束)执行过程,属于一个动态过程.是系统进行资源分配和调度的基本单位. 程序:指的是一个文件,磁盘中可执行的代码.属于一个静态文件 注:进程运行时需要把程序加载如内 ...
- python学习笔记——多进程二 进程的退出
1 进程的退出函数的基础语法 1.1 进程的退出函数 进程的退出含有有os._exit([status])和sys.exit([status])两种,从数据包来看,该退出模块仅在linux或者unix ...
- Python学习笔记——进阶篇【第八周】———进程、线程、协程篇(Socket编程进阶&多线程、多进程)
本节内容: 异常处理 Socket语法及相关 SocketServer实现多并发 进程.线程介绍 threading实例 线程锁.GIL.Event.信号量 生产者消费者模型 红绿灯.吃包子实例 mu ...
- Python学习笔记6-Python中re(正则表达式)模块学习
今天学习了Python中有关正则表达式的知识.关于正则表达式的语法,不作过多解释,网上有许多学习的资料.这里主要介绍Python中常用的正则表达式处理函数. re.match re.match 尝试从 ...
随机推荐
- 给定任意字符串,计算一共能组合成多少个单词bing
CSDN编程挑战里的题目 例如有一个字符串"iinbinbing",截取不同位置的字符‘b’.‘i’.‘n’.‘g’组合成单词"bing".若从1开始计数的话, ...
- perfect-rectangle
https://leetcode.com/problems/perfect-rectangle/ // https://discuss.leetcode.com/topic/55944/o-n-log ...
- 第十二章 ThreadPoolExecutor使用 + 工作机理 + 生命周期
1.最基础的线程池ThreadPoolExecutor 使用方式: /** * ThreadPoolExecutor测试类 * 注意: * 1.ThreadPoolExecutor是一个线程池 * 2 ...
- Metronic V1.5.2 Responsive Admin Dashboard Template build with Twitter Bootstrap 3.0
Template Name: Metronic - Responsive Admin Dashboard Template build with Twitter Bootstrap 3.0 Versi ...
- 以太网帧、IP报文格式
这几天完成一个对比以太网帧的程序(c语言),老师给了以太网帧头部和IP报文头部的结构体,跟实际抓取到的数据包的格式是相同的. 以太网帧头部的数据结构: typedef struct { unsigne ...
- [Unity-6] GameObject有时候渲染不出来
问题描写叙述:在做游戏的过程中遇到了这样一个问题.一个怪物,假设让他出如今屏幕的中央是没问题的,可是让他出如今屏幕的边缘的位置发现他没有出现. 问题原因:经过检查发现,我给这个GameObject加入 ...
- java编程思想---对象
一.对象 对于每种语言来说,都有自己操纵内存中元素的方法. 在java中,一切被视为对象.可是操纵对象的是一个"引用".举个样例,能够比作为遥控器对电视的操作,遥控器就是引用,而电 ...
- docker入门——构建镜像
前面我们已经介绍了如何拉取已经构建好的带有定制内容的Docker镜像,那么如何构建自己的镜像呢? 构建Docker镜像有以下两种方法: 使用docker commit命令. 使用docker buil ...
- 封装scrollView 循环滚动,tableViewCell(连载) mvc
封装 封装 封装 ... 封装的重要性太重要了 给大家在送点干货 从一个项目中抽取出来的.和大家一起分享 封装scrollView 循环滚动.tableViewCell(连载) 明天还会更新 tabl ...
- jQuery Mobile的学习时间bottonbutton的事件学习
程序猿都非常懒.你懂的! 生命的绝唱来机仅仅争朝夕,如诗的年华更需惜时如金. 不要让今天的懈怠成为一生的痛. 每天都在进步. 近期在学习jquery mobile开发.使用的button,绑定事件,和 ...