【王道操作系统 | 第二章】进程管理、处理机调度与死锁 操作系统第二章的主线是回答一个问题多个程序同时运行时操作系统如何管理它们、分配处理机、协调共享资源并处理资源互相等待的情况本文从进程和线程出发依次梳理进程控制、进程通信、处理机调度、同步互斥、信号量、管程和死锁。理解这些概念时建议始终抓住三个对象进程状态、PCB 信息、资源分配关系。一、进程的基本概念1.1 程序、进程与进程实体程序是静态的指令集合通常以可执行文件的形式保存在磁盘中。进程是程序的一次执行过程是动态产生、运行和结束的过程。进程实体也叫进程映像由三部分组成PCB进程控制块操作系统管理进程所需的信息例如 PID、当前状态、寄存器现场、调度信息和资源清单。程序段进程要执行的程序代码。数据段运行过程中使用的全局变量、临时数据等。进程是系统进行资源分配和处理机调度的独立单位。操作系统并不直接用一段程序代码代表一个正在运行的任务而是通过 PCB 把代码、数据、状态和资源联系起来。1.2 进程的特征动态性进程有创建、运行、阻塞和终止等过程这是进程区别于静态程序的根本特征。并发性内存中可以同时存在多个进程它们在宏观上同时推进。独立性进程可以独立获得资源、独立运行并接受调度。异步性每个进程按照自己的速度推进执行顺序和完成时间具有不确定性。结构性每个进程都配置 PCB进程实体由 PCB、程序段和数据段构成。二、进程状态与进程控制2.1 五种基本状态创建态操作系统正在建立进程分配资源并初始化 PCB。就绪态进程已经具备运行条件只等待处理机。运行态进程正在占用 CPU 执行。阻塞态进程因等待 I/O、信号或其他事件而暂时不能运行。终止态进程执行结束或发生异常操作系统正在回收资源。PCB 中的 State 字段记录进程当前状态。典型转换包括就绪态到运行态由调度程序触发运行态到阻塞态通常由等待事件触发阻塞态到就绪态由等待事件完成触发运行态到终止态则可能由 exit 或异常引起。2.2 进程的组织方式链式组织方式按照进程状态建立多个队列例如就绪队列、等待打印机的阻塞队列和等待磁盘的阻塞队列操作系统保存各队列的指针。索引组织方式则为不同状态建立索引表再由操作系统保存各索引表的入口。两种方式的目的相同让操作系统能够快速找到处于某种状态的 PCB。2.3 原语与进程控制进程控制会改变进程状态、更新 PCB、调整队列并可能分配或回收资源。完成这些操作时必须保证中间状态不会被其他程序观察到因此需要使用原语。原语是一段执行过程具有原子性的程序执行期间不能被中断。操作系统通常通过关中断指令和开中断指令保证原语一气呵成。创建原语申请空白 PCB。为新进程分配所需资源。初始化 PCB。将 PCB 插入就绪队列。用户登录、作业调度、系统提供服务以及应用程序请求都可能触发进程创建。终止原语找到目标进程的 PCB。如果进程仍在运行先剥夺其 CPU。终止其子进程或子线程。回收进程占有的资源。删除 PCB。阻塞与唤醒原语阻塞时操作系统保护进程运行现场将状态改为阻塞态再把 PCB 放入对应事件的等待队列。唤醒时操作系统将 PCB 从等待队列移出改为就绪态并插入就绪队列。阻塞和唤醒必须成对出现阻塞表示等待某个事件唤醒表示该事件已经发生。唤醒后进程进入的是就绪态不会直接占用 CPU。切换原语进程切换包括保存原进程的运行环境、将其 PCB 放入相应队列、选择新进程、更新新进程 PCB并恢复新进程的运行环境。切换本身有开销过于频繁会降低系统有效执行时间。三、进程通信与线程3.1 进程通信不同进程拥有相互独立的地址空间进程之间不能直接读写对方的内存因此需要操作系统提供进程通信机制IPC。共享存储操作系统在内存中划出共享区域让多个进程通过该区域交换数据。基于数据结构的共享限制较多属于低级通信方式基于存储区的共享由进程自行决定数据格式和存放位置速度更快属于高级通信方式。共享区同时只能被一个进程以互斥方式访问否则会出现数据覆盖和读写冲突。同步工具可以使用信号量等机制。消息传递消息传递以格式化消息为单位通过发送和接收原语完成数据交换。直接通信时发送方直接指定接收进程间接通信时发送方和接收方通过信箱交换消息。管道通信管道是由系统调用建立的特殊共享文件通常对应内存中的固定大小缓冲区。管道具有单向、先进先出和半双工特点需要双向同时通信时应建立两个管道。当管道写满时写进程阻塞当管道读空时读进程阻塞。管道中的数据被读出后就会消失因此多个进程读取同一管道时需要特别注意数据分配和同步。3.2 线程线程是程序执行流的最小单位也是基本的 CPU 执行单位。引入线程后进程主要负责分配除 CPU 之外的系统资源线程负责接受处理机调度。用户级线程由线程库管理线程切换可以在用户态完成开销较小缺点是一个用户级线程阻塞时整个进程可能被阻塞而且多个用户级线程不能真正并行使用多核 CPU。内核级线程由操作系统内核管理内核为每个线程建立 TCB。一个线程阻塞后同一进程中的其他线程仍可能运行也可以在多核处理机上并行执行代价是线程切换需要进入核心态管理开销更大。多线程模型描述用户级线程与内核级线程的映射关系一对一一个用户级线程对应一个内核级线程并发能力强但内核线程数量多。多对一多个用户级线程对应一个内核级线程切换开销小但一个线程阻塞可能导致整个进程阻塞。多对多多个用户级线程映射到多个内核级线程在并发能力和管理开销之间折中。四、处理机调度4.1 三个调度层次高级调度作业调度从外存后备队列选择作业调入内存并建立进程。每个作业通常只调入一次、调出一次。低级调度进程调度从就绪队列选择进程把 CPU 分配给它。它是最基本、发生频率最高的调度。中级调度决定哪些挂起进程重新调入内存一个进程可能多次被调出和调入。调度程序需要决定两个问题让哪个进程运行以及它可以运行多长时间。没有可运行的普通进程时系统会安排优先级最低的闲逛进程运行避免 CPU 空转。4.2 调度时机与调度方式进程主动放弃 CPU 的情况包括正常终止、运行异常终止和主动请求阻塞例如等待 I/O。时间片用完、更高优先级进程进入就绪队列或发生紧急 I/O 事件则属于被动放弃。处理中断、执行操作系统内核程序临界区以及执行原语时不能随意进行进程调度和切换否则可能破坏内核数据结构的一致性。非剥夺调度只允许进程主动释放 CPU实现简单、开销小但响应紧急任务的能力较弱。剥夺调度允许系统暂停当前进程并分配 CPU 给更紧急的进程更适合分时系统和实时系统。4.3 调度算法的评价指标CPU 利用率 CPU 忙碌时间 ÷ 总时间。吞吐量 单位时间内完成的作业数。周转时间 完成时间 - 到达时间。带权周转时间 周转时间 ÷ 实际运行时间。等待时间 进程处于等待处理机状态的时间总和。响应时间 提交请求到首次得到响应的时间。评价算法时不能只看一个指标。缩短平均等待时间的算法未必能同时提供最好的公平性和响应速度。4.4 常见调度算法先来先服务FCFS按照到达先后顺序调度规则简单且公平。缺点是长作业排在前面时会让后续短作业等待很久。短作业优先SJF每次选择当前已到达且运行时间最短的进程目标是降低平均等待时间。它需要预估运行时间长作业可能长期得不到服务产生饥饿。抢占式短作业优先也叫最短剩余时间优先。就绪队列发生变化时如果新进程的剩余时间更短就抢占当前进程。最高响应比优先HRRN响应比计算公式为响应比 等待时间 要求服务时间÷ 要求服务时间等待时间越长响应比越高因此 HRRN 在兼顾短作业的同时可以缓解长作业饥饿。它通常属于非抢占式调度。时间片轮转RR就绪队列中的进程轮流执行一个时间片常用于分时系统。时间片过大时算法接近 FCFS时间片过小时进程切换频繁保存和恢复运行环境的开销增加。优先级调度每次选择优先级最高的进程可以是抢占式也可以是非抢占式。系统进程通常高于用户进程前台进程通常高于后台进程I/O 型进程也可能获得更高优先级。优先级长期不变时同样可能发生饥饿。多级反馈队列系统设置多个就绪队列队列优先级从高到低时间片从小到大。新进程先进入高优先级队列时间片用完仍未结束时降到下一级队列。只有高优先级队列为空低一级队列才获得 CPU。多级反馈队列不要求事先准确知道进程运行时间能够较快响应新进程也能让短作业较早完成同时降低 CPU 密集型进程对交互式进程的影响。五、进程同步与互斥5.1 同步、互斥与临界区同步是进程之间为完成共同任务而形成的直接制约关系例如生产者必须先生产消费者才能消费。互斥是多个进程访问临界资源时形成的间接制约关系。临界资源是一次只允许一个进程使用的资源访问临界资源的代码称为临界区。一个正确的互斥方案应满足空闲让进临界区空闲时请求进程可以进入。忙则等待已有进程进入临界区时其他进程必须等待。有限等待请求进程不能无限期等待。让权等待等待时应释放 CPU避免忙等。5.2 软件实现方法单标志法通过轮流赋予进入权限实现互斥但临界区空闲时可能仍不允许某个进程进入违反空闲让进。双标志先检查法先检查对方标志再设置自己的标志。检查和上锁不是原子操作两个进程可能同时通过检查违反忙则等待。双标志后检查法先上锁再检查避免了同时进入但两个进程可能都先上锁造成长期等待违反空闲让进和有限等待。Peterson 算法结合标志和谦让变量能够满足空闲让进、忙则等待和有限等待但仍可能忙等不能完全满足让权等待。5.3 硬件实现方法中断屏蔽通过关中断保护临界区简单高效但只适合内核程序且不适用于多处理机环境。TestAndSet 和 Swap 指令由硬件保证原子性把检查和上锁合并为不可分割的操作适合多处理机系统。它们的共同缺点是可能让等待进程持续占用 CPU形成忙等。六、信号量与管程6.1 信号量信号量是表示系统中某类资源数量的变量进程通过 wait 和 signal 原语对它进行操作。记录型信号量除了记录 value还维护等待进程队列。典型操作可以概括为wait(S)S.value 减一若结果小于 0当前进程进入阻塞队列 signal(S)S.value 加一若仍有进程等待则唤醒其中一个wait 用于申请资源signal 用于释放资源。操作必须是原子的否则多个进程同时修改信号量会产生竞态。6.2 用信号量实现互斥与同步实现互斥时把互斥信号量初始化为 1。进程进入临界区前执行 wait离开临界区后执行 signal因此同一时刻最多只有一个进程通过。实现同步时把同步信号量初始化为 0。前驱进程完成任务后执行 signal后继进程执行 wait由于初始值为 0后继进程必须等前驱进程先释放信号。前驱关系可以抽象为前一个操作末尾执行 V后一个操作开头执行 P。分析题目时先找出“谁必须先完成”再把 V 放在前驱操作之后把 P 放在后继操作之前。6.3 管程管程把共享数据、对数据操作的过程以及同步机制封装在一起并保证同一时刻只有一个进程在管程内执行某个内部过程。相比直接在业务代码中分散使用信号量管程更容易集中维护互斥规则。七、死锁及其处理7.1 死锁与饥饿死锁是并发进程相互等待对方占有的资源导致所有相关进程都无法继续运行的状态通常至少涉及两个进程。饥饿是某个进程长期得不到所需资源或服务但系统中其他进程仍可能继续运行。死锁强调循环等待饥饿强调某个进程长期得不到机会。7.2 死锁的四个必要条件互斥条件资源一次只能被一个进程占用。不剥夺条件资源未使用完之前不能被强行夺走。请求和保持条件进程已经保持部分资源又继续请求其他资源。循环等待条件存在进程资源的循环等待链。四个条件同时成立死锁才可能发生。因此预防死锁的基本思路就是破坏其中至少一个条件。7.3 预防死锁破坏互斥条件使用 SPOOLing 等技术把独占设备改造成共享使用形式但并非所有资源都能这样处理。破坏不剥夺条件当进程申请不到新资源时主动释放已经占有的资源或允许系统剥夺部分资源。破坏请求和保持条件采用静态分配让进程运行前一次性申请全部资源。破坏循环等待条件规定资源的顺序进程必须按编号递增顺序申请资源。预防方法通常会降低资源利用率或并发度所以实际系统还会结合避免和检测方法。7.4 避免死锁银行家算法银行家算法在每次资源分配前先假设分配发生再检查系统能否找到一个安全序列。如果存在安全序列说明所有进程仍有可能依次完成可以进行分配如果进入不安全状态则暂缓本次分配。安全状态不等于当前没有资源竞争而是表示系统仍存在一条让所有进程完成的资源分配顺序。做题时应先计算各进程的剩余需求再用当前可用资源逐步尝试满足某个进程释放其资源后继续寻找下一个进程。7.5 检测与解除死锁系统也可以先允许资源分配定期检测是否形成死锁。检测到死锁后常见解除方式包括资源剥夺从部分进程中夺取资源分配给其他进程。进程撤销撤销一个或多个进程并回收其资源。进程回退让进程回退到足够安全的检查点再重新运行。选择解除方式时需要综合考虑进程优先级、已完成工作量、回退代价和系统损失。总结本章可以按一条链路理解进程通过 PCB 被操作系统管理线程提高程序的并发度调度算法决定处理机的分配顺序进程通信解决数据交换同步互斥解决共享资源竞争信号量和管程提供协作工具当资源分配形成循环等待时就需要通过预防、避免或检测解除死锁。遇到具体题目时可以先判断进程状态和资源关系再选择对应工具状态变化看原语CPU 分配看调度算法共享资源看互斥与信号量资源互相等待看死锁四条件和安全序列。参考资料王道操作系统第二章。