本页目录
同步调试
覆盖: lockdep (锁依赖图) → lockstat (竞争统计) → KCSAN (数据竞争检测) → hung task detector → NMI watchdog → CONFIG_DEBUG_ATOMIC_SLEEP 内核版本: 2.6 ~ 6.x
概述
并发 bug 是内核中最难调试的问题之一:死锁、数据竞争、原子上下文睡眠。这些 bug 往往非确定性——只在特定时序下触发。幸运的是,内核内置了一套强大的同步调试基础设施,能在 bug 发生时(甚至发生前)捕获它。
Lockdep: 锁依赖验证器
// kernel/locking/lockdep.c
// 核心思想: 构建运行时锁依赖图,检查环路 (死锁)
// 每次 lock: 在图里加一条 A→B 的边
// 如果 B→A 的边已存在 → 发出死锁警告!
//
// 不需要真的发生死锁! Lockdep 能"预测"潜在死锁
// Lockdep 跟踪的"锁类":
// - 同一类型的锁 (如所有 inode→i_lock) → 同一个 class
// - 不同函数中拿的锁 → 不同 class (即使类型相同)
// /proc/lockdep: 完整的锁依赖图
// /proc/lockdep_stats: 依赖图统计
Lockdep 报告解读
典型的 lockdep 输出:
======================================================
WARNING: possible circular locking dependency detected
======================================================
CPU 0: lock(A); ... lock(B);
CPU 1: lock(B); ... lock(A);
→ 潜在死锁! 即使它还没在实际执行中发生
// 还包括:
// - 每个锁是在哪个函数、哪个文件中被拿的
// - 从 boot 以来每个锁的持有历史
Lockstat: 竞争统计
// CONFIG_LOCK_STAT
// 记录每个锁的:
// - wait time: 等锁花了多久
// - hold time: 持锁持了多久 (需要 CONFIG_LOCK_STAT_HOLDLOCK)
// /proc/lock_stat:
// 格式: class_name contended total_wait_ns max_wait_ns min_wait_ns
KCSAN: Kernel Concurrency Sanitizer
// kernel/kcsan/
// 类似用户态的 ThreadSanitizer (TSan)
// 运行时检测数据竞争: 两个 CPU 同时访问同一内存地址,至少一个是写
// 工作原理:
// 1. 对每次内存访问: 记录地址 + 大小 + 时间戳 (watchpoint)
// 2. 如果另一个 CPU 在未同步的情况下访问同一地址 → 报告
// 报告解读:
// BUG: KCSAN: data-race in function_name (read/write of 4 bytes at addr)
// CPU 0: write to x in func_a()
// CPU 1: read from x in func_b()
// → x 的访问没有共同锁保护 → 数据竞争!
// 编译选项: CONFIG_KCSAN=y
// 带 KCSAN 的内核显著变慢 (~10x),仅用于调试
Hung Task Detector
检测: 进程在 TASK_UNINTERRUPTIBLE (D) 状态超过 120 秒
→ 通常是死锁、NFS 不可达、或驱动 bug
配置:
/proc/sys/kernel/hung_task_timeout_secs (默认 120)
/proc/sys/kernel/hung_task_panic (0=warn, 1=panic)
输出: 打印卡住进程的内核栈 (关键!)
→ 看到在 mutex_lock() 等什么 → 找哪个进程持有该锁
NMI Watchdog
基于 NMI (不可屏蔽中断) 的死锁检测:
每个 CPU 周期性地收 NMI → 重置 per-CPU 计数器
如果某个 CPU 长时间没收到 NMI:
→ 可能因为关中断太久 (spin_lock_irqsave 死循环)
→ 打印卡住 CPU 的栈回溯
工作原理:
perf event 驱动 → 溢出 → NMI → watchdog overflow handler
→ 检查 hrtimer 自上次中断以来的时间 → 如果超时 → 报警
其他调试工具
// CONFIG_DEBUG_ATOMIC_SLEEP:
// 在原子上下文中调用 schedule() (或任何可能睡眠的函数)
// → 立即 BUG()
// CONFIG_PROVE_LOCKING (lockdep 的增强):
// 每个锁操作都经过 lockdep 检查 → 更大的运行时开销
// 但能捕获更多不正确的锁使用
// CONFIG_DEBUG_SPINLOCK:
// 检查 spinlock 未初始化使用、double unlock 等
// CONFIG_PROVE_RCU:
// 检查 RCU reader 在临界区外调用 rcu_dereference、睡眠等
调试命令速查
# lockdep: 依赖图 + 统计
# lockstat: 竞争
| |
# hung task: 当前卡住的进程
# 数据竞争 (KCSAN, 需要编译时启用)
|
# 关中断检查
|
参考
- 内核文档:
Documentation/locking/lockdep-design.rst,Documentation/dev-tools/kcsan.rst - 源码:
kernel/locking/lockdep.c,kernel/kcsan/,kernel/watchdog.c,kernel/hung_task.c - LWN: "Lockdep", "The Kernel Concurrency Sanitizer"
关键词: lockdep, lockstat, KCSAN, data race, hung task, NMI watchdog, DEBUG_ATOMIC_SLEEP, PROVE_LOCKING