进程互斥的硬件实现方法
本文----------------------基于B站王道计算机教育《操作系统》课程编写(类似笔记hh),部分图片来源于课程。
一、核心思想
硬件方法通过原子性指令解决竞态条件,其本质是利用CPU底层指令的不可中断特性实现临界区保护。典型方案包括:
中断屏蔽法(适用于单处理器)
TestAndSet(TSL)指令
Swap(XCHG)指令
二、硬件实现方法
1. 中断屏蔽方法
实现原理:
- 利用“开 /关中断指令”实现
- 关中断 即不允许当前进程被中断也必然不会发生进程切换
- 临界区
- 开中断 直到当前进程访问完临界区,再执行开中断指令,才有可能有别的进程上处理机并访问临界区
// 临界区保护模板
disable_interrupts(); // 关中断
// 临界区代码
enable_interrupts(); // 开中断
优点:实现简单、性能高效
缺点:
不适用于多处理器系统
用户态进程禁止直接操作(需内核权限)
长临界区会导致系统响应延迟
2. TestAndSet(TSL)指令---->不满足“让权等待”原则
也被称为测试并设置(Test-and-Set)指令 。以 C 语言描述其逻辑如下:首先,将一个存储器字读到一个寄存器中,然后在该内存地址上存一个非零值 。这个读数和写数的操作是不可分割的,即该指令结束之前其他处理机均不允许访问该存储器字 。简单来说,TSL指令会将共享变量lock的值读取到寄存器中,并将lock设置为1(表示上锁) 。若读取到的lock原始值为0,说明锁未被占用,当前线程可以获取锁;若为1,则说明锁已被占用,线程需要继续循环执行TSL指令,直到读取到的值为0 。
原子操作流程---->不可被中断:
TSL REGISTER, LOCK
// 伪代码实现
bool TestAndSet(bool *lock) {bool oldValue = *lock;*lock = true;return oldValue;
}
典型应用:
bool lock = false;void enter_critical() {while (TestAndSet(&lock)) {// 自旋等待}// 临界区
}void exit_critical() {lock = false;
}
优点:
硬件级原子性保障
适用于单/多处理器环境
缺点:
忙等待导致CPU资源浪费
未实现"让权等待"原则
3. Swap(XCHG)指令--->不满足“让权等待”原则
即交换(Exchange 或 XCHG)指令 。它是一种硬件级别的原子操作,用于交换两个内存位置的值 。在实现互斥锁时,一个内存位置通常是锁变量,另一个内存位置是一个临时变量 。其工作原理是,读取两个内存位置的值并进行交换 。如果锁变量在交换前为false(表示锁未被占用),则当前进程或线程将锁变量设置为true(通过交换操作),并进入临界区;如果锁变量在交换前为true(表示锁已被占用),则当前进程或线程将保持等待状态,直到锁变量被其他进程或线程释放(即设置为false) 。
原子交换操作:
XCHG REG, MEM
// 伪代码实现
void Swap(bool *a, bool *b) {bool temp = *a;*a = *b;*b = temp;
}
实现逻辑:
bool lock = false;
bool old = true;void enter_critical() {while (old == true) {Swap(&lock, &old);}// 临界区
}void exit_critical() {lock = false;
}
对比:
都是先记录下此时临界区是否已经被上锁(old变量),再将上锁标记 lock 设置为 true,最后检查old,如果 old 为 false 则说明之前没有别的进程对临界区上锁,则可跳出循环,进入临界区。
指令 | 原子性保障 | 忙等待问题 | 适用场景 |
---|---|---|---|
TSL | ✔️ | ❌ | 单处理器实时系统 |
Swap/XCHG | ✔️ | ❌ | 简单并发控制 |
三、核心原则
方法 | 空闲让进 | 忙则等待 | 有限等待 | 让权等待 |
---|---|---|---|---|
中断屏蔽 | ✔️ | ✔️ | ❌ | ❌ |
TSL指令 | ✔️ | ✔️ | ❌ | ❌ |
Swap指令 | ✔️ | ✔️ | ❌ | ❌ |
结论:
硬件方法虽解决了竞态条件,但均未实现"让权等待",需结合高级同步机制(如信号量)优化