操作系统发展史
计算机的组成:
输入设备
输出设备
CPU
1.穿孔卡片
一个计算机房只能使用一个卡片
缺点:CPU利用率极低
2.联机批处理
支持多用户去使用一个计算机机房
3.脱机批量处理
高速磁盘:提高文件的读取速度
优点:提高CPU的利用率
4.多道技术
单道:必须让一个程序结束之后,下一个程序才能继续执行
多道技术:允许多个程序同时进入内存,并允许它们交替在CPU中运行
1.空间上的复用(重点):一个CPU可以提供给多个用户去使用
2.时间上的复用(重点):切换+保存状态
注意:遇到IO切换,占用CPU时间长也切换,核心在于切之前将进程的状态保存下来,这样才能保证下次切换回来时,能基于上次切走的位置继续运行
IO操作:inpot(),print(),time.sleep(3)。
(1).若CPU遇到IO操作,会立即将当前执行程序CPU使用权断开,
优点:CPU的利用率高
(2).若一个程序使用CPU时间过长,会立即将当前执行的程序CPU使用权断开
缺点:程序的执行率降低
进程
1.程序与进程:
程序:一堆代码
进程:一堆代码运行的过程
区别:同一个程序执行两次,就会在操作系统中出现两个进程,所以我们可以同时运行一个软件,分别做不同的事情也不会混乱。
2.进程的特征
动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
并发性:任何进程都可以同其他进程一起并发执行
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
异步性:由进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的,不可预知的速度向前推进。
结构特征:进程由程序,数据和进程控制块三部分组成
3.进程的调度
现在操作系统:时间片轮转法+分级反馈队列
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级:... ...
... ... ... ...
越往下的队列,优先级越低。
4.并发与并行
并发:指的是看起来像同时运行,多个程序不停切换+保存状态,目的是提高效率。
并行:真实意义上的同时运行,在多核(多个CPU)的情况下,同时执行多个程序
注意:现在的主机一般是多核,呢么每个核都会利用多道技术,有四个CPU,运行与CPU1的某个程序遇到IO阻塞,会等到IO结束在重新调度,会被调度到四个CPU中的任意一个,具体由操作系统调度算法决定。
5.程序调度时的三个状态
1.就绪态:当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。
2.运行态:当进程已获得处理机,期程序正在处理机上执行每次是的进程装太称之为执行状态
3.阻塞态:正在执行的进程,由于等待某个时间发生而无法执行时,便放弃处理机而处于阻塞状态,引起进程阻塞的事件可有多种,例如,等待I/O完成,申请缓冲区不能满足,等待信件(信号)等。
程序在进行调度时候的流程:程序在开始运行之前,并不是立即开始执行代码,而是会进入就绪状态,等待操作系统调度开始运行,等操作系统开始运行的时候,程序进入运行状态,等程序遇到等待用户输入操作时候(I/O),程序就进入阻塞状态,当用户输入完成之后,程序不是立即运行的,而是进入就绪状态,等待操作系统的调度继续运行,
6.同步和异步
指的时提交任务的方式
同步:
若有两个任务需要提提交,在提交第一个任务时,必须等待该任务执行结束后,才能执行第二个任务。简单的说就是一个一个来,前一个完成之后,后一个才会进行。类似于银行排队。
异步:
指的是若有两个任务,在提交第一个任务时,不需要等待任务结束,立即可以提交执行第二个任务。类似于多人抢购。
7.阻塞与非阻塞
阻塞:阻塞态,遇到IO一定会阻塞。在等待的时候,如果除了等待其他的事情都不能再做,就是阻塞,表现在函数中就是改善程序一直阻塞在该函数调用出不能继续往下执行
非阻塞:就绪态,运行态。除了等待还可以继续做别的事情,就是非阻塞态;
注意:同步非阻塞行式实际上时效率低下的,想象一下,你一边打电话,一边还需要抬头看队伍有没有到你,如果把打电话和还差排队的位置看成时程序的两个操作,这个层序需要在这两种不同的行为之间来回切换,效率可想而知是低下的,而异步非阻塞行式是没有这种问题的,因为打电话(等待者)时你的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换。
8.关于同步/异步与阻塞/非阻塞之间的组合
1.同步阻塞形式
效率最低。拿上面的例子来说,就是你专心排队,什么别的事都不做。
2.异步阻塞形式
如果在银行等待办理业务的人采用的是异步的方式去等待消息被触发(通知),也就是领了一张小纸条,假如在这段时间里他不能离开银行做其它的事情,那么很显然,这个人被阻塞在了这个等待的操作上面。
异步操作是可以被阻塞住的,只不过它不是在处理消息时阻塞,而是在等待消息通知时被阻塞。
3.同步非阻塞形式
实际上是效率低下的。
想象一下你一边打着电话一边还需要抬头看到底队伍排到你了没有,如果把打电话和观察排队的位置看成是程序的两个操作的话,这个程序需要在这两种不同的行为之间来回的切换,效率可想而知是低下的。
4.异步非阻塞形式
效率更高,
因为打电话是你(等待者)的事情,而通知你则是柜台(消息触发机制)的事情,程序没有在两种不同的操作中来回切换。
比如说,这个人突然发觉自己烟瘾犯了,需要出去抽根烟,于是他告诉大堂经理说,排到我这个号码的时候麻烦到外面通知我一下,那么他就没有被阻塞在这个等待的操作上面,自然这个就是异步+非阻塞的方式了。
很多人会把同步和阻塞混淆,是因为很多时候同步操作会以阻塞的形式表现出来,同样的,很多人也会把异步和非阻塞混淆,因为异步操作一般都不会在真正的IO操作处被阻塞。
9.面试题:
同步和异步,阻塞与非阻塞是同一个概念吗?
答:不是同一个概念,不能混为一谈
10.最大化提高CPU的使用率
尽可能减少不必要的IO操作
进程使用
对于通用系统来说,需要有系统运行过程中创建或撤销进程的能力。主要分为4种行式创建新的进程
1.系统初始化
2.一个进程在运行过程中开启了子进程(如nginx开启多进程)
3.用户的交互式请求,而创建一个新的进程(如双击QQ)
4.一个批处理作业的初始化(只在大型机的批处理系统中应用)
新的进程的创建都是由一个已经存在的进程执行了一个用于创建进程的系统调用而创建的,
1.进程的创建
运行中的程序就是一个进程,所有的进程都是通过他的父进程来创建的,运行起来的python程序也是一个进程,我们可以在程序中再创建进程,多个进程可以实现并发效果,也就是说,当我们的程序中存在多个进程的时候,再某些时候,就会让程序的执行速度变快。
2.进程使用的模块---multiprocess模块
1.创建进程的模块---Process模块
Process是一个创建进程的模块,借助这个模块,就可以完成进程的创建,由该类实例化得到的对象表示一个子进程中的任务(尚未启动)
Ⅰ. Process中参数的介绍:
① group参数未使用,值始终为None
② target表示调用对象,即子进程要执行的任务
③ args指定的为传给targe函数的参数,表示调用对象的位置参数元组,是一个元组形式,必须有逗号,
④ kwargs表示调用对象的字典
⑤ name为子进程的名称
Ⅱ. 使用方法
p.start():启动进程,并调用该进程中的p.run()。
p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义的类中一定要实现该方法。
p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况,如果p还保存了一个锁,呢么也将不会被释放,进而导致死锁。
p.is_alive():如果p仍运行,返回True。
p.join([timeout]):主线程等待p终止,(强调:是主线程处于等的状态,而p是出于运行状态),timeout是可选的超时时间,需要强调的是p.join只能join住start开启的进程,而不能join住run开启的进程
Ⅲ 属性介绍
p.name:进程的名称
p.pid:获取继承的子进程号
p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
Process使用方法
创建一个进程
# 创建一个进程
from multiprocessing import Process
import time
def f(name):
print('hello',name)
time.sleep(1)
print('我是子进程')
if __name__ == '__main__':
p = Process(target=f,args=('boy',))
p.start()
p.join()
print('执行主进程内容了')
# 输出内容:
执行主进程内容了
hello boy
我是子进程
join的使用方法
# 创建一个进程
from multiprocessing import Process
import time
import os
def f(name):
print('hello',name)
time.sleep(1)
print('我是子进程')
if __name__ == '__main__':
p = Process(target=f,args=('boy',))
p.start()
p.join()
print('执行主进程内容了')
# 输出结果
hello boy
我是子进程
执行主进程内容了
进程间数据是相互隔离的,主进程与子进程会产生各自的名称空间
进程号回收的两种方式
1.join,可以回收子进程与主进程
2.主进程正常结束,子进程与主进程也会被回收
from multiprocessing import Process
from multiprocessing import current_process
import os # 与操作系统交互
import time
def task(name):
print(f'{name} start...', current_process().pid)
time.sleep(1)
print(f'{name} over..', current_process().pid)
if __name__ == '__main__':
p = Process(target=task, args=('jason', ))
p.start() # 告诉操作系统,开启子进程
# 判断子进程是否存活
print(p.is_alive())
# 直接告诉操作系统,终止 子进程
p.terminate()
time.sleep(0.1)
# 判断子进程是否存活
print(p.is_alive())
p.join() # 告诉操作系统,等子进程结束后,父进程再结束。
print('主进程', os.getpid())
print('主主进程', os.getppid())
time.sleep(100)
守护进程
from multiprocessing import Process
from multiprocessing import current_process
import time
def task(name):
print(f'{name} start...', current_process().pid)
time.sleep(5)
print(f'{name} over..', current_process().pid)
print(f'管家{name}')
if __name__ == '__main__':
p1 = Process(target=task, args=('jason', ))
# 添加守护进程参数
p1.daemon = True # True代表该进程是守护进程
p1.start()
print(f'egon 驾鹤西去...')
进程的结束
1.正常退出(点击程序的关闭)
2.出错退出(自愿,文件不存在)
3.严重错误(非自愿,执行非法指令)
4.被其他程序杀死(非自愿,如kill-9)