第二章--处理机管理
井底之蛙,不知大海之宽阔,却知晓天空之蓝。
进程
- 程序:是静态的,就是个存放在磁盘里的可执行文件,是一系列的指令集合。
- 进程:是动态的,是程序的一次执行过程。
进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位。

当进程被创建时,操作系统会为该进程分配一个唯一的、不重复的身份证号——PID(Process ID,进程ID)。
除此之外,操作系统还会记录进程的以下信息:
- 分配了哪些资源
- 内存
- I/O设备
- 文件
- 进程的运行情况
- CPU时间
- 磁盘使用情况
- 网络流量
关于一个进程的所有信息都被保存在一个数据结构PCB(Process Control Block)中,即进程控制块。PCB是进程存在的唯一标志。
当一个进程被创建时,操作系统就会为其创建PCB,当进程结束时,会回收PCB。
进程实体
进程实体 = 进程PCB + 程序段 + 数据段
- 进程实体:进程在某一时刻的状态。
- PCB:存储进程的信息。
- 程序段:程序执行的指令。
- 数据段:在程序运行过程中产生的各种数据。
进程的特征
- 动态性:进程是程序的一次执行过程,是动态地产生、变化和消亡的。
- 并发性:内存中有多个进程实体,各进程可并发执行。
- 独立性:进程是能独立运行、独立获得资源、独立接受调度的基本单位。
- 异步性:各进程按各自独立的、不可预知的速度向前推进,操作系统要提供进程同步机制,来解决异步问题。
- 结构性:每个进程都会配备一个PCB。结构上看,进程由程序段、数据段、PCB组成。
进程的状态
创建态:
- 进程正在被创建时,它的状态就是创建态,在这个阶段操作系统会为进程分配资源、初始化PCB。
就绪态:
- 当进程创建完成后,便进入就绪态,处于就绪状态的进程已经具备运行条件,但由于没有空闲的CPU,就暂时不能运行。
运行态:
- 如果一个进程此时在CPU运行,那么这个进程就处于运行态。
阻塞态:
- 在进程运行过程中,可能会请求等待某个事件的发送,在这个事件发生之前,进程无法继续往下执行。
- 此时操作系统会让这个进程下CPU,并让他进入阻塞态。
- 当事件发生后,进程从阻塞态进入就绪态。
一个进程因什么事情而阻塞,就因什么事情而唤醒。
终止态:
- 一个进程可以执行exit系统调用,请求操作系统终止该进程。此时该进程会进入终止态。
- 操作系统会让该进程下CPU,并回收内存空间等资源,最后还要回收该进程的PCB。

在进程PCB中,会有一个变量state来表示进程当前的状态。
进程的组织
链接方式

索引方式

进程控制
进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能。
进程控制程序需要使用原语来实现。一气呵成,不可中断。
原语的执行具有原子性,即执行过程只能一气呵成,期间不允许被中断。
可以用”关中断”和”开中断”这两个特权指令实现原子性。
- 关中断:CPU在执行完一条指令后,不再检查是否有中断信号。
- 开中断:恢复检查中断。
进程创建->创建原语:
- 申请空白PCB
- 为新进程分配所需资源
- 初始化PCB
- 将PCB插入就绪队列
进程结束->撤销原语:
- 从PCB集合中找到终止进程的PCB
- 若进程正在运行,立即剥夺CPU,将CPU分配给其他进程。
- 终止所有子进程
- 将该进程拥有的所有资源归还给父进程或操作系统
- 删除PCB
进程阻塞->阻塞原语:
- 找到要阻塞的进程对应的PCB
- 保护进程运行现场,将PCB状态信息设置为阻塞态,暂时停止进程运行。
- 将PCB插入相应事件的等待队列。
进程唤醒->唤醒原语:
- 在事件等待队列中找到PCB。
- 将PCB从等待队列移除,设置进程为就绪状态。
- 将PCB插入就绪队列,等待被调度。
进程切换->切换原语:
- 将进程的运行环境信息存入PCB
- PCB移入相应的队列
- 选择另一个进程执行,并更新PCB
- 根据PCB恢复新进程所需的运行环境
上面过程中提到的:保存现场、恢复环境等概念,指的都是保存当前线程中CPU各种寄存器以及堆栈的状态。
进程通信
进程通信(Inter-Process Communication, IPC)是指两个进程之间产生交互数据。
进程是分配系统资源的单位(包括内存地址空间),因此各进程拥有的内存地址空间相互独立
为保证安全,一个进程不能直接访问另一个进程的地址空间。
共享存储

想要分享数据的进程,可以向操作系统申请一块共享存储区,多个进程可以在共享存储区中交互数据。
为避免出错,各个进程对共享空间的访问应该是互斥的。
各个进程可以使用操作系统内核提供的同步互斥工具。
分享方式:
基于数据结构的共享:

- 比如共享空间中只能放一个长度为10的数组。
- 这种共享方式速度很慢、限制多,是一种低级通信方式。
基于存储区的共享:

- 操作系统在内存中划出一块共享存储区,数据的形式、存放位置都由进程控制,而不是操作系统。
- 这种共享方式速度很快,是一种高级通信方式。
消息传递
进程间的数据交换以格式化的消息(Message)为单位。进程通过操作系统提供的”发送消息/接收消息”两个原语进行数据交换。
格式化的消息:
直接通信方式:消息发送进程要指明接收进程的ID。

间接通信方式:通过”信箱”间接地通信。因此又称”信箱通信方式”。

多个进程可以向同一个信箱中发送消息,也可以从同一个信箱中接收消息。
管道通信

管道:是一个特殊的共享文件,又名pipe文件。其实就是在内存中开辟一个大小固定的内存缓冲区。
管道类似于一个数据结构循环****队列,进程P负责向队列中
offer数据,而进程Q中负责poll数据。
- 管道只能采用半双工通信,某一时间段内只能实现单项传输。如果要实现双向同时通信,则需要设置两个管道。
- 各进程要互斥的访问管道
- 当管道写满时,写进程将互斥,直到读取进程将管道中的数据取走,即可唤醒写进程。
- 当管道读空时,读进程将阻塞,直到写进程往管道中写入数据,即可唤醒读进程。
- 管道权限:
- 一个管道允许多个写进程,一个读进程。
- 允许多个写进程,多个读进程,但系统会让各个读取进程轮流从管道中读取数据。
信号
区分信号与信号量:
- 信号量(Semaphore):实现进程间的同步、互斥
- 信号(Signal):实现进程间通信(IPC)。
信号:用于通知进程某个特定事件已经发生。进程收到一个信号后,对该信号进行处理。
实现原理
在进程的PCB中,一个待处理信号表和被阻塞信号表。
待处理信号表(pending):表示接收的信号但是还未处理。
被阻塞信号表(blocked):表示屏蔽的信号,置为1则屏蔽该信号。也称为信号掩码(signal mask)。

需要发送信号的进程可以使用 kill函数:
1 | |
- 接收信号的进程无法知道是谁发送的信号。
- 重复接收的信号会被丢弃。
信号的处理
时机:
- 当进程从内核态转为用户态时,例行检查是否有待处理的信号,如果有,就处理信号。

信号的处理
操作系统为每个信号设置了默认的信号处理程序。(一些信号默认忽略,不做处理)
执行进程可以为信号设置用户自定义的信号处理逻辑。(自定义的信号处理程序将覆盖默认处理程序)。
特点:
- 信号处理程序运行结束后,通常会返回进程的下一条指令继续执行(除非信号处理程序将进程阻塞或终止)。
- 一旦处理了某个信号,就将pending位重置为0。
- 重复收到的同类信号,将被简单的丢弃。
- 当同时收到多个不同类信号时,通常先处理序列号更小的信号。
- 有些信号不能被自定义,如Linux中的SIGKILL,SIGSTOP等。
线程
线程也被称为轻量级进程(LWP)。
有些进程可能需要同时做很对事情,而传统的进程只能串行的执行一系列程序。为例,引入了线程,来增加进程的并发度。

引入线程后,线程是基本的CPU执行单元,是程序执行流的最小单位。
进程与线程的资源分配:
- 进程:资源分配的基本单位(代码段、数据段、地址空间)。
- 线程:CPU调度的基本单位。
- 私有数据:堆栈、寄存器组、线程专用存储。
- 一个进程的所有线程,共享该进程的资源。
进程与线程的并发性:
- 进程间可以并发执行
- 线程间也可以并发执行
系统开销:
- 切换进程,需要切换运行环境,开销大
- 统一进程下的线程切换,不需要切换进程环境,开销小

线程的实现方式
用户级线程(User-Level Thread, UTL)
早期的操作系统(如:早期Unix)只支持进程,不支持线程。当时的”线程”是由线程库实现的。
只有用户可以感受到线程的存在,操作系统感受不到线程的存在。

最简单的线程库示例:
1 | |
手动将进程的运行再次分为三个时间片,来运行不同的代码逻辑。可以看作有三个线程。
优点:
- 用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高。
缺点:
- 当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可再多核处理机上并行运行。
内核级线程
内核级线程(kernel-Level Thread, KTL),由操作系统支持的线程。

优点:
- 当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理器上并行执行
缺点:
- 一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理成功太高,开销大。
多线程模型
在支持内核级线程的系统中,根据用户级线程和内核级线程的映射关系,可以划分为几种多线程模型。
一对一模型
一个用户级线程映射到一个内核级线程。每个用户进程有与用户级线程同数量的内核级线程。

优点:
- 当一个线程被阻塞后,别的线程还可以继续执行,并发能力强。多线程可在多核处理器上并行执行
缺点:
- 一个用户进程会占用多个内核级线程,线程切换由操作系统内核完成,需要切换到核心态,因此线程管理成功太高,开销大。
多对一模型
多个用户级线程映射到一个内核级线程,且一个进程只被分配一个内核级线程。

优点:
- 用户级线程的切换在用户空间即可完成,不需要切换到核心态,线程管理的系统开销小,效率高。
缺点:
- 当一个用户级线程被阻塞后,整个进程都会被阻塞,并发度不高。多个线程不可在多核处理机上并行运行。
操作系统此时只能看见内核级线程,因此只有内核级线程才是CPU分配的基本单位。
多对多模型
n个用户级线程映射到m个内核级线程(n >= m)。每个用户进程对应m个内核级线程。

用户级线程是”代码逻辑“的载体。
内核级线程是”运行机会“的载体。一段”代码逻辑”只有获得了”运行机会”才能被CPU执行。
内核级线程才是CPU分配的单位。
线程的状态与转换

组织与控制
组织管理线程的数据结构:TCB(线程控制块)和线程表(thread table)

处理机调度
当有一堆任务要处理,但由于资源有限,这些事情没法同时处理。这就需要确定某种规则来决定处理这些任务的顺序,这就是”调度”研究的问题。
调度的三个层次
高级调度(作业调度)
作业:一个具体的任务。
用户向系统提交了一个作业 = 用户让操作系统启动一个程序。(处理一个具体的任务)。
高级调度(作业调度):按一定的原则从外存的作业后备队列中挑选一个作业调入内存,并创建进程。**每个作业只调入一次,调出一次。**作业调入时会建立PCB,调出时才撤销PCB。

低级调度(进程调度/处理机调度)
低级调度:按照某种策略从就绪队列中选取一个进程,将处理器分配给它。
低级调度是操作系统中最基本的一种调度,调用频率很高,在一般的操作系统中都必须配置低级调度。

中级调度(内存调度)
内存不够时,可能将某些进程的数据调出外存。当内存空间空闲或者进程需要运行时再重新调入内存。
暂时调到外存等待的进程状态为挂起状态(suspend)。被挂起的进程PCB会被组织成挂起队列。
中级调度:按照某种策略决定将哪个处于挂起状态的进程重新调入内存。
一个进程可能会被多次调出、调入内存,因此中级调度发送的频率要比高级调度更高。

七状态模型
挂起状态可以进一步细分为:就绪挂起、阻塞挂起两种状态。

三层调度
| 调度类型 | 要做什么 | 调度发生在… | 发生频率 | 对进程状态的影响 |
|---|---|---|---|---|
| 高级调度 (作业调度) | 按照某种规则,从后备队列中选择合适的作业将其调入内存,并为其创建进程 | 外存$\rightarrow$ 内存 (面向作业) | 最低 | 无$\rightarrow$ 创建态 $\rightarrow$ 就绪态 |
| 中级调度 (内存调度) | 按照某种规则,从挂起队列中选择合适的进程将其数据调回内存 | 外存$\rightarrow$ 内存 (面向进程) | 中等 | 挂起态$\rightarrow$ 就绪态 (阻塞挂起 $\rightarrow$ 阻塞态) |
| 低级调度 (进程调度) | 按照某种规则,从就绪队列中选择一个进程为其分配处理器 | 内存$\rightarrow$ CPU | 最高 | 就绪态$\rightarrow$ 运行态 |
时机、方式、切换与过程
进程调度的时机
需要进行进程调度与切换的情况:
进程主动放弃CPU
- 正常终止
- 异常终止
- 进程主动请求阻塞
进程被迫放弃CPU
- 分给进程的时间片用完
- 有更紧急的事情处理
- 有更高优先级的进程进入队列
不能进行进程调度的情况:
- 在处理中断的过程中
- 在原子操作过程中
- 进程在操作系统内核程序临界区中
临界资源:一个时间段内只允许一个进程使用的资源。各进程需要互斥访问临界资源。
临界区:访问临界资源的代码。
解释:
- 访问内核程序临界区时,由于内核程序的特殊性,不会对内核程序临界区中的代码进行进程调度。希望让进程快点结束对于内核程序的访问。
- 访问其他临界区,如打印机等设备时,反而会进行进程调度,因为打印机是慢速资源,可以进程调度防止CPU空转。
进程调度的方式
非剥夺调度方式(非抢占方式):
- 只允许进程主动放弃处理机。在运行过程中即便有更紧迫的任务到达,当前进程依然会继续使用处理机,直到该进程终止或主动要求进入阻塞态。
剥夺调度方式(抢占方式):
- 当一个进程正在处理机上执行时,如果有一个更重要或更紧迫的进程需要使用处理机,则立即暂停正在执行的进程,将处理机分配给更重要紧迫的那个进程。
进程的切换与过程
进程切换:一个进程让出处理机,由另一个进程占用处理机的过程。
主要过程:
- 对原来运行进程各种数据的保存。
- 对新的进程各种数据的恢复。
进程的恢复包括:程序计数器、程序状态字、各种数据寄存器等。这些信息存储在进程控制块PCB中
进程的切换是有代价的,因此过于频繁的进行进程调度、切换,必然会导致整个系统的效率降低,使系统大部分事件都花在了进程切换上,而真正用于执行进程的时间减少。
调度器/调度程序(scheduler)

过程2,3由调度程序引起,调度程序决定:
- 让哪个进程运行。——调度算法
- 运行多长时间。——时间片大小。
调度时机:
- 创建新进程
- 进程退出
- 运行进程阻塞
- I/O中断发送(唤醒某些阻塞的进程)
非抢占式调度策略:只有运行进程阻塞或退出才触发调度程序工作。
抢占式调度策略:每个时钟中断或K个时钟中断会触发调度程序工作。
对于支持内核级线程的操作系统,调度的基本单位是线程
闲逛进程
闲逛进程(idle Process):是操作系统中的一个特殊进程,当系统中没有其他可运行的进程时,由调度器安排它运行,用来占住CPU,防止CPU处于不可控的空转状态。它通常优先级最低,只在系统完全空闲时才执行。
特性:
- 优先级最低
- 可以是0地址指令,占一个完整的指令周期。(指令周期末尾例行检查中断)
- 能耗低。
调度算法的评价指标
利用率
CPU利用率:CPU忙碌的时间占总时间的比例。
$$
\text{利用率} = \frac{\text{忙碌的时间}}{\text{总时间}}
$$
例题:

系统吞吐量
系统吞吐量:单位时间内完成作业的数量。
$$
\text{系统吞吐量} = \frac{\text{完成作业数量}}{\text{总时间}}
$$

周转时间
周转时间:指从作业被提交给系统开始,到作业完成为止的这段时间间隔。
$$
周转时间 = 作业完成总时间 - 作业提交时间
$$
平均周转时间:
$$
\text{平均周转时间} = \frac{\text{各作业周转时间之和}}{\text{作业数}}
$$
带权周转时间:对于周转时间相同的作业,实际运行时间长的作业在相同时间内被服务的时间更多,带权周转时间更小,用户满意度更高。
$$
\text{带权周转时间} = \frac{\text{作业周转时间}}{\text{作业实际运行的时间}}
$$
平均带权周转时间:
$$
\text{平均带权周转时间} = \frac{\text{各作业带权周转时间之和}}{\text{作业数}}
$$
等待时间
等待时间:指进程/作业处于等待处理机的状态时间之和,等待时间越长,用户满意度越低。
等待I/O完成期间进程也算是在被服务,所以不计入等待时间。
其实进程等待时间就是进程生命周期在就绪状态的时间段之和
响应时间
响应时间:指从用户提交请求到首次产生响应所用的时间。
响应时间是到开始响应的时间,而非到相应输出完成的时间,假设线程一提交就被CPU调度,则它的响应时间如下:

调度算法
饥饿:某个进程/作业长时间得不到服务
批处理系统调度算法
先来先服务(FCFS)
先来先服务(First Come First Serve)
等待越久的服务越先得到服务
- 算法思想:主要从公平的角度考虑
- 算法规则:按照作业/进程到达的先后顺序进行服务。
- 调度:
- 作业调度:考虑哪个作业先到达后备队列。
- 进程调度:考虑哪个进程先到达就绪队列。
- 是否抢占:非抢占算法。
- 优点:公平,算法时间简单
- 缺点:排在长作业后的短作业需要等待很长时间,带权周转时间很大,对短作业来说用户体验不好。
- 饥饿问题:不会。
示例:

短作业优先(SJF)
短作业优先(Shortest Job First)
调度当前已经到达且运行时间最短的作业运行。
算法思想:追求最少的平均等待时间,最少的平均周转时间,最少的平均带权周转时间。
算法规则:最短的作业/进程优先得到服务。
调度:即可用于作业调度,也可用于进程调度。
用于进程调度时称为”短进程优先”(SPF, Shortest Procss First)
是否抢占:SJF和SPF是非抢占式算法。但是也有抢占式版本——最短剩余时间优先算法(SRTN, Shortest Remaining Time Next)
优点:”
最短的“平均等待时间,平均周转时间。缺点:
- 不公平。对短作业有利,对长作业不利。
- 作业/进程的运行时间是由用户提供的,并不一定真实,不一定能做到真正的短作业优先。
饥饿问题:会,甚至可能让长作业一直”饿死”
上面我们提到SJF调度算法的平均等待时间、平均周转时间最少。但这其实是不严谨的。
对于非抢占式的SJF调度算法,只有在所有进程同时到达时,即所有进程同时可运行时,SJF调度算法的平均等待时间,平均周转时间最少。
而对于抢占式的SFJ算法即最短剩余时间优先算法(SRTN),它才是真正的平均等待时间、平均周转时间最少。
示例:
- 非抢占式:

- 抢占式:

高响应比优先(HRRN)
高响应比优先(Highest Response Ratio Next)
算法思想:要综合考虑作业/进程的等待时间和要求服务的时间。
算法规则:在每次调度时先计算各个作业/进程的响应比,选择响应比最高的作业/进程为其服务。
$$
\text{响应比} = \frac{\text{等待时间} + \text{要求服务时间}}{\text{要求服务时间}}
$$是否抢占:非抢占式算法。因此只有当前运行的作业/进程主动放弃处理机时,才需要调度,才需要计算响应比。
优点:
- 综合考虑了等待时间和运行时间。
- 等待时间相同时,要求服务时间短的优先。
- 要求服务时间相同时,等待时间长的优先
- 对于长作业来说,随着等待时间越来越长,其响应比会越来越大,从而避免了长期饥饿。
饥饿问题:不会
示例:

总结:

交互式系统调度算法
时间片轮转调度算法(RR)
时间片轮转(Round-Robin)
- 如果时间片太大,使得每个进程都可以在一个时间片内就完成,则时间片轮转调度算法退化为先来先服务调度算法,并且会增大进程响应时间,因此时间片不能太大。
- 如果时间片太小,进程切换过于频繁,系统花费大量的时间用来处理进程切换,从而导致实际执行任务的时间比例减少。因此时间片不能太小。
- 算法思想:公平地、轮流地为各个进程服务,让每个进程在一定时间间隔内都可以得到响应。
- 算法规则:按照各进程到达就绪队列的顺序,轮流的让各个进程执行一个时间片(如100ms)。若进程未在一个时间片内执行完,则剥夺处理机,将进程重新放到就绪队列队尾重新排队。
- 调度:用于进程调度(只有作业放入内存建立了相应的进程后,才能被分配处理机时间片)。
- 是否抢占:若进程未能在时间片内运行完成,将被强行剥夺处理机使用权,因此时间片轮转调度算法属于抢占式的算法。由时钟装置发出时钟中断来通知CPU时间片已到。
- 优点:公平,响应快,适用于分时操作系统
- 缺点:
- 由于高频率的进程切换,因此有一定开销
- 不区分任务的紧急程序
- 饥饿问题:不会。
示例:(时间片为2)

优先级调度算法
优先级调度算法(Priority scheduling)
根据优先级在创建后是否可以动态改变,可将优先级分为:
- 静态优先级
- 动态优先级
- 调整优先级的时机:
- 进程等待时间很久->适当提升优先级(响应比)
- 进程运行时间很久->适当降低优先级
- 进程频繁I/O操作->适当提升优先级
优先级排序:
- 系统进程>用于进程
- 前台进程>后台进程
- 操作系统更偏好I/O繁忙型进程
I/O设备可以和CPU并行的工作,让I/O繁忙型优先运行可以让I/O设备尽早的投入工作,可以提升资源利用率,系统吞吐量。
算法思想:随着计算机的发展,特别是实时操作系统的出现,越来越多的应用场景需要根据任务的紧急程序来决定处理顺序。
算法规则:每个作业/进程有各自的优先级,调度时选择优先级最高的作业/进程。
调度:
- 作业调度:选择一个处于外存中后备队列的作业进入内存。
- 进程调度:选择一个在内存中就绪队列的进程为他分配处理机。
- 甚至还可以用于I/O调度
是否抢占:
- 抢占:只需在进程主动放弃处理机时进行调度即可。
- 非抢占:在就绪队列变化时,检查是否会发送抢占。
优点:
- 用优先级区分紧急程度、重要程度,适用于实时操作系统。
- 可灵活地调整对各种作业/进程的偏好程度。
缺点:若源源不断地有高优先级进程到来,则可能导致饥饿。
饥饿问题:存在
抢占式:
非抢占式:
多级反馈队列调度算法
多级反馈队列算法(Multilevel Feedback Queue)
- 算法思想:对其他调度算法的折中权衡。
- 算法规则:
- 设置多级就绪队列,各级队列优先级从高到低,时间片从小到大
- 新进程到达时先进入第1级队列,按FCFS原则排队等待被分配时间片,若用完时间片进程还未结束,则进程进入下一级队列队尾。如果此时已经是在最下级的队列,则重新放回该队列队尾。
- 只有第k级队列为空时,才会为k+1级队头的进程分配时间片。
- 调度:用于进程调度
- 是否可抢占:抢占式算法。在K级队列的进程运行过程中,若是更上级的队列中进入了一个新进程,则由于新进程处于优先级更高的队列中,因此新进程会抢占处理机,原来运行的进程放回k级队列的队尾。
- 优点:
- 对各类型进程相对公平(FCFS的优点)
- 每个新到达的进程都可以很快等到响应(RR的优点)
- 短进程只用较少的时间就可以完成(SPF的优点)
- 不必实现估计进程的运行时间
- 可灵活地跳转对各类进程的偏好程度(对于I/O阻塞的进程可以放回原队列保持优先级)
- 缺点:可能导致饥饿问题。
- 饥饿问题:会
示例:

总结:

多级队列调度算法
系统中按进程类型设置多个队列,进程创建成功后插入某个队列。
例子:

队列间调度方式:
- 固定优先级:高优先级空时低优先级进程才能被调度。
- 时间片划分:队列分配不同的时间,如50%,40%,10%
各队列内部可以采取不同的调度策略:
例子:
- 系统进程队列采用优先级调度。
- 交互式采用时间片轮转。
- 批处理队列采用先来先服务。
多处理机调度
单处理机调度:只需决定让哪个就绪进程优先上处理机即可。
多处理机调度:
- 用调度算法决定让哪个就绪进程优先上处理机。
- 还需要决定被调度的进程到底上哪个处理机
调度目标:
- 负载均衡:尽可能让每个CPU都同等忙碌。
- 处理机亲和性:尽量让一个进程调度到同一个CPU上运行,以发挥CPU中缓存(Cache)的作用。
公共就绪队列

- 所有CPU共享同一个公共就绪进程队列
- 每个CPU空闲的时候,都会运行调度程序。,从公共就绪队列中选择一个进程运行。
- 每个CPU访问公共就绪队列时需要上锁。
优点:可以天然的实现负载均衡。
缺点:各个进程频繁地换CPU运行。亲和性不好。
提升亲和性的方法:
- 软亲和:由进程调度程序尽量保证”亲和性”
- 硬亲和:由用户进程通过系统调用,主动要求操作系统分配固定的CPU,确保”亲和性”。
私有就绪队列

- 每个CPU都有一个私有的就绪队列
- CPU空闲时运行调度程序,从私有就绪队列中选择一个进程运行。
实现负载均衡:
- Push迁移策略:
- 一个特定的系统程序周期性检查每个处理器的负载,如果负载不平衡,就从忙碌CPU的就绪队列中推一些就绪进程到空闲CPU的就绪队列。
- PULL迁移策略:
- 每个CPU运行调度策略时,周期性检查自身负载与其他cpu的负载。如果一个CPU负载很低,就从其他高负载CPU的就绪队列中拉一些就绪进程到自己的就绪队列。
实现亲和性:
- 私有就绪队列天然的实现了处理机的亲和性。
