信号量

信号量

2025 南京大学《操作系统原理》
信号量

用互斥锁实现精确唤醒

一个奇妙的想法

  • 创建锁时,立即 “获得” 它 (总是成功)
  • 其他人想要获得时就会等待
    • 此时 release 就实现了 happens-before
  • 一个线程上锁,在另一个线程解锁 😂

让我们来试一试吧 (demo)

  • 先把厕所门都锁上
  • 线程到达以后等待
  • 管理员把所有门都打开
2025 南京大学《操作系统原理》
信号量

用互斥锁实现计算图

Acquire-Release 实现计算图

  • 为每一条边 e=(u,v)e = (u, v) 分配一个互斥锁 🔒
  • 初始时,全部处于锁定状态
  • 对于一个节点,它需要获得所有入边的锁才能继续
    • 可以直接计算的节点立即开始计算
  • 计算完成后,释放所有出边对应的锁

挺好用 (试试 orchestra 的实现)

  • 甚至比条件变量还好用嘞!
    • ❌ This is undefined behavior
2025 南京大学《操作系统原理》
信号量

本质:“Release as Synchronization”

Release-Acquire 实现了 happens-before

  • Acquire = 等待信号 🚨
  • Release = 发出信号 🚨
    • (这不是 UNIX 的 signal...)

“信号” 可以理解为现实生活中的 “资源许可”

  • 共享,资源,凭 (信号) 进入
    • 游泳馆:有空位获得手环进入更衣室,否则排队等待
    • 餐厅:有空桌可以直接进入,否则取号等待
    • 停车场:有空车位可以直接进场,否则排队
      • 这是一大类同步问题
2025 南京大学《操作系统原理》
信号量

适合处理:计数型的同类资源

为资源实现 acquire 和 release 操作

  • Acquire: 在有车位时进入停车场
  • Release: 出停车场;车位 + 1

center

2025 南京大学《操作系统原理》
信号量

计数型的同类资源:实现同步

void enter_parking() {
    mutex_lock(&lk);
    while (!(in_parking < capacity)) {
        cond_wait(&cv, &lk);
    }
    in_parking++;  // 这个时候我已经 “进入” 了
    mutex_unlock(&lk);
}

void leave_parking() {
    mutex_lock(&lk);
    in_parking--;
    cond_broadcast(&cv);  // 退出以后,可能有人能进入
    mutex_unlock(&lk);
}
  • 这是生产者-消费者呀
    • 确保进入前存在一个 “空位” (count < capacity) 就行
2025 南京大学《操作系统原理》
信号量

计数型的同类资源:实现同步另一种方法

void consume_parking() {  // enter_parking
    mutex_lock(&lk);
    while (!(parking_spot > 0)) {
        cond_wait(&cv, &lk);
    }
    count--;
    mutex_unlock(&lk);
}

void magic_parking() {  // leave_parking
    mutex_lock(&lk);
    parking_spot++;
    cond_broadcast(&cv);
    mutex_unlock(&lk);
}
  • “空位” 和 “占用” 是相对的
    • 停车 = “吃掉” 车位;离开 = “创造” 车位 (允许凭空创造)
2025 南京大学《操作系统原理》
信号量

于是你发明了 “信号量”!

void P(sem_t *sem) {
    // Prolaag - try + decrease/down/wait/acquire
    mutex_lock(&sem->lk);
    while (!(sem->count > 0)) {
        cond_wait(&sem->cv, &sem->lk);
    }
    sem->count--;  // 消耗一个信号 (车位/手环/...)
    mutex_unlock(&sem->lk);
}

void V(sem_t *sem) {
    // Verhoog - increase/up/post/signal/release
    mutex_lock(&sem->lk);
    sem->count++;  // 创建一个信号 (车位/手环/...)
    cond_broadcast(&sem->cv);
    mutex_unlock(&sem->lk);
}
2025 南京大学《操作系统原理》
信号量

我们可以把信号量当互斥锁用

sem_t sem = SEM_INIT(1);

void lock() {
    P(&sem);
}

void unlock() {
    V(&sem);
}
  • 回到了我们开头的代码
    • 允许在不同的线程的 release/acquire
    • “信号量是扩展的互斥锁”
2025 南京大学《操作系统原理》