《操作系统》学习笔记---进程、死锁、调度和中断
目录
进程
进程的概念
进程是一个程序在某个数据集合的一次运行
- 程序:
- 静态的概念,属于单道程序系统中的概念
- 顺序性:程序所规定的每个动作都在上一个动作结束后才开始
- 封闭性:只有程序本身的动作才能改变程序运行的环境
- 可再现性:程序的执行结果和程序的执行速度无关
- 进程
- 动态的概念,属于多道程序系统中的概念
- 动态性:有多种状态
- 并发性:多个进程在一段时间内都能得到运行
- 异步性:各进程的推进速度不可预知
进程的状态
- 就绪状态:进程可以运行,等待分配CPU
- 运行状态:进程分配到CPU
- 阻塞状态:进程不可以运行,等待某种事情的发生(比如输入)
- 新建状态:进程刚被创建,没完成所有分配工作。创建完毕后就进入就绪状态
- 终止状态:不会再被调度,等待系统撤销
- 挂起状态:系统回收内存资源,将其实体复制到外存
进程的状态转换
- 新建->就绪:系统将新建好的进程放入就绪队列
- 就绪->运行:就绪进程被调度程序选中,分配CPU
- 运行->阻塞:正在运行的进程因某种条件为满足而放弃CPU(比如进程要读入文件,读入过程进程无法执行)
- 阻塞->就绪:阻塞进程等待的事件已发生(比如读完文件了)
- 运行->就绪:正在运行的进程用完了CPU时间片
- 运行->终止:正在运行的进程完成了任务或发生了异常
进程映像(进程上下文)
- 概念上来说是PCB+栈+程序+数据集合
- 用户级上下文:虚拟地址空间中0~3GB的用户空间,即代码段、数据段、栈段(用户栈)、堆段、共享内存段
- 内核级上下文:虚拟地址空间中3~4GB的内核空间,即PCB表项、页表、内核栈
- 硬件级上下文:程序计数器、段寄存器、通用寄存器、指针寄存器、标志寄存器
进程队列(组织PCB)
- 线性方式:一个PCB线性表
- 链接方式:各个状态有自己的指针,指针指向对应状态的PCB
- 索引方式:一个PCB线性表,各个状态有自己的索引表,索引表存放PCB地址
线程(只是概念,所以和进程一起总结)
线程是进程中实施调度和分配的基本单位,有时候也称LWP
- 线程控制块
- 线程描述符
- 一组寄存器的内容
- 用户栈、内核栈
- 私有存储区,存放现场保护信息和其他与该进程相关的统计信息
- 进程和线程的关系
- 进程是资源分配的最小单位;线程是CPU调度的最小单位
- 一个进程可以有多个进程,至少一个;一个线程只属于一个进程
- 进程切换开销大;线程切换开销小,共享进程上下文
- 线程实现
- 用户级线程:线程库放在用户空间,内核感知不到,由程序实现调度
- 优点:① 线程切换速度快,不需要系统调用;②可运行在任意操作系统上,甚至是不支持线程机制的操作系统;③根据程序需求定制调度算法,不干扰底层的调度程序
- 缺点:①一个线程阻塞导致同个进程内其他线程也被阻塞;②使用多处理器时无法使用多线程的优点,因为核心只为进程分配处理器
- 内核级线程:在内核空间实现线程,内核可以感知,由内核进行调度
- 优点:①多处理器系统中,内核可以调度同一进程的多个线程;②一个进程内的某个线程阻塞了,内核可以调度该进程的另一个线程
- 缺点:①调度开销大
- 用户级线程:线程库放在用户空间,内核感知不到,由程序实现调度
进程通信
低级进程通信(解决进程的同步与互斥)
信号量
- 同步:逻辑相关的两个或多个进程,协调使用同一资源,而产生执行时序的关系
- 互斥:逻辑不相关的两个或多个进程,争用同一资源,不具有执行时序的关系
- 临界资源:一次只运行一个进程使用的共享资源
- 临界区:每个进程中访问临界资源的那段代码
- 经典进程同步问题
- 生产者-消费者问题
- 读者-写者问题
- 哲学家进餐问题
- 理发师问题
信号
软件层次上对中断机制的模拟,异步进程通过发送信号实现简单的通信。其中,信号的发送方相当于中断源,接收方(必定是进程)相当于CPU
- 信号机制和中断机制的相似之处
- 信号机制和中断机制在概念上是一样的。进程接收信号,处理器接收中断请求;进程可以向自身或其他进程发送信号,处理器可以向自身或其他处理器发送中断请求
- 都是异步的。进程(处理器)执行时,不需要等待信号(中断请求),也不知道什么时候有信号(中断请求)到达。(所有进程间通信方式只有信号是异步的)
- 都采用了“向量表”进行实现。中断有中断向量表从而找到中断处理程序;信号也有对应的向量表从而进入信号处理程序
- 都有屏蔽手段。
- 信号机制和中断机制的差别
- 中断实现需要软硬件结合,信号实现只需要软件
- 中断向量表和中断处理程序都在内核空间;而信号的向量表在内核空间,信号处理程序在用户空间
- CPU接到中断请求后会立即做出响应和处理;而信号的检测和处理需要在特定情况进行
- 发送过程:比如kill发送
- TASK_STRUCT中有共享挂起信号队列(在信号描述符结构体中,线程共享一个结构体)和私有挂起信号队列(每个TASK_STRUCT有自己的)
- 内核查看目标进程TASK_STRUCT关于信号的屏蔽位图检查进程是否屏蔽该信号
- 查看目标进程TASK_STRUCT的挂起信号队列是否已存在该信号,若存在则返回
- 目标进程的TASK_STRUCT放入挂起信号队列,根据目标进程的状态选择是否唤醒
- 若已唤醒,则强制当前进程进行调度,让目标进程尽快注意到新挂起信号
- 关于多线程:如果是指定线程,放在私有挂起信号队列;如果是指定线程组,放在共享挂起信号队列,唤醒没有阻塞该信号的线程处理
- 信号的检测和处理
- 检测时机
- 在陷入(系统调用)处理程序退出之前,即从内核空间返回用户空间的前夕
- 时钟中断处理程序结束之前
- 进程以低优先级请求睡眠时
- 总的来说就是从内核态返回用户态时检查
- 处理
- 检测时机
- 缺点
- 不可避免的迟延
- 信号会被丢失,信号不可靠(位图只能记录一个相同信号)
高级进程通信
管道
管道是一个文件,写进程向文件写入数据,读进程从文件读出数据
消息传递
- 直接通信:发送进程直接把消息挂在接收进程的消息缓冲队列,接受进程从消息缓冲队列中得到消息
- 间接通信:信箱(消息队列),发送进程把消息送到一个共享的数据结构
共享内存
在内存分配一片空间作为共享内存,需要进行通信的进程把共享内存附加到自己的地址空间,然后就想操作自己的数据一样操作共享内存的数据
网络通信(客户-服务器系统)
socket
一对进程利用socket通过网络进行通信
远程过程调用RPC
- remote procedure call,允许程序调用另外机器上的过程
- 具体步骤:
- 客户过程以通常方式调用客户代理
- 客户代理构造一个消息,陷入内核
- 客户内核发送消息给远程内核
- 远程内核把消息送给服务器代理
- 服务器代理从信包中取出参数并调用服务器
- 服务器完成相应的服务,并将结果送给服务器代理
- 服务器代理将结果打包成消息,陷入内核
- 远程内核把消息发送给客户内核
- 客户内核把消息传给客户代理
- 客户代理取出结果,并返回客户的调用程序
死锁
一个进程集合中的每个进程都在等待仅由该集合中的另一个进程才能引发的事件而无限期地僵持下去的局面
- 口语化:两个或多个进程都在等待对方占用的资源,从而都阻塞了
- 根本原因:资源操作不当
死锁的条件
当计算机系统同时具备下面4个条件,会发生死锁
- 互斥条件:独占资源在一段时间内只能由一个进程占有,不能同时被两个以及以上的进程占有
- 占有且等待条件:进程至少已经占有了一个资源,但又申请新的资源
- 不可抢占条件:一个进程占有的资源只能自己释放,其他进程不可抢夺
- 循环等待条件:存在两个或多个进程互相等待对方占有的资源,形成一个循环
死锁处理
死锁的预防(静态策略)
破坏其中一个条件
- 破坏互斥条件
- 不成立,因为有些资源必定是互斥的,有些共享的资源也不需要互斥
- 破坏占有且等待条件
- 预分资源策略:一个进程执行之前就分配到所有资源
- 空手申请资源策略:进程只能在不占有资源时才能申请资源
- 缺点:
- 进程是动态的,不会执行前就知道自己需要的所有资源
- 资源利用率低,有些资源可能进程结束之前才使用一次
- 降低进程并发性,资源是有限的,能占有其需要的所有资源的进程必定很少
- 可能出现饥饿现象,一个进程需要很多紧张的资源,那么总有别的进程占有它需要的部分资源
- 破坏不可抢占条件
- 抢占等待者的资源:适用于资源容易保留和恢复的环境,比如CPU寄存器和内存空间;不适用于打印机之类必定不能抢占的资源
- 破坏循环等待条件
- 资源有序策略:为资源排序,进程必须按序申请资源
- 优点:
- 提高资源利用率
- 提高系统吞吐量
- 缺点:
- 限制了进程对资源的清求,并且对资源编号很困难
- 增加进程对资源的占用时间
死锁的避免(动态策略)
在进行资源分配之前,对进程申请的资源进行检查,根据检查结果决定是否分配资源
- 银行家算法:用户申请一组资源时,系统做出判断,如果把资源分配出去,系统剩余资源是否还能满足任意一个进程。若可以,分配;否则,不分配
- 相当于破坏了循环等待条件
- 优点:
- 限制条件少了
- 资源利用率提高
- 缺点:
- 要求进程数不变
- 响应不够及时,因为查找安全序列(预测过程)需要时间
死锁检测和恢复(常用)
静态和动态策略都有限制条件,会影响操作系统的效率。
系统为进程分配资源不加限制,但是会按策略定下的时间来(比如定时或发生资源请求时)检测发现死锁,再从死锁状态中解脱出来
- 检测
- 单体资源:利用等待图,有环路即发生死锁
- 多体资源:和银行家算法类似
- 恢复
- 抢占资源:临时性地把资源从当前占有它的进程那里拿过来分给另外的进程,直至死锁环路被打破。资源是否能抢占取决于其属性,一般来说很难实现
- 回退执行:系统定期记录,并将检查点写入文件。发生死锁时,将占有必要资源的进程回退,释放资源给死锁进程
- 一次回退一个特定进程,并且确定这个进程后退多远。最简单就是直接重启一个进程
- 全体死锁的进程一起回退到前面定义的某个监测点
- 杀死进程:
- 终止所有死锁进程
- 一次终止一个进程直到消除死锁环路
死锁的忽略
鸵鸟策略,不管死锁,重启系统。适用于普通主机,不适用于服务器
饥饿
在某个策略下,某个进程一直得不到运行的机会,因为它所需要的资源不断被别的进程抢占
- 和死锁不同的地方:死锁必定处于阻塞状态;饥饿不一定阻塞,可能在就绪状态
- 例子:打印机,系统每次选出需要打印文件最小的进程
- 解决:动态优先级、FIFO的资源分配
活锁
一个或多个进程在轮询地等待某个不可能为真的条件为真,导致一直重复尝试
- 导致问题:耗尽CPU资源,使系统效能大大下降
- 和死锁不同的地方:死锁处于阻塞状态不变;活锁的状态一直变化,是活的,并且是忙时等待,占有CPU但不会主动让出CPU
- 例子:避让算法设置的不合适,两个进程申请、退让、申请一直同步,就一直得不到资源;允许抢占的自旋锁
- 解决:退让时间随机生成
调度
进程调度
功能
- 保存现场
- 挑选进程
- 恢复现场
时机
- 创建进程
- 进程终止
- 等待事件
- 中断发生
- 运行到时
基本方式
- 非抢占方式:得到CPU的进程会一直运行直到结束或需要等待某一事件导致阻塞才会进行调度
- 抢占方式:允许调度程序根据某种策略中止正在运行的进程,比如创建了新进程、中断发生、运行到时
评价准则
- CPU利用率:在系统运行过程中,CPU使用的时间百分比
- 吞吐量:单位时间内CPU完成作业的数量
- 周转时间:从作业提交(创建)到作业完成需要的时间
- 周转时间Ti=作业等待进入内存+进程在就绪队列等待+进程在CPU上执行+IO操作的总时间
- 平均周转时间:(ΣTi)/n。用来衡量不同调度算法对相同作业流的调度性能
- 平均带权周转时间:Wi=Ti/Ri,(ΣWi)/n,R是实际运行时间(进程在CPU上执行)。用来衡量不同调度算法对不同作业流的调度性能
- 就绪等待时间:进程在就绪队列等待的时间
- 响应时间:作业提交到第一次响应的时间。因为周转时间算上了IO操作的时间,受到输出设备速度的限制
调度算法
- 先来先服务算法:非抢占式。顾名思义就是从就绪队列中选择最先来的进程运行,如果阻塞了就放到队尾
- 有利于长作业,不利于短作业,因为短作业需要等待长作业的话T大R小,导致平均带权周转时间很大
- 和IO繁忙型比较更适合CPU繁忙型,因为IO等待阻塞会重新放到队尾,导致平均带权周转时间很大
- 短作业优先算法:非抢占式。CPU先分配给运行时间短的进程
- 优点:提高系统吞吐量,平均等待时间短
- 缺点:不利于长作业,长作业需要等所有短作业完成才可以运行
- 最短剩余时间优先法:抢占式。当新进程加入就绪队列,如果它需要的运行时间比当前运行的进程所需的剩余时间短,则进行切换
- 优点:保证新的短进程能很快得到服务
- 缺点:增加系统开销,抢占式需要保存现场,统计剩余时间也需要开销
- 优先级法:
- 静态优先级:创建进程时就确定,运行期间保持不变
- 优点:系统开销小,易于实现
- 缺点:会出现饥饿现象
- 动态优先级:随着进程推进不断改变,运行时间越长优先级越低,等待事件越长优先级越高
- 优点:防止饥饿现象
- 缺点:需要一定的系统开销,进程运行过程需要调整优先级
- 非抢占式优先法:非抢占式。从就绪队列中取出优先级最高的进程运行
- 抢占式优先法:抢占式。当就绪队列中出现优先级比当前运行的进程的优先级高的进程,则进行切换
- 静态优先级:创建进程时就确定,运行期间保持不变
- 时间片轮转法:抢占式。先来先服务算法的基础上,每个进程只能在CPU运行一个时间片的时间
- 性能取决于时间片大小
- 一个时间片长度:略大于一次典型交互所需要的时间,至少用户不会感觉到卡顿
- 时间片大小的取决因素:
- 系统的响应时间
- 就绪队列的进程数目,时间片大小反比就绪队列的进程数目
- 进程的切换时间
- CPU运行指令的速度
- 优点:时间片小,CPU忙于切换进程,开销大
- 缺点:时间片大,退化成先来先服务算法
- 有利于CPU繁忙型,因为IO密集型进场用不完一个时间片就被阻塞,而CPU繁忙型进程每次都可以用完一个时间片
- 改进:添加一个优先级比就绪队列高的辅助队列,发生IO阻塞后解除的进程移进辅助队列
- 多级队列法:按时间片抢占式。将就绪队列根据进程特性划分成多个队列,每个队列有自己的调度算法、静态优先级。
- 运行完高优先级的队列才可以运行低优先级
- 进程被抢占时降级
- 高优先级队列使用FCFS,低优先级队列使用轮转
- 缺点:饥饿现象,不断有新的进程到达
- 多级反馈队列法:抢占式。在多级队列的基础上,加上反馈机制,动态优先级
- 具体实现
- 系统中设置多个就绪队列,每个队列对应一个优先级
- 各就绪队列中进程的运行时间片不同,高优先级队列的时间片小,低优先级队列的时间片大
- 新进程进入系统后,先放入第一个队列的末尾。运行过后没有完成的话加入到下一个队列的末尾
- 系统先运行第一个队列的进程,直到该队列为空后,运行第二个队列的进程,以此类推
- 特点:后进入的进程也可能先完成;低优先级进程运行时,高优先级进程进入队列,低优先级进程运行完时间片后马上到高优先级进程运行
- 优点:解决多级队列法的饥饿现象
- 缺点:出现新的饥饿现象,由于高优先级也会降到低优先级,又会重新出现多级队列的饥饿现象。解决方法是可以在低优先级进程等待一段时间后提高其优先级
- 具体实现
- 高响应比优先法:非抢占式。前面计算性能都是在事后计算周转时间按的,可以事前计算响应比模拟性能计算作为优先级,即调度时计算响应时间再选择响应比最高的进程
- 优点:同时照顾到了短进程和长进程
- 缺点:调度时计算响应比,增加系统开销
- 公平共享法:按用户拥有进程数占总进程数的比例来进行CPU的分配
- 优先级反转问题:低优先级进程占有资源阻塞,高优先级进程需要这个资源也阻塞住了,但是低优先级进程一直得不到运行,高优先级的进程也无法运行,一直都是中优先级的进程在运行
- 解决方法:①高优先级进程因此阻塞时,占有该资源阻塞的进程优先级被提高到高优先级;②将申请资源的进程优先级提高到需要该资源最高优先级的进程的优先级
线程调度
-
用户级线程
- 内核不知道线程的存在,所以不负责线程的调度。核心为进程分配时间片,进程的内部线程调度程序选择线程运行
- 任意一种进程的调度算法都能用于线程调度,唯一限制是时间中断对线程不起作用
- 可能序列:A1,A2,A3,B1,B2,B3
- 不可能序列:A1,B1,A2,B2,A3,B3
-
内核级线程
- 内核支持现成的情况下,由内核调度线程,即内核从就绪线程中选出一个线程分配时间片(不考虑是哪个进程的,当然内核是知道线程是哪个进程的)
- 可能序列:A1,A2,A3,B1,B2,B3
- 可能序列:A1,B1,A2,B2,A3,B3
中断
中断是指CPU对系统发生的某个事件做出的一个反应,它使CPU暂停正在执行的程序,保留现场后自动执行响应的处理程序
中断的作用
- 提高主机的利用率,使高速CPU和低速外设并行工作
- 及时进行事故处理
- 通过时钟中断实现分时操作
- 实现实时操作
- 方便设置断点调试程序
中断的类型
- 中断:由CPU以外的事件引起的,I/O中断、时钟中断等,和当前执行的进程无关。异步的
- 异常:由CPU内部的事件或程序执行中的事件引起的过程,CPU故障(电压低)、程序故障(地址越界、除数为0)、系统调用等。同步的
中断处理过程
- 中断响应(硬件)
- 通常,CPU在执行完一条指令后,立即检查有无中断请求
- 中止当前程序的执行,关中断
- CPU根据控制器提供的中断号在中断向量表中检索,转到相应的中断处理程序
- 在内核栈中保存现场
- 执行完毕后恢复环境,开中断,返回被中断进程
中断屏蔽
- 中断屏蔽:在提出中断请求后,CPU不予响应的状态
- 中断禁止:在可引起中断的事件发生时系统不接收该中断信号,因而就不可能提出中断请求而导致中断
- 中断屏蔽和中断禁止的作用:
- 延迟或禁止对某些中断的响应。某些系统程序运行时不希望被干扰,以避免对重要数据操作失误
- 协调中断响应与中断处理的关系
- 防止同类中断的相互干扰。有些系统的同类中断只有一个中断处理程序状态字,所以在处理一个中断时不能响应后一个同类的中断
多重中断
- 顺序处理:一个中断在处理时屏蔽其他中断;处理完再开中断处理其他中断
- 嵌套处理:赋予每类中断不同优先级,允许高优先级中断打断低优先级中断的处理程序