Skip to content

[Fix] 信号 handler 修复:re-raise 防止死循环,启用 core dump - #1286

Open
ljccsu wants to merge 2 commits into
ModelEngine-Group:developfrom
ljccsu:fix/signal-handler-reraise
Open

[Fix] 信号 handler 修复:re-raise 防止死循环,启用 core dump#1286
ljccsu wants to merge 2 commits into
ModelEngine-Group:developfrom
ljccsu:fix/signal-handler-reraise

Conversation

@ljccsu

@ljccsu ljccsu commented Aug 26, 2026

Copy link
Copy Markdown

背景

spdlog_logger.h 中的 _signal_handler 原实现只调用了 Flush() 就直接 return,对于 SIGSEGV/SIGFPE/SIGILL 这类硬件信号,从 handler 返回后会重新执行出错的指令,导致无限循环:

  • 进程永远不会终止,无法生成 core dump
  • 日志文件无限增长(实测 3 秒内涨到 8MB / 64 万行)

修复方案

Flush() 之后,恢复信号默认动作并重新发给自身:

static void _signal_handler(int signum)
{
    static std::atomic_flag flushed = ATOMIC_FLAG_INIT;
    if (!flushed.test_and_set()) { Logger::GetInstance().Flush(); }
    std::signal(signum, SIG_DFL);
    std::raise(signum);
}
  • SIG_DFL + raise 让内核按信号类型决定终止方式
  • SIGSEGV/SIGABRT/SIGFPE/SIGILL → Term+Core,生成 core dump
  • SIGINT → Term,不生成 core dump
  • atomic_flag 防止 Flush 期间另一个不同信号导致重入

替代 #1256

本 PR 替代并关闭 #1256#1256SIGINT 使用了 std::exit(128 + signum),导致 waitpid 返回 WIFEXITED 而非 WIFSIGNALED——虽然 shell 的 $? 都显示 130,但二者在进程层面是不同的:WIFSIGNALED 表示被信号杀死,WIFEXITED 表示主动退出。统一 re-raise 后所有信号都是 WIFSIGNALED,语义正确。

测试

通过 fork/waitpid 验证全部 5 个注册信号:

信号 编号 结果
SIGSEGV 11 WIFSIGNALED, signal=11
SIGABRT 6 WIFSIGNALED, signal=6
SIGFPE 8 WIFSIGNALED, signal=8
SIGILL 4 WIFSIGNALED, signal=4
SIGINT 2 WIFSIGNALED, signal=2
  • 代码编译通过
  • 手动测试全部 5 个信号

Comment thread ucm/shared/infra/logger/cc/spdlog_logger.h Outdated
@dante159753

Copy link
Copy Markdown
Contributor

这里flush会拿mutex锁,存在死锁可能性,但需要拿这个锁的是其他的flush或者logger初始化逻辑。运行到这里应该早就过了初始化,然后flush逻辑也相对简单,没啥用户输入,出现异常信号的概率很低,因此感觉此风险可以接收。相对来说出错后保证日志flush能看到出错的信息会更重要一些。

ljccsu added 2 commits August 31, 2026 16:26
… core dump

The original _signal_handler only called Flush() and returned, which caused
SIGSEGV/SIGFPE/SIGILL to re-execute the faulting instruction in an infinite
loop. The process never terminated, no core dump was produced, and the log
file grew unbounded.

Fix by restoring the default signal action (SIG_DFL) and re-raising the
signal. This ensures the process terminates correctly with WIFSIGNALED,
producing a core dump for fatal signals (SIGSEGV/SIGABRT/SIGFPE/SIGILL) and
clean termination for SIGINT.

Also adds atomic_flag guard to prevent Flush re-entry if a different fatal
signal arrives during flushing.
@ljccsu
ljccsu force-pushed the fix/signal-handler-reraise branch from 420f10f to c01dcef Compare August 31, 2026 08:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants