一篇围绕 Unix 进程部分展开的总览笔记。
在 概述 里,已经把操作系统拆成了进程、内存、文件、I/O 这些大块内容。这里开始进入“进程”这一章。
这一篇不展开 fork、exec、调度算法、线程库实现、同步原语或进程间通信,而是先建立整章会反复出现的前置信息:进程是什么、它拥有哪些资源、操作系统如何用 PCB 管理它、pid / ppid 和进程状态分别表示什么。
导览
1. 核心问题
把”进程”作为一章单独拿出来,核心关注下面几个方面:
| 方面 | 关键对象 |
|---|---|
| 程序的加载与执行 | 进程、地址空间、执行上下文 |
| 进程的识别与管理 | pid、ppid、PCB、进程状态 |
| 进程的创建、替换与回收 | fork、exec、wait |
| 进程内的多执行流 | 线程 |
| 多执行实体共享 CPU | 调度 |
| 多执行实体协作与数据交换 | 同步、IPC |
本篇关注前两类:进程抽象的定义与进程的管理方式。API、线程、调度、同步和通信由后续各篇分别展开。
2. 路线图
本章的路线图可以表述如下:
| 主题 | 核心问题 | 对应文章 |
|---|---|---|
| 进程 API | 进程的创建、替换与回收 | 进程生命周期 POSIX API |
| 线程 | 进程内出现多条执行流 | 线程基础 |
| 调度 | 多执行实体共享 CPU | 进程(线程)调度 |
| 同步 | 多执行实体安全协作 | 进程(线程)同步 |
| 通信 | 多进程交换数据 | 进程间通信 |
本篇是这一章的入口,聚焦进程抽象的定义与管理方式,不展开 API 细节或并发机制。
进程是什么
1. 程序与进程
程序是静态代码,进程是正在运行的程序实例。
磁盘上的可执行文件只是指令和数据的组织形式;只有当它被装入内存、分配运行所需资源并开始执行时,它才成为进程。同一个程序文件可以同时对应多个进程,而这些进程彼此独立,各自维护自己的运行状态。
可以表述如下:
| 对象 | 含义 |
|---|---|
| 程序 | 静态代码与数据 |
| 进程 | 正在运行的程序实例 |
2. 进程的作用
本章主要关心进程的两个层面:
| 层面 | 关注点 |
|---|---|
| 资源环境 | 这个运行实体拥有什么 |
| 执行上下文 | 这个运行实体当前执行到哪里 |
进程首先是资源容器,也是操作系统管理程序运行的基本对象——即资源分配与调度的基本单位。
3. 进程与线程的关系
当系统中存在线程时,内核直接调度的基本单位是线程而非整个进程。在展开线程之前,先建立一个基础区分:
| 对象 | 角色 |
|---|---|
| 进程 | 资源容器与管理单位 |
| 线程 | 进程内部的一条执行流 |
进程回答”资源归谁”,线程回答”谁在执行”。线程部分在线程基础中单独展开。
进程的基本组成
1. 资源环境
从资源视角看,一个进程通常包含下面这些内容:
| 组成部分 | 作用 |
|---|---|
| 地址空间 | 规定这个进程可访问的虚拟内存范围 |
| 代码段 | 保存要执行的机器指令 |
| 数据段 | 保存全局变量、静态变量等运行时数据 |
| 堆 | 保存动态分配得到的内存 |
| 打开的文件 | 保存当前进程已打开的文件、管道、终端、socket 等 |
| 信号处理方式 | 保存进程级别的信号处理配置 |
这些内容共同构成了进程的资源环境,也就是“这个进程拥有什么”。
2. 执行现场
执行现场包含一组能恢复运行的硬件上下文:
| 组成部分 | 作用 |
|---|---|
| 程序计数器 | 指出下一条要执行的指令 |
| CPU 寄存器 | 保存当前计算状态 |
| 栈 | 保存函数调用过程中的局部状态 |
| 调度状态 | 表示当前是运行、就绪还是等待 |
上下文切换的核心操作,就是保存和恢复这组状态。
资源环境与执行现场的对照:
| 层次 | 典型内容 | 关注点 |
|---|---|---|
| 资源环境 | 地址空间、代码段、数据段、堆、打开的文件 | 这个进程拥有什么 |
| 执行现场 | 程序计数器、寄存器、栈、调度状态 | 这个进程当前执行到哪里 |
3. 文件描述符表
文件描述符表是进程的核心 I/O 资源。它记录当前进程打开的所有对象——普通文件、终端、管道、socket 和设备文件。open、read、 write、重定向、管道和进程间通信都基于这张表。
程序启动时,内核默认打开 3 个文件描述符:
| 名称 | fd | 默认含义 |
|---|---|---|
stdin | 0 | 标准输入 |
stdout | 1 | 标准输出 |
stderr | 2 | 标准错误 |
PCB:进程控制块
PCB(Process Control Block,进程控制块)是操作系统为每个进程维护的管理数据结构,记录”这个进程是谁、当前处于什么状态、执行到哪了、拥有哪些资源”。
1. pid 与父子进程关系
pid 是操作系统为每个进程分配的唯一标识符,ppid 记录创建该进程的父进程。父进程通过 fork() 创建子进程,子进程继承父进程的环境。
父子关系会直接影响生命周期中的几个特殊状态:
| 情况 | 含义 |
|---|---|
| 僵尸进程 | 子进程已经结束,但父进程还没有 wait 它 |
| 孤儿进程 | 父进程先结束,子进程仍然继续运行 |
wait 的意义之一,就是把已经结束的子进程从“僵尸状态”推进到真正被系统回收的状态。
2. 进程状态与生命周期
进程并不是一创建出来就一直运行到结束。典型生命周期会在几种状态之间切换:
| 状态 | 含义 |
|---|---|
| 新建 | 进程刚被创建,还未真正进入运行阶段 |
| 就绪 | 已经具备运行条件,只是在等待 CPU |
| 运行 | 当前正在 CPU 上执行 |
| 等待 / 阻塞 | 正在等待某个事件,例如 I/O 完成 |
| 终止 | 进程执行结束,等待资源回收 |
典型流转过程:
新建 -> 就绪 -> 运行 -> 终止
|
v
等待
|
v
就绪这些状态直接对应调度中的就绪队列与等待队列。
3. PCB 的典型组分
PCB 的关键记录项:
| 类别 | 内容 |
|---|---|
| 标识信息 | pid、ppid、用户 ID |
| 进程状态 | 新建、就绪、运行、等待、终止等 |
| 程序计数器 | 下一条要执行的指令位置 |
| CPU 上下文 | 通用寄存器、栈指针等 |
| 调度信息 | 优先级、时间片、队列指针 |
| 内存信息 | 地址空间、页表、代码段 / 数据段相关信息 |
| I/O 信息 | 打开的文件、设备、文件描述符表 |
| 统计信息 | 已使用 CPU 时间、创建时间等 |
PCB 承担四个核心职责:
| 职责 | 说明 |
|---|---|
| 标识进程 | 内核通过 PCB 知道”这是谁” |
| 记录状态 | 内核通过 PCB 知道”它现在处于什么状态” |
| 保存/恢复执行现场 | 上下文切换时保存和恢复寄存器、栈等现场 |
| 资源回收 | 进程结束时回收 PCB 关联的全部资源 |
上下文切换的本质:将当前进程的执行现场保存到其 PCB,再从下一进程的 PCB 恢复对应现场。
后续章节
| 序号 | 主题 | 对应文章 |
|---|---|---|
| 1.2 | 进程 API | 进程生命周期 POSIX API |
| 1.3 | 线程 | 线程基础 |
| 1.4 | 调度 | 进程(线程)调度 |
| 1.5 | 同步 | 进程(线程)同步 |
| 1.6 | 通信 | 进程间通信 |