操作系统原理与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,所有进程控制块在系统启动时预分配。进程创建时,内核从数组中分配一个空闲项。
进程组成与 PCB 结构 进程内存映像 代码段 (Text) 数据段 (Data) 堆栈段 (Stack + Heap) 进程控制块 (PCB) pid: 进程 ID state: 进程状态 context: 寄存器 pgdir: 页表指针 ofile[]: 文件表 PCB 是进程的唯一凭证 内核通过 PCB 管理进程 xv6 使用 struct proc 数组 最大支持 NPROC=64 个进程

3.2 进程的状态与转换

进程在其生命周期中会经历多种状态。操作系统通过状态机模型管理进程的创建、运行、等待和终止。

3.2.1 三态模型

三态模型是进程状态管理的基础模型,包含三种基本状态:

  • 就绪(Ready):进程已具备运行条件,等待 CPU 调度。
  • 运行(Running):进程正在 CPU 上执行指令。
  • 阻塞(Blocked/Waiting):进程因等待某事件(如 I/O 完成)而暂停执行。
状态 触发事件 目标状态
就绪 → 运行 调度程序选中该进程 运行
运行 → 就绪 时间片用完或被更高优先级进程抢占 就绪
运行 → 阻塞 进程请求 I/O 或等待资源 阻塞
阻塞 → 就绪 I/O 完成或资源可用 就绪
进程三态模型 就绪 Ready 运行 Running 阻塞 Blocked 调度 时间片到 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 尚未被父进程回收。
进程五态模型 创建 New 就绪 Ready 运行 Running 阻塞 Blocked 终止 Terminated 创建完成 调度 时间片到 I/O 请求 I/O 完成 进程退出

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、地址空间等) 快(共享进程资源)
进程 vs 线程 结构对比 进程 A(独立地址空间) 代码段 数据段 线程 1 (栈+PC) 线程 2 (栈+PC) 线程 3 (栈+PC) 进程 B(独立地址空间) 代码段 数据段 线程 1 (栈+PC) 线程 2 (栈+PC) 进程间地址空间隔离,线程间共享进程资源(代码段、数据段、打开文件等)
Linux 实现视角: Linux 内核并不严格区分进程和线程,两者都通过 task_struct 表示。线程本质上是与其他任务共享地址空间(通过 clone() 的 CLONE_VM 标志)的进程。xv6 目前仅支持单线程进程,但理解其 PCB 结构是后续扩展多线程支持的基础。