操作系统八股文:进程与线程全面梳理(附调度算法+IPC+锁机制)
文章目录
前言
这篇文章把进程、线程相关的操作系统八股文全部梳理一遍,面试前看一遍基本够用。
从基础概念到调度算法,从上下文切换到进程通信,覆盖高频考点,每个知识点都给出了面试官最想听到的角度。
一、用户态和内核态
本质区别是 CPU 的执行权限不同。
内核态下 CPU 可以执行所有指令,访问所有硬件资源;用户态下只能执行部分指令集,无法直接操作硬件。
为什么要这么设计?三个原因:
- 安全性:用户程序无法直接破坏硬件资源
- 稳定性:用户态程序崩溃不会拖垮整个系统
- 隔离性:内核和用户程序之间有明确边界,便于维护
内核态的核心操作:内存管理、进程管理、设备驱动、系统调用。
二、进程和线程
2.1 本质区别
| 维度 | 进程 | 线程 |
|---|---|---|
| 定义 | 资源分配的基本单位 | 任务调度和执行的基本单位 |
| 地址空间 | 独立 | 共享所属进程的地址空间 |
| 切换开销 | 大(需切换地址空间) | 小(只切换栈和寄存器) |
| 崩溃影响 | 子进程崩溃不影响其他进程 | 线程崩溃可能导致整个进程崩溃 |
| 通信方式 | 管道、消息队列、共享内存等 | 直接读写共享内存 |
2.2 为什么要有线程
用视频播放器举例。播放器有三个核心模块:读取数据、解压缩、播放画面。
单进程串行执行,读取卡住了,后面全卡死,画面不连贯。
多进程方式解决了并发问题,但进程间通信复杂,切换开销大。
线程的出现解决了这个矛盾:并发执行 + 共享地址空间,通信效率高,切换开销小。
2.3 进程、线程、协程对比
进程
├── 独立内存空间、独立堆栈
├── 内核调度
├── 切换开销:最大(需切换页表、TLB 刷新)
└── 隔离性最强
线程
├── 共享进程的堆和全局变量,独立栈
├── 内核调度
├── 切换开销:中等(只切换栈+寄存器)
└── 存在数据竞争问题,需要同步机制
协程
├── 共享堆,独立寄存器上下文和栈
├── 用户态调度,不需要内核参与
├── 切换开销:最小(无内核态切换)
└── 需要程序员显式管理调度
2.4 多线程的优劣
优势:充分利用多核 CPU,多任务并发,提高吞吐量。
劣势:
- 数据竞争,需要加锁,锁本身有开销
- 有死锁风险
- 消耗更多内存(每个线程都有自己的栈)
线程是不是越多越好?
不是。线程过多会导致:
- 频繁上下文切换,CPU 时间大量浪费在切换上
- 内存占用上升
- 锁竞争加剧,死锁概率增大
三、进程的五种状态
┌─────────────────────────────────┐
▼ │
NULL ──► 创建态 ──► 就绪态 ◄──────────────── 运行态 ──► 终止态
│ 时间片用完 / 被抢占 │
│ 调度器选中 │
▼ ▼
运行态 ──────────────────────► 阻塞态
等待 I/O 等事件
│
事件完成 │
就绪态 ◄──────────────────────┘
关键转换:
- 阻塞态 → 运行态:不能直接转换,必须先回到就绪态,再被调度器选中
- 就绪态 → 阻塞态:不能转换,没在运行,无法发起等待
四、进程上下文切换
4.1 进程上下文有哪些
分三个层次:
用户级上下文
- 代码段、数据段、堆、用户栈(用户空间的内存内容)
寄存器上下文
- PC(程序计数器):下一条指令地址
- SP(栈指针):当前栈顶
- PSW(程序状态字):标志位,零标志/进位标志/特权位等
- 通用寄存器:运算中间结果、函数参数、返回值
系统级上下文
- PCB 本身(进程状态、PID、优先级)
- 页表(内存映射)
- 文件描述符表
- 内核栈
寄存器上下文保存在 PCB(进程控制块)中,PCB 存在内核内存里,用户态无法直接访问。
4.2 切换流程
进程 A 运行
│
▼ 触发切换(时间片用完 / 系统调用 / I/O 阻塞)
│
├── 1. 保存 A 的寄存器上下文 → A 的 PCB
├── 2. A 的状态改为就绪或阻塞
├── 3. 调度器从就绪队列选出进程 B
├── 4. 从 B 的 PCB 恢复寄存器上下文
└── 5. 切换地址空间(换页表,TLB 刷新)
│
▼
进程 B 运行
4.3 进程切换 vs 线程切换
| 操作 | 进程切换 | 线程切换(同进程内) |
|---|---|---|
| 地址空间 | 需要切换(刷 TLB) | 不需要(共享) |
| 寄存器 | 保存/恢复 | 保存/恢复 |
| 内核栈 | 切换 | 切换 |
| 总开销 | 大 | 小 |
线程切换快,本质上节省的是地址空间切换的代价(页表切换 + TLB 失效)。
五、线程切换详细过程
线程 A 运行
│
▼ 调度器介入
│
├── 1. 保存 A 的 CPU 上下文 → A 的 TCB(线程控制块)
│ 包括:PC、SP、PSW、通用寄存器
├── 2. A 的状态 Running → Ready 或 Blocked
├── 3. 调度器按算法选出线程 B
├── 4. 从 B 的 TCB 恢复上下文
└── 5. 跳转到 B 的 PC 指向的位置继续执行
│
▼
线程 B 继续运行
上下文保存在 TCB(线程控制块)中,TCB 是 OS 管理线程的数据结构,存在内核内存里。
六、三级调度
| 调度层级 | 别名 | 调度对象 | 频率 |
|---|---|---|---|
| 长程调度 | 作业调度 | 作业 | 低(从后备队列选作业进内存) |
| 中程调度 | 内存调度 | 进程 | 中(换入/换出内存) |
| 短程调度 | CPU 调度 | 进程(现代OS是线程) | 极高(毫秒级) |
注意:长程调度的对象是作业,不是进程。作业经过长程调度才创建进程进入内存。
七、进程调度算法
7.1 先来先服务(FCFS)
按进入就绪队列的顺序调度,非抢占式。
- 优点:实现简单,公平
- 缺点:长作业先来,短作业等待时间过长
适合 CPU 繁忙型,不适合 I/O 繁忙型。
7.2 最短作业优先(SJF)
优先选运行时间最短的进程。
- 优点:提高系统吞吐量
- 缺点:长作业可能永远饿死
7.3 高响应比优先(HRRN)
响应比 = (等待时间 + 要求服务时间) / 要求服务时间
等待越久,响应比越高,兼顾了长作业和短作业,是 FCFS 和 SJF 的折中。
7.4 时间片轮转(RR)
每个进程分配固定时间片,用完就切换,循环执行。
时间片太短 → 切换频繁,CPU 效率低
时间片太长 → 短作业响应慢
通常设为 20ms~50ms。
7.5 最高优先级(HPF)
选就绪队列中优先级最高的进程运行。
- 静态优先级:创建时确定,不变
- 动态优先级:随等待时间增加而提升(避免饿死)
- 非抢占式 / 抢占式两种处理方式
缺点:低优先级进程可能永远得不到执行。
八、进程间通信(IPC)
8.1 六种方式对比
管道(Pipe)
├── 匿名管道:只能用于父子/兄弟进程,单向,内核缓存
└── 命名管道:突破亲缘限制,基于文件系统
消息队列
└── 内核中的消息链表,支持自定义数据类型,有用户态/内核态拷贝开销
共享内存(最快)
└── 直接映射到相同物理内存,零拷贝,但需要配合信号量防竞争
信号量
└── 计数器,P操作(-1)可阻塞,V操作(+1)可唤醒,解决互斥/同步
信号
└── 异步通知机制,SIGKILL/SIGSTOP 无法被捕捉或忽略
Socket
└── 跨主机通信,也支持本地通信,TCP/UDP/Unix域套接字
信号 vs 信号量(高频混淆点):
| 信号 | 信号量 | |
|---|---|---|
| 用途 | 异步事件通知 | 资源计数,同步互斥 |
| 本质 | 软件中断 | 计数器 |
| 场景 | Kill 进程、Ctrl+C | 控制并发访问数量 |
共享内存为什么最快?
共享内存把同一块物理内存映射到多个进程的虚拟地址空间,进程直接读写,无需拷贝,不需要陷入内核。
九、线程间通信与同步
Linux 提供五种机制:
| 机制 | 核心用途 | 等待方式 |
|---|---|---|
| 互斥锁 | 保护临界区,互斥访问 | 阻塞睡眠 |
| 自旋锁 | 临界区极短的互斥场景 | 忙等(CAS 原子指令) |
| 读写锁 | 读多写少的共享资源 | 读并发,写独占 |
| 条件变量 | 等待某个条件成立(配合互斥锁) | 阻塞睡眠 |
| 信号量 | 控制资源访问数量 | P操作可阻塞,V操作唤醒 |
互斥锁 vs 自旋锁
两者目的相同:都是保护共享资源,区别只在于抢不到锁时怎么等。
互斥锁:抢不到就睡觉,让出 CPU(适合锁持有时间长的场景)
自旋锁:抢不到就原地转圈,继续占着 CPU(适合锁持有时间极短的场景)
单核 CPU 不要用自旋锁——持有锁的线程没机会运行,自旋线程会一直空转。
十、总结
面试高频考点速记
进程 vs 线程:资源分配 vs 调度执行,切换开销不同
五种状态:阻塞→就绪→运行,不能跳步
上下文切换:保存寄存器→换状态→调度→恢复→执行
三级调度:长程=作业,中程=换入换出,短程=CPU调度
进程通信:共享内存最快,管道最简单,Socket跨主机
线程同步:互斥锁睡眠等,自旋锁忙等,读写锁读并发写独占
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)