昨日回顾
操作系统发展史
-
穿孔卡片
-
联机批处理
-
脱机批处理
-
多道技术:
单道:多个程序一串串执行
多道:
切换+保存状态
-
空间上的复用
一个计算机(CPU)的空间可以提供给多个程序使用.
-
时间上的复用
当前程序遇到IO操作,就会立马切换CPU的执行权限
当前程序使用CPU时间过长,就会立马切换CPU的执行权限
-
-
并发与并行
- 并发:看起来像同时运行。
- 并行:真是意义上的同时运行。
- 在单核的情况下不能实现并行,要想实现并行,必须有多个CPU
进程
-
程序:一堆代码。
-
进程:一堆代码运行的过程。
-
进程调度:时间片论法 + 分级反馈队列
-
进程的三种状态:
-
就绪态
所有进程在创建时都会先进入就绪态
-
运行态
通过进程调度将每一个进程调入运行态
-
阻塞态
在运行态中的程序遇到IO就会进入阻塞态
若IO结束,会立马进入就绪态,不会直接进入运行态
注意:CPU的执行时间过长会看起来像阻塞,其实并不是阻塞,剥夺其CPU执行权限,然后会将该进程重新返回就绪态。
-
-
同步与异步
- 同步:一个任务提交后,在原地等待该任务结束后,下一个才能提交并且执行
- 异步:一个任务提交后,不需要原地等待,立马可以执行下一个任务
-
阻塞与非阻塞
- 阻塞:阻塞态(遇到IO,会进入阻塞态)
- 非阻塞:就绪态、运行态
-
创建进程的两种方式
from multiprocessing import Process
import time
方式一:
def task(): print('子进程开始执行') time.sleep(1) print('子进程结束完毕') # 在windows系统下创建,必须要再__main__执行 - __main__ 下: # target = 执行任务(函数地址) p = Process(target = task) p.start() #告诉操作系统,创建子进程 # join p.join() # 让主进程等待所有子进程结束后才能结束 print('主进程')
方式二:
- 自定义类,继承Process class Myprocess(Process): def run(self): # 此处是子进程的任务 print('子进程开始执行') time.sleep(1) print('子进程结束完毕') - __main__下: p = MyProcess() p.start() # 告诉操作系统,创建子进程 # join p.join() # 让主进程等待所有子进程结束后才结束 print('主进程')
- join() 让主进程等待所有子进程结束后才能结束
- 进程间数据是隔离的
- 进程对象的属性
# 可以获取子进程 pid号 current_process().pid --->return Process().pid # 在子进程中打印是子进程的pid号,在父进程中打印父进程的pid号 os.getpid() # 主主进程pid号 os.getppid() current_process()-->p # 终止子进程 p.terminate() # 判断进程是否存活 is_alive
-
守护进程
主进程结束后,子进程也要跟着结束
# 注意:必须要在start()之前设置 p.daemon = True p.start() # p.daemon = True 报错
-
父进程回收子进程PID号的两种方式:
- join
- 父进程正常结束后
-
僵尸进程与孤儿进程(了解):
-
僵尸进程:子进程结束后,父进程没有回收
PID
,导致子进程永久保留PID
号缺点:占用
PID
号,占用操作系统资源 -
孤儿进程:子进程还未结束,父进程意外结束,导致子进程在结束后父进程无法回收
此使子进程就像一个"孤儿",然后由操作系统自带的"福利院"来回收.
-
进程互斥锁
串行
A和B两个任务运行在一个CPU线程上,在A任务执行完之前不可以执行B。即,在整个程序的运行过程中,仅存在一个运行上下文,即一个调用栈一个堆。程序会按顺序执行每个指令。
进程互斥锁
让并行变串行,牺牲了执行效率、保证了数据安全,保证了数据安全,
在程序并发执行时,需要修改数据时使用。
队列
队列:先进先出
相当于一个内存中产生一个队列空间,可以存放多个数据,但数据的顺序是由先进去的排前面。
堆栈
先进后出
IPC
(进程间通信)
进程间数据是相互隔离的,若想实现进程间通信,可以利用队列
生产者与消费者
生产者:生产数据的
消费者:使用数据的
生活中:卖油条,一边生产油条,一边卖油条,供需不平衡
程序中:通过队列,生产者把数据添加队列中,消费者从队列中获取数据
线程
定义:线程与进程都是虚拟单位,目的是为了更好地描述某种事物。
- 进程:资源单位
- 线程:执行单位
开启一个进程,一定会有一个线程,线程才是真正的执行者。
为什么要使用线程?
节省内存资源.
- 开启进程:
- 开辟一个名称空间,每开启一个进程都会占用一份内存资源
- 会自带一个线程
- 开启线程:
- 一个进程可以开启多个线程
- 线程的开销远小于进程
注意:内存就像一个工厂,子进程就像一个工厂车间,线程就像车间内的流水线
线程之间数据是共享的