linux死锁排查需先定位d状态线程,再通过wchan和stack回溯锁等待链,最终确认循环等待闭环;启用lockdep可直接捕获循环依赖告警。

Linux死锁排查的核心在于识别“谁在等谁的锁”,而不是泛泛查看进程状态。关键要定位到处于不可中断睡眠(D 状态)的线程,再顺着它的等待链逐层回溯持有者,最终找到循环等待闭环。
快速定位可疑线程
死锁发生时,至少有两个线程处于 UNINTERRUPTIBLE(D 状态),无法响应信号或调度。用以下命令筛选:
- ps -eo pid,comm,wchan:20,state,ppid | awk '$4=="D" {print}' —— 查看所有 D 状态进程及其等待的内核函数(wchan)
- ps -eLo pid,tid,comm,state,wtchan | grep ' D ' —— 更细粒度显示线程级等待点
- 重点关注 watchdog、kthreadd、system_server(Android 场景)等关键线程是否卡在锁相关函数,如 down_read、mutex_lock、__lock_acquire
分析锁等待路径
对每个 D 状态线程,用 crash 工具(配合 vmcore/ramdump)或 /proc/PID/stack 查看其调用栈,重点找锁操作上下文:
- 若栈中出现 proc_pid_cmdline_read → down_read(&mm->mmap_sem),说明该线程正等待某进程的内存映射读锁
- 用 crash> bt -v
查看寄存器值(如 x21、x28),结合 whatis struct mm_struct 推导出被等待的 mmap_sem 实例地址 - 再用 crash> sym
或 crash> struct rw_semaphore 查看该锁当前的 owner 字段,得到持锁线程 PID
确认循环等待闭环
拿到第一个等待者 A 和它等待的持有者 B 后,继续查 B 的栈和锁状态:
- 检查 B 是否也在等待另一个锁,而该锁恰好由 A 或其他线程持有
- 常见闭环模式:A 持 L1 等 L2,B 持 L2 等 L1;或 A 持 mmap_sem 等 task_lock,B 持 task_lock 等 mmap_sem
- 使用 crash> foreach ps -u | grep "D" 批量扫描所有 D 状态线程,比对它们的等待目标与持锁对象,画出依赖图
借助内核锁检测机制
若系统启用了 lockdep(CONFIG_LOCKDEP=y),死锁发生时内核通常会直接打印警告信息:
- 检查 dmesg 输出,搜索 "possible recursive locking detected"、"circular locking dependency" 等关键词
- 输出中会明确列出涉及的两个锁类(lock-class)、各自获取顺序、以及触发死锁的调用路径
- 即使未触发 panic,也可通过 cat /proc/lockdep 或 cat /proc/lockdep_stats 观察锁依赖关系累积情况










