进程与多进程
多任务编程
可以有效的利用计算机资源,同时执行多个任务
进程
定义
进程就是程序在计算机中一次执行的过程
程序与进程的区别
程序是一个静态文件的描述,不占计算机的系统资源
进程是一个动态的过程,占有CPU内存等资源,有一定的生命周期
同一个程序的不同执行过程即为不同的进程
什么决定了进程的创建
- 用户通过应用层程序进行进程的创建申请
- 调用操作系统接口进行进程创建
- 告知系统内核创建新的进程提供给应用层使用
进程如何占有CPU
- 同一个内核同一个时刻只能运行一个进程
- 多个进程对内核资源进行抢占,由操作系统进行分配
- 哪个进程占有计算机内核我们称为该进程占有CPU的时间片
进程在运行过程中的形态和附带内容
PCB(进程控制块)
在Linux和Unix操作系统中,进程创建后会在内存中开辟一块空间存放进程的相关信息,这个空间称为PCB。
-
PID:
在操作系统中进程的唯一标,是一个大于0的正整数,由操作系统分配。
ps aux
查看进程列表信息 -
虚拟内存:
每个进程占有4G内存地址空间,这里的内存指的是虚拟内存。 -
进程状态:
-
三态
- 就绪态:进程具备运行条件,等待系统分配处理器以便运行
- 运行态:进程占有CPU处于运行状态
- 等待态:又称为阻塞或者睡眠态,指进程不具备运行条件,正在等待某些条件的达成
-
五态
- 新建态:创建一个进程的过程,直接表现为执行某个程序或者在程序中创建新的进程
- 终止态:进程执行结束,完成回收的过程
-
Linux下进程状态表示
- D:等待态(不可中断)
- S:等待态 (可中断)
- T:等待态(暂停)
- R:运行态
- Z:僵尸态
- +:前台进程
- N:低优先级的进程
- <:高优先级的进程
- l:有进程链接
- s:会话组
-
三态
进程的优先级
优先级往往决定了一个进程的执行权限和占有系统资源的优先级
-
优先级范围
-20 ~ 19
数越小,优先级越高,用户创建进程默认优先级为0 -
top 命令
动态查看系统进程运行情况 -
nice 命令
以指定的优先级运行某个进程
nice -9 ./while.py
以9的优先级运行程序
sudo nice --9 ./while.py
以-9的优先级运行程序
renice n PID
修改一个正在运行的进程的优先级
父子进程
在系统中除了初始化进行之外每个进程都是由父进程创建的,每个进程有一个唯一的父进程,可能有多个子进程
os模块:
-
os
模块提供大量和系统相关的功能函数接口 -
os
模块的使用时系统相关的,在不同的系统中,可能使用方法不同
os.fork()
-
平台:只能在linux和unix下使用
-
功能:创建一个新的进程
-
参数:无
-
返回值:
-
< 0
表示进程创建失败 -
== 0
在子进程中fork的返回值为0 -
> 0
在父进程中fork的返回值大于0
-
-
注意:
- 父进程中fork之前的内容,子进程会同样复制,但是父子进程空间内容的修改不会相互影响
- 父子进程在执行上互不影响,理论上不一定谁先执行
- 子进程虽然复制父进程的空间,但也有自己独特的德行,比如自己的PID,进程控制块,进程栈等。父进程中fork的返回值即为创建的子进程的PID号。
- 子进程的退出不会影响父进程
进程相关函数
-
os.getpid()
获取当前进程的PID号 -
os.getppid()
获取当前进程父进程的PID号 -
os._exit(status)
功能:结束一个进程
参数:一个数字表示进程的退出状态,通常0表示正常退出进程,其他数字表示非正常退出 -
sys.exit([status])
功能:结束一个进程,如果处理了抛出的异常则不结束进程
参数:一个数字表示进程的退出状态,还可以是一个字符串,则在进程退出时会打印这个字符串
僵尸进程
定义:子进程先于父进程退出,父进程没有对子进程的退出做出相应的处理,此时子进程就会变成僵尸进程
影响:进程退出后,仍有部分信息残留在内存中占用空间,大量的僵尸进程会影响系统运行,所以应该尽量避免僵尸进程的产生。
处理僵尸进程的方法
- 让父进程先退出 (不好控制)
- 父进程处理子进程的退出 (阻塞父进程的运行)
-
os.wait()
阻塞函数:进程处于等待状态,等待某种条件的达成才会继续运行
功能:等待子进程退出进行处理
参数:无
返回值:返回一个包含两个元素的元组,第一个是退出的子进程的PID号,第二个是子进程的退出状态 -
os.waitpid(pid,option)
功能:同wait,处理子进程退出使其不会变成僵尸
参数:-
pid
-1
表示等待任意子进程退出 -
pid
>0
表示等待指定进程号的子进程退出 -
option
0
表示阻塞等待 -
option
WNOHANG
表示非阻塞状态 wait() === waitpid(-1,0)
-
-
- 创建二级子进程处理
原理:让子进程创建二级子进程,创建完成就退出,二级子进程成为孤儿进程 - 在父进程中使用信号处理的方法忽略子进程发来的信号
signal(SIGCHLD,SIG_ING)
孤儿进程
定义:父进程先于子进程退出,此时子进程就会变为孤儿进程
影响:当一个进程变为孤儿进程,系统会自动的使用一个进程成为孤儿进程的父进程。当孤儿进程退出时,该系统会自动回收孤儿,使它不会成为僵尸。所以孤儿进程对系统资源没什么影响。
守护进程
- 生命周期长
- 与前端控制台无关
- 后台运行,一般用作系统进程或者自动化运行进程
多进程
更方便高效的进程创建方法
-
multiprocessing
模块 (标准库模块)
创建进程的步骤
- 将要完成的事件封装成一个函数
- 使用
multiprocessing
提供的接口函数创建j进程 - 使新的进程和指定的函数相关联去完成函数中的工作,并启动进程
- 对进程进行回收处理
进程的回收
- 作用:阻塞等待对应子进程的退出,然后回收子进程
- 注意:内核会帮助应用层记录子进程的退出情况,当使用
join
函数时内核会及时返回进程状态给应用层进行处理
创建子进程:
Process()
类
- 参数:
-
target
指定要绑定的函数 -
name
给创建的进程起一个名字 -
args
需要一个元组,给target
指定的函数按位置传参 -
kwargs
需要一个字典,给taregt
指定的函数按键值传参
-
- 属性与方法(
p
代表一个进程)-
p.name
进程名称 -
p.pid
进程PID -
p.daemon
-
False
(默认值)表示主进程运行结束后,不会影响子进程的运行,直到所有子进程运行完毕,进程才会结束 -
True
主进程运行完毕则所有子进程也不再运行一起退出。 - 注意:
- 该属性的设置必须要在
start()
前 - 该属性的设置并不是将进程设置为linux/unix中的守护进程
- 该属性的设置必须要在
-
-
p.is_alive()
进程状态 -
p.start()
启动子进程 -
p.join()
回收子进程- 参数:
p.join(timeout)
设置最长阻塞时间,如果超过这个时间还没有子进程退出,则不会继续等待
- 参数:
-
- 注意
- 函数当付给
Process
的target
变量后函数内容就是对应进程的进程内容,此时函数才有特殊性 - 多个子进程和父进程之间的执行互不影响
- 函数当付给
多进程的优缺点
优点
- 可以并行执行多个任务,提高运行效率
- 空间独立,数据安全
- 创建方便
缺点
- 进程的创建和销毁过程需要消耗较多的计算机资源
- 在需要频繁的创建和销毁较多进程情况下,资源消耗过多,不适宜使用多进程完成任务
进程池
什么是进程池
在利用Python进行系统管理的时候,特别是同时操作多个文件目录,或者远程控制多台主机,并行操作可以节约大量的时间。当被操作对象数目不大时,可以直接利用multiprocessing中的Process动态成生多个进程,十几个还好,但如果是上百个,上千个目标,手动的去限制进程数量却又太过繁琐,此时可以发挥进程池的功效。
进程池可以提供指定数量的进程供用户调用,当有新的请求提交到pool中时,如果池还没有满,那么就会创建一个新的进程用来执行该请求;但如果池中的进程数已经达到规定最大值,那么该请求就会等待,直到池中有进程结束,才会创建新的进程来它。
进程池的创建
- 创建进程池,在池内放入合适数量的进程
- 将事件加入进程池的等待队列
- 使用进程池内的进程不断的执行等待事件
- 所有事件处理完毕后,关闭回收进程池
进程池的具体操作
-
Pool
- 功能:创建进程池
- 参数:
processes
进程池中进程的数量
-
apply_async()
- 功能:以异步的方式将要执行的事件放入进程池
- 参数:
-
func
要执行的函数 -
args
给函数按位置传参 -
kwds
给函数按照键值传参
-
- 返回值:
返回事件执行后的返回值对象,可以通过调用get()
函数获取事件函数return
的内容
-
apply()
功能:按照顺序添加要执行的事件,执行一个再添加一个 -
close()
功能:关闭进程池,使其不能再加入新的事件 -
join()
功能:阻塞等待进程池将事件都执行结束后回收进程池 -
map()
功能:类似于内建函数map
,将第二个参数的迭代对象中的数据逐个带入第一个函数作为参数。只不过兼顾了apply_async
功能,将函数放入进程池
创建自己的进程类
- 继承
Process
类以获取原有的属性 - 实现自己的功能部分
- 使用自己的类创建进程即可