Проблема с таймером с длинным обработчиком сигнала (SIGALARM) - PullRequest
3 голосов
/ 25 мая 2011

Есть таймер, который посылает сигнал SIGALARM каждые 1 сек. Обработчик сигнала, который спит 2 сек зарегистрировано. Что просходит? В частности, у меня есть следующий код, в котором процесс запускается несколькими потоками. Интересно, что с помощью этого обработчика длинных сигналов другие потоки заблокированы от выполнения ... Кто-нибудь может объяснить, почему это так?

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> //rand
#include <sys/wait.h>
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
#define NUM_THREADS 2

int init_timer(int real_time, int msec) {
    struct itimerval timeslice;
    timeslice.it_interval.tv_sec = msec / 1000;
    timeslice.it_interval.tv_usec = (msec % 1000) * 1000;
    timeslice.it_value.tv_sec = msec / 1000;
    timeslice.it_value.tv_usec = (msec % 1000) * 1000;
    setitimer(real_time ? ITIMER_REAL : ITIMER_VIRTUAL, &timeslice, NULL);
    return 0;
}

void install_handler(int signo, void(*handler)(int)) {
    sigset_t set;
    struct sigaction act;

    /* Setup the handler */
    act.sa_handler = handler;
    act.sa_flags = SA_RESTART;
    sigaction(signo, &act, 0);

    /* Unblock the signal */
    sigemptyset(&set);
    sigaddset(&set, signo);
    sigprocmask(SIG_UNBLOCK, &set, NULL);
    return;
}

void timerTest(int signo)
{
    printf("000\n");
    sleep(1);
    printf("111\n");
}

void * threadTest(void * threadId)
{
    while(true)
    {
        printf("222\n");
    }
}

int main(int argc, char *argv[]) {
    int real_time = 1;
    int tick_msec = 10;
    init_timer(real_time, tick_msec);
    install_handler(real_time ? SIGALRM : SIGVTALRM, &timerTest);

    pthread_t threads[NUM_THREADS];
    int rc;
    long t;
    for (t = 0; t < NUM_THREADS; t++) {
        rc = pthread_create(&threads[t], NULL, threadTest, (void *) t);
        if (rc) {
            exit(-1);
        }
    }

    void * status;
    for (t = 0; t < NUM_THREADS; t++) {
        rc = pthread_join(threads[t], &status);
        if (rc) {
            exit(-1);
        }
    }
    pthread_exit(NULL);
}

распечатка:

222
222
222
222
...
222
000
111
000
111
...

не будет 222 после появления первого 111? почему так?

Ответы [ 3 ]

4 голосов
/ 26 мая 2011

Сигнал доставляется в определенный поток, поэтому обработчик сигнала работает в определенном потоке (поток, в который был доставлен сигнал). Если сигнал доставляется в поток, записывающий 222\n, то этот поток должен прекратить запись в 222\n и запустить обработчик сигнала. Ваш примерный обработчик сигнала занимает целую секунду, так что это полная секунда, в течение которой этот поток может не записать 222\n.

Кроме того, поскольку вы используете printf для записи всех этих байтов, в libc выполняется некоторая блокировка. Так как printf не является функцией, безопасной для асинхронных сигналов, фактически не определено, что произойдет, если вы будете использовать ее в обработчике сигналов. Одно из возможных объяснений поведения, которое вы наблюдаете, заключается в следующем. Если сигнал доставляется потоку, пока этот поток удерживает блокировку stdout, никакой другой поток не сможет записывать в stdout до тех пор, пока обработчик не вернется и блокировка не будет снята «нормальным» кодом, выполняющимся в этом потоке. В этом случае обработчик сигнала все еще может записывать в стандартный вывод, поскольку блокировка - это rlock , которая может быть повторно получена любым конкретным потоком. Однако это может варьироваться в зависимости от конкретной платформы, библиотеки C, библиотеки потоков или фазы луны. Ваш пример легко конвертируется в использование write (2), хотя оно демонстрирует более или менее одинаковое поведение проблемы, имеет более или менее такое же исправление и не полагается на неопределенное поведение.

Если вы используете SIG_BLOCK сигнал таймера в потоках 222\n, то обработчик сигнала всегда будет работать в главном потоке, и вы продолжите получать 222\n вывод, пока обработчик сигнала спит.

Сет также делает большое замечание только об использовании безопасных функций в обработчиках сигналов. Использование любых других означает, что поведение вашей программы не определено.

2 голосов
/ 25 мая 2011

Часто сигналы (или, по крайней мере, этот сигнал) блокируются, когда вы находитесь в обработчике сигналов. Это также плохая идея делать очень много в обработчике сигналов. Обычно вы должны установить переменную или что-то в этом роде, а затем обработать сигнал, как только вы окажетесь в вашем обычном кодовом пути.

См. Флаг sigaction SA_NODEFER, чтобы узнать, как разрешить или запретить прием сигнала внутри обработчика сигнала.

Существует также ограниченное количество функций, которые безопасно вызывать из обработчика сигнала. Страница man (7) описывает это. «Функция обработчика сигнала должна быть очень осторожной, поскольку обработка в другом месте может быть прервана в какой-то произвольный момент при выполнении программы. POSIX имеет понятие« безопасная функция ». Если сигнал прерывает выполнение небезопасной функции, и обработчик вызывает небезопасную функцию, затем поведение программы не определено"

Программа, которая вызывает небезопасную функцию из обработчика сигнала, является нарушена . На некоторых машинах это будет "Работа"; на других это будет coredump. Разрешено делать что-либо или ничего, включая переформатирование диска, подвергая пользователя неуклюжие записи Барри Манилоу, сыгранные задом наперед или уронившие на Далласе. Попытка вызова небезопасной функции переводит программу в сумеречную зону неопределенного поведения.

С извинениями перед Мышью.

0 голосов
/ 14 января 2014

Неправильно использовать printf в обработчике сигналов.

Относительно потока, который будет обрабатывать SIGALARM, вы можете найти некоторую полезную информацию о блокировке сигналов здесь .Пост содержит полезный код ядра Linux.По сути, сигнал обрабатывается основным потоком, если он хочет его получить.Если нет, он будет обработан любым другим потоком, который хочет этого.Вы можете маскировать определенные сигналы в потоке, используя pthread_sigmask(3)

...