在软件开发和运维中,经常会遇到进程假死的情况,这种情况会导致系统性能下降或者服务不可用。本文介绍如何使用 pstack 工具来排查进程假死问题,通过分析进程的堆栈信息,找出问题的原因并解决。

背景:风控系统子服务出现假死,导致风控服务不可用。由于缺乏服务可用性监控,无法及时发现进程假死的情况,导致系统不可用。

正文

进程假死是指进程停止响应,但并没有退出的状态。这种情况可能由于多种原因引起,比如死锁、资源耗尽、异常等。为了解决这类问题,我们可以使用 pstack 工具来分析进程的堆栈信息,找出问题的根源。

步骤

pstack 是一个常用的工具,通常随着 gdb(GNU 调试器)一起提供。你可以通过以下命令安装它:

sudo apt-get install gdb

获取进程ID:首先,我们需要获取假死进程的进程ID(PID)。可以使用 ps 命令来列出所有进程,并找到需要排查的进程ID。 使用 pstack 工具分析进程堆栈,一旦获取到进程ID,就可以使用 pstack 工具来获取该进程的堆栈信息。运行以下命令:

pstack <PID>

这将输出该进程的堆栈信息,显示出当前正在执行的函数调用序列。通过分析这些信息,可以发现进程停滞的位置,进而定位问题。

分析堆栈信息,通过查看堆栈信息,可以找到导致进程假死的原因。可能会发现一些死锁情况、无限循环或者其他异常情况。根据具体情况采取相应的措施,比如释放锁、修复代码逻辑等。

案例

简单 demo,main 函数启动以后,新建子线程,实际执行函数进入死循环,导致程序无法正常结束,陷入假死的状态。

cmake_minimum_required(VERSION 3.0.0)
project(pstack_main VERSION 0.1.0 LANGUAGES C CXX)

include(CTest)
enable_testing()

# 查找线程库
find_package(Threads REQUIRED)

add_executable(pstack_main main.cpp)

# 链接线程库
target_link_libraries(pstack_main PRIVATE Threads::Threads)

set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
#include <iostream>
#include <thread>
#include <chrono>

void infiniteLoop() {
    while (true) {
        // 主线程进入死循环
    }
}

int main() {
    std::thread thread(infiniteLoop); // 创建一个线程,执行死循环函数
    thread.join(); // 等待线程结束
    return 0;
}

启动程序,执行 pstack 结果:

Thread 2 (Thread 0x7eff3619b700 (LWP 1315017)):
#0  infiniteLoop () at /root/pstack/main.cpp:6
#1  0x0000000000402ca9 in std::__invoke_impl<void, void (*)()> (__f=@0x2260eb8: 0x4029a6 <infiniteLoop()>) at /usr/include/c++/8/bits/invoke.h:60
#2  0x0000000000402b02 in std::__invoke<void (*)()> (__fn=@0x2260eb8: 0x4029a6 <infiniteLoop()>) at /usr/include/c++/8/bits/invoke.h:95
#3  0x0000000000403150 in std::thread::_Invoker<std::tuple<void (*)()> >::_M_invoke<0ul> (this=0x2260eb8) at /usr/include/c++/8/thread:244
#4  0x0000000000403126 in std::thread::_Invoker<std::tuple<void (*)()> >::operator() (this=0x2260eb8) at /usr/include/c++/8/thread:253
#5  0x000000000040310a in std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)()> > >::_M_run (this=0x2260eb0) at /usr/include/c++/8/thread:196
#6  0x00007eff36bceb23 in execute_native_thread_routine () from /lib64/libstdc++.so.6
#7  0x00007eff36ea91ca in start_thread () from /lib64/libpthread.so.0
#8  0x00007eff361d58d3 in clone () from /lib64/libc.so.6
Thread 1 (Thread 0x7eff372e1740 (LWP 1315016)):
#0  0x00007eff36eaa6cd in __pthread_timedjoin_ex () from /lib64/libpthread.so.0
#1  0x00007eff36bceda7 in std::thread::join() () from /lib64/libstdc++.so.6
#2  0x00000000004029d2 in main () at /root/pstack/main.cpp:13

可以看到,进程假死的原因是死循环,主线程进入死循环,子线程无法退出,导致进程假死。