Ожидание сигнала внутри самого обработчика сигнала - PullRequest
1 голос
/ 10 марта 2019

Я пытался написать программу, которая включает в себя отправку сигнала процессу для уведомления его о приостановке на некоторое время и возобновлении работы после получения другого сигнала. Я написал обработчик сигнала так:

void sig_handler(int alrm)
{
    if(sig_rcv==0)
    {
        printf("Signal received..sig_rcv value: %d\n",sig_rcv);
        sig_rcv = (sig_rcv+1)%2;
        printf("After increment sig_rcv value: %d\n",sig_rcv);
        signal(SIGUSR1,sig_handler);
        if(pause()<0)
        {
            printf("Signal received again..\n");
        }
    }
    else
    {
        sig_rcv = (sig_rcv+1)%2;
        printf("Signal received..sig_rcv value: %d\n",sig_rcv);
        signal(SIGUSR1,sig_handler);
    }
    printf("Exiting..\n");
}

Здесь я поддерживаю глобальную переменную sig_rcv, которая изначально равна 0, и если сигнал получен, когда он равен нулю, он переходит в условие if и делает паузу для другого сигнала. С другой стороны, если он получает сигнал во время sig_rcv равен 1, он просто изменит значение этой переменной. Моя цель написать обработчик сигнала таким образом - использовать один и тот же сигнал для двух разных целей. Я использовал этот системный вызов:

signal(SIGUSR1,sig_handler);

Теперь, когда я отправлял SIGUSR1 процессу, он выполняется до оператора pause(). Но после отправки SIGUSR1 во второй раз, он не показывает никакого эффекта. Он просто останавливается в операторе pause. Эта проблема была решена после того, как я использовал другой сигнал для удаления состояния паузы, например:

void sig_handler2(int a)
{
    sig_rcv = (sig_rcv+1)%2;
    printf("Restarting reading...\n");
}

и

signal(SIGUSR2,sig_handler2);

В этом случае все работало идеально. Итак, мой вопрос: Почему происходит это явление? Разве мы не можем ждать сигнала при выполнении обработчика сигнала, записанного для того же сигнала? Если это так, то в чем причина? И есть ли способ достичь того же результата без использования SIGUSR2?

Ответы [ 3 ]

5 голосов
/ 10 марта 2019

Разве мы не можем ждать сигнала при выполнении обработчика сигнала, записанного для того же сигнала?

Будет ли код ждать «вечно» или получит тот же сигнал снова, уже обрабатывая его, зависит от реализации (C / OS).

Из документации POSIX :

В течение времени между генерацией сигнала и его доставкой или приемом сигнал считается "ожидающим".

и

Если генерируется последующее вхождение ожидающего сигнала, это определяется реализацией относительно того, доставлен или принят сигнал более одного раза [...]


И, кстати, вызов printf() и друзей из обработчика сигнала небезопасен .

2 голосов
/ 11 марта 2019

Поскольку вы используете signal вместо sigaction, важно отметить следующее из стандарта POSIX :

Когда возникает сигнал, и func указывает на функцию, это определяется реализацией, является ли эквивалент:

signal(sig, SIG_DFL);

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

Это отражает две исторические реализации сигналов, SVID и BSD. Поскольку вы используете Ubuntu 18.04, вы, вероятно, используете glibc , который реализует семантику последнего (BSD). SIGUSR1 маскируется во время выполнения его обработчика.

Так как вам нужна семантика SVID, в которой никакие сигналы не маскируются, и вам необходимо восстанавливать обработчик сигналов каждый раз, когда вызывается обработчик сигналов, вы должны заменить свои signal(SIGUSR1, sig_handler); вызовы следующим:

struct sigaction sa = { .sa_handler = sig_handler, .sa_flags = SA_NODEFER|SA_RESETHAND };
sigemptyset(&sa.mask);
sigaction(SIGUSR1, &sa, NULL);

Флаг SA_NODEFER вместе с пустой маской означает, что никакие сигналы не будут маскироваться; SA_RESETHAND означает, что действие сигнала будет сброшено на SIG_DFL.

Кроме того, как уже говорилось в других ответах, вы не должны вызывать printf из обработчика сигнала. Справочная страница Linux signal safety говорит о том, какие функции можно вызывать. sigaction, signal и pause в порядке. Вы можете использовать write для записи строк вместо printf.

2 голосов
/ 10 марта 2019

В целом, чем ответ @ alk, вы не можете вызвать какую-либо библиотечную функцию из обработчика сигнала в строго соответствующем коде C.

За 7.1.4 Использование библиотечных функций , параграф 4 стандарта C :

Функции в стандартной библиотеке не гарантированно являются реентерабельными и могут изменять объекты со статическим или потоковым сроком хранения. 188

Примечание сноска 188 , для этого самого абзаца говорится:

Таким образом, обработчик сигнала, как правило, не может вызывать стандартные библиотечные функции.

В состоянии @alk POSIX позволяет безопасно вызывать только функции async-signal-safe из обработчика сигнала.

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

...