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

您不能从 Unix 信号处理器中调用 Qt 函数。标准 POSIX 规则适用:您只能从信号处理器中调用异步信号安全的函数。有关可以从 Unix 信号处理器中调用的函数完整列表,请参阅信号操作

但不要气馁,有一种方法可以与 Qt 一起使用 Unix 信号处理器。策略是让您的 Unix 信号处理器执行某些操作,这些操作最终会导致 Qt 信号被发射,然后您只需从中返回 Unix 信号处理器。在您的 Qt 程序中,该 Qt 信号被发射,然后被您的 Qt 槽函数接收。在 Qt 信号处理器中,您可以安全地执行您不允许在 Unix 信号处理器中做的任何 Qt 相关操作。

实现这一点的简单方法是为每个要处理的 Unix 信号在您的类中声明一个套接字对。套接字对被声明为静态数据成员。您还为每个套接字对的读取端创建一个QSocketNotifier 进行监控,将您的 Unix 信号处理器声明为静态类方法,并为每个 Unix 信号处理器声明一个对应的槽函数。在此示例中,我们旨在处理 SIGHUP 和 SIGTERM 信号。注意:在深入以下代码片段之前,您应该阅读 socketpair(2) 和 sigaction(2) 的 man 页面。

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 = new QSocketNotifier(sighupFd[1], QSocketNotifier::Read, this);
    connect(snHup, SIGNAL(activated(QSocketDescriptor)), this, SLOT(handleSigHup()));
    snTerm = new QSocketNotifier(sigtermFd[1], QSocketNotifier::Read, this);
    connect(snTerm, SIGNAL(activated(QSocketDescriptor)), this, SLOT(handleSigTerm()));

    ...
}

在您的启动代码的另一个地方,您使用 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);
}

© 2024 Qt 公司有限公司。文档贡献的版权归其各自的拥有者。此处提供的文档采用自由软件基金会发布的 GNU 自由文档许可证版本 1.3 的条款进行许可。Qt 及其相应的标志是芬兰和/或世界其他地区的 The Qt Company Ltd. 的商标。所有其他商标均为其各自所有者的财产。