kernel/locking 内核锁机制与原理详解

kernel/locking 内核锁机制与原理详解

源码路径rk3588/kernel-6.1/kernel/locking/
内核版本:Linux 6.1(RK3588 平台)
平台:RK3588(4×Cortex-A76 + 4×Cortex-A55 big.LITTLE,ARM64)

该目录实现 Linux 内核 同步原语(Synchronization Primitives) 的核心运行时:自旋锁、互斥锁、读写信号量、计数信号量、实时互斥锁,以及 Lockdep 运行时锁依赖验证框架。这些原语是调度器、内存管理、文件系统、驱动等所有内核子系统并发控制的基础。

定位:内核并发控制的「基础设施层」;上层子系统通过 include/linux/spinlock.hmutex.hrwsem.h 等头文件调用本目录实现。


目录


一、源码目录结构

1.1 编译依赖(Makefile)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 始终编译
obj-y += mutex.o semaphore.o rwsem.o percpu-rwsem.o

# 条件编译
obj-$(CONFIG_DEBUG_IRQFLAGS) += irqflag-debug.o
obj-$(CONFIG_DEBUG_MUTEXES) += mutex-debug.o
obj-$(CONFIG_LOCKDEP) += lockdep.o lockdep_proc.o
obj-$(CONFIG_SMP) += spinlock.o
obj-$(CONFIG_LOCK_SPIN_ON_OWNER) += osq_lock.o
obj-$(CONFIG_QUEUED_SPINLOCKS) += qspinlock.o
obj-$(CONFIG_RT_MUTEXES) += rtmutex_api.o
obj-$(CONFIG_PREEMPT_RT) += spinlock_rt.o ww_rt_mutex.o
obj-$(CONFIG_QUEUED_RWLOCKS) += qrwlock.o
obj-$(CONFIG_LOCK_EVENT_COUNTS) += lock_events.o
配置项 说明
CONFIG_SMP 多核下编译 spinlock 通用实现
CONFIG_QUEUED_SPINLOCKS MCS 风格排队自旋锁(ARM64 默认 y)
CONFIG_QUEUED_RWLOCKS 排队读写自旋锁
CONFIG_MUTEX_SPIN_ON_OWNER mutex 乐观自旋(SMP + 原子 RMW)
CONFIG_RWSEM_SPIN_ON_OWNER rwsem 乐观自旋
CONFIG_RT_MUTEXES 实时互斥锁(PI 支持)
CONFIG_LOCKDEP 运行时锁依赖图验证
CONFIG_PROVE_LOCKING Lockdep 完整证明模式
CONFIG_LOCK_STAT 锁竞争统计(perf lock)
CONFIG_PREEMPT_RT RT 内核:spinlock→sleeping lock 转换

Kconfig 主要位于 kernel/Kconfig.lockslib/Kconfig.debug

1.2 源文件

文件 行数 功能
lockdep.c 6611 锁依赖图、死锁/逆序/IRQ 上下文检测
rwsem.c 1716 读写信号量:读者计数 + 写者独占
rtmutex.c 1834 RT 互斥锁:优先级继承、自适应自旋
mutex.c 1151 互斥锁:handoff、乐观自旋、WW-Mutex
locktorture.c 1117 锁压力测试(debug 配置)
lockdep_proc.c 730 /proc/lockdep* 接口
test-ww_mutex.c 683 WW-Mutex 自测
rtmutex_api.c 612 rtmutex 导出 API(含 #include rtmutex.c
qspinlock.c 596 排队自旋锁慢路径
ww_mutex.h 569 Wait/Wound 死锁避免算法
qspinlock_paravirt.h 562 虚拟化 paravirt qspinlock
spinlock.c 415 SMP spinlock/rwlock 通用实现
rwbase_rt.c 298 PREEMPT_RT 下 rwsem 基类
spinlock_rt.c 280 PREEMPT_RT spinlock→rtmutex 包装
percpu-rwsem.c 282 Per-CPU 读写信号量
semaphore.c 279 计数信号量
osq_lock.c 232 乐观自旋队列锁
lock_events.c 179 debugfs 锁事件计数
spinlock_debug.c 235 spinlock 调试检查
mutex-debug.c 118 mutex 调试检查
qrwlock.c 92 排队读写锁慢路径
ww_rt_mutex.c 101 PREEMPT_RT 下 WW-Mutex

相关头文件(目录外)

文件 功能
include/linux/spinlock.h spinlock/rwlock API
include/linux/mutex.h mutex API
include/linux/rwsem.h rwsem API
include/linux/rtmutex.h rtmutex API
include/linux/lockdep.h Lockdep 宏与 lock_class
arch/arm64/include/asm/spinlock.h ARM64 自旋锁类型与 inline
include/asm-generic/qspinlock.h 通用 qspinlock 实现

二、整体架构与锁分类

2.1 锁类型对比

锁类型 睡眠 上下文 典型用途
spinlock 否(自旋) 不可睡眠;IRQ 变体 短临界区、per-CPU、IRQ
rwlock 同上 读多写少短临界区
mutex 进程上下文 长临界区、严格所有权
rwsem 进程上下文 读多写少长临界区
semaphore 含 IRQ(down_trylock) 计数资源、遗留接口
rtmutex 进程上下文 PI、PREEMPT_RT、futex
percpu-rwsem 读者无锁 进程上下文 极多读、极少写

2.2 架构层次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
应用 / 驱动 / 文件系统 / MM / 网络 ...


include/linux/{spinlock,mutex,rwsem,rtmutex,semaphore}.h


kernel/locking/ ← 本目录
├── qspinlock.c ← 自旋锁慢路径
├── mutex.c ← 睡眠锁
├── rwsem.c
├── rtmutex.c
├── lockdep.c ← 验证层(插桩于各锁 API)
└── osq_lock.c ← 乐观自旋辅助


arch/arm64/include/asm/spinlock.h ← 架构相关 fastpath
include/asm-generic/qspinlock.h

2.3 设计原则

  1. Fastpath / Slowpath 分离:热路径 inline 于头文件或架构代码,冷路径在本目录
  2. 乐观自旋(Optimistic Spinning):mutex/rwsem 在 owner 运行且未阻塞时短暂自旋,避免调度开销
  3. Handoff 机制:直接传递锁所有权,减少唤醒延迟
  4. Lockdep 插桩:通过 lock_acquire/lock_release 构建全局依赖图

三、自旋锁与排队自旋锁

3.1 spinlock.c — SMP 通用实现

CONFIG_GENERIC_LOCKBREAK 且非 debug 时,提供带 lockbreak 的 spin 循环:长时间自旋时 preempt_enable() 让出 CPU,并向持有者 CPU 发送 break 信号。

1
2
3
4
5
6
7
8
9
10
#define BUILD_LOCK_OPS(op, locktype) \
void __lockfunc __raw_##op##_lock(locktype##_t *lock) {
for (;;) {
preempt_disable();
if (likely(do_raw_##op##_trylock(lock)))
break;
preempt_enable();
arch_##op##_relax(&lock->raw_lock);
}
}

Lockdep 启用时 禁用 preempt spin-ops,因 lockdep 假设加锁期间不重新开中断。

3.2 qspinlock.c — MCS 排队自旋锁

传统 ticket/spinlock 在 SMP 上导致 缓存行乒乓;qspinlock 基于 MCS lock 改进,将 {tail, next->locked} 压缩进 32 位 spinlock_t

1
2
3
lock byte (1B)  : 锁状态 / pending
tail (3B) : (cpu+1)<<偏移 | nesting_level(2bit)
nesting: task / softirq / hardirq / nmi 最多 4 层

慢路径流程

1
2
3
4
5
6
queued_spin_lock_slowpath()
├── pending 位置自旋(减少 CAS 竞争)
├── 获取 per-CPU qnode(MAX_NODES=4)
├── MCS 队列入队 tail
├── 前驱 unlock 后 acquire
└── 队首获取锁,设置 _Q_LOCKED_VAL

Per-CPU 队列节点

1
static DEFINE_PER_CPU_ALIGNED(struct qnode, qnodes[MAX_NODES]);

3.3 qrwlock.c — 排队读写锁

读写锁慢路径:queued_read_lock_slowpath / queued_write_lock_slowpath

  • 读者:减 _QR_BIAS 入队等待,无写者时 acquire
  • 写者:设置 _QW_WAITING,等所有读者离开后 _QW_LOCKED
  • 中断上下文读者:若写者仅 waiting 未 holding,直接 spin acquire(避免睡眠)

3.4 mcs_spinlock.h

MCS 锁节点定义,供 qspinlock 与 osq_lock 共用:

1
2
3
4
struct mcs_spinlock {
struct mcs_spinlock *next;
int locked; /* 1=等待, 0=可运行 */
};

四、互斥锁 mutex(mutex.c)

4.1 数据结构

1
2
3
4
5
6
7
8
9
struct mutex {
atomic_long_t owner; /* task_struct* + 低 3 bit 标志 */
raw_spinlock_t wait_lock;
struct list_head wait_list;
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
struct optimistic_spin_queue osq;
#endif
struct lockdep_map dep_map;
};

owner 标志位

Bit 名称 含义
0 MUTEX_FLAG_WAITERS 等待队列非空,unlock 需 wakeup
1 MUTEX_FLAG_HANDOFF 需 handoff 给 top waiter
2 MUTEX_FLAG_PICKUP handoff 完成,等待 pickup

4.2 Fastpath

1
2
3
4
5
6
void __sched mutex_lock(struct mutex *lock)
{
might_sleep();
if (!__mutex_lock_fastpath(lock))
__mutex_lock_slowpath(lock);
}

__mutex_trylock_common() 通过 CAS 设置 owner 为 current;失败进入 slowpath。

4.3 Slowpath 与 Handoff

1
2
3
4
5
__mutex_lock()
├── mutex_optimistic_spin() ← 见 §九
├── 加入 wait_list
├── schedule() 睡眠
└── 被唤醒后 __mutex_trylock(handoff=true) pickup

Handoff:unlock 时 __mutex_handoff(lock, top_waiter) 直接将 owner 设为等待者并置 PICKUP,避免「释放→再竞争」开销。

4.4 乐观自旋

1
2
3
4
5
mutex_optimistic_spin(lock, ww_ctx, waiter)
├── need_resched() / !owner_on_cpu() → 放弃
├── osq_lock(&lock->osq) ← 单 CPU 一个 MCS 节点
├── spin while owner->on_cpu && !need_resched
└── trylock 成功 → 返回 true

仅在 owner 正在其他 CPU 运行 时自旋;owner 睡眠则立即 schedule。

4.5 WW-Mutex 集成

ww_mutex_lock() 通过 ww_acquire_ctx 实现 Wait/Wound 机制(见 §十),用于 DRM/GPU 等多对象锁顺序场景。


五、读写信号量 rwsem(rwsem.c)

5.1 count 位域(64 位)

1
2
3
4
5
Bit 0     : RWSEM_WRITER_LOCKED  — 写者持有
Bit 1 : RWSEM_FLAG_WAITERS — 有等待者
Bit 2 : RWSEM_FLAG_HANDOFF — handoff 进行中
Bits 8-62 : 55-bit reader count
Bit 63 : RWSEM_FLAG_READFAIL — 读者获取失败守卫位

5.2 owner 字段

1
2
3
atomic_long_t owner;
/* Bit 0: RWSEM_READER_OWNED — 读者模式 */
/* Bit 1: RWSEM_NONSPINNABLE — 禁止乐观自旋 */

写者 acquire 时写入 task_struct*;读者设置 READER_OWNED。用于 乐观自旋 判断 owner 是否在运行。

5.3 读者/写者路径

操作 机制
down_read() atomic_long_fetch_add(READER_BIAS) fastpath
down_write() cmpxchg 获取 WRITER_LOCKED
竞争 wait_list + schedule
Lock stealing 写者 handoff 后读者可「偷取」锁
Optimistic spin 类似 mutex,spin on writer owner

5.4 PREEMPT_RT

#ifndef CONFIG_PREEMPT_RT 包裹主实现;RT 内核使用 rwbase_rt.c 将 rwsem 基于 rtmutex 实现。


六、实时互斥锁 rtmutex(rtmutex.c)

6.1 特性

  • 优先级继承(PI):解决优先级反转
  • 自适应自旋:owner 运行时 spin
  • Futex 基础:用户态锁的 kernel 侧实现
  • PREEMPT_RT:替代普通 mutex 作为睡眠锁

6.2 owner 编码

1
/* lock->owner: task_struct* + RT_MUTEX_HAS_WAITERS (bit 0) */

与 mutex 类似,bit 0 表示 waiters;fastpath 在 owner 无 waiters 时 CAS acquire/release。

6.3 PI 链

1
2
3
4
rt_mutex_adjust_prio_chain()
→ 沿 pi_waiters 链传播 boost
→ dl_server 支持 SCHED_DEADLINE
→ max_lock_depth = 1024 限制链深度

6.4 rtmutex_api.c

通过 #include "rtmutex.c" 编译,导出 rt_mutex_lock/unlockrt_mutex_init 等符号;CONFIG_DEBUG_LOCK_ALLOC 下提供 nested 变体。


七、计数信号量 semaphore(semaphore.c)

1
2
3
4
5
struct semaphore {
raw_spinlock_t lock;
unsigned int count; /* 剩余可用计数 */
struct list_head wait_list;
};
API 行为
down() count–;为 0 则睡眠(已废弃,应用 down_interruptible)
down_interruptible() 可被信号中断,返回 -EINTR
down_trylock() 非阻塞,可在 IRQ 上下文
up() count++;唤醒 wait_list

与 mutex 区别:无 owner 概念、可多次 down(计数>1)、调试能力弱;新代码优先 mutex。


八、Per-CPU 读写信号量(percpu-rwsem.c)

适用于 读者极多、写者极少 场景(如 mmap_lock 读者路径优化)。

1
2
3
4
5
6
7
struct percpu_rw_semaphore {
int __percpu *read_count;
atomic_t block; /* 写者阻塞新读者 */
struct rcu_sync rss; /* 与 RCU 协调 */
struct rcuwait writer;
wait_queue_head_t waiters;
};

读者 fastpath

1
2
3
4
5
6
preempt_disable()
this_cpu_inc(*read_count)
smp_mb()
if (!atomic_read_acquire(&sem->block)) → 成功
else → dec, 唤醒 writer, 重试/睡眠
preempt_enable()

写者atomic_xchg(&block, 1) 阻止新读者 → 等所有 per-CPU reader count 归零 → 写临界区 → rcu_sync + 解除 block。


九、乐观自旋 OSQ(osq_lock.c)

Optimistic Spin Queue:专为 sleeping lock 的 optimistic spin 设计的 MCS 变体。

1
static DEFINE_PER_CPU_SHARED_ALIGNED(struct optimistic_spin_node, osq_node);
  • 每 CPU 一个 spin node(sleeping lock 不在 IRQ 上下文 spin)
  • osq_lock() / osq_unlock() 维护全局 tail 队列
  • mutex、rwsem 在 spin 前 osq_lock(),保证 同一时刻每 CPU 只有一个 optimistic spinner

十、WW-Mutex 死锁避免(ww_mutex.h)

Wait/Wound / Acquired 机制,解决 AB-BA 类死锁(多 mutex 不同获取顺序):

1
2
3
4
5
6
Context A: 已持锁 1,等待锁 2
Context B: 已持锁 2,等待锁 1 → 经典死锁

WW-Mutex:
- 后启动的 context 被 "wound"(强制 drop 已持锁,重试)
- 或先启动的 "wait" 对方释放

实现要点(ww_mutex.h + mutex.c/rtmutex.c):

  • ww_acquire_ctx 记录 acquire 顺序
  • __ww_mutex_add_waiter() 按 stamp 排序等待队列
  • ww_mutex_lock() 检测冲突并 wound 等待者

DRM GPU 驱动广泛使用;PREEMPT_RT 下由 ww_rt_mutex.c 适配。


十一、Lockdep 锁依赖验证(lockdep.c)

11.1 检测能力

Lockdep 在运行时构建 锁依赖有向图,检测:

问题类型 说明
锁逆序(AB-BA) 即使未死锁,历史上不同顺序也被报告
循环依赖 锁环
IRQ 安全违规 在 IRQ 上下文使用非 IRQ-safe 锁
软 IRQ / Hard IRQ 混用 上下文不匹配

核心思想:证明观测到的所有锁序在数学上正确,而非仅检测当前是否死锁。

11.2 核心概念

1
2
3
struct lockdep_map;     /* 锁实例 → lock_class 映射 */
struct lock_class; /* 锁类(同类锁共享依赖规则) */
struct lock_chain; /* 锁获取链 */

Usage stateslockdep_states.h):

1
2
LOCKDEP_STATE(HARDIRQ)
LOCKDEP_STATE(SOFTIRQ)

每位锁类记录:在 HARDIRQ/SOFTIRQ/PROCESS 等上下文的使用与使能状态。

11.3 关键 API

1
2
3
4
5
void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
int trylock, int read, unsigned long ip, ...);
void lock_release(struct lockdep_map *lock, unsigned long ip);
void lock_acquired(struct lockdep_map *lock, unsigned long ip);
void lock_contended(struct lockdep_map *lock, unsigned long ip);

各锁 *_lock() 宏展开时调用上述函数(CONFIG_PROVE_LOCKING 时完整验证)。

11.4 内部保护

Lockdep 自身用 raw arch_spinlock__lock)保护图结构,避免递归进入 lockdep:

1
2
static arch_spinlock_t __lock;
/* lockdep_lock() 要求 irqs_disabled() */

11.5 proc/debug 接口

  • lockdep_proc.c/proc/lockdep/proc/lockdep_chains
  • CONFIG_LOCK_STAT:竞争统计 → perf lock
  • CONFIG_LOCK_EVENT_COUNTS:debugfs lock_event_counts/

十二、调试与测试组件

组件 文件 功能
DEBUG_SPINLOCK spinlock_debug.c 未初始化/死锁检测
DEBUG_MUTEXES mutex-debug.c owner 校验、waiter 跟踪
DEBUG_IRQFLAGS irqflag-debug.c IRQ flag 状态一致性
LOCK_TORTURE_TEST locktorture.c 多锁类型压力测试
WW_MUTEX_SELFTEST test-ww_mutex.c WW-Mutex 单元测试
PREEMPT_RT spinlock_rt.c, rwbase_rt.c spinlock→rtmutex 语义转换

特殊编译标志

1
2
3
KCOV_INSTRUMENT := n          # 锁路径非确定性,不测 KCOV
KCSAN_SANITIZE_lockdep.o := n # 避免 lockdep ↔ KCSAN 递归
CFLAGS_REMOVE_lockdep.o = $(CC_FLAGS_FTRACE) # lockdep 不 ftrace

十三、RK3588/ARM64 平台说明

13.1 架构 Kconfig 选择

配置 ARM64 说明
ARCH_USE_QUEUED_SPINLOCKS ✅ select 使用 qspinlock
ARCH_USE_QUEUED_RWLOCKS ✅ select 使用 qrwlock
QUEUED_SPINLOCKS def_y SMP 下自动启用
QUEUED_RWLOCKS def_y 非 PREEMPT_RT
MUTEX_SPIN_ON_OWNER def_y SMP + ARCH_SUPPORTS_ATOMIC_RMW
RWSEM_SPIN_ON_OWNER def_y 同上
LOCKDEP_SUPPORT 支持 lockdep
ARCH_SUPPORTS_ATOMIC_RMW LL/SC 或 LSE 原子操作

13.2 自旋锁实现路径

1
2
3
4
5
6
arch/arm64/include/asm/spinlock.h
→ #include <asm/qspinlock.h>
arch/arm64/include/asm/Kbuild
→ generic-y += qspinlock.h ← 使用 asm-generic 实现
arch/arm64/include/asm/spinlock_types.h
→ #include <asm-generic/qspinlock_types.h>

ARM64 无独立 qspinlock 汇编 fastpath,使用 include/asm-generic/qspinlock.h 的原子操作 fastpath + kernel/locking/qspinlock.c 慢路径。

13.3 big.LITTLE 影响

RK3588 4×A76 + 4×A76 大小核:

  1. qspinlock 排队 减少跨核缓存乒乓,对大小核混合竞争有益
  2. 乐观自旋 依赖 owner_on_cpu():任务迁移后可能提前结束 spin
  3. Lockdep 开销较大,生产内核通常 CONFIG_LOCKDEP=n
  4. PREEMPT_RT 非 RK3588 默认;启用后 spinlock 语义变为可睡眠

13.4 典型 defconfig 预期

  • CONFIG_SMP=yCONFIG_QUEUED_SPINLOCKS=y
  • CONFIG_MUTEX_SPIN_ON_OWNER=y
  • CONFIG_LOCKDEP 通常 debug 配置启用
  • CONFIG_RT_MUTEXES=y(init/Kconfig 默认)

十四、典型加锁路径时序

14.1 mutex_lock 完整路径

调度器mutex slowpathoptimistic spinmutex fastpath任务调度器mutex slowpathoptimistic spinmutex fastpath任务alt[owner 运行中 && trylock 成功]alt[CAS 成功][CAS 失败]mutex_lock()持有锁mutex_optimistic_spin()持有锁__mutex_lock_slowpath()加入 wait_listschedule()被 handoff 唤醒__mutex_trylock(handoff)持有锁

14.2 qspinlock 竞争路径

1
2
3
4
CPU0: spin_lock() → fastpath CAS 成功
CPU1: spin_lock() → fastpath 失败 → queued_spin_lock_slowpath()
→ 分配 qnodes[0] → MCS 入队 → spin on prev->locked
CPU0: spin_unlock() → 释放 → CPU1 acquire

14.3 Lockdep 插桩路径

1
2
3
4
5
6
mutex_lock()
→ mutex_acquire(&lock->dep_map, ...) ← lock_acquire()
→ ... 实际加锁 ...
→ lock_acquired(&lock->dep_map, ip)
mutex_unlock()
→ lock_release(&lock->dep_map, ip)

十五、总结

组件 职责
qspinlock/qrwlock SMP 可扩展自旋锁,MCS 排队减少缓存竞争
mutex/rwsem 进程上下文睡眠锁,handoff + 乐观自旋优化
rtmutex PI + RT 内核基础 + futex 后端
semaphore 计数信号量(遗留,少用)
percpu-rwsem 多读少写极致读性能
osq_lock 乐观自旋排队,每 CPU 单节点
ww_mutex 多对象锁顺序死锁避免
lockdep 运行时锁依赖正确性证明

kernel/locking/ 是内核并发控制的基石;RK3588 ARM64 默认启用 排队自旋锁乐观自旋,在 8 核 big.LITTLE 上提供较好的锁扩展性。开发与调试阶段建议启用 Lockdep 捕获锁序问题,生产环境按需求权衡性能开销。


附录:源文件清单

文件 行数
kernel/locking/lockdep.c 6611
kernel/locking/rwsem.c 1716
kernel/locking/rtmutex.c 1834
kernel/locking/mutex.c 1151
kernel/locking/locktorture.c 1117
kernel/locking/lockdep_proc.c 730
kernel/locking/test-ww_mutex.c 683
kernel/locking/rtmutex_api.c 612
kernel/locking/qspinlock.c 596
kernel/locking/ww_mutex.h 569
kernel/locking/qspinlock_paravirt.h 562
kernel/locking/spinlock.c 415
kernel/locking/rwbase_rt.c 298
kernel/locking/spinlock_rt.c 280
kernel/locking/percpu-rwsem.c 282
kernel/locking/semaphore.c 279
kernel/locking/osq_lock.c 232
kernel/locking/lock_events.c 179
kernel/locking/spinlock_debug.c 235
kernel/locking/mutex-debug.c 118
kernel/locking/qrwlock.c 92
kernel/locking/ww_rt_mutex.c 101
头文件等 ~700
合计 ~19645

关联源码(目录外)

文件 说明
include/linux/spinlock.h 自旋锁 API 与 lockdep 宏
include/linux/mutex.h mutex 类型与 API
include/linux/rwsem.h rwsem API
include/linux/lockdep.h Lockdep 公共接口
arch/arm64/include/asm/spinlock.h ARM64 锁类型
include/asm-generic/qspinlock.h 通用 qspinlock fastpath
kernel/Kconfig.locks 锁相关 Kconfig
lib/Kconfig.debug Lockdep/调试 Kconfig
Documentation/locking/ 官方设计文档

文章互动

阅读 --

留言

0 条留言

正在加载留言…