参考资料 :小林Coding、阿秀、代码随想录
一、进程调度
当⼀个进程的状态发⽣改变时,操作系统需要考虑是否要换⼀个进程执行,这就需要⽤到“进程调度算法”。
1. 调度目标
不同的调度算法具有不同的特性,因为使用以下标准比较处理机调度算法的性能:
- CPU利⽤率:CPU是计算机系统中最重要和昂贵的资源之⼀,应该使CPU保持“忙碌”状态
- 系统吞吐量:单位时间内CPU完成作业的数量。
- 周转时间:作业从提交到完成所需要的时间,是作业等待、在就绪队列中排队、在处理机上运⾏及输⼊输出操作所花费时间的总和。
2. 进程调度方式
- 非抢占调度方式:当⼀个进程正在处理中,即使有更为重要的进程进入到就绪队列中,仍然让正在执行的进程继续执行。
- 抢占调度方式:当⼀个进程正在处理中,如果有更为重要的进程进入到就绪队列中,则允许调度程序根据某种原则去暂停正在执行的进程,将处理机分配给更为重要或紧迫的进程
3. 调度算法
3.1 先来先服务调度算法(First Come First Severd, FCFS)
每次从就绪队列选择最先进入队列的进程,然后一直运行,直到进程退出或被阻塞,才会继续从队列中选择第一个进程接着运行。
非抢占式的调度算法,按照请求的顺序进行调度。
有利于长作业,但不利于短作业,因为短作业必须一直等待前面的长作业执行完毕才能执行,而长作业又需要执行很长时间,造成了短作业等待时间过长。适用于 CPU 繁忙型作业的系统,而不适用于 I/O 繁忙型作业的系统。
3.2 最短作业优先调度算法 (shortest job first, SJF)
优先选择运行时间最短的进程来运行,这有助于提高系统的吞吐量。
非抢占式的调度算法,按估计运行时间最短的顺序进行调度。
长作业有可能会饿死,处于一直等待短作业执行完毕的状态。因为如果一直有短作业到来,那么长作业永远得不到调度。
3.3 高响应比优先调度算法(Highest Response Ratio Next, HRRN)
高响应比优先调度算法主要是权衡了短作业和长作业。每次进行进程调度时,先计算响应比优先级,然后把响应比优先级最高的进程投入运行,响应比优先级的计算公式:
- 如果两个进程的「等待时间」相同时,「要求的服务时间」越短,「响应比」就越高,这样短作业的进程容易被选中运行;
- 如果两个进程「要求的服务时间」相同时,「等待时间」越长,「响应比」就越高,这就兼顾到了长作业进程,因为进程的响应比可以随时间等待的增加而提高,当其等待时间足够长时,其响应比便可以升到很高,从而获得运行的机会。
3.4 时间片轮转调度算法(Round Robin, RR)
最古老、最简单、最公平且使用最广的算法就是时间片轮转调度算法。
将所有就绪进程按 FCFS 的原则排成一个队列,每次调度时,把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。
时间片轮转算法的效率和时间片的大小有很大关系:
- 因为进程切换都要保存进程的信息并且载入新进程的信息,如果时间片太小,会导致进程切换得太频繁,在进程切换上就会花过多时间。
- 而如果时间片过长,那么实时性就不能得到保证。
通常时间片设为 20ms ~ 50ms 通常是一个比较合理的折中值。
3.5 最高优先级调度算法 (Highest Priority First,HPF)
从就绪队列中选择最高优先级的进程进行运。
进程的优先级可以分为,静态优先级或动态优先级:
- 静态优先级:创建进程时候,就已经确定了优先级了,然后整个运行时间优先级都不会变化;
- 动态优先级:根据进程的动态变化调整优先级,比如如果进程运行时间增加,则降低其优先级,如果进程等待时间(就绪队列的等待时间)增加,则升高其优先级,也就是随着时间的推移增加等待进程的优先级。
该算法也有两种处理优先级高的方法,非抢占式和抢占式:
- 非抢占式:当就绪队列中出现优先级高的进程,运行完当前进程,再选择优先级高的进程。
- 抢占式:当就绪队列中出现优先级高的进程,当前进程挂起,调度优先级高的进程运行。
但是依然有缺点,可能会导致低优先级的进程永远不会运行。
3.6 多级反馈队列调度算法(Multilevel Feedback Queue)
多级反馈队列调度算法是时间片轮转算法和最高优先级算法的综合和发展。
- 多级表示有多个队列,每个队列优先级从高到低,同时优先级越高时间片越短。
- 反馈表示如果有新的进程加入优先级高的队列时,立刻停止当前正在运行的进程,转而去运行优先级高的队列;
- 设置了多个队列,赋予每个队列不同的优先级,每个队列优先级从高到低,同时优先级越高时间片越短;
- 新的进程会被放入到第一级队列的末尾,按先来先服务的原则排队等待被调度,如果在第一级队列规定的时间片没运行完成,则将其转入到第二级队列的末尾,以此类推,直至完成;
- 当较高优先级的队列为空,才调度较低优先级的队列中的进程运行。如果进程运行时,有新进程进入较高优先级的队列,则停止当前运行的进程并将其移入到原队列末尾,接着让较高优先级的进程运行;
3.7 最短剩余时间优先 (shortest remaining time next,SRTN)
最短作业优先的抢占式版本,按剩余运行时间的顺序进行调度。 当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。
如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
二、进程同步
1. 临界区
对临界资源进行访问的那段代码称为临界区。为了互斥访问临界资源,每个进程在进入临界区之前,需要先进行检查。
四大区:
- 临界区:临界段,在每个程序中,访问临界资源的那段程序。
- 进入区:用于进入临界区前检查临界资源是否正在被访问的代码块。
- 退出区:在临界区之后用于将临界区正在被访问的标志恢复为未被访问的状态的代码块。
- 剩余区:除进入区、临界区及退出区之外的其它部分的代码。
2. 同步和互斥
- 同步:多个进程因为合作产生的直接制约关系,使得进程有一定的先后执行关系。
- 互斥:多个进程在同一时刻只有一个进程能进入临界区。
同步机制应遵循的规则:
- 空闲让进:当无进程处于临界区时,表明临界资源处于空闲状态,应允许一个请求进入临界区的进程立即进入自己的临界区,以有效地利用临界资源。(多中选一)
- 忙则等待:当已有进程进入临界区时,表明临界资源正在被访问,因而其它试图进入临界区的进程必须等待,以保证对临界资源的互斥访问。 (互斥访问)
- 有限等待:对要求访问临界资源的进程,应保证在有限时间内能进入自己的临界区,避免陷入"死等"状态。 (避免死等)
- 让权等待:当进程不能进入自己的临界区时,应立即释放处理机,避免进程陷入"忙等"状态。 (避免忙等)
3. 信号量
信号量(Semaphore)是一个整型变量,可以对其执行 down 和 up 操作,也就是常见的 P 和 V 操作。
- down : 如果信号量大于 0 ,执行 -1 操作;如果信号量等于 0,进程睡眠,等待信号量大于 0;
- up :对信号量执行 +1 操作,唤醒睡眠的进程让其完成 down 操作。
down 和 up 操作需要被设计成原语,不可分割,通常的做法是在执行这些操作的时候屏蔽中断。
如果信号量的取值只能为 0 或者 1,那么就成为了 互斥量(Mutex) ,0 表示临界区已经加锁,1 表示临界区解锁。
4. 管程
为每个可共享资源设立一个专门的管程,来统一管理各进程对该资源的访问。
管程的定义:一个管程包含一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据(构成一个操作系统的资源管理模块)。
管程有一个重要特性:在一个时刻只能有一个进程使用管程。进程在无法继续执行的时候不能一直占用管程,否则其它进程永远不能使用管程。
管程引入了 条件变量 以及相关的操作:wait() 和 signal() 来实现同步操作。对条件变量执行 wait() 操作会导致调用进程阻塞,把管程让出来给另一个进程持有。signal() 操作用于唤醒被阻塞的进程。