操作系统原理与xv6 Linux内核核心技术剖析 - 第3节
3.1 进程的定义与组成(PCB)
进程(Process)是操作系统中最核心的抽象概念之一。它不仅是程序的一次动态执行过程,更是系统进行资源分配和调度的独立单位。理解进程的定义与组成,是掌握操作系统原理的基石。
3.1.1 进程的定义
从不同角度,进程有多种定义:
- 程序的一次执行:进程是正在运行的程序的实例,具有生命周期。
- 资源分配的单位:操作系统以进程为单位分配内存、文件句柄等资源。
- 调度运行的单位:CPU 调度器在进程之间切换,实现并发执行。
关键区分:程序 vs 进程
程序是静态的指令集合,存储在磁盘上;进程是动态的执行环境,包含程序代码、当前活动(如程序计数器值)和资源状态。一个程序可以对应多个进程(如同时打开多个浏览器窗口)。
程序是静态的指令集合,存储在磁盘上;进程是动态的执行环境,包含程序代码、当前活动(如程序计数器值)和资源状态。一个程序可以对应多个进程(如同时打开多个浏览器窗口)。
3.1.2 进程的组成
一个进程在内存中通常由以下三部分组成:
- 代码段(Text Section):存放可执行的机器指令,通常是只读的。
- 数据段(Data Section):存放全局变量、静态变量。
- 堆栈段(Stack & Heap):栈用于函数调用、局部变量和返回地址;堆用于动态内存分配。
3.1.3 进程控制块(PCB)
PCB 是操作系统用于描述和管理进程的数据结构,是进程存在的唯一标识。内核通过 PCB 感知进程的存在。在 xv6 中,PCB 对应 struct proc。
// xv6 内核中的进程结构体(简化)
struct proc {
uint sz; // 进程内存大小
pde_t* pgdir; // 页表基址
char *kstack; // 内核栈底指针
enum procstate state; // 进程状态
int pid; // 进程 ID
struct proc *parent; // 父进程
struct trapframe *tf; // 陷阱帧(保存寄存器)
struct context *context; // 上下文切换点
void *chan; // 睡眠通道
int killed; // 是否被杀死
struct file *ofile[NOFILE]; // 打开的文件
struct inode *cwd; // 当前工作目录
};
PCB 的核心字段可归纳为以下四大类:
| 类别 | 字段示例 | 说明 |
|---|---|---|
| 进程标识 | pid | 唯一标识进程的数字 |
| 处理机状态 | trapframe, context | 保存 CPU 寄存器,用于上下文切换 |
| 进程调度 | state, priority | 进程状态、优先级等调度信息 |
| 资源管理 | pgdir, ofile, cwd | 内存页表、打开文件、工作目录 |
xv6 内核视角: xv6 的
struct proc 数组大小固定为 NPROC = 64,所有进程控制块在系统启动时预分配。进程创建时,内核从数组中分配一个空闲项。
3.2 进程的状态与转换
进程在其生命周期中会经历多种状态。操作系统通过状态机模型管理进程的创建、运行、等待和终止。
3.2.1 三态模型
三态模型是进程状态管理的基础模型,包含三种基本状态:
- 就绪(Ready):进程已具备运行条件,等待 CPU 调度。
- 运行(Running):进程正在 CPU 上执行指令。
- 阻塞(Blocked/Waiting):进程因等待某事件(如 I/O 完成)而暂停执行。
| 状态 | 触发事件 | 目标状态 |
|---|---|---|
| 就绪 → 运行 | 调度程序选中该进程 | 运行 |
| 运行 → 就绪 | 时间片用完或被更高优先级进程抢占 | 就绪 |
| 运行 → 阻塞 | 进程请求 I/O 或等待资源 | 阻塞 |
| 阻塞 → 就绪 | I/O 完成或资源可用 | 就绪 |
3.2.2 五态模型
五态模型在三态基础上增加了创建(New)和终止(Terminated)状态,更完整地描述了进程的完整生命周期。
- 创建(New):进程正在被创建,尚未进入就绪队列。
- 就绪(Ready):进程已创建完毕,等待调度。
- 运行(Running):进程正在 CPU 上执行。
- 阻塞(Blocked):进程等待某事件。
- 终止(Terminated):进程执行完毕或被强制结束,等待系统回收资源。
注意: 在 xv6 中,进程状态定义在
enum procstate 中,包括 UNUSED、EMBRYO、SLEEPING、RUNNABLE、RUNNING、ZOMBIE。其中 ZOMBIE 状态对应五态模型中的终止状态,表示进程已退出但 PCB 尚未被父进程回收。
3.3 线程的概念与引入
传统进程有两个基本属性:资源所有权和调度执行。随着操作系统的发展,人们发现这两个属性可以分离,从而引入了线程(Thread)的概念。
3.3.1 线程的定义
线程是进程内的一个执行单元,是 CPU 调度的基本单位。一个进程可以包含多个线程,它们共享进程的资源(如地址空间、打开的文件),但每个线程拥有独立的栈和寄存器上下文。
3.3.2 引入线程的原因
- 减少开销:线程创建、切换的开销远小于进程。进程切换需要切换地址空间(TLB 刷新),而同一进程内的线程切换只需保存/恢复寄存器。
- 提高并发:多线程可以在多核 CPU 上真正并行执行,提升计算效率。
- 简化编程:对于需要同时处理多个任务的场景(如 Web 服务器处理多个请求),多线程模型比多进程模型更易于编程和通信。
xv6 内核提示: xv6 目前仅支持单线程进程,即每个进程只有一个执行流。但 xv6 的
struct context 和 struct trapframe 机制为线程切换提供了底层支持。在 Linux 中,线程通常通过 clone() 系统调用实现,共享地址空间。
3.4 进程与线程的区别
进程和线程是操作系统并发执行的两个基本单位,它们在资源拥有、调度开销、通信方式等方面存在显著差异。
| 对比维度 | 进程 | 线程 |
|---|---|---|
| 资源拥有 | 独立的地址空间、文件、信号等资源 | 共享所属进程的地址空间和资源 |
| 调度单位 | 资源分配的单位 | CPU 调度的基本单位 |
| 切换开销 | 大(需切换页表、刷新 TLB) | 小(仅保存/恢复寄存器) |
| 通信方式 | IPC(管道、消息队列、共享内存等) | 直接读写共享内存(需同步) |
| 健壮性 | 一个进程崩溃不影响其他进程 | 一个线程崩溃可能导致整个进程崩溃 |
| 创建速度 | 慢(需分配 PCB、地址空间等) | 快(共享进程资源) |
Linux 实现视角: Linux 内核并不严格区分进程和线程,两者都通过
task_struct 表示。线程本质上是与其他任务共享地址空间(通过 clone() 的 CLONE_VM 标志)的进程。xv6 目前仅支持单线程进程,但理解其 PCB 结构是后续扩展多线程支持的基础。