linux下需用sigaction提前注册sigsegv等崩溃信号处理器,必须在qapplication构造前调用,仅处理可捕获信号,函数须noexcept、无栈分配、禁用qt gui和非异步安全函数,用write()写日志,预分配内存保存上下文。

Linux下用sigaction接管SIGSEGV等信号
Qt程序在Linux崩溃时,默认由系统终止进程,无法执行自定义清理或日志记录。必须用sigaction提前注册信号处理器,且不能依赖signal()——它不可靠,不支持信号屏蔽和重入安全。
关键点:
-
sigaction需在QApplication构造前调用,否则Qt内部可能已覆盖部分信号行为 - 只处理
SIGSEGV、SIGABRT、SIGFPE、SIGILL这几类典型崩溃信号,SIGKILL和SIGSTOP无法捕获 - 处理器函数必须是
noexcept、无栈分配、不调用Qt GUI函数(如QMessageBox),否则可能二次崩溃 - 建议用
write()系统调用写日志,避免std::cout或qDebug()触发锁或内存操作
Windows上用SetUnhandledExceptionFilter捕获异常
Windows的SEH机制与POSIX信号不同,Qt程序需用SetUnhandledExceptionFilter注册顶层异常过滤器。注意:该函数仅对当前线程有效,主线程设置后,子线程仍需单独处理(或改用SetThreadExceptionFilter)。
常见陷阱:
- Qt 5.15+ 使用MSVC编译时,若启用了/SAFESEH,部分异常可能被拦截前就终止,需确认链接器选项
- 不要在过滤器中调用
qApp->quit()或任何事件循环相关函数,此时消息泵已不可用 - 用
MiniDumpWriteDump生成dmp文件比仅打印堆栈更实用,但需链接dbghelp.lib - Qt Creator调试时该过滤器常被绕过,务必在Release模式下测试
Qt自身未捕获的C++异常怎么兜底
Qt默认不拦截未处理的C++异常(如throw未被catch),它们会直接调用std::terminate。要捕获,必须在程序启动时设置std::set_terminate,并确保其处理函数是异步信号安全的。
实操要点:
- 在
main()最开头就调用std::set_terminate,早于任何Qt对象创建 - 处理函数内禁止使用
new、malloc、std::string、QByteArray等可能分配内存的设施 - 可安全调用
write()、backtrace()(Linux)、StackWalk64(Windows)获取调用栈 - Qt信号槽中抛出的异常不会进入此路径——它们被Qt内部
try/catch吞掉并转为警告,所以这层兜底只管裸throw
崩溃后如何安全地保存上下文而不二次崩溃
崩溃现场的堆栈、寄存器、线程状态都处于不稳定态,任何复杂操作都可能让程序彻底挂死。所谓“优雅”,本质是克制——只做最小必要动作。
推荐做法:
- 用
mmap()预分配一块固定大小的共享内存(Linux)或CreateFileMapping()(Windows),崩溃时仅write()或memcpy()写入结构化数据 - 避免格式化字符串,直接写二进制结构体(含时间戳、PID、信号编号、RIP/RSP等关键寄存器)
- 不尝试弹窗、不写入数据库、不发网络请求——这些全留到下次启动时由主程序分析上次崩溃快照
- Qt 6.2+ 提供了
QCrashHandler实验性接口,但尚未稳定,生产环境仍建议手写轻量级方案
最易被忽略的是信号处理器的可重入性:同一个信号可能在极短时间内多次触发,处理器必须能应对重复进入,否则日志错乱或进程卡死。别省那几行static std::atomic_flag检查。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











