本页目录

futex: 用户态锁的内核心跳

覆盖: futex(FUTEX_WAIT/FUTEX_WAKE) → pthread_mutex 内部 → PI futex → robust futex → futex2 (6.x) 适用: Linux 用户态, glibc pthread, 任何语言运行时

概述

futex (Fast Userspace muTEX) 是 Linux 上所有用户态同步原语的基础。pthread_mutex、semaphore、barrier、Rust std::sync::Mutex、Go runtime 的信号量——全部基于 futex。它的核心思想是:⁠竞争少时在用户态解决(原子的 CAS),只在真的需要等待/唤醒时才进内核⁠。

futex 系统调用

#include <linux/futex.h>
#include <sys/syscall.h>

// futex(uaddr, futex_op, val, timeout, uaddr2, val3);

// 等待: 如果 *uaddr == val → 睡眠等待
syscall(SYS_futex, uaddr, FUTEX_WAIT, val, NULL, NULL, 0);

// 唤醒: 唤醒最多 val 个等待在 uaddr 上的线程
syscall(SYS_futex, uaddr, FUTEX_WAKE, val, NULL, NULL, 0);

// 等待 + 原子操作 (FUTEX_OP):
//   如果 *uaddr2 == val3 → uaddr2 += op → 然后 FUTEX_WAIT on uaddr

pthread_mutex 内部: futex 经典模式

// glibc: nptl/pthread_mutex_lock.c (简化)
// 状态: 0=空闲, 1=locked (无等待), 2=locked (有等待)

void pthread_mutex_lock(pthread_mutex_t *m) {
    // 快速路径: CAS 拿锁 (用户态, 无 syscall!)
    int old = 0;
    if (atomic_compare_exchange_strong(&m->__lock, &old, 1))
        return;  // 已获得锁! 零系统调用

    // 慢路径: 有人持锁
    // 设锁为 2 (通知 unlock: 有人等待, 需要 futex_wake)
    if (atomic_exchange(&m->__lock, 2) == 0)
        return;  // 锁在交换时变为空闲 → 获得

    // 真的需要等待:
    while (atomic_exchange(&m->__lock, 2) != 0) {
        // FUTEX_WAIT_PRIVATE: 同进程内 → 不用 mm_struct 查找
        futex_wait(&m->__lock, 2, NULL);
    }
}

void pthread_mutex_unlock(pthread_mutex_t *m) {
    // 如果锁 == 1 → 设回 0 (无等待者) → 不需要 futex_wake!
    if (atomic_exchange(&m->__lock, 0) == 1)
        return;  // 释放成功! 零系统调用

    // 有等待者 (lock was 2) → 需要唤醒
    futex_wake(&m->__lock, 1);  // 唤醒 1 个等待者
}

分析

无竞争: lock + unlock 全程 2 次 atomic CAS → ~20ns → 零系统调用!
轻微竞争: lock 慢路径, unlock 无等待者 → 1 futex_wake per lock cycle
重度竞争: lock 始终在等待 → 1 futex_wait + 1 futex_wake per lock cycle

这就是为什么"锁很慢"其实是"锁竞争很慢"

PI futex: 优先级继承

// 问题: 高优先级线程等锁 → 低优先级持锁者被中优先线程抢占
//       → 优先级反转 (Mars Pathfinder 1997)

// PI futex: 持锁者临时提升到等待者中最高的优先级
pthread_mutexattr_setprotocol(&attr, PTHREAD_PRIO_INHERIT);
// → 内核 futex(PI) 机制自动管理优先级 → 适合实时系统

Robust futex: 持锁者崩溃保护

// 问题: 线程持锁时崩溃 → 锁永远不释放 → 其他线程死锁

// Robust mutex:
pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
pthread_mutex_lock(&m);
// 如果返回 EOWNERDEAD:
//   → 上一个持锁者崩溃 → 锁处于"脏"状态
//   → 需要检查数据结构并修复 → pthread_mutex_consistent(&m)

futex2: 新一代 (6.x)

// 当前 futex 的问题:
//   - 只有 32-bit futex word (需要 64-bit 时只能用额外的锁)
//   - NUMA-unaware (唤醒可能在远程 NUMA 节点的 CPU)
//   - 没有 deadline-aware waiting

// futex2 (6.x, discussion since 5.x):
//   - 支持 8/16/32/64-bit futex word
//   - NUMA-aware wake (优先唤醒本地节点等待者)
//   - FUTEX_WAIT_MULTIPLE (同时等待多个 futex, 类似 select)

参考

  • man: futex(2), pthread_mutex(3)
  • 源码⁠: kernel kernel/futex/, glibc nptl/pthread_mutex_lock.c
  • LWN: "Futexes are tricky", "The futex2 proposal"
  • 论文⁠: "Fuss, Futexes and Furwocks: Fast Userlevel Locking in Linux" (2002)

关键词: futex, FUTEX_WAIT, FUTEX_WAKE, pthread_mutex, CAS, PI futex, robust futex, futex2