Linux内核死锁检测与锁调试深度工程实战:从lockdep验证器到生产级死锁定位的完整方案
一、为什么内核死锁是最棘手的Bug
内核死锁是操作系统开发中最隐蔽、最难定位的一类问题。与用户态不同,内核死锁往往导致整个系统完全冻结,无法响应任何外部干预(键盘、SSH、sysrq都可能完全死住),且复现率极低——有时只在特定CPU调度时序下才会触发。
内核死锁的核心难点在于三个方面:
- 时序依赖性:两个CPU交错获取锁的顺序决定了死锁是否发生
- 调用链深度:锁可能在中断上下文、软中断、工作队列、用户态系统调用路径等多个上下文中被获取
- 递归多样性:同一个锁可能在不同的代码路径中被嵌套获取,且上下文组合爆炸
本文从lockdep验证器的架构原理出发,系统讲解内核死锁检测的完整方案。
二、内核锁类型与死锁模型
2.1 内核中所有可能导致死锁的同步原语
Linux内核中与死锁相关的同步原语包括:
| 原语 | 特性 | 可嵌套场景 |
|---|---|---|
spinlock_t |
自旋等待,禁止抢占 | 中断上下文、软中断、进程上下文 |
rwlock_t |
读写自旋锁 | 读-读可并行,读-写互斥 |
struct mutex |
可睡眠互斥锁 | 仅进程上下文 |
struct rw_semaphore |
读写信号量 | 仅进程上下文,可睡眠 |
struct semaphore |
计数信号量 | 仅进程上下文 |
raw_spinlock_t |
原始自旋锁(RT中也自旋) | 所有上下文 |
rcu_read_lock() |
RCU读侧临界区 | 不可睡眠 |
2.2 死锁的四种必要条件
操作系统理论中死锁的四个必要条件:
- 互斥条件(Mutual Exclusion):资源在同一时刻只能被一个执行流持有
- 持有并等待(Hold and Wait):持有至少一个资源的同时等待获取其他资源
- 不可剥夺(No Preemption):资源只能由持有者主动释放
- 循环等待(Circular Wait):存在{P1, P2, ..., Pn}使得P1等P2、P2等P3...Pn等P1
在内核中,破坏条件3(不可剥夺)不可行;破坏条件1和2又违背了同步保护的初衷。因此,内核通过锁顺序约定Lock Ordering来破坏条件4——定义全局获取顺序规则,确保不会出现循环等待。
2.3 内核特有的死锁类型
2.3.1 AA型死锁(同一锁重入)
// ❌ 错误:同一函数重复获取同一个spinlock
void bad_function(spinlock_t *lock)
{
spin_lock(lock); // 第一次获取
do_something();
spin_lock(lock); // 第二次获取 → 死锁(spinlock自旋等待释放,但自己持有)
do_more();
spin_unlock(lock);
spin_unlock(lock);
}
spinlock在同CPU第二次获取时会自旋等待锁释放,而锁的持有者(自己)永远不会释放——这是典型的自死锁。
2.3.2 AB-BA型死锁
// CPU 0 // CPU 1
spin_lock(&A); spin_lock(&B);
// ... // ...
spin_lock(&B); // ⬅️ 等待B spin_lock(&A); // ⬅️ 等待A
// 死锁!永远无法继续 // 死锁!永远无法继续
两个CPU以相反顺序获取两个锁,构成经典死锁。
2.3.3 中断上下文死锁
// 进程上下文
spin_lock(&my_lock);
// 此时硬件IRQ触发
// → 中断处理函数
spin_lock(&my_lock); // ❌ 死锁!中断在同一个CPU上等待进程上下文释放锁
spin_unlock(&my_lock);
正确的做法是使用 spin_lock_irq() 或 spin_lock_irqsave() 在获取锁时禁用本地中断。
2.3.4 软中断/RBH死锁(Bottom-Half)
spin_lock(&lock);
// 触发软中断,软中断也获取同一把锁
call_softirq_handler(); // 内部 spin_lock(&lock) → ❌
spin_unlock(&lock);
网络中常见:进程上下文TCP处理获取锁 → 触发NAPI软中断 → NAPI轮询也需获取同一把锁。
三、lockdep架构全景
3.1 lockdep是什么
lockdep是Linux内核内置的运行时锁验证器(Lock Dependency Validator),2006年由Ingo Molnár引入内核。它不是静态分析工具,而是在运行时动态跟踪所有锁的获取事件和顺序,构建一个有向图来检测潜在的循环等待。
lockdep的核心能力包括:
| 能力 | 说明 |
|---|---|
| 死锁检测 | 检测AB-BA、AA型、中断反转、软中断反转等死锁模式 |
| 锁类验证 | 将同类型但不同实例的锁视为同一类,正确性统计 |
| 未持有解锁 | 释放未持有的锁 |
| 未初始化使用 | 使用未初始化或已销毁的锁 |
| 中断上下文违规 | 在错误上下文中使用了不正确的锁API |
| 过度持有检测 | 锁占用时间过长(配合lockstat) |
| 内存顺序检查 | barrier使用不当(配合smp_mb验证) |
3.2 核心数据结构
lockdep的核心数据结构是一个有向图来表示锁的获取顺序:
lockdep_map:每个锁对应的节点(lock class)
├─ key:锁类标识(由初始化位置+调用栈确定)
├─ lock_chain:锁获取链表(该锁之后可能获取的其他锁列表)
├─ locks_after:该锁之后可以获取的其他锁
└─ locks_before:该锁之前必须获取的锁
held_lock:当前执行流已持有的锁
├─ prev_chain_key:上一次获取事件的标识
├─ acquire_when:获取时刻的调用栈
├─ nest_lock:嵌套锁标记
└─ hardirqs_off / softirqs_enabled:上下文快照
lockdep为每个锁类分配一个唯一的lock_class(通过初始化时的地址或静态变量名),图中节点是锁类,边表示"先获取锁A之后可能获取锁B"。如果添加新边时检测到有反向路径存在,就发现了潜在死锁。
3.3 锁类(Lock Class)vs 锁实例
lockdep不追踪每个独立的锁个体,而是按类型验证。所有由同一个初始化点创建的锁属于同一"锁类"(lock class):
// 这两个spinlock_t是不同的实例,但属于同一个lock class
static DEFINE_SPINLOCK(lock_a); // 锁类 = __FILE__:__LINE__
static DEFINE_SPINLOCK(lock_b); // 锁类不同,因为行号不同
这样大幅降低内存开销,同时仍保证正确性——同类锁的任何实例都会触发同样的验证规则。
3.4 六种上下文追踪
lockdep追踪执行流当前所处的是6种上下文之一:
enum lock_usage_bit {
LOCK_USED_IN_HARDIRQ = 0, // 硬件中断上下文使用
LOCK_USED_IN_SOFTIRQ = 1, // 软中断上下文使用
LOCK_USED_IN_HARDIRQ_READ = 2, // 硬中断读侧使用(rwlock/rwsem)
LOCK_USED_IN_SOFTIRQ_READ = 3, // 软中断读侧使用
LOCK_USED_IN_READ = 4, // 进程上下文读侧使用
LOCK_USED = 5, // 进程上下文使用
...
};
当锁被在不同上下文中获取时,lockdep维护一个6位的usage bitmap,内核中有六种上下文状态用于追踪锁的使用场景。如果同一个锁在两种不同上下文中以冲突方式使用就会触发警告。
四、lockdep工作原理解析
4.1 锁获取事件的处理流程
每次调用 spin_lock()、mutex_lock() 等API时,lockdep会执行以下步骤:
spin_lock(lock)
└→ do_spin_lock()
└→ lock_acquire(&lock->dep_map, ...)
└→ __lock_acquire()
│
├─ 1. 查找或创建lock class节点
├─ 2. 检查当前上下文是否允许此锁操作
├─ 3. 从当前"最后持有的锁"开始BFS遍历图
├─ 4. "locks_before"路径检查 → 确保无反向路径
├─ 5. 如有反向路径:打印死锁报告
└─ 6. 无环路:添加边("锁A之后可获取锁B")
4.2 前向检查与后向检查
lockdep使用两种图遍历方式:
- 前向检查(forward search):从当前锁出发,沿
locks_after边前进,看能否到达当前持有的锁 - 后向检查(backwards search):从当前持有的最后一把锁出发,沿
locks_before边反向看能否到达当前锁
如果两种检查都成功遍历到对方,说明存在环路 → 死锁。
4.3 锁粒度验证:irq在锁内外的状态一致性
lockdep通过检查上下文的一致性来防止中断反转死锁:
如果锁L已在进程上下文中被获取(此时irq可能开启)
然后在同一锁L的中断上下文中被获取
→ lockdep发现"LOCK_USED已被置位",但中断到来时持有者的 irq 状态不一致
→ 输出警告:"previous hardirq-unsafe lock usage"
这正是检测 spin_lock() + spin_lock_irq() 不匹配问题的关键机制。
五、lockdep启用与配置
5.1 内核配置选项
# 基础lockdep
CONFIG_PROVE_LOCKING=y # 启用lockdep验证器核心
CONFIG_LOCKDEP=y # lockdep基础设施
CONFIG_LOCK_STAT=y # 锁统计(等待时间、持有时间)
CONFIG_DEBUG_LOCK_ALLOC=y # 锁分配的调试追踪
# 深度验证选项
CONFIG_DEBUG_SPINLOCK=y # spinlock额外检查
CONFIG_DEBUG_MUTEXES=y # mutex深度验证
CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y # WW mutex死锁测试
CONFIG_DEBUG_RT_MUTEXES=y # RT-mutex深度验证
# lockdep运行时参数
CONFIG_LOCKDEP_CIRCULAR_QUEUE_BITS=12 # 环形缓冲区大小(记录事件)
5.2 启动参数调整
# 指定lockdep最大锁类数(默认300000)
lockdep_classes=500000
# 增加lockdep哈希表大小以提升性能
lockdep_hash_size=16384
# 禁用特定场景以减少噪音
lockdep_off # 运行时关闭
lockdep_on # 运行时开启
5.3 运行时/sys接口
# 查看lockdep统计
cat /proc/lockdep_stats
# 查看所有lock class列表
cat /proc/lockdep
# 重置lockdep状态
echo 1 > /proc/sys/kernel/lockdep_reset
# 获取锁持有时间统计(需LOCK_STAT)
cat /proc/lockdep_stats
六、lockdep死锁报告解读
6.1 典型死锁报告结构
当lockdep检测到潜在死锁时,输出如下完整报告:
[ INFO: possible circular locking dependency detected ]
5.15.0-rc1-debug #15 Not tainted
------------------------------------------------------
-> #0 (&type->i_mutex_dir_key#2){+.+.}
[<ffffffff81abc234>] lock_acquire+0x134/0x3e0
[<ffffffff81cb567a>] down_write+0x3a/0xd0
[<ffffffff81a8e9f0>] kernfs_iop_mkdir+0x40/0x100
[<ffffffff81abc234>] vfs_mkdir+0x64/0x1a0
-> #1 (&mm->mmap_lock){++++}
[<ffffffff81abc234>] lock_acquire+0x134/0x3e0
[<ffffffff81cb567a>] down_write+0x3a/0xd0
=> *** DEADLOCK ***
[INFO: task kworker/0:2:1234 blocked for more than 120 seconds.]
报告分为三个区域:
- 锁需求声明(Lock-0 / Lock-1):每个锁被持有时的完整调用栈
- 当前尝试的锁(-> #0):当前代码执行路径和尝试获取的锁
- 已持有锁链(-> #1 → -> #2 → ...):当前任务已持有的锁获取序列
6.2 实际死锁报告:AB-BA型
======================================================
[ INFO: possible circular locking dependency detected ]
------------------------------------------------------
task:PID=2345 is trying to acquire lock:
(&dev->lock){+.+.} at: device_write+0x45/0x200
but task already holds lock:
(&priv->tx_lock){+.+.} at: open+0x80/0x1a0
backtrace:
[<c0a1b234>] netif_tx_lock_bh+0x20/0x80
and the first lock already held at that point was:
(<hardirq-safe lock>) at: irq_handler+0x30/0x100
*** The lock dependency graph has detected a DEADLOCK ***
existing dependency: (dev->lock → priv->tx_lock)
new dependency: (priv->tx_lock → dev->lock)
这个报告清晰地表明:
- 某任务持有
dev->lock后尝试获取priv->tx_lock(新边) - 但已有记录显示
priv->tx_lock之后可以获取dev->lock(旧边) - 两条边构成环路 → 死锁
6.3 中断上下文反转报告
[ INFO: inconsistent lock state ]
[<ffffffff81abc234>] lock_acquire+0x134/0x3e0
dev->lock {INITIAL-SOFTIRQ-SAFE} -> {IN-SOFTIRQ-W}
这种报告说明:锁标记为"最初用于硬中断上下文",但当前获取位置可能在软中断写侧——存在被反转风险。
七、lockdep扩展机制
7.1 lockdep_map:自定义锁类
内核中许多自定义锁需要通过lockdep验证,使用lockdep_map机制:
struct my_driver {
spinlock_t rx_lock;
struct lockdep_map rx_lock_map;
// ...
};
void my_driver_init(struct my_driver *drv)
{
lockdep_init_map(&drv->rx_lock_map, "my_driver:rx_lock", &rx_class_key, 0);
spin_lock_init(&drv->rx_lock);
}
void my_driver_rx(struct my_driver *drv)
{
// 使用lockdep_register获取验证
spin_lock(&drv->rx_lock);
// ... 使用drv->rx_lock_map作为验证key
spin_unlock(&drv->rx_lock);
}
7.2 lockdep_set_class:指定独立锁类
当多个锁实例需要lockdep区分时使用:
spinlock_t my_lock;
void init(void)
{
static struct lock_class_key __key;
spin_lock_init(&my_lock);
lockdep_set_class(&my_lock, &__key);
// 现在my_lock有自己独立的lock class
}
7.3 lockdep_assert_held:生产断言
void fast_path(struct my_device *dev)
{
// 不验证(运行时无开销),只在lockdep开启时检查
lockdep_assert_held(&dev->lock);
// 快速处理
process(dev);
}
lockdep_assert_held()在lockdep关闭时编译为空,无运行时开销;仅在lockdep开启的debug内核中执行断言检查。
7.4 lock_acquire_exclusive / lock_acquire_shared
对于读写锁(rwlock/rwsem),lockdep区分独占获取和共享获取以避免假阳性:
void reader(struct rw_semaphore *sem)
{
down_read(sem); // lock_acquire_shared
// ... 读操作
up_read(sem);
}
void writer(struct rw_semaphore *sem)
{
down_write(sem); // lock_acquire_exclusive
// ... 写操作
up_write(sem);
}
lockdep正确建模:多个共享持有者并发执行不会产生死锁循环。
7.5 lockdep_off() / lockdep_on():临时禁用
在某些已知安全的热路径(如lockdep已知误报),可临时关闭验证:
lockdep_off();
// 执行已知安全的锁操作
spin_lock_known_ok(&lock);
spin_unlock_known_ok(&lock);
lockdep_on(); // 恢复验证
这会让lockdep跳过此期间的边构建,避免误报污染。但应极度谨慎使用。
八、生产环境Lockdep实战
8.1 内核模块单元测试中的lockdep使用
在写内核模块时,应在代码中集成lockdep测试用例:
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/kthread.h>
#include <linux/delay.h>
static spinlock_t lock_A;
static spinlock_t lock_B;
/* 模拟AB-BA死锁场景 */
static int deadlock_test(void *unused)
{
spin_lock(&lock_A);
msleep(10); // 增加时序窗口
spin_lock(&lock_B); // ← lockdep在这里检测
spin_unlock(&lock_B);
spin_unlock(&lock_A);
return 0;
}
static int reverse_test(void *unused)
{
spin_lock(&lock_B);
msleep(10);
spin_lock(&lock_A); // ← 反向获取,与上面形成环路
spin_unlock(&lock_A);
spin_unlock(&lock_B);
return 0;
}
static int __init test_init(void)
{
spin_lock_init(&lock_A);
spin_lock_init(&lock_B);
kthread_run(deadlock_test, NULL, "dl_test_1");
kthread_run(reverse_test, NULL, "dl_test_2");
return 0;
}
上面的模块在不修改代码的情况下加载就会触发lockdep报告——它自动检测了潜在的AB-BA死锁模式。
8.2 lockdep在ftrace中的集成
# 开启ftrace中的lockdep追踪
echo 1 > /sys/kernel/debug/tracing/events/lock/enable
echo 1 > /sys/kernel/debug/tracing/events/lock/acquire/enable
echo 1 > /sys/kernel/debug/tracing/events/lock/release/enable
# 查看结果
cat /sys/kernel/debug/tracing/trace_pipe
# 输出示例:
# bash-1234 [001] ...1 1234.567890: lock_acquire: (file->f_pos_lock){+.+.}, 0x1234
# bash-1234 [001] ...1 1234.567891: lock_release: (file->f_pos_lock){+.+.}
8.3 使用bpftrace动态监控锁行为
# 监控mutex_lock延迟超过1ms的事件
bpftrace -e '
kprobe:mutex_lock* {
@start[tid] = nsecs;
}
kretprobe:mutex_lock* /@start[tid]/ {
$dur = nsecs - @start[tid];
if ($dur > 1000000) {
printf("Slow mutex: %d ms %s\n", $dur / 1000000, kstack());
}
delete(@start[tid]);
}'
# 监控spinlock竞争
bpftrace -e '
kprobe:spin_lock {
@lock_addr = arg0;
@start[tid] = nsecs;
}
kretprobe:spin_lock /@start[tid]/ {
$dur = nsecs - @start[tid];
if ($dur > 50000) {
printf("Spin wait: %u ns lock=%lx cpu=%d comm=%s\n",
$dur, @lock_addr, cpu, comm);
}
delete(@start[tid]);
}'
8.4 lockstat:锁竞争热点分析
利用 /proc/lockstat 识别锁竞争瓶颈:
class_name ...... con-bounces contentions waittime-total acq-b-total
dentry_lock ..... 2345678 1234567 9876543 ms 12345 ms
inode->i_mutex . 1234567 654321 4567890 ms 65432 mm
(&dev->lock) .. 543210 321098 3456789 ms 32109 ms
关键指标:
- con-bounces:锁从一CPU转移到另一CPU的次数(高值=CPU在自旋中切换)
- contentions:总争用次数
- waittime-total:所有等待的总时间
- acq-b-total:获取锁的总时间(不含等待)
九、lockdep误报处理
9.1 误报场景分析
lockdep虽然强大,但在以下场景可能误报(假阳性):
| 场景 | 原因 | 最小化解法 |
|---|---|---|
| 条件锁嵌套 | 锁B仅在特定条件下才被A之后获取,但lockdep无条件下假设 | lockdep_set_class_and_name区分假锁 |
| 递归读锁 | rwlock递归读被视为反向获取 | 使用rcu_assign_pointer等替代 |
| 跨模块锁 | 静态证明困难,运行时依赖动态调用 | 显式依赖声明 |
| 硬件ordering | 锁的正确性依赖硬件内存序而非逻辑顺序 | smp_mb__before_atomic等显式屏障 |
9.2 最小化解法
锁类拆分:区分同一类型锁的不同语义用途
// 不行:两个用途共用一个lock class
static spinlock_t lock; // 在中断和进程上下文中使用
// 好:显式声明用途
static spinlock_t irq_lock;
static spinlock_t proc_lock;
// lockdep自动为它们分配不同的class
反向依赖声明:lockdep_set_subclass
// 同一把锁但是不同递归深度
spin_unlock(&lock);
// 调用链A
spin_unlock_something(&lock, 1); // subclass=1
// 现在lockdep认为1和0是不同的锁类
十、lockdep与其他调试工具集成
10.1 KASAN + lockdep联合排查
# 启用两种检测
CONFIG_KASAN=y
CONFIG_KASAN_INLINE=y
CONFIG_PROVE_LOCKING=y
# 同一份崩溃报告中可同时看到:
# 1. 内存破坏的位置
# 2. 锁状态快照
10.2 lockdep + hung_task检测
[ 120.456789] INFO: task kworker/0:2:1234 blocked for more than 120 seconds.
[ 120.456790] Not tainted 5.15.0-rc1
[ 120.456791] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
...
[ 120.456798] task:kworker/0:2 state:D stack: 0 pid: 1234
当deadlock真正发生时,hung_task_detector会报告任务被阻塞;如果同时有lockdep开启,能获得完整的调用链。
10.3 lockdep + hardlockup_panic
# 当CPU被spinlock死锁占满时,NMI watchdog可触发panic
echo 10 > /proc/sys/kernel/watchdog_thresh
echo 1 > /proc/sys/kernel/hardlockup_panic
# 获取NMI dump来分析?
echo c > /proc/sysrq-trigger # 主动触发crash dump
十一、内核锁调试最佳实践
11.1 编码规范
// 1. 始终使用正确的API
spin_lock(&lock); // 仅调用者知道上下文安全时使用
spin_lock_irq(&lock); // 需要禁用中断
spin_lock_irqsave(&lock, flags); // 需要保存irq状态
spin_lock_bh(&lock); // 禁用软中断(bottom-half)
// 2. 锁顺序注解:在头文件中定义全局顺序
// lock_order.h
// LOCK_ORDER: A → B → C
// 即:获取A之后可以获取B,获取B之后可以获取C
// 但获取C之后不可再获取A或B(除非特殊声明)
// 3. 明确的锁命名
struct driver {
spinlock_t rx_ring_lock; // 环形缓冲锁
spinlock_t tx_ring_lock;
struct mutex config_lock; // 配置变更锁
};
// 4. 避免在持有spinlock时调用可能睡眠的API
spin_lock(&lock);
// ❌ copy_from_user(); → 可能引发页错误→睡眠→死锁或oops
// ✅ copy_from_user_nofault()或提前复制到临时缓冲区
11.2 调试检查清单
| 步骤 | 检查项 | 工具 |
|---|---|---|
| 1 | 锁初始化是否正确(spin_lock_init / mutex_init) |
lockdep "unused lock" 警告 |
| 2 | 是否所有异常路径都正确释放了锁 | kasan + 覆盖率分析 |
| 3 | 中断上下文使用的锁是否正确禁用中断 | lockdep irq-safety 警告 |
| 4 | 是否有跨模块的锁依赖 | lockdep 报告中的 "already held" |
| 5 | 读写锁使用是否匹配(读获取→读释放) | lockdep "wrong release order" |
| 6 | 信号量嵌套深度 | lock_recursive 检查 |
| 7 | CPU hotplug与锁的交互 | lockdep + CPU hotplug测试 |
| 8 | 性能回归:lockstat对比 | perf lock stat |
11.3 性能优化建议
当lockdep发现正确性问题后,可采取以下优化:
- 减小锁粒度:用per-CPU数据替代全局锁
- 读写分离:读多写少场景用rwlock替代spinlock
- RCU替代读侧锁:读极为频繁、写极少的场景
- 锁拆分:将一个大锁拆分为多个子锁
- 无锁设计:CAS、原子操作、per-CPU变量
- 减少锁持有时间:将非关键操作移出临界区
十二、前沿:lockdep在新内核中的演进
12.1 内核6.x的lockdep增强
- WW Mutex(Wound-Wait Mutex):解决多锁顺序死锁的新算法。当检测到潜在死锁时,让年老的事务"伤害"年轻事务(wound-wait),使年轻事务回滚释放锁。用于drm_mode_config_lock的框架中。
- 动态锁类池扩展:减少内存使用,支持更多锁类
- lockdep环形缓冲区持久化:支持oops后从缓冲区恢复最后N条锁事件
- CFSDL(Cross-Function Static Deadlock Lockdep):基于编译期分析的辅助信息
12.2 Rust-for-Linux的锁验证
Rust利用类型系统在编译期避免部分死锁场景:
// Rust的类型系统自动追踪锁顺序
struct OrderedLocks<A, B> {
a: A,
b: B,
}
impl<A, B> OrderedLocks<A, B> {
fn new(a: A, b: B) -> Self {
OrderedLocks { a, b }
}
}
// 一旦创建顺序,编译器强制后续代码不可逆
Rust的borrow checker天然防止了AA型死锁(不可重入不安全的锁)。
十三、总结
lockdep是Linux内核中最成功的安全工具之一,它通过运行时图构建+环路检测的方式,在不影响正常运行的前提下捕获几乎95%的潜在死锁模式。
使用lockdep的核心原则:
- 所有新代码必须在lockdep保护的内核上编译和测试 — 不要等到生产环境暴露
- 理解六种上下文模型 — 明确中断上下文使用正确的锁API
- 维护全局锁顺序 — 在头文件中显式定义,代码审查时检查
- 结合ftrace/bpftrace — 运行时追踪锁行为,不仅依赖lockdep
- 不要轻易使用lockdep_off() — 先找到真正的原因再选择绕过
- 单元测试主动构造死锁场景 — lockdep的价值在于提前捕获
内核死锁不再是"随机恐怖"事件——借助lockdep和现代追踪工具,我们可以在开发阶段系统性地消除这类问题。
参考资料
- Linux内核源码
kernel/locking/lockdep.c - Linux内核源码
kernel/locking/lockdep_internals.h - Documentation/locking/spinlocks.rst
- Documentation/locking/lockdep-design.rst
- Ingo Molnár, "Finding kernel bugs with lockdep," LWN.net
tools/perf/builtin-lock.c— perf lock子命令实现- kernel Documentation:
locking/ww-mutex-design.rst

发表评论 取消回复