从 Unix 信号处理程序中调用 Qt 函数

您无法在Unix 信号处理程序中调用 Qt 函数。此处适用 POSIX 标准规则:在信号处理程序中,您只能调用异步信号安全函数。有关可在 Unix 信号处理程序中调用的函数完整列表,请参阅“信号操作”。

但请不要灰心,还是有办法在 Qt 中使用 Unix 信号处理程序的。具体策略是:让您的 Unix 信号处理程序执行某些操作,这些操作最终会触发一个 Qt 信号的发出,然后您只需从 Unix 信号处理程序中返回即可。 回到您的 Qt 程序中,该 Qt 信号会被发出,并由您的 Qt 槽函数接收;在该槽函数中,您可以安全地执行那些在 Unix 信号处理程序中不被允许的 Qt 相关操作。

实现这一目标的一种简单方法是:在类中为每个需要处理的 Unix 信号声明一对套接字。这些套接字对应作为静态数据成员进行声明。 你还需要创建一个QSocketNotifier 来监听每个套接字对的读取端,将 Unix 信号处理程序声明为静态类方法,并为每个 Unix 信号处理程序声明一个对应的槽函数。在本示例中,我们将同时处理 SIGHUP 和 SIGTERM 信号。 注意:在深入研究以下代码片段之前,您应先阅读 socketpair(2) 和 sigaction(2) 的手册页。

class MyDaemon : public QObject
{
    Q_OBJECT

  public:
    MyDaemon(QObject *parent = 0);
    ~MyDaemon();

    // Unix signal handlers.
    static void hupSignalHandler(int unused);
    static void termSignalHandler(int unused);

  public slots:
    // Qt signal handlers.
    void handleSigHup();
    void handleSigTerm();

  private:
    static int sighupFd[2];
    static int sigtermFd[2];

    QSocketNotifier *snHup;
    QSocketNotifier *snTerm;
};

在 MyDaemon 的构造函数中,使用 socketpair(2) 函数初始化每对文件描述符,然后创建QSocketNotifier 来监视每对的读取端。每个QSocketNotifier 的 activated() 信号被连接到相应的槽函数,这实际上将 Unix 信号转换为QSocketNotifier::activated() 信号。

MyDaemon::MyDaemon(QObject*parent)
        : QObject(parent)
{
    if(::socketpair(AF_UNIX,SOCK_STREAM, 0,sighupFd))
       qFatal("Couldn't create HUP socketpair");

   if(::socketpair(AF_UNIX,SOCK_STREAM, 0,sigtermFd))
       qFatal("Couldn't create TERM socketpair");
    snHup= newQSocketNotifier(sighupFd[1],QSocketNotifier::Read, this);
    connect(snHup, &QSocketNotifier::activated, this, &MyDaemon::handleSigHup);
    snTerm= newQSocketNotifier(sigtermFd[1],QSocketNotifier::Read, this);
    connect(snTerm, &QSocketNotifier::activated, this, &MyDaemon::handleSigTerm);

    ...
}

在实现文件中初始化静态成员:

int MyDaemon::sighupFd[2] = {0, 0};
int MyDaemon::sigtermFd[2] = {0, 0};

在启动代码的其他位置,使用 sigaction(2) 安装 Unix 信号处理程序。

static int setup_unix_signal_handlers()
{
    struct sigaction hup, term;

    hup.sa_handler = MyDaemon::hupSignalHandler;
    sigemptyset(&hup.sa_mask);
    hup.sa_flags = 0;
    hup.sa_flags |= SA_RESTART;

    if (sigaction(SIGHUP, &hup, 0))
       return 1;

    term.sa_handler = MyDaemon::termSignalHandler;
    sigemptyset(&term.sa_mask);
    term.sa_flags = 0;
    term.sa_flags |= SA_RESTART;

    if (sigaction(SIGTERM, &term, 0))
       return 2;

    return 0;
}

在 Unix 信号处理程序中,您向套接字对的写端写入一个字节,然后返回。这将导致相应的 `QSocketNotifier ` 发出其 `activated()` 信号,进而触发相应的 Qt 槽函数运行。

void MyDaemon::hupSignalHandler(int)
{
    char a = 1;
    ::write(sighupFd[0], &a, sizeof(a));
}

void MyDaemon::termSignalHandler(int)
{
    char a = 1;
    ::write(sigtermFd[0], &a, sizeof(a));
}

在连接到QSocketNotifier::activated()信号的槽函数中,你读取该字节。此时,你已通过该信号安全地返回Qt环境,可以执行所有在Unix信号处理程序中被禁止的Qt操作。

void MyDaemon::handleSigTerm()
{
    snTerm->setEnabled(false);
    char tmp;
    ::read(sigtermFd[1], &tmp, sizeof(tmp));

    // do Qt stuff

    snTerm->setEnabled(true);
}

void MyDaemon::handleSigHup()
{
    snHup->setEnabled(false);
    char tmp;
    ::read(sighupFd[1], &tmp, sizeof(tmp));

    // do Qt stuff

    snHup->setEnabled(true);
}

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.