유닉스 신호 핸들러에서 Qt 함수 호출하기

Unix 신호 핸들러에서는 Qt 함수를 호출할 수 없습니다. 표준 POSIX 규칙이 적용됩니다. 즉, 신호 핸들러에서는 비동기 신호 안전(async-signal-safe) 함수만 호출할 수 있습니다. Unix 신호 핸들러에서 호출할 수 있는 함수의 전체 목록은 신호 동작(Signal Actions )을 참조하십시오.

하지만 실망하지 마세요. Qt에서 유닉스 시그널 핸들러를 사용할 수 있는 방법이 있습니다. 그 방법은 유닉스 시그널 핸들러에서 결국 Qt 시그널이 발생하도록 하는 작업을 수행한 다음, 유닉스 시그널 핸들러에서 간단히 반환하는 것입니다. Qt 프로그램으로 돌아오면, 해당 Qt 신호가 발송되고 Qt 슬롯 함수에서 수신됩니다. 이때 Unix 신호 핸들러에서는 수행할 수 없었던 Qt 관련 작업을 안전하게 수행할 수 있습니다.

이를 구현하는 간단한 방법 중 하나는 처리하고자 하는 각 유닉스 신호에 대해 클래스 내에 소켓 쌍을 선언하는 것입니다. 소켓 쌍은 정적 데이터 멤버로 선언됩니다. 또한 각 소켓 쌍의 읽기 쪽을 모니터링할 ` QSocketNotifier `를 생성하고, 유닉스 신호 핸들러를 정적 클래스 메서드로 선언하며, 각 유닉스 신호 핸들러에 대응하는 슬롯 함수를 선언합니다. 이 예제에서는 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() 신호는 적절한 슬롯 함수에 연결되며, 이는 유닉스 신호를 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, &QSocketNotifier::activated, this, &MyDaemon::handleSigHup);
    snTerm = new QSocketNotifier(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;
}

유닉스 신호 핸들러 내에서 소켓 쌍의 쓰기 쪽( write end)에 바이트 하나를 쓰고 반환합니다. 이렇게 하면 해당 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로 돌아왔으므로, 유닉스 신호 핸들러에서는 수행할 수 없었던 모든 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.