进程:程序在计算机中一次执行的过程
程序: 是一个静态的描述,不占有计算机资源
进程: 是一个动态的过程,占有cpu内存的计算机资源,有一定的生命周期
多任务编程:通过应用程序利用多个计算及核心达到多任务同时执行的目的,以此来提升程序执行效率
同一个程序,每次执行都是不同的进程,因为分配的计算机资源不同
进程的创建流程:
用户空间运行程序创建进程申请 ---> 调用操作系统内核接口创建进程--->分配计算机资源,确定进程状态---> 将新的进程提供用户使用
多个进程如何占用cpu
一个内核同一时刻只能运行一个任务
多个进程对内核资源进行争夺,操作系统决定哪个进程占有计算机核心
占有计算机核心进程我们称为该进程占有cpu的时间片
进程如何保存相关信息:
PCB(进程控制块):在unix,linux系统中进程创建后,会在内核开辟一块空间存放进程的相关信息,称为PCB
查看进程信息 ps -aux
进程的信息:用户 PID 占有内存 优先级 等
PID:在操作系统中进程的唯一标志,是大于0的整数,由系统自动分配
进程特征
进程是操作系统资源分配的最小单位
每个进程单独占有4G虚拟内存
进程之间相互独立,运行不受影响
进程的状态
三态
就绪态 :进程具备运行条件,等待系统分配处理器运行
运行态 :进程占有cpu处于运行的状态
等待态 :又称为阻塞态,睡眠态,指进程暂时不具备运行的条件,需要阻塞等待(sleep accept...)
五态
新建态 :创建一个进程,获取资源,直接表现为运行一个程序,或者在程序中创建新的进程
就绪态 :进程具备运行条件,等待系统分配处理器运行
运行态 :进程占有cpu处于运行的状态
等待态 :又称为阻塞态,睡眠态,指进程暂时不具备运行的条件,需要阻塞等待(sleep accept...)
终止态 :进程执行结束,资源回收过程
进程状态的表示:
D 等待态 (不可中断等待)
S 等待态 (可中断等待)
T 等待态 (暂停)
R 运行态
Z 僵尸态 (死亡状态)
+ 前台进程 (不带+即为后台进程)
< 高优先级
N 低优先级
l 有进程链接
s 会话组
进程优先级
优先级决定了一个进程的执行权限和占有资源的优先程度
top : 动态查看当前运行的进程的状态 q退出
< > 可以翻页
#!/usr/bin/env python3(加env会在top中显示文件名)
linux 中优先级范围 -20 ---- 19 -20最高
用户程序默认优先级为0
nice : 以指定的优先级运行进程
示例:
nice -9 ./.py文件 以9的优先级运行此程序
sudo nice --9 ./.py文件 以-9的优先级运行此程序(小于0的优先级一般要加sudo)
renice : 改变某个进程的优先级
示例:
renice 2 PID(NI)
进程(process)
父子进程:在系统出除了初始化进程其他进程都有一个父进程,可能有多个子进程
父子进程的创建过程:
import os
fork()
多程序:无法根据情况动态创建进程
通过接口创建进程:便于控制,可以在程序中随时根据需要创建
os.fork()
功能 : 创建一个新的进程
参数 : 无
返回值 : 失败返回一个负数 -1
成功 0 在子进程中fork的返回值 (成功即为创建新的进程成功了)
>0的正整数(新的进程的PID)在父进程中的返回值
示例:
#os 为系统相关模块,不同操作系统使用情况不同
import os
print("准备创建进程")
pid=os.fork()
if pid<0:
print("创建进程失败")
elif pid==0:
print("创建了一个新的进程")
else:
print("这是原有的进程")
print("进程进行完毕")
注意:
父进程中fork创建之前的内容子进程同样会复制,但父子进程空间独立,fork创建之后的修改不会影响到对方
父子进程在执行上互不影响,谁先执行,谁先执行完不确定(一般是父进程先执行)
子进程虽然复制父进程的空间,但是有自己的特性,比如自己的PID,进程PCB,进程栈空间等
进程相关函数
获取PID号
os.getpid()
功能:获取当前进程的PID号
返回值 : 当前进程PID
os.getppid()
功能 : 获取当前进程父进程的PID号
返回值 : 父进程PID
进程的退出
os._exit(status)
功能 : 结束一个进程
参数 : 表示进程的结束状态 是一个整数
带一个下划线的不能被from os import * 引用
示例:
import os
from time import *
pid=os.fork()
if pid<0:
print("创建子进程失败")
elif pid==0:
print("创建子进程成功")
print("getpid:",os.getpid())#子进程自己获取自己的pid
print("getppid:",os.getppid())#子进程获取父进程的pid
else:
sleep(1)
print("这里是父进程")
print("pid",pid)#父进程fork返回值就是子进程PID
print("getpid",os.getpid())#父进程获取自己的pid
sys.exit([status])
功能 : 结束一个进程,抛出异常
参数 : 传入一个正整数表示结束状态
传入字符串表示结束打印
示例:
import sys
try:
sys.exit(1)#这时抛出一个异常,下面的print语句不会执行
print("我要执行")
except:
print("出错了")
孤儿进程 :父进程先于子进程执行完毕并退出,此时子进程变为孤儿进程
* 孤儿进程会被系统指定的进程所接收,该进程则成为孤儿进程新的父进程。在孤儿进程退出时,系统指定的进程会进行处理不会使其成为僵尸
僵尸进程 : 子进程先于父进程执行完毕并退出,但是父进程没有处理子进程的退出状况,子进程就会成为僵尸进程
* 僵尸进程会滞留PCB的部分信息在内存中,大量的僵尸进程会消耗系统资源,所以应该尽量避免僵尸进程的产生
避免僵尸进程产生的三种方法:
1.让父进程先退出 (不好控制,一般不会采用此方法)
2. 让父进程处理子进程的退出
使用wait 或者 waitpid 函数(使用阻塞函数,等待并处理子进程执行完后,再执行并退出)
os.wait() 阻塞函数,直到子进程退出才会结束阻塞
功能:等待子进程的退出进行处理
参数: 无
返回值:一个二元元组,第一个值为退出的子进程pid
第二个值为子进程退出状态
os.waitpid(pid,option)
功能:处理子进程的退出
参数:pid: -1 :表示等待任意子进程的退出
>0 :表示等待相应的PID号的子进程
option: 0 :表示阻塞等待
WNOHANG : 表示非阻塞等待
返回值:一个二元元组,第一个值为退出的子进程pid
第二个值为子进程退出状态
os.waitpid(-1,0)等同于os.wait()
使用信号处理
import signal
signal.signal(signum,handler)
功能: 处理一个信号
参数: signum: 要处理的信号
handler: 对该信号的处理方法
取值:SIG_DFL 采用默认方法处理(等同于默认为signum)
SIG_IGN 忽略这个信号
func自己定义的方法处理(回调函数把一个函数当成参数传递过去)
func 格式要求:
def func(sig,frame):
...
sig:接收到的信号
frame:信号对象
*signal函数是一个异步处理信号函数,只要执行,在进程中就会按照指定方法处理信号
*signal不能处理SIGSTOP SIGKILL信号
在父进程中,忽略子进程的发送信号
signal(SIGCHLD,SIG_IGH)
3.创建二级子进程(创建二级子进程,这时一级子进程变为父进程,一级子进程退出时,将不再产生僵尸进程)
父进程创建子进程后等待子进程退出
子进程创建二级子进程后马上退出,二级子进程成为孤儿
让父进程和二级子进程处理具体事件
示例:
import os
#创建以及子进程
pid1=os.fork()
if pid1<0:
print("创建一级子进程失败")
elif pid1==0:
#创建二级子进程
pid2=os.fork()
if pid2<0:
print("创建二级子进程失败")
elif pid2==0:
print("我创建出来了")
else:
#一级子进程退出,使二级进程成为孤儿
os._exit(0)
else:
#等待一级子进程退出
os.wait()
print("做父进程该做的事情")
multiprocessing 模块创建进程 标准库模块
需要将事件封装为函数
使用multiprocessing提供的类创建新的进程
新的进程和对应的函数相关联,进程启动会自动执行函数,完成事件
进程回收
创建子进程类
multiprocessing.Process()
功能:创建子进程
参数:name : 给创建的进程起个名字
默认 process-1
target:目标函数
args: 元组 要给函数传递的参数
kwargs: 字典 要给函数传递的参数 键值
进程对象属性函数
p.start()
功能:启动子进程 此时进程真正创建
p.join([timeout])
功能:阻塞等待回收相应的子进程
参数:默认为阻塞,timeout为超时时间
p的其他属性
p.name 进程名称
p.pid 创建进程的PID号
p.is_alive() 进程状态
p.daemon
默认值为False 表示主进程结束后 不影响子进程的执行 如果设置为True 则主进程执行完毕后 所有的子进程一同退出不执行
注意:
*设置必须在start()前
*一般使用daemon=True时不用加join
*该属性并不是linux/unix系统中所说的守护进程设置(只是一个标识位)
守护进程:生命周期长,随系统创建 随系统销毁 不受前端控制,后台运行 操作系统进程,或者是自动化运行进程居多
示例:
import multiprocessing as mp
import os
import time
a=1
def fun():
global a
a=1000
print(os.getppid(),"----",os.getpid())
print("吃早饭")
time.sleep(1)
print("吃午饭")
time.sleep(2)
print("吃晚饭")
time.sleep(3)
def fun2():
print(os.getppid(),"----",os.getpid())
print("睡午觉")
time.sleep(2)
time.sleep(3)
print("睡觉")
print(a)
def fun3():
print(os.getppid(),"----",os.getpid())
print("打豆豆")
time.sleep(2)
print("打小豆豆")
time.sleep(2)
#创建三个新的进程,关联上面三个事件,达到运行的目的
#生成三个进程对象分别表示这三个进程
p1=mp.Process(target=fun)
p2=mp.Process(target=fun2)
p3=mp.Process(target=fun3)
#通过生成的进程对象启动子进程
#子进程有父进程的代码段 只不过只执行对应的函数
p1.start()
p2.start()
p3.start()
print("Parent PID",os.getpid())
#阻塞等待回收进程
p1.join()
p2.join()
p3.join()
多进程
优点:
并行运行多个任务,提高运行效率
空间独立,数据安全,创建方便
缺点:
进程创建销毁的过程中消耗较多的计算机资源
进程池
使用场景:
在需要频繁的创建删除较多的情况下,导致计算机资源消耗过多
进程池创建步骤
创建进程池,在池内放入适量的进程
将事件加入进程池等待队列
使用进程池中的进程不断处理事件
所有事件处理后,回收关闭进程池
from multiprocessing import Pool
Pool()
功能:创建进程池
参数:procdsses:指定进程池中进程数量
返回:得到进程池对象
Poll.apply_async()
功能:异步方式将事件放入进程池执行
参数: func :要执行的事件函数
args:同Process中args 给函数传参
kwds:同Process中kwargs 给函数传参
返回值:返回一个对象 该对象可以通过get()方法得到func函数的返回值
Pool.close()
功能:关闭进程池,使其无法加入新的事件
Pool.join()
功能:阻塞等待进程池退出(当所有事件处理完毕后)
Pool.apply()
用法和apply_asnc一样,只是需要顺序执行,一个事件结束再执行另一个事件
Pool.map(func,iter)
功能:类似于内建函数map 将第二个参数的迭代数传递给第一个参数的函数执行,同时兼容了使用进程池执行
返回值:返回func的返回值列表
r = pool.map(fun,test) 等同于
r = []
for i in test:
res = pool.apply_async(fun,(i,))
r.append(res.get())
示例:
from multiprocessing import Pool
from time import sleep
import os
def worker(msg):
sleep(2)
print(msg)
return msg+' over'
#创建进程池
pool=Pool(processes=4)
result=[]
#放入事件到进程池中
for i in range(10):
msg='hello %d'%i
#加入事件后就会立即操作事件
#apply_async的返回值对象,该对象可以获取worker返回结果
r=pool.apply_async(worker,(msg,))
#pool.apply(worker,(msg,))
result.append(r)
sleep(3)
print("+++++++")
#关闭进程池,不能再加入事件
pool.close()
sleep(3)
print("********")
#阻塞等待回收
pool.join()
print("========")
#通过aply_async()返回对象get()方法获取返回值
for res in result:
print(res.get())
进程间通信
磁盘交互
速度慢
不安全
套接字通信socket 本地套接字
以上两种通常不用
管道 消息队列 共享内存 信号 信号量 套接字
管道通信(pipe)
在内存中开辟一块空间,对多个进程可见,通过管道,多进程进行通信
multpprocessing---->pipe
fd1,fd2=Pipe(duplex=True)
功能:创建一个管道
参数:duplex 默认为True 表示双向管道
设置为False 则表示单向管道
返回值:返回两个管道流对象,表示管道的两端
如果是双向管道则两个均可读写
如果为单向管道则 fd1只能读,fd2只能写
fd1.recv()
功能:接收消息(每次接受一条)
参数:无
返回值:接收消息
*如果管道没有消息会阻塞
fd2.send(data)
功能:发送消息 可以是字符串或者是其他类型
参数:要发送的内容
from multiprocessing import Process,Pipe
import os,time
# #创建一个双向管道
fd1,fd2=Pipe()
#如果参数为False 则fd1只能recv fd2只能send
# fd1,fd2=Pipe(False)
def fun(i):
time.sleep(3)
#发字符串到管道
fd1.send('hello'+ str(i))
print(os.getppid(),"----",os.getpid())
jobs=[]
for i in range(5):
p=Process(target=fun,args=(i,))
jobs.append(p)
p.start()
for i in range(5):
data=fd2.recv()
print(data)
for i in jobs:
i.join()
消息队列
队列:先进先出
在内存中开辟队列模型,用来存放消息,任何拥有队列的进程都可以存取消息
创建队列
q=Queue(maxsoze=0)
功能:创建一个消息队列
参数:maxsize默认为0 表示队列可存放多少消息有内存而定
>0 表示队列最多存放多少条消息
返回值:返回消息队列对象
q.put()
功能:向队列中存放消息
参数:要存的消息(字符串 整数 列表)
*当队列满时会阻塞
q.full()
判断队列是否为满 满返回True
q.get()
功能:向队列中取出消息
返回值:取出的消息
*当队列满时会阻塞
q.empty()
判断队列是否为空 空返回 True
q.qsize()
得到当前队列中消息的个数
q.close() 关闭队列
*put get 中均有可选参数 block 和timeout block 默认为True 表示阻塞函数 如设置为False则不阻塞
timeout block 为True时设置超时时间
示例:
from multiprocessing import Queue
from time import sleep
#创建队列
q=Queue(3)
q.put(1)
print(q.full())
q.put(2)
q.put(3)
print(q.full())
#超时时间为3
# q.put(4,True,3)
print("取出值为",q.get())
print("队列中有%d条消息"%q.qsize())
sleep(0.1)
print("队列是否为空:",q.empty())
q.close()#关闭队列
共享内存
在内存中开辟一段空间 ,存储数据,对多个进程可见,每次写入共享 内存的数据会覆盖之前的内容,由于对内存格式化较少,所以存取速度快
只能存储一条消息
from multiprocessing import Vale,Array
obj=Value(ctype,obj)
功能:开辟内存空间
参数:ctype str 要转变的c类型 (只接受c语言) (对照ctype表)
obj 写入共享内存的初始值
返回值:返回一个共享内存对象
obj.value 即可得到共享内存中的值
obj=Array(ctype,obj)
功能:开辟共享内存空间
参数:ctype 要转换的类型
obj 存入到共享内存的数据
是一个列表,要求列表中数类型一致
正整数,则表示开辟一个多大的序列空间
返回值:返回一个共享内存对象
示例一:
from multiprocessing import Array,Process
import time
def fun(shm):
for i in shm:
print(i)
shm[2]=1000
#开辟共享内存空间,可容纳6个整数
#初始值[1,2,3,4,5,6]
shm=Array('i',[1,2,3,4,5,6])
#表示在共享内存中开辟一个包含6个整数的空间
# shm=Array('i',6)
p=Process(target=fun,args=(shm,))
p.start()
p.join()
for i in shm:
print(i)
示例二:
from multiprocessing import Value,Process
import time
import random
def deposite(money):
for i in range(100):
time.sleep(0.03)
money.value+=random.randint(1,200)
def withdraw(money):
for i in range(100):
time.sleep(0.02)
money.value-=random.randint(1,10)
#创建共享内存对象
money=Value('i',2000)
d=Process(target=deposite,args=(money,))
w=Process(target=withdraw,args=(money,))
d.start()
w.start()
d.join()
w.join()
print(money.value)
进程间通信的对比:
管道 消息队列 共享内存
_________________________________________________________
开辟空间 内存 内存 内存
_________________________________________________________
读写方式 双向/单向 先进先出 操作覆盖内存
_________________________________________________________
效率 一般 一般 较快
__________________________________________________________
应用 多用于亲缘进程 方便灵活广泛 较复杂
__________________________________________________________
是否需要 是 否 需要
互斥机制
信号
一个进程向另一个进程通过信号传递某种讯息
kill -l 查看信息
kill -signame PID 给PID的进程发送一个信号
信号的名称 :系统定义,信号的名字
信号的含义 :系统定义的,信号的作用
信号的默认处理方法 :系统定义的,信号给接受进程带来的行为一般有 终止 暂停 忽略
python 如何处理
发送
os.kill(pid,sig)
功能:向一个进程发送一个信号
参数: pid:要发送信号的进程PID
sig:要发送的信号
signal.alarm(sec)
功能:向自身发送一个时钟信号 SIGALRM
参数:sec 时钟秒数
*信号属于异步通信方式,信号的发送不会影响进程的持续执行
*一个进程中只能同时有一个时钟,后面的时钟时间会覆盖前面的时钟时间
处理
signal.pause()
功能:阻塞等待一个信号的发生
信号量
给定一定的信号数量,对多个进程可见,并且多个进程均可操作,进程根据信号量的多少,可以有不同的行为
multiprocess------->Semaphore()
Semaphore(num)
功能:定义信号量
参数:num : 给定信号量的初始个数
返回值:返回信号量对象
sem=Semaphore(num)
sem.acquire() 将信号量 减一 信号量为0时调用会阻塞
sem.release() 将信号量 加一
同步和互斥
目的:对共有资源的操作会产生争夺,同步和互斥是一种解决争夺的方案
临界资源:多个进程或者线程都可以操作的资源
临界区: 操作临界资源的代码段
同步:同步是一种合作关系,为完成某个任务多进程或多线程之间形成一种协调,按照条件次序执行,传递告知资源情况,这种协调可能是因为阻塞关系达成的
互斥:互斥是一种制约关系,当一个进程或线程进入到临界区会进行加锁操作,此时其他进程(线程)在企图操作临界资源就会阻塞,只有当资源被释放后才能进行操作
Event事件
创建事件对象
e=Event()
提供事件阻塞
e.wait()
对事件对象进程设置,此时e.wait判断事件被set则结束阻塞
e.set()
清除该事件对象的set
e.clear()
检测对象是否被设置 设置返回True
e.is_set()
示例:
from multiprocessing import Event
#创建事件对象
e=Event()
print(e.is_set())
e.set()
print(e.is_set())
#将设置清除 wait又阻塞
e.clear()
e.wait()
Lock 锁
multiprocessing------>Lock
创建一个对象:
lock=Lock()
上锁
lock.acquire()
解锁
示例:
from multiprocessing import Process,Lock
import sys
from time import sleep
def worker1():
# with lock:
lock.acquire()#上锁
for i in range(5):
sleep(1)
sys.stdout.write("worker1 输出\n")
lock.release()#解锁
def worker2():
#with方式上锁
with lock:
for i in range(5):
sleep(1)
sys.stdout.write("worker2 输出\n")
#创建一个lock对象
lock=Lock()
#标准输入
sys.stdin
#标准输出
sys.stdout
w1=Process(target=worker1)
w2=Process(target=worker2)
w1.start()
w2.start()
w1.join()
w2.join()
lock.release()
with Lock :------>给wait代码段上锁
...
...
-------------------->with代码段结束自动解锁
*在lock对象处于上锁状态的时候,在企图上锁则会阻塞直至锁被释放,才会继续执行上锁操作