Неблокирующая проверка сигналов в петле - PullRequest
4 голосов
/ 09 января 2012

У меня есть нить в приложении, которая имеет такой цикл:

...
while (1)
{
    checkDatabase();
    checkChildren();

    sleep(3);
}
...

checkDatabase() говорит само за себя; checkChildren() просто вызывает waitpid(-1, &status, WNOHANG), чтобы иметь дело с дочерними процессами, которые либо вышли, либо получили сигнал.

Приложение работает довольно хорошо, но имеет обработку сигналов по умолчанию. Проблема в том, что у этого родительского процесса есть несколько потоков (пока не беспокойтесь о дочерних процессах), и у меня нет опыта работы с синхронными сигналами, не говоря уже о приложении потоков POSIX. Я использовал signal() и раньше, но, видимо, он не переносимый и в любом случае он не делает то, что мне нужно. У меня нет никакого опыта работы с sigaction методами, и я не могу найти хорошую документацию о том, как заполнять структуры и т. Д.

Что мне нужно сделать, так это синхронно перехватить завершающие сигналы, такие как SIGINT, SIGTERM и SIGQUIT в вышеуказанном цикле (и мне нужно вообще игнорировать SIGPIPE, чтобы я мог отловить ошибку EPIPE из методов ввода-вывода), так это будет выглядеть так:

...
while (1)
{
    checkDatabase();
    checkChildren();
    checkForSignals();

    sleep(3);
}
...

Все остальные темы не должны иметь ничего общего с сигналом; только поток, который выполняет этот цикл, должен знать об этом. И, очевидно, это должна быть проверка неблокирующая , чтобы цикл не блокировался во время первой итерации. Метод, вызываемый в случае обнаружения сигнала, будет сортировать другие потоки и уничтожать мьютексы, и все такое.

Может кто-нибудь дать мне хедз-ап? Большое спасибо.

Ответы [ 4 ]

3 голосов
/ 09 января 2012

(Следуя комментариям вопроса и для полноты картины, это решение пытается избежать обработчиков сигналов.)

Можно заблокировать передачу сигналов через sigprocmask () (или, скорее, pthread_sigmask () , поскольку вы используете потоки).С этого момента сигналы, которые были подняты, но заблокированы, доступны через sigpending () .

Следовательно, вы можете сделать что-то вроде (проверка ошибок опущена для краткости):

sigset_t blocked;
sigemptyset(&blocked);
sigaddset(&blocked, SIGINT);
sigaddset(&blocked, SIGTERM);
sigaddset(&blocked, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &blocked, NULL); // Block SIGINT, SIGTERM and SIGQUIT.
signal(SIGPIPE, SIG_IGN);                   // Ignore SIGPIPE.

Затем, позже:

void checkForSignals(void)
{
    sigset_t pending;
    sigpending(&pending);
    if (sigismember(&pending, SIGINT)) {
        // Handle SIGINT...
    }
    if (sigismember(&pending, SIGTERM)) {
        // Handle SIGTERM...
    }
    if (sigismember(&pending, SIGQUIT)) {
        // Handle SIGQUIT...
    }
}

Поскольку sigpending() не блокирует, это, кажется, соответствует вашим требованиям.

2 голосов
/ 09 января 2012

Создайте обработчик сигнала для SIGINT, SIGTERM и SIGQUIT, используя ту же функцию.В этой функции сигнала просто установите флаг, который можно опрашивать в вашем цикле.

Примерно так:

/* Global variable, will be set to non-zero if SIGINT, SIGTERM or SIGQUIT is caught */
int term_signal_set = 0;

void my_signal_handler(int)
{
    term_signal_set = 1;
}

/* ... */

signal(SIGINT, my_signal_handler);
signal(SIGTERM, my_signal_handler);
signal(SIGQUIT, my_signal_handler);
signal(SIGPIPE, SIG_IGN);  /* So functions return EPIPE */

while (1)
{
    /* ... */

    if (term_signal_set > 0)
        break;  /* Or do something else */

    sleep(3);
}
1 голос
/ 09 января 2012

В многопоточном приложении, получающем сигнал, нет предопределения, какой поток получает сигнал.Типичные обходные пути включают установку глобальной переменной в обработчике сигналов и проверку ее из выделенного потока.

Таким образом, в вашем случае обработчик сигнала (вызываемый из любого потока) просто установит нечто вроде глобальной переменной для полученного сигналаи в CheckForSignals() вы бы это проверили.

0 голосов
/ 09 января 2012

sigaction это путь. man sigaction должно помочь вам. Вот пример из интернета

#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h> 

struct sigaction act;

void sighandler(int signum, siginfo_t *info, void *ptr)
{
    printf("Received signal %d\n", signum);
    printf("Signal originates from process %lu\n",
        (unsigned long)info->si_pid);
}

int main()
{
    printf("I am %lu\n", (unsigned long)getpid());
        memset(&act, 0, sizeof(act));

    act.sa_sigaction = sighandler;
    act.sa_flags = SA_SIGINFO;

    sigaction(SIGTERM, &act, NULL);
        // Waiting for CTRL+C...
    sleep(100);  
    return 0;
}
...