From cf6e954e7399e368b2b7a7222c4a3a883a6341f6 Mon Sep 17 00:00:00 2001 From: zhangkun2 Date: Mon, 14 Sep 2026 11:41:56 +0800 Subject: [PATCH] fix: reap zombie children before exit in sig_crash signal handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUG-371743: 在 Loong64 V25 环境下登出时存在僵尸进程 bash,其父进程为 dde-session。 根因:sig_crash 信号处理函数标记 [[noreturn]],在调用 exit(-1) 前未回收 已退出的子进程。exit() 不会调用栈上局部对象(如 EXEC_COMMAND 宏创建的 QProcess)的析构函数,当信号在 waitForFinished(-1) 阻塞期间到达时, QProcess 析构被跳过,子进程退出后成为僵尸进程残留。 修复(两层): 1. 根因层:在 sig_crash 的 doLogout() 之后、exit(-1) 之前,添加 while (waitpid(-1, nullptr, WNOHANG) > 0) {} 回收已退出的子进程。 waitpid 是异步信号安全函数,WNOHANG 保证不阻塞,覆盖所有信号中断场景。 2. 症状层:launchAutostopScripts() 中将 EXEC_COMMAND 宏替换为 QProcess::execute(),使用 Qt 标准 API,生命周期管理更规范。 PMS: https://pms.uniontech.com/bug-view-371743.html --- src/dde-session/impl/sessionmanager.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/dde-session/impl/sessionmanager.cpp b/src/dde-session/impl/sessionmanager.cpp index 2999ea6..a483225 100644 --- a/src/dde-session/impl/sessionmanager.cpp +++ b/src/dde-session/impl/sessionmanager.cpp @@ -23,6 +23,7 @@ #include #include +#include #include using namespace Dtk::Core; @@ -630,7 +631,10 @@ void SessionManager::launchAutostopScripts() const QFileInfoList &fileInfos = scriptDir.entryInfoList(QDir::Files); for (auto info : fileInfos) { qDebug() << "[autostop] script will be launched: " << info.absoluteFilePath(); - EXEC_COMMAND("/bin/bash", QStringList() << "-c" << info.absoluteFilePath()); + int exitCode = QProcess::execute("/bin/bash", QStringList() << "-c" << info.absoluteFilePath()); + if (exitCode != 0) { + qWarning() << "failed to run autostop script:" << info.absoluteFilePath(); + } } } } @@ -871,6 +875,11 @@ void SessionManager::setDPMSMode(bool on) [[noreturn]] void sig_crash(int sig) { Q_UNUSED(sig); SessionManager::instance()->doLogout(); + // exit() 不会调用栈上局部对象(如 EXEC_COMMAND 宏创建的 QProcess)的析构函数, + // 若信号在 waitForFinished(-1) 阻塞期间到达,子进程退出后将因未被回收而成为僵尸进程。 + // waitpid 是异步信号安全函数,WNOHANG 保证不阻塞:仅回收已退出的子进程, + // 仍在运行的子进程在 dde-session 退出后会被 reparent 到 init/systemd 回收。 + while (waitpid(-1, nullptr, WNOHANG) > 0) { } exit(-1); };