内核有上千把锁——谁能保证不死锁?lockdep 是运行时锁依赖验证器 → 第一次看到 AB-BA 顺序就报警。
一、先看图
flowchart TD
LOCK_A[lock A] --> LOCK_B[lock B<br/>记录 A→B 依赖]
LOCK_B2[lock B] --> LOCK_A2[lock A<br/>检测到 B→A]
LOCK_A2 --> WARN[lockdep 警告<br/>ABBA 死锁风险]
classDef normal fill:#3fb95022,stroke:#3fb950,color:#adbac7;
classDef bad fill:#f8514922,stroke:#f85149,color:#adbac7;
class LOCK_A,LOCK_B normal
class LOCK_B2,LOCK_A2,WARN bad
二、lockdep 原理
CONFIG_PROVE_LOCKING=y
每次获取锁 → lockdep 记录:
- 锁的类(lock class)→ 按代码位置分类
- 依赖关系(A → B 意味着持有 A 时获取 B)
- 检查有向图中是否存在环 → 有环 = 死锁风险
2.1 锁类
static DEFINE_MUTEX(my_lock); // 代码位置 = 锁类标识同一代码位置定义的所有锁实例共享一个锁类。
2.2 检测范围
- mutex、rwsem、spinlock、rwlock
- 嵌套锁顺序
- 中断上下文与锁的交互(hardirq/softirq safe/unsafe)
三、lockdep 输出
WARNING: possible circular locking dependency detected
task/1234 is trying to acquire lock:
(&fs_info->reloc_mutex){+.+.}-{3:3}
but task already holds lock:
(&fs_info->delete_unused_bgs_mutex){+.+.}-{3:3}
which lock already depends on the new lock:
-> (&fs_info->reloc_mutex) -> (&fs_info->delete_unused_bgs_mutex)
关键信息:
- 哪两把锁
- 获取顺序
- 调用栈
四、lockdep_assert_held
lockdep_assert_held(&my_lock); // 断言持有锁
lockdep_assert_held_read(&my_rwlock); // 断言持有读锁
lockdep_assert_not_held(&my_lock); // 断言未持有编译为断言 → 运行时检查 → 文档化锁约定。
五、KCSAN
CONFIG_KCSAN=y
Kernel Concurrency Sanitizer → 检测数据竞争:
BUG: KCSAN: data-race in func_a / func_b
write to 0xffff... of 4 bytes by task 1234
read to 0xffff... of 4 bytes by task 5678
KCSAN 通过编译时插桩 → 运行时检测并发访问而无同步。
六、KFENCE
CONFIG_KFENCE=y
Kernel Electric-Fence → 轻量级内存越界/UAF 检测:
- 随机选取 slab 分配 → 用 guard page 包围
- 越界访问 → page fault → 报告
- 生产可用(默认每 100ms 采样一次)
七、false positive
lockdep 可能误报:
- 锁类粒度太粗(同一代码位置不同实例语义不同)
- 条件锁获取(实际不会同时持有)
解决:
// 子类标记
mutex_lock_nested(&lock, SINGLE_DEPTH_NESTING);
// 或定义新锁类
static struct lock_class_key my_key;
lockdep_set_class(&lock, &my_key);八、运行时开销
| 工具 | 内存 | CPU | 生产可用 |
|---|---|---|---|
| lockdep | 高(~100MB) | 中 | 开发/测试 |
| KCSAN | 中 | 中 | 开发/测试 |
| KFENCE | 低 | 低 | 生产 |
九、观察
# lockdep 统计
cat /proc/lockdep_stats
# 锁依赖
cat /proc/lockdep
cat /proc/lockdep_chains
# KFENCE 报告
dmesg | grep KFENCE
# 内核配置检查
zcat /proc/config.gz | grep -E 'PROVE_LOCKING|KCSAN|KFENCE'十、小结
- lockdep = 运行时锁依赖验证 → 检测死锁风险
- 基于锁类和有向图环检测
- KCSAN 检测数据竞争
- KFENCE 检测内存越界(生产可用)
- lockdep_assert_held 文档化锁约定
参考文献
kernel/locking/lockdep.cDocumentation/locking/lockdep-design.rstmm/kfence/Documentation/dev-tools/kcsan.rst
工具
/proc/lockdep/proc/lockdep_statsdmesg
上一篇:kdump 与 crash 下一篇:内核日志
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【操作系统百科】mutex 与 rwsem
内核 mutex 先自旋再睡眠——adaptive spin 策略。本文讲 mutex 实现、MCS wait list、owner handoff、rwsem 公平与不公平、PREEMPT_RT 替换、lockdep 死锁检测。
【操作系统百科】内核内存调试
内核内存 bug 是最难追的:UAF、OOB、double free、leak 都可能沉默数月。本文讲 KASAN 三种模式、KFENCE 生产采样、kmemleak、SLUB_DEBUG、UBSAN/KCSAN 联动。
【操作系统百科】内存回收
Linux 内存回收是 VM 最复杂的子系统之一。本文讲 active/inactive LRU、kswapd 与 direct reclaim、watermark 三线、swappiness 的真实含义、MGLRU 改造、memcg 回收与 PSI。
【操作系统百科】交换
swap 还值得开吗?本文讲 swap area 基础、swap cache、zram 压缩内存、zswap 前端压缩池、swappiness 的真实含义、容器里的 swap 策略,以及为什么现代 Android 全靠 zram 不靠磁盘。