同步与互斥(2)--临界区互斥的基本方法
操作系统|进程互斥实现:4 种软件方案 + 3 种硬件方案
上一篇我们弄懂了进程同步与互斥的核心定义,以及互斥必须遵守的空闲让进、忙则等待、有限等待、让权等待四大铁则。 本篇我们顺着知识点往下讲:在没有操作系统原生锁 API 的前提下,程序员如何靠纯代码、硬件指令两种思路实现临界区互斥访问
前置统一类比
临界区 = 卫生间内部使用环节
进入区 = 进门检查 + 上锁
退出区 = 开门解锁离开
四大互斥原则就是卫生间使用的 4 条管理规矩
为什么必须做进程互斥?不互斥的严重后果
并发环境下,多个进程会共享系统中的各类资源。如果对临界资源(同一时间只能被一个进程使用的资源)不做互斥保护,直接让多个进程并发访问,就会出现资源竞争、结果错乱的问题。
我们以最经典的「打印机共享」场景为例: 系统中只有一台打印机,进程 A 和进程 B 都需要执行打印任务,两个进程并发运行。
- 操作系统先调度进程 A 上 CPU 运行,A 开始使用打印机打印文档;
- 当 A 刚打印到一半时,分配给它的时间片用完了,操作系统触发调度,切换到进程 B 运行;
- 进程 B 也直接调用打印机,开始打印自己的文档。
最终的结果就是:打印机同时接收两个进程的打印指令,输出的纸张上会夹杂 A 和 B 的内容,两行 A 的文字、三行 B 的文字交错在一起,两份打印任务全部失败,谁都得不到完整正确的结果。
核心本质:临界资源不支持并行使用,并发访问会破坏资源的使用完整性。进程互斥的核心目的,就是保证临界资源在同一时间内只被一个进程独占使用,确保资源访问的正确性。
一、进程互斥的 4 种软件实现方案
软件实现完全依靠共享标记变量 + 代码逻辑完成加锁解锁,不依赖 CPU 特殊硬件指令,从粗糙到标准一共四代方案。
方案 1:单标志法
核心逻辑
全局只设 1 个标记 turn,用来指定下一个允许进入临界区的进程编号。 进程进入区只做检查:如果 turn 不等于自己编号,就循环等待;退出区直接把 turn 改成另一个进程编号,相当于把使用权强行移交对方。

例子
卫生间规定:只允许两个人轮流使用,A 用完必须指定下一个只能是 B,B 用完必须指定下一个只能是 A。 哪怕 A 用完离开、卫生间空着,此时 A 想再次使用也不行,必须等 B 先用完才行。
致命缺陷
违反【空闲让进】原则 临界资源明明空闲,但轮到谁才能谁进,空闲时新来进程无法直接使用,资源利用率极低。
方案 2:双标志先检查法
核心逻辑
设置两个布尔标记 flag [0]、flag [1],代表进程 0 / 进程 1 是否想要进入临界区。 进入区先检查对方 flag 是否为 false,确认对方不想进之后,再把自己 flag 置 true 上锁;退出区把自身 flag 改为 false 解锁。

例子
两个人同时走到卫生间门口,都先看一眼对方有没有要进的想法,俩人都看到对方没打算用,于是俩人同时抬手锁门,一起往卫生间里挤,直接冲突。
致命缺陷
违反【忙则等待】原则 「检查对方状态」和「标记自己上锁」是两条分开的语句,两条指令中间会发生进程切换,两个进程同时判断对方不用、同时上锁,最终同时进入临界区,互斥彻底失效。
方案 3:双标志后检查法
核心逻辑
依旧是双 flag 标记,调换执行顺序:进入区先把自己 flag 置 true 上锁,再去循环检查对方 flag 是否为 true;如果对方也标记要进,就原地等待;退出区清空自身 flag。

例子
俩人一到门口就先举手声明 “我要用卫生间”,然后再互相观望。 极端场景:俩人同时举手声明要用,互相死等对方放弃,无限僵持,谁都进不去。
致命缺陷
- 极端情况会互相卡死,无法满足空闲让进;
- 若某一进程长期霸占标记,另一进程永久无法进入,违反有限等待,会产生饥饿死锁。
方案 4:Peterson 算法
核心逻辑
融合「双 flag 意愿标记」+「turn 礼让标记」,三步完成进入区逻辑:
- 主动争取:把自身 flag 设为 true,表明我想要进入临界区;
- 主动谦让:把 turn 赋值为对方进程号,表示我愿意礼让对方先使用;
- 循环判断:只要对方 flag 为 true 并且 turn 是让对方优先,就循环等待。 退出区将自身 flag 置 false,释放标记.

例子
俩人都先举手说自己想用卫生间,然后主动说 “我让对方先上”,再看对方愿不愿意谦让;如果对方也举手且没礼让,自己就排队等待。 完美规避前面三种算法的漏洞,满足空闲让进、忙则等待、有限等待三条原则。
唯一短板
不满足【让权等待】 进程无法进入临界区时,会不停循环轮询判断条件,CPU 一直空转忙等,不会主动放弃处理机,浪费 CPU 算力。
四种软件方案对比速记
表格
| 算法 | 核心操作顺序 | 违背的互斥原则 |
|---|---|---|
| 单标志法 | 先检查,退出强制转交权限 | 空闲让进 |
| 双标志先检查 | 先查对方,再标记自己 | 忙则等待 |
| 双标志后检查 | 先标记自己,再查对方 | 空闲让进、有限等待(饥饿) |
| Peterson 算法 | 先标意愿→主动礼让→循环校验 | 仅缺让权等待(忙等) |
二、进程互斥的 3 种硬件实现方案
软件算法绕不开指令切换导致的并发漏洞,操作系统依托 CPU 提供原子硬件指令,实现不可打断的加锁操作,分为三大类。
方式 1:中断屏蔽法(开关中断)
实现原理
进程进入临界区直接关闭所有 CPU 中断,退出临界区再打开中断。 中断是进程调度、抢占 CPU 的唯一触发源,关掉中断后 CPU 不会切换进程,当前进程独占 CPU,自然不会有其他进程插入临界区,天然实现互斥。
例子
卫生间使用者进门之后直接锁死整栋楼大门,外面所有人都没法进来换人,自己用完再开门放行。
优缺点
优点:逻辑极简、执行效率高; 缺点:
- 仅适配单处理机 CPU,多核环境下其他核心依然能调度新进程访问临界资源,互斥失效;
- 关中断权限极高,只允许操作系统内核程序使用,普通用户进程无权调用,使用场景受限。
方式 2:TSL 指令(Test And Set Lock,测试并上锁)
硬件原子逻辑
CPU 提供不可拆分的原子指令:一次性完成「读取锁变量 → 把锁强制置为 true」,整条指令执行期间不会被打断。 进入区循环执行 TSL 指令:如果锁原本是 false,代表空闲,原子上锁直接进入临界区;如果锁已经是 true,就循环重试;退出区手动把锁改回 false 解锁。
例子
卫生间门锁是一键机械锁,按下把手瞬间完成 “查看是否上锁 + 直接锁门”,动作不可拆分,不可能两个人同时锁门。
优缺点
优点:硬件原子操作,不会出现软件指令拆分带来的并发问题;天然支持多处理机多核环境,通用性极强; 缺点:依旧是循环轮询等待,不满足让权等待,进程拿不到锁时持续忙等占用 CPU。
方式 3:Swap 交换指令(XCHG 互换指令)
实现原理
同样是 CPU 原子指令,将锁变量与寄存器内容整块互换值,逻辑等价于 TSL 指令,只是底层硬件实现方式不同,锁机制完全一致。
补充说明
考试中默认 Swap 与 TSL 指令能力、优缺点完全相同,都只能解决互斥,无法解决忙等问题,不满足让权等待原则。
三、软件 & 硬件方案共性总结
- 所有纯软件互斥算法里,Peterson 算法是唯一合规双进程软件解法,但无法避免忙等;
- 硬件指令依靠原子性杜绝并发漏洞,是现代操作系统锁机制底层基础,但 TSL/Swap/ 关中断全部做不到让权等待;
- 想要同时满足四大互斥原则全部达标,必须引入信号量机制(后续文章会讲解),从内核层面实现阻塞等待,彻底消除忙等。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)