进程、线程与任务 — 专题笔记

← 操作系统知识地图


进程

程序是静态的,进程是程序的一次执行过程,是 OS 进行资源分配的基本单位。

OS 管理进程的核心数据结构是 PCB(Process Control Block,进程控制块)。进程创建时建立 PCB,调度、阻塞、唤醒、切换、回收全部依赖它。

PCB 记录什么

类别典型内容作用
进程标识PID、父进程 PID、所属用户唯一标识
进程状态创建/就绪/运行/阻塞/终止处于生命周期的哪一步
调度信息优先级、时间片、等待原因、队列指针决定何时运行、是否被抢占
资源与内存打开的文件、I/O 设备、页表/段表、地址空间占用了什么、内存如何组织
处理机现场通用寄存器、PC、PSW、栈指针切换时保存/恢复 CPU 现场

PCB 位于内核空间,不跟用户态的代码段、堆、栈并列。

进程内存映像

区域内容说明
.text机器指令只读,多进程可共享同一份
.data已初始化全局/静态变量进程私有
.bss未初始化全局/静态变量运行时置 0,进程私有
heapmalloc/new 动态分配生命周期自行控制
stack函数调用现场、局部变量、返回地址每个线程有自己的栈
共享库/映射区动态链接库、mmap 映射只读部分可共享

对照 编译与链接导论

进程状态

创建态 → 就绪态 ⇄ 运行态 → 终止态
              ↘ 阻塞态 ↗
状态含义
创建态资源和 PCB 正在建立
就绪态具备运行条件,只差 CPU
运行态正在 CPU 上执行
阻塞态等待某事件(I/O、数据、资源),不能运行
终止态运行结束,等待回收

进程间通信(IPC)

方式机制关键点
共享存储多进程访问同一块内存效率高(免复制);必须配合同步/互斥,否则数据冲突
消息传递发方→收方传递完整消息直接通信:发方直接指定目标进程;间接通信:通过信箱/消息队列中转
管道内核提供的固定大小环形缓冲区有序数据流,空则阻塞读、满则阻塞写;需处理互斥、同步、对端状态
信号轻量异步通知,“某事件发生了”内核或进程发出;PCB 中维护 pending 位图 + blocked 位图,信号到达置位,处理完清零,阻塞则暂挂

互斥与同步的细节见 同步、互斥与通信

线程

线程是 CPU 调度的基本单位。进程是资源容器,线程是真正上 CPU 跑的执行流。

线程与进程的资源关系

同进程线程共享每个线程独立拥有
代码段、数据段、堆寄存器现场
打开的文件/文件描述符程序计数器 PC
大部分系统资源
线程局部存储 TLS

一句话:共享资源,不共享执行现场。

TCB

TCB(Thread Control Block)管线程:

类别典型内容
线程 ID唯一标识
寄存器集合通用寄存器、PC、状态字
当前状态运行/就绪/阻塞
调度优先级静态/动态优先级
TLS线程私有数据
堆栈指针指向该线程自己的栈

线程开关为何比进程开关轻量 — 空间与时间

对比项N 个独立进程1 个进程 + N 个线程
控制块N 份完整 PCBN 份精简 TCB(无内存/文件/IO 信息)
切换时做什么换掉整个地址空间(页表、TLB 刷新)+ 保存/恢复全部上下文只切换寄存器、PC、栈指针,不动地址空间
共享信息的位置各自复制,IPC 搬运堆、数据段、文件直接共享,无需传递

直觉:把原来 8 个独立进程当作 1 个进程,内部改成 8 个线程 → 控制块变小、切换不动地址空间 → 时空开销都降下来。

线程状态

核心三态:运行态 ↔ 就绪态 → 阻塞态(与进程状态同理,不再展开)。

线程实现方式

类型管理方特点
用户级线程(ULT)用户空间线程库切换快(不陷内核);但一线程阻塞→全进程阻塞,无法利用多核
内核级线程(KLT)内核直接管理支持多核并行,一线程阻塞不拖累其他;但切换开销大

为什么 KLT 切换比 ULT 慢?

  • ULT:切换全在用户态完成,线程库自己保存/恢复寄存器、换栈、跳 PC。OS 根本不知道发生了切换,不存在用户态↔内核态的往返。
  • KLT:切换必须走内核。先 陷入指令(trap)从用户态切到内核态,内核调度器选下一个线程,再从内核态切回用户态。多出来的就是这个 用户态 ↔ 内核态的模式切换——它不是线程切换本身的开销,而是”要切线程必须先去找内核”的代价。

映射模型

ULT 是用户态自己搞出来的线程,内核不认识。内核只认 KLT。所以必须有一种映射关系,把 ULT “挂”到 KLT 上,内核才能给这个进程分 CPU。

模型映射关系怎么干活死穴
多对一多个 ULT → 1 个 KLT用户空间线程库在进程内部轮转 ULT,内核完全不知情,它只看到”一个线程”一个 ULT 阻塞(调了阻塞系统调用)→ 内核把唯一的 KLT 挂起 → 全部 ULT 都瘫痪;也无法利用多核
一对一1 个 ULT → 1 个 KLT每个 ULT 背后都对应一个内核线程,内核直接调度,能真正并行到多核创建/删除/切换每次都要陷内核(trap),开销上去了
多对多M 个 ULT ↔ N 个 KLT(N ≤ M)用户态分配 ULT 到空闲的 KLT 上跑,一个 ULT 阻塞了,同进程其他 ULT 可以换绑到另的 KLT两头都要协调,实现复杂

直觉

  • 多对一 = 全公司共用一个电话,一个人占线,全公司失联
  • 一对一 = 每人一部座机,但打电话必须去前台转接(trap)
  • 多对多 = N 部公用电话,谁用谁拿,一个占线换另一部

与 RTOS 任务的对应

在 MCU + RTOS 场景中,Task 更像线程,不是完整进程——多数小型 RTOS 没有进程隔离和独立地址空间。

对照 FreeRTOS 任务模型