在开发Linux桌面应用时,我们经常需要处理系统信号,比如用户按下Ctrl+C
(SIGINT)或系统发送终止信号(SIGTERM)。传统的信号处理方式在Qt应用中存在局限性——无法安全地与Qt事件循环交互。
本文将介绍一种高效可靠的方法,使用QSocketNotifier
将系统信号集成到Qt事件循环中,实现应用的优雅退出。
SocketPair通信管道:
QSocketNotifier桥梁:
线程安全设计:
初始化阶段:
信号触发阶段:
Qt处理阶段:
最小化信号处理函数:
启用/禁用Notifier:
SA_RESTART标志:
// 添加对SIGHUP的支持
static int sigHupFd[2];
// ... 初始化代码类似
void SignalHandler::_onSignals()
{
char buffer[16];
ssize_t count = read(sigFd[1], buffer, sizeof(buffer));
// 根据读取到的信号数量执行操作
}
// 使用QMetaObject::invokeMethod确保在主线程执行
QMetaObject::invokeMethod(this, [](){
// 安全访问Qt对象
}, Qt::QueuedConnection);
#pragma once
#include
#include
class QSocketNotifier;
Q_DECLARE_LOGGING_CATEGORY(SignalHandlerLog)
class SignalHandler : public QObject
{
Q_OBJECT
public:
explicit SignalHandler(QObject *parent = nullptr);
static SignalHandler *instance();
static int setupSignalHandlers();
static void intSignalHandler(int signum);
static void termSignalHandler(int signum);
private slots:
void _onSigInt();
void _onSigTerm();
private:
static int sigIntFd[2];
static int sigTermFd[2];
QSocketNotifier *_notifierInt = nullptr;
QSocketNotifier *_notifierTerm = nullptr;
};
#include "SignalHandler.h"
#include "QGCApplication.h"
#include "QGCLoggingCategory.h"
#include
#include
#include
#include
#include
QGC_LOGGING_CATEGORY(SignalHandlerLog, "qgc.utilities.signalhandler")
int SignalHandler::sigIntFd[2] = {0, 0};
int SignalHandler::sigTermFd[2] = {0, 0};
Q_GLOBAL_STATIC(SignalHandler, _signalHandlerInstance);
SignalHandler *SignalHandler::instance()
{
return _signalHandlerInstance();
}
SignalHandler::SignalHandler(QObject *parent)
: QObject(parent)
{
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigIntFd)) {
qCFatal(SignalHandlerLog) << "Couldn't create INT socketpair";
}
_notifierInt = new QSocketNotifier(sigIntFd[1], QSocketNotifier::Read, this);
connect(_notifierInt, &QSocketNotifier::activated, this, &SignalHandler::_onSigInt);
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigTermFd)) {
qCFatal(SignalHandlerLog) << "Couldn't create TERM socketpair";
}
_notifierTerm = new QSocketNotifier(sigTermFd[1], QSocketNotifier::Read, this);
connect(_notifierTerm, &QSocketNotifier::activated, this, &SignalHandler::_onSigTerm);
}
void SignalHandler::_onSigInt()
{
_notifierInt->setEnabled(false);
char b;
::read(sigIntFd[1], &b, sizeof(b));
qCDebug(SignalHandlerLog) << "Caught SIGINT—shutting down gracefully";
if (qgcApp() && qgcApp()->mainRootWindow()) {
qgcApp()->mainRootWindow()->close();
QEvent ev(QEvent::Quit);
qgcApp()->event(&ev);
}
_notifierInt->setEnabled(true);
}
void SignalHandler::_onSigTerm()
{
_notifierTerm->setEnabled(false);
char b;
::read(sigTermFd[1], &b, sizeof(b));
qCDebug(SignalHandlerLog) << "Caught SIGTERM—shutting down gracefully";
if (qgcApp() && qgcApp()->mainRootWindow()) {
qgcApp()->mainRootWindow()->close();
QEvent ev(QEvent::Quit);
qgcApp()->event(&ev);
}
_notifierTerm->setEnabled(true);
}
void SignalHandler::intSignalHandler(int signum)
{
Q_ASSERT(signum == SIGINT);
char b = 1;
::write(sigIntFd[0], &b, sizeof(b));
}
void SignalHandler::termSignalHandler(int signum)
{
Q_ASSERT(signum == SIGTERM);
char b = 1;
::write(sigTermFd[0], &b, sizeof(b));
}
int SignalHandler::setupSignalHandlers()
{
struct sigaction sa_int{};
sa_int.sa_handler = SignalHandler::intSignalHandler;
sigemptyset(&sa_int.sa_mask);
sa_int.sa_flags = SA_RESTART;
if (sigaction(SIGINT, &sa_int, nullptr)) {
return 1;
}
struct sigaction sa_term{};
sa_term.sa_handler = SignalHandler::termSignalHandler;
sigemptyset(&sa_term.sa_mask);
sa_term.sa_flags = SA_RESTART;
if (sigaction(SIGTERM, &sa_term, nullptr)) {
return 2;
}
return 0;
}
// 在main.cpp中初始化
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
// 初始化信号处理器
#ifdef Q_OS_LINUX
#ifndef Q_OS_ANDROID
SignalHandler::instance();
if (SignalHandler::setupSignalHandlers() != 0) {
qWarning() << "Failed to setup signal handlers";
}
#endif
#endif
// ... 应用初始化代码
return app.exec();
}