4、进程与线程基础:进程的概念、进程控制块PCB、进程状态与转换、进程控制(创建、撤销、阻塞、唤醒)、进程的层次结构
操作系统作为计算机系统的资源管理者与程序运行的支撑环境,其核心任务之一便是对“正在运行的程序”进行高效、安全的管理。在《计算机考研操作系统课程》中,“进程”是贯穿全课程的第一大核心抽象概念。理解进程的本质、其唯一标识PCB、状态流转的规律以及操作系统对其进行的控制操作,是后续学习线程、调度、同步与死锁的基石。本章将系统性地拆解进程从“概念”到“控制”的完整知识链条,帮助考生建立起动态、立体的进程观。
4.1 核心原理:从程序到进程的抽象跃迁
在早期的批处理系统中,内存中同时只存在一道程序,CPU 执行完该程序后才加载下一道,这种“单道顺序执行”的方式虽然简单,但资源利用率极低。为了提升系统吞吐量,引入了多道程序设计技术,即允许多个程序同时进入内存并交替执行。然而,程序本身是静态的、有序的指令集合,它无法描述“执行到哪一行”“占用了哪些资源”“等待什么事件”等动态信息。因此,操作系统引入了进程(Process)这一概念。
进程可以被定义为:一个具有独立功能的程序关于某个数据集合的一次运行活动。它不仅仅是程序代码,还包括当前的活动状态、相关的数据、堆栈以及系统资源。进程具有以下五个重要特征:
- 动态性:进程是程序的一次执行过程,有创建、运行、消亡的生命周期;程序是静态的,可永久保存。
- 并发性:多个进程可同时存在于内存中,并在宏观上同时运行、微观上交替执行。
- 独立性:进程是系统进行资源分配和调度的独立单位,各进程独立拥有地址空间和资源。
- 异步性:进程按各自独立的、不可预知的速度向前推进,可能导致结果的不确定性,因此需要同步机制。
- 结构性:进程实体由程序段、数据段和进程控制块(PCB)三部分组成,其中PCB是进程的灵魂。
4.2 进程控制块 PCB:进程存在的唯一标志
操作系统为了管理进程,必须为每个进程建立一个数据结构,用于记录其全部属性和状态信息,这个数据结构就是进程控制块(Process Control Block, PCB)。PCB是进程实体的一部分,也是进程存在的唯一标志。当系统创建一个进程时,即为它分配一个PCB;当进程被撤销时,系统回收其PCB,进程也随之消亡。
PCB中通常包含以下四大类信息:
| 信息类别 | 具体内容 | 作用说明 |
|---|---|---|
| 进程标识符 | 进程内部标识符(PID)、外部标识符、父进程标识符(PPID) | 唯一标识一个进程,用于系统调用和进程管理 |
| 处理机状态信息 | 通用寄存器值、程序计数器PC、程序状态字PSW、栈指针 | 保存进程被中断时的现场信息,用于恢复执行 |
| 进程调度信息 | 进程状态(运行/就绪/阻塞)、优先级、等待事件、调度算法所需参数 | 供调度程序判断何时分配CPU给该进程 |
| 资源管理信息 | 内存空间地址(代码段/数据段/栈)、打开文件列表、I/O设备使用情况 | 记录进程占用的系统资源,便于回收与共享 |
PCB的组织方式通常有链接方式和索引方式两种。链接方式将同一状态的PCB通过指针链接成队列(如就绪队列、阻塞队列);索引方式则通过索引表按状态分类,表中记录PCB在内存中的地址。无论哪种方式,PCB都常驻内存,由操作系统内核统一管理,用户程序无法直接访问。
4.3 进程状态与转换:生命周期的动态刻画
进程从创建到终止,会经历多种状态。经典的三态模型是考研的基础,而五态模型则更贴近实际系统。以下是各状态的定义与转换条件:
- 运行态(Running):进程已获得CPU,正在执行。单核CPU下,任一时刻最多只有一个进程处于运行态。
- 就绪态(Ready):进程已具备运行条件,等待CPU分配。就绪队列中可能有多个进程。
- 阻塞态(Blocked/Waiting):进程因等待某事件(如I/O完成、信号量、消息)而暂时无法运行,即使分配CPU也无法执行。
- 创建态(New):进程正在被创建,尚未进入就绪队列。系统正在为其分配PCB和资源。
- 终止态(Terminated):进程已结束运行,系统正在回收资源,PCB即将被清除。
状态转换的四个关键路径如下:
- 就绪态 → 运行态:进程被调度程序选中,获得CPU。这是唯一进入运行态的路径。
- 运行态 → 就绪态:时间片用完,或更高优先级进程抢占CPU,进程被迫让出处理器。
- 运行态 → 阻塞态:进程主动请求I/O或等待某事件,如执行
wait()系统调用。 - 阻塞态 → 就绪态:进程等待的事件发生(如I/O完成),被唤醒后进入就绪队列,等待重新调度。
在某些操作系统中,还引入了挂起态(Suspend),用于将进程从内存交换到外存,以缓解内存不足。挂起态分为“就绪挂起”和“阻塞挂起”,其转换关系更为复杂,但考研大纲通常以三态和五态为主,挂起态作为扩展了解即可。
4.4 进程控制:操作系统对进程生命周期的干预
进程控制是操作系统通过原语(Primitive)操作来实现的。原语是由若干条机器指令构成的、用于完成特定功能的原子操作,其执行过程不可被中断。进程控制的核心原语包括创建、撤销、阻塞和唤醒。
4.4.1 进程创建
进程创建的原语流程如下:
- 申请空白PCB,为新进程分配唯一的PID。
- 为新进程分配运行所需的资源(内存空间、文件、I/O设备等)。
- 初始化PCB,包括设置状态为就绪态、设置优先级、初始化寄存器等。
- 将新进程的PCB插入就绪队列,等待调度。
触发进程创建的事件通常有:用户登录、作业调度、系统服务请求(如打印服务)、用户程序通过 fork() 或 CreateProcess() 创建子进程。
4.4.2 进程撤销
进程撤销(终止)的原语流程:
- 根据PID找到目标进程的PCB,读取其状态。
- 若进程正在运行,立即终止其执行,并设置调度标志以重新调度CPU。
- 回收该进程占用的所有资源(内存、文件、设备等),并撤销其所有子孙进程(防止孤儿进程)。
- 将该进程的PCB从所在队列中移除,并释放PCB空间。
触发撤销的事件包括:进程正常结束(执行完最后一条指令)、进程因错误异常结束(如除零、越界)、被父进程或系统强制终止(如 kill() 命令)。
4.4.3 进程阻塞与唤醒
阻塞原语是进程主动发起的操作。当进程需要等待某事件(如I/O完成)时,它调用阻塞原语:
- 立即停止当前进程的执行。
- 将进程状态从运行态改为阻塞态,并保存其现场信息到PCB。
- 将PCB插入到对应事件的阻塞队列中。
- 转调度程序,重新分配CPU给其他就绪进程。
唤醒原语是由系统或其他进程发起的被动操作。当阻塞事件发生时(如I/O完成),系统调用唤醒原语:
- 从阻塞队列中移出目标进程的PCB。
- 将进程状态从阻塞态改为就绪态。
- 将PCB插入就绪队列,等待调度。
4.5 进程的层次结构:父子进程与进程树
在UNIX/Linux系统中,进程之间存在明确的层次关系。一个进程可以通过 fork() 系统调用创建子进程,创建者称为父进程,被创建者称为子进程。子进程可以继续创建自己的子进程,从而形成一棵进程树(Process Tree)。系统启动后,第一个进程是 init(PID为1),它是所有进程的祖先。
进程层次结构具有以下特点:
- 资源继承:子进程可以继承父进程的部分资源(如环境变量、打开的文件描述符),但拥有独立的地址空间。
- 独立执行:父子进程是并发执行的独立实体,父进程不会因子进程的阻塞而阻塞(除非使用
wait()等待)。 - 撤销传播:当父进程被撤销时,其所有子进程通常也会被撤销(级联终止),以保证系统资源不泄漏。
- 进程组与会话:多个相关进程可组成进程组,用于信号传递和作业控制。
在Windows系统中,进程没有严格的树形结构,而是采用“句柄(Handle)”机制来管理进程间关系,父子进程的关联相对松散。但考研大纲以UNIX/Linux的进程树模型为主要考查对象。
4.6 实战场景:Linux下进程创建与状态观察
为了将抽象概念落地,我们通过一个简单的Linux C程序来观察进程的创建与状态变化。假设我们编写如下代码:
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork(); // 创建子进程
if (pid < 0) {
printf("Fork failed!\n");
} else if (pid == 0) {
// 子进程执行路径
printf("Child process, PID=%d, PPID=%d\n", getpid(), getppid());
sleep(5); // 模拟I/O等待,子进程进入阻塞态
printf("Child wakes up and exits.\n");
} else {
// 父进程执行路径
printf("Parent process, PID=%d, Child PID=%d\n", getpid(), pid);
wait(NULL); // 父进程阻塞等待子进程结束
printf("Parent reaps child and exits.\n");
}
return 0;
}
运行过程分析:
- 父进程执行
fork()后,系统创建一个子进程,子进程获得父进程PCB的副本,但PID不同。 - 父子进程并发执行。子进程执行
sleep(5)时,子进程从运行态转为阻塞态(等待定时器事件)。 - 父进程执行
wait(NULL)时,父进程也进入阻塞态,等待子进程终止。 - 5秒后,子进程的定时器事件触发,子进程被唤醒,进入就绪态,随后运行并打印退出信息,进入终止态。
- 子进程终止后,父进程被唤醒,回收子进程的PCB(僵尸进程被清理),父进程退出。
通过 ps -l 命令可以实时查看进程状态:R(运行)、S(可中断睡眠/阻塞)、D(不可中断睡眠)、Z(僵尸)。这个案例完整展示了进程的创建、阻塞、唤醒、撤销全过程,是考研面试中常见的综合题素材。
wait() 调用会使父进程阻塞,而 sleep() 会使当前进程阻塞,两者都是阻塞态的典型触发条件。
4.7 常见误区与总结复盘
在学习和应试中,考生容易在以下环节出现理解偏差,需要特别警惕:
| 误区点 | 错误理解 | 正确认知 |
|---|---|---|
| 进程与程序 | 认为程序就是进程,或进程就是程序 | 进程是动态的,程序是静态的;一个程序可对应多个进程 |
| PCB的作用 | 认为PCB只是记录PID | PCB记录状态、寄存器、资源、调度信息,是进程存在的唯一标志 |
| 状态转换路径 | 认为阻塞态可直接转运行态 | 阻塞态必须先转就绪态,再由调度程序分配CPU |
| 原语与系统调用 | 混淆原语和普通系统调用 | 原语具有原子性,不可中断;系统调用可能被中断 |
| 父子进程关系 | 认为父子进程共享全部内存 | 父子进程拥有独立地址空间,采用写时复制(COW)技术共享物理页 |
本章总结:进程是操作系统中最核心的抽象,PCB是进程的“身份证”,状态转换是进程的“生命轨迹”,原语操作是系统的“干预手段”,进程树是进程间的“血缘关系”。掌握这四者的内在联系,就掌握了进程管理的骨架。后续学习线程时,可以将线程视为“轻量级进程”,其PCB(TCB)更小,共享进程的地址空间,但调度和资源分配的基本逻辑与进程一脉相承。
fork() 分析进程树和状态变化。建议考生亲手在Linux环境中运行上述代码,观察 ps 输出,将理论与实践结合,方能牢固掌握。