一、操作系统的发展史:
1:早期的计算工作方式,手工操作---穿孔卡片
手工操作方式的两个特点:
二、进程理论
程序:就是一堆代码
进程,顾名思义就是正在执行的一个过程
1:操作系统的作用:
(1):隐藏丑陋复杂的硬件接口,提供良好的抽象接口
(2):管理、调度进程,并且将多个进程对硬件的竞争变得有序
2:多道技术:
(1):产生背景:针对单核,实现并发
(2):空间上的复用:如内存中同时有多道程序 ,多个程序共用一套计算机硬件
(3):时间上的复用:复用一个cpu的时间片,切换+保持状态(如洗衣服的等待同时可以去烧水、做饭等)
1.当一个程序遇到IO操作 操作系统会剥夺该程序的cpu执行权限(提高了cpu的利用率 并且也不影响程序的执行效率)
2..当一个程序长时间占用cpu 操作系统也会剥夺该程序的cpu执行权限(降低了程序的执行效率)
3:进程调度:
(1):先来先服务调度算法
(2):短作业优先调度算法
(3):时间片轮转法
(4):多级反馈队列
4:进程的并行与并发
并行:并行是指两者同时进行,
并发:看起来像是同时运行,如客户端在同时访问,时间的间隔很短
单核的计算机不能实现并行,但是可以实现并发
区别:
并行:也就是在一个精确的时间片刻,有不同的程序在执行。并发:在一个时间端上可以看出是同时进行的
多线程中的并发,并行与串行的区别:
你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。
你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。
并行的关键是你有同时处理多个任务的能力。
并发就是同步的串行,一个任务执行完执行下一个任务;
三、同步异步阻塞非阻塞
同步异步:表示的是任务的提交方式
同步:任务提交之后,原地等待任务的执行并拿到返回结果才走,期间不做任何事(程序层面的表现就是卡住了)
异步:任务提交之后,不再原地等待 而是继续执行下一行代码(结果是可以获取,但是用其他方式获取)>>> 结果的获取使用异步回调机制
阻塞非阻塞:表示的程序的运行状态
阻塞:阻塞态
非阻塞:就绪态或者运行态
注意:强调:同步异步 阻塞非阻塞是两对概念 不能混为一谈
在程序中的表示
同步阻塞形式:
效率最低:比如只专心的排队,什么事也不用管
异步阻塞形式:排队的同时可以做别的事,等待通知在执行下去
四、进程的创建与结束
1: 创建进程的两种方式:
from multiprocessing import Process import time def test(name): print('%s is running'%name) time.sleep(3) print('%s is over'%name) # """ # windows创建进程会将代码以模块的方式 从上往下执行一遍 # linux会直接将代码完完整整的拷贝一份 # windows创建进程一定要在if __name__ == '__main__':代码块内创建 否则报错 # """ if __name__ == '__main__': p = Process(target=test,args=('egon',)) # 创建一个进程对象 p.start() # 告诉操作系统帮你创建一个进程 print('主') >>> 主进程 james is running james is over
第二种是利用模块的内置方法继承
# 创建进程的第二种方式 以继承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('主') >>>
# 创建进程就是在内存重新开辟一块内存空间
# 将允许产生的代码放进去
# 一个进程对应在内存就是一块独立的内存空间
# 进程与进程之间数据是隔离的 无法直接交互
# 但是可以通过某些技术实现间接的交互
2:join 方法
from multiprocessing import Process import time def test(name,i): print('%s in running' % name) time.sleep(i) print('%s in over '% name ) if __name__ == '__main__': p_list= [] p=Process(target=test,args=("james",1)) p1= Process(target=test,args=('jack',2)) p2= Process(target=test,args=('kevin',3)) start_time = time.time() p.start() # 仅仅是告诉操作系统帮你创建一个进程 至于这个进程什么时候创 操作系统随机决定 p1.start() p2.start() # p3.start() p.join() # 主进程代码等待子进程运行结束 print("主进程") print(time.time()-start_time)
p.start() 仅仅只是告诉操作系统帮你创建一个进程,至于什么时候创,随机的.
p.join() 主进程等待子进程完全结束 后才执行之后的代码
进阶,多个进程同时运行(注意,子进程的执行顺序不是根据启动顺序决定的)
五、进程间的数据是隔离的
# 进程之间的数据是隔离的 import time from multiprocessing import Process money =100 def index(): global money money=90000000 if __name__ == '__main__': p=Process(target=index) p.start() p.join() time.sleep(1) # 休眠1s 确保 子进程已经完全运行 print(money) print('数据获取了全局的值吗?') >>> 100 数据获取了全局的值吗?
通过代码执行的结果验证了子进程与主进程的数据是隔离的状态,不能访问到全局外的数据
六、multiprocess模块,multtiprocess.process模块
process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建。
习惯看函数的内部封装原理,了解Process 内部的传参target:目标 对象
强调:
1. 需要使用关键字的方式来指定参数
2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号,即使所传的元素只有一个也要加,不然会报错。
参数介绍:
1 group参数未使用,值始终为None
2 target表示调用对象,即子进程要执行的任务
3 args表示调用对象的位置参数元组,args=(1,2,'egon',)
4 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
5 name为子进程的名称
方法介绍:
1:p.start():启动进程,并调用该子进程中的p.run()
2:p.join只能join住start开启的进程,而不能join住run开启的进程
3:p.terminate():强制终止进程p,不会进行任何清理操作
4 p.is_alive():如果p仍然运行,返回True
属性:
1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
2 p.name:进程的名称
3 p.pid:进程的pid
在windows中使用process模块的注意事项
创建子进程时必须放到if __name__ ==‘__main__ 后面
七、多进程的其他方法
1:查看主进程和子进程的进程号
例如:python是在pycharm里运行的 pycharm就是python的主主进程
import os from multiprocessing import Process def f(x): print('子进程id :',os.getpid(),'父进程id :',os.getppid()) return x*x if __name__ == '__main__': print('主进程id :', os.getpid()) p_lst = [] for i in range(5): p = Process(target=f, args=(i,)) p.start()
2: p.terminate() 强制终止当前进程,只是告知操作系统把进程结束,至于什么时候结束不一定,
加个time.sleep(),让子进程完全结束掉
p.is_alive() 判断进程是否存活,结果返回的是布尔值,False或True
from multiprocessing import Process,current_process import os import time def test(name): # print('%s is running'%name,current_process().pid) print('%s is running'%name,'子进程%s'%os.getpid(),'父进程%s'%os.getppid()) time.sleep(3) print('%s is over'%name) if __name__ == '__main__': p = Process(target=test,args=('egon',)) p.start() p.terminate() # 杀死当前进程 其实是告诉操作系统帮你杀死一个进程 time.sleep(0.1) print(p.is_alive()) # 判断进程是否存活 # print('主',current_process().pid) print('主',os.getpid(),'主主进程:%s'%os.getppid())
3:守护进程:p.daemon
会随着主进程的结束而结束
主进程创建守护进程:
1:守护进程会在主进程代码执行结束后就终止
2:守护进行内无法在开启子进程,否者抛出异常:AssertionError:
进程自己是互相独立的,主进程代码运行结束,守护进程随即终止
from multiprocessing import Process import time def test(name): print("%s老二还活着"%name) time.sleep(2) print("%s老弟正常死亡"%name) if __name__ == '__main__': p=Process(target=test,args=('james',)) p.daemon=True p.start() time.sleep(0.1) #守护进程时间过短执行完第一句就直接回主进程打印 print("老大奄奄一息!") >> james老二还活着 老大奄奄一息!
七、僵尸进程与孤儿进程
僵尸进程: 一个父进程利用fork创建子进程,如果子进程退出,而父进程没有利用wait 或者 waitpid 来获取子进程的状态信息,那么子进程的状态描述符依然保存在系统中。
父进程回收子进程资源的两种方式
1.join方法
2.父进程正常死亡
所有的进程都会步入僵尸进程
孤儿进程:一个父进程退出, 而它的一个或几个子进程仍然还在运行,那么这些子进程就会变成孤儿进程,孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集的工作
孤儿进程
子进程没死 父进程意外死亡
针对linux会有儿童福利院(init) 如果父进程意外死亡他所创建的子进程都会被福利院收
互斥锁
当多个进程操作同一份数据的时候 会造成数据的错乱, 这个时候必须加锁处理,例如抢票,在只有一张票的情况不加锁的时候,都显示抢票成功
将并发变成串行
虽然降低了效率但是提高了数据的安全
注意:
1.锁不要轻易使用 容易造成死锁现象
2.只在处理数据的部分加锁 不要在全局加锁
锁必须在主进程中产生 交给子进程去使用
from multiprocessing import Process,Lock # 模块 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()
结果:加锁后,当有一个抢票成功后,后面的已经没有了,数据
: