操作系统(8)第二章- 进程同步与互斥
进程同步与互斥的基本概念
一、前提:并发与共享引发的问题
多道程序环境下,进程并发执行,且会访问共享资源(硬件设备、全局变量、缓冲区、文件等)。 多个进程同时读写共享数据时,执行结果不可预期,这种现象称为竞态条件(Race Condition)。 为规避竞态条件、保证数据一致性与执行逻辑正确,引入进程同步与互斥机制。
二、临界区(Critical Section)
1. 定义
进程中访问共享资源的代码片段,是引发竞态条件的根源。 其余不访问共享资源的代码,称为剩余区。
2. 临界区使用四大准则(必考)
- 空闲让进:临界区无人使用时,允许请求进入的进程立即进入。
- 忙则等待:临界区已被占用,其他请求进程必须等待。
- 有限等待:等待的进程不能无限阻塞,在有限时间内一定能进入临界区(避免饥饿)。
- 让权等待:进程等待时,主动放弃 CPU,不忙等(提升 CPU 利用率)。
补充:忙等(忙等待):进程循环检测临界区状态,一直占用 CPU。
三、进程互斥(Mutual Exclusion)
1. 定义
多个竞争关系的进程,同一时刻最多只能有一个进程进入临界区访问共享资源;一个进程在临界区时,其他进程必须等待。
2. 核心特征
- 关系:进程间相互竞争共享资源,无固定执行顺序。
- 目的:保证共享资源的独占访问,杜绝同时操作。
- 典型场景:独占设备(打印机)、全局变量、账户余额修改。
3. 通俗理解
排他使用,你用我就不能用。
四、进程同步(Synchronization)
1. 定义
多个协作关系的进程,按照预先规定的先后次序执行;一个进程的执行依赖另一个进程的运行结果,形成执行顺序约束。
2. 核心特征
- 关系:进程间相互协作,存在逻辑依赖。
- 目的:协调执行顺序,保证业务逻辑正确。
- 典型场景:生产者 - 消费者、数据先写入再读取、流水线任务。
3. 通俗理解
有序执行,你做完,我才能做。
五、互斥与同步的区别与联系
1. 核心区别
| 对比项 | 进程互斥 | 进程同步 |
|---|---|---|
| 进程关系 | 竞争关系,互不依赖 | 协作关系,存在执行依赖 |
| 核心要求 | 禁止同时进入临界区 | 保证固定先后执行顺序 |
| 访问规则 | 随机竞争,谁先抢到谁执行 | 顺序固定,强制等待前驱进程 |
2. 联系
- 二者都是为了解决并发进程访问共享资源的问题,保障执行正确性。
- 同步场景中通常包含互斥:例如生产者 - 消费者,既要控制生产 / 消费顺序(同步),又要独占访问缓冲区(互斥)。
- 二者可使用同一套工具实现(信号量、锁等)。
六、两类典型制约关系
从逻辑上划分,并发进程的制约分为两种,对应同步与互斥:
- 间接制约(源于资源共享)→ 对应互斥多个进程争抢同一独占资源,相互制约。
- 直接制约(源于进程合作)→ 对应同步进程间有数据 / 任务传递,一方执行依赖另一方的结果。
Linux 下的同步与互斥
一、Linux 下的同步互斥 = 3 层结构
从操作系统原理 → 内核实现 → 你写代码用的 API
上层:你用的接口(mutex、sem、rwlock、futex) 中层:内核同步原语(信号量、自旋锁、互斥锁、完成量) 下层:硬件原子指令(CAS、atomic、lock 指令)二、Linux 同步互斥的核心思想
1. 互斥(Mutual Exclusion)
保护共享资源,同一时间只能有一个执行单元访问。
Linux 实现方式:
mutex互斥锁spinlock自旋锁atomic原子操作
本质:把多线程并行 → 变成临界区串行。
2. 同步(Synchronization)
让多个进程 / 线程按顺序执行,你做完我再做。
Linux 实现方式:
- 信号量(semaphore)
- 条件变量(pthread_cond_t)
- 完成量(completion)
- 等待队列(wait queue)
本质:控制执行顺序,解决生产者 - 消费者模型。
三、Linux 最核心的 4 种同步机制(操作系统级)
1)互斥锁(mutex)→ 最常用
Linux 应用层 & 内核层最标准的互斥工具。
原理(操作系统角度)
- 底层基于futex(快速用户态互斥锁)
- 无竞争时:用户态完成,不进内核
- 有竞争时:阻塞线程,不占 CPU(让权等待)
pthread_mutex_t lock; pthread_mutex_lock(&lock); // 进入临界区 临界区代码 pthread_mutex_unlock(&lock); // 退出临界区操作系统特点
- 睡眠等待,不浪费 CPU
- 临界区可以很长
- 应用层开发 90% 用这个
2)信号量(semaphore)→ 同步 + 互斥全能
原理
- 计数器 + 等待队列
- P 操作(down):申请资源,计数器 - 1
- V 操作(up):释放资源,计数器 + 1
两种用法
- 互斥:sem = 1
- 同步:sem = 0 / 资源数
Linux API
sem_t sem; sem_wait(&sem); // P sem_post(&sem); // V操作系统定位
信号量 = 通用同步互斥工具互斥锁是信号量的特例(=1)。
3)自旋锁(spinlock)→ 内核专用
原理
- 加锁失败时while 循环忙等待
- 不睡眠,不切换进程
适用场景
- 临界区非常短
- 中断上下文
- 多核 SMP 环境
特点(操作系统重点)
- 不睡眠、不调度、不释放 CPU
- 不能在自旋锁里调用可能阻塞的函数
4)条件变量(condition variable)→ 同步专用
Linux 下实现 “等待 - 唤醒” 最标准工具。
原理
- 线程可以主动等待某个条件成立
- 另一个线程完成后唤醒它
典型模型
- 生产者 - 消费者
- 缓冲区空 / 满等待
pthread_mutex_lock(&lock); while(条件不满足) pthread_cond_wait(&cond, &lock); // 等待并释放锁 // 操作共享资源 pthread_mutex_unlock(&lock);