同步、互斥与通信 — 专题笔记

← 操作系统知识地图


基本概念

术语定义
临界资源同一时刻只允许一个进程访问(打印机、共享变量、缓冲区)
临界区访问临界资源的代码段
同步约束执行顺序——“谁先谁后”,信号量初值通常 0
互斥排他访问——“不能同时进”,信号量初值通常 1
准则含义违反后果
空闲让进空着就放行利用率低
忙则等待有人就排队互斥失效
有限等待不能无限排饥饿/死锁
让权等待进不去就挂起,别空转CPU 浪费

软件方案

方案机制致命问题
单标志法turn 轮流对方不想进时自己也不能进 → 违背空闲让进
双标志先检查先看对方,再设自己检查与置位非原子 → 可能同时进
双标志后检查先设自己,再看对方双方都设了标志又互等 → 死锁
Petersonflag[2] + turn 谦让满足前三准则,但用忙等待 → 缺让权等待
// Peterson — Pi 进入
flag[i] = true;
turn = j;
while (flag[j] && turn == j);   // 对方想进且轮到对方 → 等
// 临界区
flag[i] = false;

硬件方案

方案做法问题
关中断单 CPU 关中断不调度多 CPU 无效、关太久影响响应、仅内核可用
Test-and-Set原子读锁并置 true,返回 false = 获锁(原未加锁)忙等待
Swapswap(key, lock),交换后 key=false = 获锁忙等待

1781603322627

自旋锁互斥锁
等待方式CPU 空转阻塞挂起
让权等待
适用临界区极短、多核、不可睡眠上下文临界区长

信号量

P (wait/down)V (signal/up)
行为申请资源,减信号量,不足则阻塞释放资源,增信号量,唤醒等待者
来源荷兰语_proberen_(测试)荷兰语_verhogen_(增加)
整型记录型
结构int Sint value + 等待队列
P 操作while (S<=0); S--;S--; if(S<0) block();
等待方式忙等阻塞
让权等待
typedef struct { int value; queue L; } semaphore;
void wait(semaphore *S) { S->value--; if (S->value < 0) block(S->L); }
void signal(semaphore *S) { S->value++; if (S->value <= 0) wakeup(S->L); }
// value: >0 = 可用数, <0 = |等待进程数|
用途初值模式口诀
互斥1P(mutex) …临界区… V(mutex)
同步0前驱后 V,后继前 P
前驱关系每边一个 0边起点后 V,边终点前 P等前面→P,放行后面→V
S1 → S2 → S4
  ↘ S3 ↗
semaphore a=0,b=0,c=0,d=0;
S1; V(a); V(b);
P(a); S2; V(c);
P(b); S3; V(d);
P(c); P(d); S4;

经典问题

生产者-消费者,临界区只能有一个

semaphore mutex=1, empty=N, full=0;   // 互斥 + 空位数 + 满位数
 
producer() {
    produce;
    P(empty);           // 先同步:等空位
    P(mutex);           // 再互斥
    put;
    V(mutex);
    V(full);            // 放行消费者
}
consumer() {
    P(full);            // 等满位
    P(mutex);
    take;
    V(mutex);
    V(empty);           // 放行生产者
    consume;
}
// ⚠️ P顺序不能换:先P(同步)再P(mutex),否则满时生产者持锁等empty → 消费者等锁 → 死锁

读者-写者

int readcount = 0;
semaphore mutex = 1, rw = 1;
 
reader() {
    P(mutex);
    if (++readcount == 1) P(rw);  // 第一个读者锁文件
    V(mutex);
    读;
    P(mutex);
    if (--readcount == 0) V(rw);  // 最后读者放文件
    V(mutex);
}
writer() { P(rw); 写; V(rw); }
// 读者优先:读者源源不断 → 写者饥饿
// 写者优先:有写者在等时阻新读者插队,额外加 w 信号量

哲学家进餐

死锁条件:5 人同时拿左边等右边 → 环形等待。

避免方案做法打破哪条死锁条件
限制人数最多 4 人同时拿筷不剥夺
奇偶策略奇先左后右,偶先右后左环路等待
一次拿俩mutex 保护取筷,原子化保持并等待

管程

信号量管程
互斥程序员手写 P/V编译器/运行时自动
风险P/V 忘写/配错 → 死锁
结构分散封装(数据+操作+同步)
条件变量操作行为
wait(cv)阻塞当前进程,释放管程互斥权
signal(cv)唤醒 cv 上一个等待者

管程 = 共享数据 + 互斥访问 + 等待唤醒,统一封装。信号量是手动挡,管程是自动挡。