gdb attach 进程卡住通常因进程处于d状态或权限不足;d状态是内核限制,无法attach;非root用户需调整yama.ptrace_scope或用sudo;卡住超10秒大概率内核态死锁,应改用/proc/pid/stat、stack或gcore离线分析。

gdb attach 进程后卡住不动?先确认进程状态和权限
死锁进程往往处于 T(stopped)或 D(uninterruptible sleep)状态,gdb attach 会失败或无响应。用 ps -o pid,stat,comm -p <pid></pid> 看真实状态;若为 D,gdb 无法 attach——这不是操作问题,是内核限制,别硬试。
权限方面:必须是同一用户,或 root;非 root 用户 attach 非子进程需开启 /proc/sys/kernel/yama/ptrace_scope(通常为 1,需设为 0 才允许跨用户调试,但线上慎改)。
- 线上环境优先用
sudo -u <app_user> gdb -p <pid></pid></app_user>,避免权限错位 - attach 前加
strace -p <pid></pid>看是否真在锁等待(如反复出现futex(... FUTEX_WAIT_PRIVATE ...)) - 若
gdb -p卡住超过 10 秒,大概率进程已卡在内核态,gdb 无能为力,换/proc/<pid>/stack</pid>或crash工具
thread apply all bt 能否直接定位死锁点?不能,但它是起点
thread apply all bt 只是把所有线程栈打出来,不分析依赖关系。死锁需至少两个线程互相持有对方所需锁,靠人工比对栈里的 pthread_mutex_lock、__lll_lock_wait、sem_wait 等调用链。
重点看哪些线程停在锁函数上、锁地址是否重复出现、哪个线程持有了 A 锁却在等 B 锁,而另一线程持有了 B 锁却在等 A 锁。
- 用
info threads先列出线程 ID 和状态,标出running和sleep的差异 - 对疑似线程逐个执行
thread <id>; bt -n 20</id>,-n 控制深度,避免被模板展开刷屏 - 若栈里有
std::mutex::lock或absl::Mutex::Lock,说明 C++ 层面锁未超时,基本坐实用户态死锁 - 注意
bt中的??符号——缺少 debuginfo 时无法解析符号,需提前部署.debug包或用strip --strip-debug逆向排除
死锁现场无法复现?用 gcore + 离线分析更安全
线上进程不能长时间挂起,gdb attach 后若要查堆内存、全局变量或锁结构体内容,gcore 比持续 attach 更稳妥——它生成 core 文件,原进程立刻恢复运行。
生成的 core 可用 gdb <binary><core></core></binary> 离线分析,不受线上稳定性影响,也避开了 ptrace 导致的性能抖动。
-
gcore -o /tmp/core.12345 12345,路径需 app 用户有写权限,且磁盘空间充足(core 大小 ≈ 进程 RSS) - 二进制必须与线上完全一致(含 build id),否则
gdb加载后符号错乱,info proc mappings会报No symbol table loaded - 离线后用
thread apply all bt+info registers+x/20xg <mutex_addr></mutex_addr>查锁字段(如__data.__count是否为 0,__data.__owner是否非零)
堆栈里看不到锁地址?用 info sharedlibrary 和 pstack 辅助交叉验证
gdb 里有时 bt 显示函数名但不显示参数或局部变量,尤其动态链接库中锁对象在堆上,print 会提示 can't compute CFA。这不是 bug,是帧指针被优化掉或 debuginfo 不全。
此时别只盯 gdb,用系统级工具补全视角:pstack <pid></pid> 是轻量替代(本质是 gdb -batch -ex "thread apply all bt" -p <pid></pid> 的封装),而 info sharedlibrary 能确认 libpthread.so 等关键库是否加载成功及路径。
- 若
info sharedlibrary里 libpthread 显示No debugging symbols found,则pthread_mutex_t结构体字段不可见,需用readelf -d <lib> | grep NEEDED</lib>核对版本是否匹配 -
pstack <pid></pid>输出更快,适合快速扫一眼线程分布,但无寄存器和内存查看能力 - 真正要查锁状态,还得回到
cat /proc/<pid>/stack</pid>(内核栈)和cat /proc/<pid>/status | grep -i thre</pid>看线程数突增是否异常
死锁分析最耗时间的从来不是命令怎么敲,而是判断哪两个锁形成了环路——这需要你同时看懂业务逻辑里的资源获取顺序,gdb 只负责把证据摊开给你看。











