C/C++进程卡死

定位思路

C/C++进程未按预期执行,进程仍在后台运行,这种情况称为进程卡死,它往往是由于死锁或者进程进入死循环导致的。定位思路如C/C++进程卡死所示。

图1 C/C++进程卡死问题定位思路
  1. 使用top命令查看相关进程仍在运行,确认是进程卡死。
  2. 重新编译可调试版本,运行新版本,复现问题,GDB调试新版本。
  3. 分析堆栈信息及业务逻辑,找出卡死原因。
  4. 修改代码,重新编译进行验证。

    • 若问题解决,则确认修改,合入原代码。
    • 若问题未解决,增加定位信息,重新编译运行。

案例

问题现象:

某软件在服务器上运行出现卡死问题。

定位过程:

  1. 使用top命令查看进程仍存在,确认是卡死问题。
  2. 重新编译该程序为可调试版本,重新运行,复现问题,查询该进程PID号。
  3. 进入到程序的调测模式。

    gdb attach 2573

    2573为该程序PID号。

  4. 开启日志,将堆栈信息导出到文件。

    1
    2
    3
    set logging file core_info.log
    set logging on
    thread apply all bt
    

  5. 查看线程状态。

    1
    info threads
    

    绝大部分线程都处在等待状态__lll_lock_wait。其中,等待的锁变量主要有两个,resource1和resource2。

  6. 通过print命令查看,两个锁变量的信息。其中__owner表示当前该锁的持有者线程ID。

    可见,resource1由线程3889持有未释放,resource2由线程3892持有未释放。

  7. 从上述收集的日志文件中找到ID为3889和3892的线程日志。

    从这两个线程的状态信息可以看出,3889号线程持有了锁resource1,但是本身在请求resource2;而3892号线程持有了锁resource2,但是本身在请求resource1。这两个线程的锁调用关系构成了死锁条件导致无法退出,同时,其他所有依赖这两个锁的线程都陷入了等待状态。

  8. 根据上述堆栈信息,是threadOne和threadTwo直接相互调用导致死锁,查看业务代码逻辑,修改代码,重新编译,运行,问题未复现,确认修改代码合入,验证问题解决。