看王道做的笔记,结合一些自己的思考
目录
组成
PID 唯一,有表示,控制,分配,PSW,PC
进程是动态的,但进程实体(程序?)是静态的,多了程序段和数据段,进程是进程实体的运行过程,是资源分配和调度的唯一单位
在一般情况下,我们把进程实体就称为进程。所谓的创建进程, 实际上是创建进程实体中的 PCB,而撤销进程,实际上是撤销进程实体的 PCB,进程实体是进程的一个快照,一个照片
3 个 QQ 是三个进程,PCB 和数据段不同,但程序段(程序)不同
所以进程是动态的,程序是静态的,进程是程序的一次执行
动态(动态产生,动态消亡),并发(多个并发),独立(分配和调度的唯一单元),异步(进度不可预知),结构(都是 PCB+ 程序 + 数据)
状态
堵塞态是程序在运行态主动变的,
堵塞队伍可能有多个
就绪态是具备了执行所需的资源,至少现在认为、
控制
原语通过关中断和开中断这两条特权指令实现,实现中间无法外中断
堵塞和唤醒成对
创建
创建原语:
-
申请 PCB
-
分配资源
-
初始化 PCB
-
插入就绪队列
不可中断,看起来就只有 PCB 申请和分配资源可以交换
在登录,放入,要求服务,应用请求的时候创建
所以用户是第一个进程
终止
撤销原语:
-
找到要终止的 PCB
-
终止子进程
-
剥夺 CPU
-
资源归还
-
删除 PCB
要先终止子进程,否则子进程就成了孤儿进程了
在正常结束,异常,外界干预(如用户关了它)时终止
唤醒
原语:找到,改信息,插入就绪队列
要求:资源到了
堵塞
原语:找到,改信息,插入堵塞队列
注意,已经分配的资源会被保留,
阻塞会释放 CPU,一般不释放内存
切换
原语:
运行环境存入 PCB
PCB 存入相应队列
通信
共享
基于数据结构,低级,慢,限制多
基于存储区,高级,快,自由
消息传递
(直接通信)通过消息队列传递,要知道 PID
(间接通信)通过信箱为中介
管道
半双工,像水管
消息被读了就会消失
读堵塞:空了
写堵塞:满了
多个写,一个读(应试)
多个写,轮流读(Linux)
进程
超进程是利用不满的流水线,模拟的双进程
用户级
系统不知道,靠线程库
切换方便,但一个堵塞全堵了,无法利用多核
内核级
系统知道,是处理机调度的单位,基本 CPU 执行单元
不用系统资源
状态和进程一样
只有一个堵了其他还可以跑,可以多核,切换不变,要利用内核
模型
首先是内核级
一对一:
原始的内核级
多对一:
相当于用户级,现在的 python 就是如此
多对多:
部分内核级线程对应多个用户级进程,切换方便,堵了其他大概率还是可以跑的
TCB
TID、PC、状态、优先级、堆栈、其他寄存器
调度
现在的一般都是抢占式的,一个时间片几十毫秒,windows10 和 LInux 都是这个量级
调度类型
高级(作业调度)
少,创建进程
中级(内存调度)
相对少,指挂起态到就绪态,使用虚拟内存,休眠和挂起并没有写入线程、进程生命周期的状态
挂起:
挂起是一般主动的,恢复是主动的,会占有 CPU,但释放内存,和休眠不同
时间片到了,也会挂起线程。???
休眠:
释放了锁,不占有 CPU,到点就来排队,如果是 sleep,那就是占着 CPU 睡觉,这里指的是 wait
低级(进程调度)
让进程从就绪到运行
调度时机
主动
终止或者异常或者主动要求(如等待 I/O)
被动
时间片结束,或者更紧急的事情抢占
无法调度
-
中断
-
在内核程序临界区(即访问临界资源(互斥的资源)的代码) 所以访问临界区是可能可以的,然后使用内核不当,容易无法调度卡死
-
原语
调度器
调度的对象是线程(内核级线程)
在调度的时候运行
闲逛进程
优先级最低
可以是零地址(不要缓存)
结束时检测中断,看一看有没有其他进程
低能耗
调度算法
指标
CPU 利用率=忙碌 %
系统吞吐量=作业完成平均时间(注意,只算完成的,未完成的就只是分母)
等待时间=等待时间和
响应时间=运行-提交时间
周转时间
作业完成时间-提交时间
平均:和/作业数
带权周转时间:周转时间/实际运行时间
平均带权周转时间:带权周转时间和/作业数
非抢占
用于批处理
FCFS
先来后到,公平,但短作业要排好久
SJF/SPF
Shortest Job Time First,短的先跑,平均等待时间最短,可能饥饿,不利于长作业
HRRN
Highest Response Ratio Next,响应比=(等待时间 + 要求服务时间)/要求服务时间,即预计周转时间/预期需求时间,响应比最高的优先
优先级调度
有抢占式版本
抢占
时间片轮转
SRTN
shortest remaining time next,最短剩余时间优先
优先级调度
有非抢占式版本,系统优先,I/O 型优先
多级反馈队列
先进一队,时间片结束到二队,以此类推,前面没好,后面不运行,或者时间片长度不一样。可以交互式用 RR,批处理用 FCFS
互斥和同步
CPU 的 L3 cache 是共享的,这个速度是 SSD 的五到十倍
进入,临界,退出,剩余
原则
空闲可以进,忙就等
等待时间有限,进不了要释放处理机
互斥锁
锁
软件略
硬件
中断:
只能单处理机,只能内核,毕竟中断指令不是谁都可以用
swap、TSL、testAndSet:
通过交换,给谁就是谁上
不可以让权等待
多处理器使用,单处理机忙等无法解锁
信号量
P、V,即 wait 和 signal,PV 来自荷兰语
没申请到要排队,反正忙等
初始值为资源数,负数的绝对值为队伍长度
C++ 中并没有在语言级别上支持信号量,可以看一看这篇文章自己实现
同步
初始没有资源,所以是 0
所以是先 V 后 P。即先生产,再取走
合在一起可以写前驱关系
即先是检查入边的信号量,然后执行,再为出边的信号量添加一
生产者和消费者
管程
信号量机制的封装,意思是内部函数只能一个进程(线程)执行
java 的 synchronized,就是类似管程的操作
读者写者问题
读,正在写的信号量为一,每次消耗零,如何读的量先 P 再 V,顺序不可调
写,没有读的,正在读的人没有
死锁
概念
相互等待其他人的资源,导致一起堵塞(堵塞态),和饥饿不同,饥饿是得不到(可能只有一个,可能堵塞,可能一直在就绪队列排队)
条件
互斥
不剥夺:未使用完,不可以被强行夺走
请求和保持:至少有一个了,还要发出请求,但请求被堵塞,却又对已有的不放手
循环等待:已获得的又被其他的请求,还是循环的
如果每个资源只有一个,那循环等待等价于死锁
发生条件
互斥资源的竞争
推进不当
处理策略
预防
破坏必要条件
-
把互斥的变为可以共享的(少)
-
要求如果不满足,就全部释放,或者叫操作系统帮其强制剥夺需要的(如从低优先级的那里) 但整体实现复杂,而且可能出错,只能用于容易保存和恢复的资源,反复申请释放开销大,用前面那个可能饥饿
-
一次性就分配好,一直保持,但效率低,会饥饿
-
给资源编号,按编号递增申请,有了大编号就不会再需要小编号了,但用户使用麻烦,增添资源麻烦,实际顺序如果和编号顺序不符,就会导致浪费
避免
银行家算法
检测和解除
运行发生,系统检测然后消除
请求结点和资源节点
请求边和资源边
可消除就没有死锁(即有安全序列)
算法
寻找不堵塞的,消了,然后释放,以此类推,可以全部消除就可以简化
否则要解除:
-
资源剥夺 挂起某些死锁节点,然后夺走资源
-
终止 强制撤销部分
-
回退 还原到足以避免死锁的时候,这个可能就需要一些可持续性算法