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

Unix のシグナルハンドラから Qt の関数を呼び出すことはできません。標準の POSIX ルールが適用されます:シグナルハンドラから呼び出せるのは非同期シグナルセーフな関数だけです。Unix のシグナルハンドラから呼び出せる関数の完全なリストはSignal Actionsを参照してください。

しかし、QtでUnixシグナルハンドラを使う方法はあります。その方法とは、UnixシグナルハンドラにQtシグナルを発生させるような処理をさせ、Unixシグナルハンドラからリターンすることです。Qt プログラムに戻ると、Qt シグナルが発信され、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,SIGNAL(activated(QSocketDescriptor)), this,SLOT(handleSigHup())); snTerm= newQSocketNotifier(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);
}

© 2025 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.