Skip to content

进程的组织与控制

2026 大纲 二(一)4 进程与线程的组织与控制

谁来把进程从无到有地造出来,又把它抹掉

前三节讲的都是"进程是什么样":它有 PCB、有状态、还能再拆成线程。 这一节讲动作——创建、终止、阻塞、唤醒,这四件事具体怎么做。

先看一处结构问题。系统里同时存在成百上千个进程,PCB 得组织起来, 否则调度程序每次都要全表扫描。而组织方式的取舍只有一条轴: 查得快还是改得快。链接方式(按状态把 PCB 串成队列)两头都还行, 所以实际系统普遍采用它;就绪队列、若干阻塞队列、空闲队列各串一条, 把"找一个就绪进程"这件事退化成取队首

再看四个动作本身。它们有一个共同的硬约束:必须原子执行

理由很直接。以"把一个进程从阻塞队列摘下、挂到就绪队列"为例—— 这要改好几个指针,做到一半被打断,队列就处于自相矛盾的中间状态; 此时若切换到另一个进程也来动这个队列,数据结构就彻底坏了。 所以这四个动作被做成原语,靠关中断保证原子性, 而开关中断是特权指令,于是它们必然运行在内核态—— 这条推导链在 CPU 运行模式已经走过一遍。

⚠️ 有一处边界要先立住:关中断只挡得住本 CPU。 多处理机上另一个核照样能同时进来,所以那里必须换成 TSL / Swap 这类 硬件原子指令(见同步互斥实现方法)。

最后是四个动作之间的对称性:阻塞只能是进程自己调用的(别人替你等没有意义), 唤醒必然来自外部(自己已经睡着了叫不醒自己)。两者必须成对出现, 否则就是永久阻塞。

一、进程的组织

系统中可能同时有数十到数千个 PCB,要能按状态快速找到它们,就得组织起来。教材给的是三种:

组织方式怎么做查找代价增删代价适用场景
线性方式所有 PCB 放在一张线性表里,表首址存于内存专用区差:每次查找都要扫描整张表低:状态变了只需改表项里的状态字段,PCB 不用搬家进程数目不多的系统
链接方式按状态把 PCB 用 PCB 内的链接字串成队列:就绪队列、若干阻塞队列、空白(空闲)队列好:取队首即得,就绪队列常按优先级排好序中:一次状态转换要摘链 + 挂链两步指针操作通用,实际系统普遍采用
索引方式按状态建若干张索引表,各表首址记在内存专用单元;表项记录相应 PCB 在 PCB 表中的地址好:查索引表即得,且支持随机访问第 k 项高:索引表是紧凑数组,插入/删除要移动表项需要按状态频繁遍历、状态转换相对不频繁时

系统的 PCB 表容量是固定的,这也是"系统最多能有多少个进程"的上限来源。

把三种组织方式的查找代价算成具体数字(想看清"链接方式最常用"的量化理由时展开)

设 PCB 表可容纳 512 个 PCB,某时刻 40 个就绪、60 个阻塞(等 I/O 25 个、等缓冲区 20 个、等信号量 15 个),PCB 位置随机分布、按顺序比较查找。

线性方式找出第一个就绪进程。 要找的是 40 个目标当中位置最靠前的那一个n 个位置里随机放 k 个目标,最靠前那个目标的位置期望是

E=n+1k+1

直观解释:k 个目标把 n 个位置切成 k+1 段,各段长度期望相等,而第一个目标恰好落在第 1 段之后。代入 n=512, k=40E=513/4112.51 个表项。注意它不是 512/2=256——要找的不是某个指定进程,而是"任意一个就绪进程",目标越多命中越早。

链接方式。 就绪队列的队首就是优先级最高的就绪进程,1 次即得。而且进程数从 512 涨到 5120 时,线性方式的代价跟着涨(5121/41124.9),链接方式仍是 1 次。

阻塞队列分不分原因。 这次要找的是某个指定进程,用顺序查找的平均比较次数 (m+1)/2:共用一条队列(m=60)需 30.5 次;按原因分三条、只在"等信号量"那条里找(m=15)需 8.0 次,平均少比较 22.5 次,降幅 73.8%。两问换了公式,是因为前者求一批目标中的最小位置、后者求一个确定目标的位置

"按原因分队列"的收益等于阻塞原因的分散程度:若系统只有一种阻塞原因,分队列毫无收益;若种类多到几十种,维护几十个队列头指针的空间代价又会反过来压过收益,此时该考虑按事件建索引表。

二、进程控制原语

创建原语

procedure Create(...)
    申请空白 PCB,并为新进程分配唯一的数字标识符
    为新进程分配运行所需的资源(内存、文件、I/O 设备、CPU 时间等)
    初始化 PCB:
        标识信息    ← 填入自己的标识符与父进程标识符
        处理机状态  ← PC 指向程序入口地址,栈指针指向栈顶
        控制信息    ← 状态置为「就绪态」或「静止就绪态」,优先级通常置为最低
    若就绪队列能接纳新进程,将其插入就绪队列
end

系统调用 fork 是创建原语的一种典型形态,语义不是"从头造一个新进程"而是把调用者复制一份

复制/共享内容
复制一份(子进程得到独立副本)地址空间的内容(代码、数据、堆、栈)、PCB 中除标识符外的绝大部分表项、打开文件的文件描述符表
不复制(子进程另起一份)PID(新的)、PPID(指向父进程)、CPU 时间统计等计时信息
共享(父子指向同一份)打开文件的文件读写位置——因为复制的是描述符,描述符指向的是同一个打开文件表项

为什么"复制"不等于真的拷一遍内存

真复制一整个地址空间代价极高,而 fork 之后子进程往往马上装入新程序、把复制来的内容全扔掉。现代系统的做法是先让父子共享同一批物理页并标记为只读,谁写谁才触发缺页、单独复制那一页。"复制"的语义不变,代价却只跟真正被改写的页数成正比。这一机制与 页式管理 的页表项保护位直接相关。

撤销(终止)原语

procedure Terminate(pid)
    据标识符从 PCB 集合中检索出该进程的 PCB,读出其状态
    若该进程正处于执行状态,立即终止其执行,并置调度标志为真
    若该进程还有子孙进程,将其所有子孙进程也予以终止   ← 级联终止
    将该进程拥有的全部资源归还给其父进程或归还给系统
    将该 PCB 从所在队列(链表)中移出
end

三个由"进程树 + 谁来回收 PCB"推出来的边界情形,名字像、后果完全不同:

情形定义由什么导致后果
级联终止终止一个进程时连同其所有子孙进程一起终止撤销原语的固有步骤子孙进程不会变成失控进程
孤儿进程父进程先于子进程终止,子进程还在跑系统不做级联终止、或父进程异常退出需要有一个"祖先"进程把它收养过去,否则它终止时没人回收
僵尸进程子进程已终止,但它的 PCB 还没被回收父进程尚未读取子进程的退出信息PCB 表项被长期占用;PCB 表容量有限,僵尸堆积会导致无法创建新进程

僵尸进程的成因值得单独推一遍:撤销原语的最后一步是"将 PCB 从队列中移出,等待其他程序来搜集信息"——退出码、CPU 用时这些信息存在 PCB 里,父进程还没来取,PCB 就不能立刻销毁。于是"进程已经死了但 PCB 还在"是设计必然,不是缺陷;缺陷只发生在父进程一直不来取的时候。

阻塞原语与唤醒原语

procedure Block()                    procedure Wakeup(pid)
    停止执行,保存 CPU 现场到 PCB        从相应阻塞队列中取出该 PCB
    将进程状态改为「阻塞态」              将进程状态改为「就绪态」
    将 PCB 插入相应事件的阻塞队列          将 PCB 插入就绪队列
    转调度程序重新调度                 end
end

Suspend 把进程从活动态换到静止态(换出外存),Active 把它换回来,发起者的四类场景见 进程状态与转换

三、原语的原子性

关中断          ← 不再响应中断,从而不会引发调度,也就不会发生进程/线程切换
  ... 原语操作 ...
开中断          ← 恢复中断响应

状态转换涉及 PCB 队列的增删,中途被打断会留下"PCB 既不在就绪队列、也不在阻塞队列"这类中间状态,进程从此人间蒸发。关中断/开中断是特权指令,只能在内核态执行,因此进程控制原语运行在内核态。

教材明确指出关中断这个方法的三条缺点:① 滥用关中断的权力可能导致严重后果——一段代码关了中断迟迟不开,时钟中断进不来,系统就失去了调度能力;② 关中断时间过长会影响系统效率,限制了处理器交叉执行程序的能力;③ 不适用于多 CPU 系统。第三条是硬边界,替代方案见 同步互斥的基本实现方法

四、线程的组织与控制

把 TCB 与 PCB 的四类信息对照,TCB 缺了整整一类:地址空间信息与页表基址(同进程线程共享同一地址空间,各存一份纯属重复且会因不一致而出错)、打开文件表与 I/O 设备与资源清单(进程级资源,按定义归资源分配单位所有)、内存分配与回收信息、家族关系(线程之间没有父子层次)。一句话:TCB 只装"执行流私有的东西"——这与 进程基本概念 里"切换执行流时必须换掉的才归线程私有"是同一条判据的两次应用,一次划分内存里的东西(栈私有、堆共享),一次划分控制块里的字段。

线程终止时回收其栈空间和 TCB。有的系统为了减少开销,撤销线程时并不立即回收资源和 TCB,下次创建新线程时直接复用这块 TCB。两类线程实现的其余分界见 线程

TCB 的七项内容,以及线程创建比进程创建省掉了哪几步(想逐项对照 TCB 与 PCB 时展开)
TCB 中的项内容
线程标识符每个线程唯一的 ID
一组寄存器程序计数器 PC、状态寄存器、通用寄存器的内容
线程运行状态执行/就绪/阻塞
优先级描述线程执行的优先程度
线程专有存储区线程切换时存放现场保护信息,以及与该线程相关的统计信息
信号屏蔽对某些信号加以屏蔽
两个堆栈指针指向用户栈的指针(线程在用户态运行时用)与指向核心栈的指针(线程在核心态运行时用)

两种栈同时存在且都属于这个线程,所以要两个指针分别指着——"线程的栈是私有的"这句话说的是两个栈。

线程创建进程创建
分配控制块分配一个 TCB申请空白 PCB
地址空间不建,用所属进程的分配地址空间、建页表
分配数百至数千字节的栈空间和局部存储区分配用户栈与核心栈
打开文件不复制,用所属进程的继承/复制父进程的
结果填好 TCB 即可立即执行置为就绪,插入就绪队列

考点速记

  1. PCB 的三种组织方式是在"查得快与改得快"之间取舍,链接方式两头都还行因而实际系统普遍采用;空闲队列与按原因分开的阻塞队列都是为了把查找退化成常数时间。
  2. 四个原语必须原子执行,靠关中断实现,因而运行在内核态。⚠️关中断只挡得住本 CPU,多处理机上必须换成 TSL / Swap 这类硬件原子指令。
  3. 阻塞只能是进程自己调用的、唤醒必然来自外部,二者必须成对——否则就是永久阻塞。
  4. 创建进程时必做的申请一个空白 PCB初始化 PCB(填 PID、状态、优先级、程序计数器等)、为进程分配资源、把它插入就绪队列。⚠️不会把状态设成"执行态"——新进程只能进就绪队列,能不能上 CPU 由调度程序决定。
  5. 什么操作会导致创建新进程:判据是是否需要一个新的执行实体用户登录成功(要为这个会话建一个进程)、启动程序执行(要跑一个新程序)都要创建;⚠️设备分配不创建进程——那只是把一台设备指给某个已有进程。
  6. 终止进程时必做的撤销 PCB回收内存资源回收占用的设备。⚠️**"终止子进程"不一定执行**——它只在该进程有子进程、且系统采用级联终止时才发生;没有子进程时根本无从终止。
  7. 进程树决定继承、归还、级联终止三件事(Windows 是反例,它不做级联终止)。
  8. TCB 只装执行流私有的东西(寄存器、栈指针、线程标识、状态),页表、打开文件表、资源清单、家族关系都留在 PCB 里。⚠️ 这既是线程便宜的结构性原因,也决定了进程终止或挂起时其全部线程一并终止或挂起
  9. TCB 在内核空间还是用户空间,正是两类线程实现的分水岭(见线程)。

这一节在真题里被考过的形式

三道题恰好各考一个动作:什么时候创建、创建时做什么、终止时做什么。 共同点是都在考"必做"与"不一定做"的分界

  • 问哪些操作会导致创建新进程(2010-24)。答用户登录成功 + 启动程序执行。⚠️设备分配不创建进程——判据是速记第五条:看这件事需不需要一个新的执行实体。登录要为会话建进程、启动程序要跑新代码,而分配设备只是把资源指给某个已经存在的进程。
  • 问创建新进程时必须完成的操作(2021-24)。答申请空白 PCB + 初始化 PCB 两项。⚠️**"设置进程状态为执行态"不对——新进程创建完只能进就绪队列,能不能上 CPU 得等调度程序挑(这与进程状态与转换速记第五条是同一条道理:创建和调度是两件事**)。
  • 问终止进程时不一定执行的操作(2024-24)。答终止子进程。⚠️ 另三项(回收内存、撤销 PCB、回收设备)都是必做的——不做就是资源泄漏。而终止子进程有两个前提:这个进程得有子进程系统得采用级联终止(Windows 就不)。这道题的设计点是分清"这件事总要做"和"这件事有条件"

复习优先级必须拿满,三道题的判据都很短。 第四条(创建后进就绪队列不是执行态) 与第六条(终止子进程不一定做)是最容易设错项的两处。第二条那条 "关中断 → 特权指令 → 必然在内核态"的推导链在 CPU 运行模式 已经出现过,这里是它的第二次应用,理解一次即可。

易错:认为创建进程时会把状态设成执行态。只能进就绪队列——上不上 CPU 由调度决定。

易错:认为设备分配会创建新进程。它只是把资源指给某个已存在的进程。

易错:认为终止进程时一定会终止子进程。要它有子进程、且系统采用级联终止,两个前提缺一不可。

易错:认为回收内存或撤销 PCB 是可选的。都是必做,不做就是资源泄漏。

易错:认为关中断在多处理机上也能保证原子性。它只挡得住本 CPU,另一个核照样进得来。

易错:认为进程可以唤醒自己、或替别人执行阻塞。阻塞只能自调用,唤醒必然来自外部

易错:认为 TCB 里也有页表和打开文件表。那些属于进程,留在 PCB 里——这正是线程轻量的原因。

教材出处
  • 汤小丹《计算机操作系统》2.2.4 进程管理中的数据结构,印刷 p41:PCB 的三种组织方式(线性方式、链接方式、索引方式),链接方式形成就绪队列、若干阻塞队列和空白队列
  • 汤小丹《计算机操作系统》2.3.1 操作系统内核,印刷 p43:原语是原子操作,"一个操作中的所有动作要么全做,要么全不做",在系统态下执行、常驻内存
  • 汤小丹《计算机操作系统》2.3.2 进程的创建,印刷 p44–p45:进程的层次结构、进程图(进程树)、"进程不能拒绝其子进程的继承权"、Windows 无进程层次结构;引起创建的四类事件;创建原语的四步
  • 汤小丹《计算机操作系统》2.3.3 进程的终止、2.3.4 进程的阻塞与唤醒,印刷 p45–p46:引起终止的三类事件、终止过程五步(含级联终止与"等待其它程序来搜集信息");引起阻塞与唤醒的四类事件
  • 汤小丹《计算机操作系统》2.4.2 硬件同步机制,印刷 p51:关中断的三条缺点,其中"关中断方法也不适用于多 CPU 系统,因为在一个处理器上关中断并不能防止进程在其它处理器上执行相同的临界段代码"
  • 汤小丹《计算机操作系统》2.7.3 线程的状态和线程控制块,印刷 p78:TCB 的七项内容,含用户栈指针与核心栈指针两个堆栈指针
  • 汤小丹《计算机操作系统》2.8.2 线程的实现,印刷 p81:内核支持线程的任务数据区 PTDA 与 TCB 空间组织,撤销线程时保留 TCB 供复用

相关知识

进程基本概念进程状态与转换进程间通信上下文切换机制线程多处理机调度

真题练习