UnixシグナルハンドラからのQt関数の呼び出し

UnixのシグナルハンドラからはQt関数を呼び出すことはできません。POSIXの標準規則が適用されます。つまり、シグナルハンドラからはasync-signal-safeな関数のみを呼び出すことができます。Unixのシグナルハンドラから呼び出せる関数の完全な一覧については、「シグナルアクション」を参照してください。

しかし、絶望する必要はありません。Qt で Unix シグナルハンドラを使用する方法があります。その方法は、Unix シグナルハンドラ内で、最終的に Qt シグナルが発信されるような処理を行い、その後、Unix シグナルハンドラから単純に戻るというものです。 Qt プログラム側では、その Qt シグナルが発火し、Qt スロット関数によって受信されます。そこで、Unix シグナルハンドラ内では許可されていなかった Qt 関連の処理を安全に行うことができます。

これを実現する簡単な方法の1つは、処理したい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シグナルハンドラ内では、ソケットペアの書き込み側へ1バイトを書き込んでから戻ります。これにより、対応する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.