操作系统发展史
1.穿孔卡片
手工操作,且一个计算机机房,只能被一个用户使用
2.联机批处理系统
支持多用户去使用一个计算机机房,主机与输入机之间增加一个存储设备——磁带,在运行于主机上的监督程序的自动控制下,计算机可自动完成
3.脱机批处理系统
为克服与缓解:高速主机与慢速外设的矛盾,提高CPU的利用率
特征是:增加一台不与主机直接相连而专门用于与输入/输出设备打交道的卫星机。主机与卫星机可并行工作,二者分工明确,可以充分发挥主机的高速计算能力。
高速磁盘: 提高文件的读取速度
为改善CPU的利用率,又引入了多道程序系统。
4.多道程序系统
所谓多道程序设计技术,就是指允许多个程序同时进入内存并运行,并允许它们交替在CPU中运行,当一道程序因I/O请求而暂停运行时,CPU便立即转去运行另一道程序
单道程序运行过程
多个使用者使用CPU时是串行的
在A程序计算时,I/O空闲, A程序I/O操作时,CPU空闲(B程序也是同样);必须A工作完成后,B才能进入内存中开始工作,两者是串行的,全部完成共需时间=T1+T2。
多道程序运行过程
将A、B两道程序同时存放在内存中,它们在系统的控制下,可相互穿插、交替地在CPU上运行
* 空间上的复用:
多个程序公用一个内存,彼此隔离,物理级别的隔离
* 时间上的复用:
共用一个cpu
1.若CPU遇到IO操作,会立即将当前程序CPU使用权断开
优点: CPU利用率高
2.若一个程序使用CPU的时间过长,会立即将房钱执行程序CPU使用权断开
缺点: 程序执行率降低
多道程序系统的出现,标志着操作系统渐趋成熟的阶段,先后出现了作业调度管理、处理机管理、存储器管理、外部设备管理、文件系统管理等功能。
并发与并行
并发: 指的是看起来像同时在运行,其实是多个程序不停切换+保存状态
并行: 真实意义上的同时运行,在多核的情况下,同时执行多个程序. 多核
串行: 一个任务完整执行结束,再运行下一个任务
进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础
1.程序与进程
程序: 一堆代码
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
进程: 一堆代码运行的过程
进程是程序在处理机上的一次执行过程,它是一个动态的概念。
2.进程调度
1.先来先服务调度:
a,b程序,若a程序先来,先占用CPU
缺点: 程序a先使用,程序b必须等待程序a使用CPU结束后才能使用
2.短作业优先调度:
a,b程序,谁的用时短,先优选调度使用CPU
缺点: 若程序a使用时间最长,有N个程序使用时间短,必须等待所有用时 短的程序结束后才能使用
3.时间片轮转法:
CPU执行的时间1秒中,加载N个程序,要将1秒等分为N个时间片
4.分级反馈队列
将执行优先分为多层级别
1级:优先级最高
2级:优先级第二
3级:...
5.调度状态
进程执行时的间断性,决定了进程可能具有多种状态。
就绪态
所有进程创建时都会进入就绪态,准备调度
运行态
调度后的进程,进入进程态
阻塞态
凡是遇到I/O操作的进程,都会进入阻塞态,I/O结束后,必须重新进入就绪态
6.进程回收
1.进程回收的两种条件
- join,可以回收子进程与主进程
- 主进程正常结束,子进程与主进程也会被回收
2.僵尸进程
指的是子进程已经结束,但PID号还存在,未销毁
缺点: 占用PID号,占用操作系统资源
PID号:PID是各进程的代号,运行时系统随机分配,但是进程终止后PID标识符就会被系统回收,进程号具有固定数量.
3.孤儿进程
指的是子进程还在执行,但父进程意外结束.
操作系统优化机制:自动回收此类子进程
4.守护进程
指的是主进程结束后,该主进程产生的所有子进程跟着结束,并回收.
p.daemon = true
设置为守护进程
p.daemon = true (设置为守护进程)
p.start
# 必须在p.start之前设置,如果在之后会报错
3.进程对象的属性
1.current_process().pid
在子进程内使用,可以获取子进程号
from multiprocessing import Process
from multiprocessing import current_process
def task():
print('开始执行...',current_process().pid)
print('结束执行')
if __name__ == '__main__':
p = Process(target=task) # 获得process对象
p.start() # 创建子进程
p.join() # 执行完子进程在执行主进程
print('主进程')
'''开始执行... 2092
结束执行
主进程'''
2.os.getpid()
在主进程中获得主进程号.
from multiprocessing import Process
from multiprocessing import current_process
import os
def task():
print('开始执行...',current_process().pid)
print('结束执行')
if __name__ == '__main__':
p = Process(target=task) # 获得process对象
p.start() # 创建子进程
p.join() # 执行完子进程在执行主进程
print('主进程',os.getpid())
'''
开始执行... 11072
结束执行
主进程 13892
'''
3.os.getppid
可以查看主主进程的进程号(pycharm中运行代码,主主进程就是pycharm)
def task():
print('开始执行...',current_process().pid)
time.sleep(1)
print('结束执行')
if __name__ == '__main__':
p = Process(target=task) # 获得process对象
p.start() # 创建子进程
p.join() # 执行完子进程在执行主进程
print('主进程',os.getpid())
print('主主进程',os.getppid())
time.sleep(1000)
'''
开始执行... 12368
结束执行
主进程 10332 # python.exe
主主进程 4152 # pycharm64.exe
'''
4.join()方法
代码
会让子进程结束后再结束主进程
# 让子进程结束后,父进程才结束
from multiprocessing import Process
import time
def task(name):
print('任务开始')
time.sleep(1)
print('任务结束')
if __name__ == '__main__':
p = Process(target= task,args=('你好',))
p.start() # 告诉操作系统,开启子进程
print('join上面的不算')
p.join() # 告诉操作系统,等子进程结束后,父进程再结束
print('主进程')
'''
join上面的不算
任务开始
任务结束
主进程'''
多个子程序的运行
# 多个子进程的运行
from multiprocessing import Process
import time
def task(name,n):
print(f'{name}任务{n}开始')
time.sleep(1)
print(f'{name}任务{n}结束')
if __name__ == '__main__':
p1 = Process(target= task,args=('AAA',1))
p2 = Process(target= task,args=('BBB',2))
p1.start() # 告诉操作系统,开启子进程
p2.start() # 告诉操作系统,开启子进程
print('join上面的不算')
p1.join() # 告诉操作系统,等子进程结束后,父进程再结束
p2.join() # 告诉操作系统,等子进程结束后,父进程再结束
print('主进程')
'''
join上面的不算
AAA任务1开始
BBB任务2开始
AAA任务1结束
BBB任务2结束
主进程
'''
打印时会出现任务1,2顺序的不一致,貌似是因为程序并行导致cpu分配执行打印速度导致
5.is_alive()
判断子进程是否存活
def task():
print('开始执行...',current_process().pid)
if __name__ == '__main__':
p = Process(target=task) # 获得process对象
p.start() # 创建子进程
print(p.is_alive()) # 判断子进程是否存活
print('主进程',os.getpid())
'''
True
主进程 3740
开始执行... 7004'''
6..terminate()
直接告诉操作系统,终止子程序
def task():
print('开始执行...',current_process().pid)
if __name__ == '__main__':
p = Process(target=task) # 获得process对象
p.start() # 创建子进程
# 判断子进程是否存活
print(p.is_alive())
# 告诉操作系统直接终止掉子进程
p.terminate()
time.sleep(0.1)
# 判断子进程是否存活
print(p.is_alive())
print('主进程',os.getpid())
'''
True
False
主进程 7976'''
7.tasklist | findstr+进程号
# cmd 中查询进程号: tasklist | findstr 进程号
4.进程的创建
错误示范
from multiprocessing import Process
import time
# 1.定义一个任务
def task(name):
print(f'{}的任务开始执行')
time.sleep(1)
# target = 执行函数的地址 args= 是传入函数的变量元组形式
p = Process(target=task,args=('abc0',))
# 向操作系统提交创建进程的任务
p.start()
'''注意: 在Windows下:创建子进程,会将当前父进程的代码重新加载执行一次
以上执行创建子进程时会创建新的空间,会形成递归.
linux/mac: 会将当前父进程代码重新拷贝一份,再去执行
'''
1.创建进程方式1
from multiprocessing import Process
import time
# 1.定义一个任务
def task(name):
print(f'{name}的任务开始执行')
time.sleep(1)
print(f'{name}的任务结束执行')
if __name__ == '__main__':
# target = 执行函数的地址 args= 是传入函数的变量元组形式
p = Process(target=task,args=('abc0',))
# 向操作系统提交创建进程的任务
p.start()
print('主进程')
'''# 先执行主进程,在创建的字进程执行
主进程
abc0的任务开始执行
abc0的任务结束执行
'''
2.创建进程方式2
自定义一个类,并继承Process
# 1.自定义一个类,并继承Process
class MyProcess(Process):
# 父类的方法
def run(self):
print('任务开始执行...')
time.sleep(1)
print('任务结束执行...')
if __name__ == '__main__':
p = MyProcess()
p.start() # 创建子进程
print('主进程')
'''
主进程
任务开始执行...
任务结束执行...
'''
5.进程之间的数据相互隔离
主进程子进程会产生各自的名称空间
from multiprocessing import Process
x = 100
def func():
global x
x = 20
if __name__ == '__main__':
p = Process(target= func)
p.start()
print(x)
print('主程序')
'''x = 100 '''
并行/串行/并发
1. 概念
假设有AB两个任务,则串行、并行、并发的区别如图1所示。
###### 串行
A和B两个任务运行在一个CPU线程上,在A任务执行完之前不可以执行B。即,在整个程序的运行过程中,仅存在一个运行上下文,即一个调用栈一个堆。程序会按顺序执行每个指令。
###### 并行
并行性指两个或两个以上事件或活动在同一时刻发生。在多道程序环境下,并行性使多个程序同一时刻可在不同CPU上同时执行。比如,A和B两个任务可以同时运行在不同的CPU线程上,效率较高,但受限于CPU线程数,如果任务数量超过了CPU线程数,那么每个线程上的任务仍然是顺序执行的。
###### 并发
并发指多个线程在宏观(相对于较长的时间区间而言)上表现为同时执行,而实际上是轮流穿插着执行,并发的实质是一个物理CPU在若干道程序之间多路复用,其目的是提高有限物理资源的运行效率。 并发与并行串行并不是互斥的概念,如果是在一个CPU线程上启用并发,那么自然就还是串行的,而如果在多个线程上启用并发,那么程序的执行就可以是既并发又并行的。
同步和异步
指的是提交任务的方式
同步
若有两个任务需要提交,在提交第一个任务时,必须等待该任务执行结束后,才能继续提交并执行第二个任务
同步,是所有的操作都做完,才返回给用户结果
异步
指的是若有两个任务需要提交,在提交第一个任务时,不需要原地等待,立即可以提交并执行第二个任务
异步,不用等所有操作等做完,就响应用户请求
阻塞与非阻塞
阻塞
阻塞态,遇到IO一定会阻塞
非阻塞
就绪态,运行态
面试题
同步和异步,阻塞与非阻塞是同一个概念么
答:不是同一个概念,不能混为一谈. IO操作与代码的执行
最大化提高CPU的使用率提高,尽可能减少io操作