进程
操作系统的发展史
老式计算机的手工操作特点:
1.用户独占全机,不会出现因资源已被其它用户占用而等待的现象,但资源利用率低。
2.CPU等待手工操作。CPU的利用不充分。
手工操作属于单道程序,也就是主机内存中只能执行一个程序,其它的程序只能等待它执行完后才能执行。
多道
多道程序设计技术就是指允许多个程序同时进入内存并运行。
优点:
1.空间上的复用:
多个程序共用一套计算机硬件
2.时间上的复用(切换+保存状态)
①.当一个程序遇到IO操作,操作系统会剥夺该程序的CPU执行权限(提高了CPU的利用率,并且不影响程序的执行效率)
②.当一个程序长时间占用CPU操作系统也会剥夺该程序的CPU执行权限(降低程序的执行效率)
进程理论
进程和程序
程序:就是一坨代码
进程:就是正在运行的程序
多个进程执行时的进程调度
多个进程交替运行,操作系统必须对这些进程进行调度,这个调度也不是随机进行的,而是需要遵循一定的法则,于是有了进程的调度算法。
1.先来先服务:谁先来就给谁先服务,先来的程序先进行运行。
2.短作业优先调度:谁消耗的时间比较短,先运行谁。
3.时间片轮转法和4.多级反馈队列
同步异步、阻塞非阻塞
同步异步:表示的是程序的运行状态
同步:任务提交之后,原地等待的任务执行并拿到返回结果才走,期间不做任何事情(在代码层面看就是卡住了)
异步:任务提交之后,不再原地等待,而是继续执行下一行代码(通过其它的方式拿到结果)
阻塞和非阻塞:表示的程序的运行状态
阻塞:阻塞态
非阻塞:就绪态,运行态
(1)就绪(Ready)状态
当进程已分配到除了CPU外的所有资源时,只要获得处理机便可以立即执行,这是的进程状态就是就绪态。
(2)运行(Running)状态
当进程已获得处理机制,其它程序正在处理机上执行,这时的进程状态被称为就绪态。
(3)阻塞(Blocked)状态
正在执行的状态,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可能有很多种,比如等待I\O才做、申请缓冲区不能满足、等待信号等。
创建进程的两种方式
multiprocess模块
multiprocess并不是一个模块,而是python中一个操作、管理进程的包。之所以叫mulit是取自multiple的多功能的意思,这个包几乎包含了和进程有关的所有子模块。
multiprocess.process模块
Process模块
process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建。
#使用process模块创建进程 from multiprocessing import Process import time def test(name): print('%s is running'%name) print('%s is over'%name) # windows创建进程一定要在if __name__ == '__main__':代码块内创建 否则报错 if __name__ == '__main__': p = Process(target=test,args=('egon',)) #创建一个进程对象 p.start() # 告诉操作系统帮你创建一个进程 time.sleep(1) print('主')
windows创建进程一定要在__main__中写,因为windows会将代码以模块的方式,从上往下执行一遍,而linux会直接将代码完完整整的拷贝一份。
#以继承Process类的形式开启进程的方式 from multiprocessing import Process import time class MyProcess(Process): def __init__(self,name): super().__init__() self.name = name def run(self): print('%s is running'% self.name) time.sleep(3) print('%s is over'%self.name) if __name__ =='__main__': p = MyProcess('egon') p.start() print('主')
创建进程就是在内存中重新开辟一块内存空间,将允许产生的代码丢进去,一个进程对应在内存里的就是一块独立的内存空间。
#进程间的数据是隔离的 from multiprocessing import Process import time money = 100 def test(): global money # 修改全局变量,通过这个来进行测试 money = 99999 if __name__ == '__main__': p = Process(target = test) p.start() p.join() print(money) >>>:100 #创建的子进程和主进程是两个独立的空间,代码块是在子进程中执行的,所以只能修改子进程空间中的变量,主进程空间不受到影响
进程对象以及一些方法
join方法
def f(name): print('hello',name) time.sleep(1) print('子进程在此') if __name__ == '__main__': p = Process(target=f,args=('bob',)) p.start() p.join() print('父进程在此') #start()它起到的作用的作用是让操作系统帮你创建一个进程,至于什么时候创建,由操作系统随机决定 #当我们想让子进程先运行的时候我们可以用join,它的作用是让主进程等待子进程的运行结束
terminate和is_alive
from multiprocessing import Process,current_process import os import time def test(name): print('%s is running'%name,'子进程%s'%s.getpid(),'父进程%s'%os.getppid) time.sleep(3) print('%s is over'%name) if __name__ == '__main__': p = Process(targs=test,args=('egon',)) p.start() p .termainate() # 杀死当前进程 time.sleep(0.1) print(p.si_alive) time.sleep(0.1) print(p.is_alive) # 判断是否存活 print('主',os.getpid(),'主主进程%s'%os.getppid())
守护进程
from multiprocessing import Process import time def test(name): print('%s总管正常活着'%name) time.sleep(1) print('%s总管正常死亡'%name) if __name__ == '__main__': p = Process(target=test,args=('egon',)) #p.daemon = True # 守护进程,当主程序代码运行结束之后,守护进程随机终止 p.start() time.sleep(0.1) print('皇帝jason寿终正寝')
僵尸进程和孤儿进程
僵尸进程:当进程结束之后还保留着一些信息以及它占用的PID,所有的进程都步入成僵尸进程。
父进程回收子进程资源的两种方式
1.join方法
2.父进程正常死亡
孤儿进程:子进程没死,但父进程意外死亡
针对linux会有儿童福利院(init)如果父进程意外死亡他创建的子进程都会被福利院收养
互斥锁
多个程序操作用一份数据的时候会出现数据错乱的现象。
避免的方法:将操作数据的部分加锁处理,会将并发变成串行牺牲了效率但是保证了数据的安全。
#写一个简单的抢票系统 from multiprocessing import Process,Lock import time import json #查询余票 def search(i): with open('data','r',encoding='utf-8') as f: data = f.read() t_d = json.loads(data) print('用户%s查询余票为:%s'%(i,t_d.get('ticket'))) #买票 def buy(i): with open('data','r',encoding='utf-8') as f: data = f.read() t_d = json.loads(data) time.sleep(1) if t_d.get('ticket') > 0: t_d['ticket'] -= 1 with open('data','w',encoding='utf-8') as f: json.dump(t_d,f) print('用户%s抢票成功'%i) else: print('没票了') def run(i): search(i) buy(i) if __name__ == '__main__': for i in range(10): # 生成10个子进程去抢票 p = Process(target=run,args=(i,)) p.start() #这段代码运行之后会出现10个用户都抢到了票的情况,原因是每个用户拿的并不是一个实时的数据,所以在对数据进行修改时,所有人都在对拿到的数据进行修改,所以每个人都将自己拿到的票取了出来(程序中就是将数字减一) #怎么避免这种情况 from multiprocessing import Process,Lock import time import json # 查票 def search(i): with open('data','r',encoding='utf-8') as f: data = f.read() t_d = json.loads(data) print('用户%s查询余票为:%s'%(i,t_d.get('ticket'))) # 买票 def buy(i): with open('data','r',encoding='utf-8') as f: data = f.read() t_d = json.loads(data) time.sleep(1) if t_d.get('ticket') > 0: # 票数减一 t_d['ticket'] -= 1 # 更新票数 with open('data','w',encoding='utf-8') as f: json.dump(t_d,f) print('用户%s抢票成功'%i) else: print('没票了') def run(i,mutex): search(i) mutex.acquire() # 抢锁 只要有人抢到了锁 其他人必须等待该人释放锁 buy(i) mutex.release() # 释放锁 if __name__ == '__main__': mutex = Lock() # 生成了一把锁 for i in range(10): p = Process(target=run,args=(i,mutex)) p.start()
加锁之后,当一个进程拿到了这个数据的操作权限时,其它进程是无法对这个数据进行操作,甚至无法查看(就是当一个人抢到票在支付的时候其它人是看不到这张票的),其它进程只能等到这个进程的修改操作结束之后才能看到这个数据,然后也是要先抢到锁(所以一次只能有一个人),才能对数据进行修改,当这个进程结束对数据的操作要退出时,会释放锁,这时别的程序才能来抢锁,谁抢到谁来操作。