死锁导致进程卡顿但不崩溃,需通过ps查线程状态、gdb分析堆栈、dmesg检查内核锁依赖环、pstack/lsof辅助验证,定位A→B→C→A循环等待链并统一加锁顺序修复。

进程卡顿但不崩溃,CPU和内存占用看起来正常,实际可能是死锁导致的线程僵持。这种问题不会触发OOM Killer,也不会在应用日志里报错,排查关键在于确认是否真有循环等待的锁关系,而不是只看进程状态。
看线程状态是否全部阻塞在锁上
用 ps -T -o pid,tid,stat,comm -p $PID 查目标进程的所有线程,重点关注 STAT 列:
-
D:不可中断睡眠(常因I/O或内核锁) -
S:可中断睡眠(可能在等信号或条件变量) - 若多个线程长期处于
D或S,且ps -eo pid,comm,wchan显示它们都停在类似mutex_wait、futex_wait、__lock_text_start这类函数上,就高度可疑。
抓取线程堆栈定位锁持有关系
对疑似进程执行:gdb -p $PID -ex "thread apply all bt" -ex "quit" 2>/dev/null | grep -A5 -B5 "mutex\|futex\|pthread\|lock"
重点找两件事:
- 哪些线程正在调用
pthread_mutex_lock或futex并卡住 - 哪些线程正持有某把锁(比如堆栈里有
pthread_mutex_unlock未返回,或显示owner字段) - 对照找出 A 等 B、B 等 C、C 又等 A 的闭环链
检查内核是否启用了死锁检测机制
Linux 内核自带 CONFIG_LOCKDEP(锁依赖跟踪),但默认关闭。若系统已开启,死锁发生时会直接在 dmesg 打印完整环路报告:
- 运行
dmesg -T | grep -i "possible recursive locking" -A10 -B5 - 或查
dmesg -T | grep -A20 "circular lock dependency" - 有输出即说明内核已捕获到锁依赖环,信息里会列出每个锁的地址、所属模块、调用路径
用 pstack + lsof 辅助交叉验证
当 gdb 不便使用时,可用轻量方式快速扫描:
-
pstack $PID查所有线程当前调用栈,人工比对谁在等哪把锁 -
lsof -p $PID | grep -E "(lock|mutex|sem)"看进程打开了哪些锁相关文件描述符(如/dev/shm下的 POSIX 信号量) - 结合
/proc/$PID/stack(需 root)查看每个线程内核态堆栈,确认是否陷在__mutex_lock_slowpath类函数中
死锁排查不靠猜,核心是把锁的申请者、持有者、等待者串成图。一旦发现 A→B→C→A 这样的环,就能准确定位代码中资源获取顺序不一致或嵌套加锁的问题。修复方向很明确:统一加锁顺序、用超时锁、拆分临界区,或引入死锁检测回调。


















