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.h、mutex.h、rwsem.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.locks 与 lib/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 设计原则
Fastpath / Slowpath 分离 :热路径 inline 于头文件或架构代码,冷路径在本目录
乐观自旋(Optimistic Spinning) :mutex/rwsem 在 owner 运行且未阻塞时短暂自旋,避免调度开销
Handoff 机制 :直接传递锁所有权,减少唤醒延迟
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; };
四、互斥锁 mutex(mutex.c) 4.1 数据结构 1 2 3 4 5 6 7 8 9 struct mutex { atomic_long_t owner; 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 字段
写者 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 编码
与 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/unlock、rt_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 ; 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 ; struct lock_class ; struct lock_chain ;
Usage states (lockdep_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;
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 KCSAN_SANITIZE_lockdep.o := n CFLAGS_REMOVE_lockdep.o = $(CC_FLAGS_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 大小核:
qspinlock 排队 减少跨核缓存乒乓,对大小核混合竞争有益
乐观自旋 依赖 owner_on_cpu():任务迁移后可能提前结束 spin
Lockdep 开销较大,生产内核通常 CONFIG_LOCKDEP=n
PREEMPT_RT 非 RK3588 默认;启用后 spinlock 语义变为可睡眠
13.4 典型 defconfig 预期
CONFIG_SMP=y、CONFIG_QUEUED_SPINLOCKS=y
CONFIG_MUTEX_SPIN_ON_OWNER=y
CONFIG_LOCKDEP 通常 debug 配置启用
CONFIG_RT_MUTEXES=y(init/Kconfig 默认)
十四、典型加锁路径时序 14.1 mutex_lock 完整路径 调度器 mutex slowpath optimistic spin mutex fastpath 任务 调度器 mutex slowpath optimistic spin mutex fastpath 任务 alt [owner 运行中 && trylock 成功] alt [CAS 成功] [CAS 失败] mutex_lock() 持有锁 mutex_optimistic_spin() 持有锁 __mutex_lock_slowpath() 加入 wait_list schedule() 被 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/
官方设计文档