Что означает ожидание и ожидание, когда сигнал перехватывается?

От APUE:

Чтобы приложениям не приходилось обрабатывать прерванные системные вызовы, в 4.2BSD введен автоматический перезапуск некоторых прерванных системных вызовов. Автоматически перезапускаются следующие системные вызовы: ioctl, read, readv, write, writev, wait, и waitpid. Как мы уже упоминали, первые пять из этих функций прерываются по сигналу только в том случае, если они работают на медленном устройстве; wait и waitpid всегда прерываются при перехвате сигнала. Поскольку это вызывало проблему для некоторых приложений, которые не хотели перезапускать операцию, если она была прервана, 4.3BSD позволяла процессу отключать эту функцию при основе для каждого сигнала.

Означает ли это, что до введения автоматического перезапуска, если процесс ловил сигнал, wait и waitpid немедленно прекращали ожидание и выполняли последующий код?

Например:

#include <unistd.h>
#include <sys/types.h>
#include <signal.h>

void handler(int sig){}
void handler2(int sig){}


int main(){
    pid_t pid;
    int status;
    signal(SIGUSR1, handler);
    signal(SIGUSR2, handler2);    

    if((pid == fork()) < 0){
        printf("fork error\n");
    }else{
        if(pid){
            //child
            //do something, needs several hours.
        }else{
            //parent
            waitpid(pid, &status, 0);
            printf("Hello world\n");
        }
    }
    return 0;
}

Если не обеспечить автоматический перезапуск, когда я запускаю эту программу в фоновом режиме gcc test.c && ./a.out &, то я посылаю сигнал kill -SIGUSR1 pid или kill -SIGUSR2 pid, waitpid возвращается и выполняется код после waitpid(pid, &status, 0);.

А если обеспечить автоматический перезапуск, waitpid будет выполняться снова, а родительский элемент останется в ожидании.

Правильно ли я понимаю?


person Rick    schedule 29.03.2019    source источник
comment
Прерванный системный вызов вернет -1 (или что-то еще, что он использует для обозначения ошибки) и установит errno в EINTR. Что бы ни случилось дальше, зависит от того, как вызывающая сторона обрабатывает этот случай. Если он перезапускается, он не вернется с этим конкретным кодом ошибки, если иное не задокументировано для этого. Это будет продолжаться до тех пор, пока не вернется по какой-то другой причине.   -  person Shawn    schedule 29.03.2019
comment
Обратите внимание, что вы должны использовать sigaction(), а не signal(), чтобы получить контроль над этим, а sigaction() предоставляет флаг управления SA_RESTART, чтобы указать, следует ли возобновлять системные вызовы после обработки сигнала.   -  person Jonathan Leffler    schedule 29.03.2019
comment
@JonathanLeffler Однако signal() по умолчанию действительно применяет поведение перезапуска в настоящее время в Linux и bsd (по крайней мере)   -  person Ctx    schedule 29.03.2019


Ответы (2)


Первоначальное поведение signal() (семантика System-V) заключалось в том, чтобы прервать любой системный вызов, если процесс в данный момент спит, выполнить обработчик сигнала, и системный вызов возвращается с -EINTR. Затем в BSD4.3 был изобретен механизм restart, который автоматически перезапускал любой системный вызов после того, как он был прерван. Это позволяет избежать необходимости писать цикл для каждого системного вызова, если задействованы обработчики сигналов.

Linux не изменил семантику signal() системного вызова. Однако функция-оболочка glibc signal() в настоящее время по умолчанию вызывает системный вызов sigaction() с флагом SA_RESTART. Итак, если вам не нужно поведение перезапуска, вам нужно вызвать sigaction() и опустить этот флаг.

Итак, ваш код действительно использует механизм restart как в BSD, так и в Linux.

person Ctx    schedule 29.03.2019
comment
Привет @Ctx, я снова прочитал книгу, и у меня возник вопрос. 1. Кажется, что можно прервать только так называемые медленные системные вызовы. 2. И в пределах всех медленных системных вызовов, не все из них могут быть перезапущены автоматически. Я прав насчет этих двух утверждений? - person Rick; 29.03.2019
comment
Если эти 2 утверждения были правильными, как я могу узнать, какой системный вызов является одним из медленных системных вызовов? И в этих медленных системных вызовах, как я узнаю, какие из них могут быть перезапущены автоматически? Содержит ли man-страница эту информацию конкретно? - person Rick; 29.03.2019
comment
@Rick Медленные системные вызовы - это все системные вызовы, которые могут спать в течение неопределенного времени. Если для системных вызовов задокументировано EINTR, его можно прервать. Насколько я знаю, все медленные системные вызовы можно перезапустить. - person Ctx; 29.03.2019
comment
Я использую Linux кстати. :) - person Rick; 29.03.2019
comment
Хорошо, я понял. Хорошо. Просто проверьте, было ли EINTR задокументировано. Я запомню это. Последний вопрос, пожалуйста????????????: я не понимаю, зачем нужен код повторного цикла из предыдущего вопроса, который я задал путаница-о-реализации-системной-функции-в-unix. Смотрите комментарии, которые я только что сделал под принятым ответом. - person Rick; 29.03.2019
comment
Хм, если попытаться подвести итог, вопрос таков: для системы, в которой нет механизма перезапуска, обязательно нужен код повтора. Но возьмем пример из моего предыдущего вопроса: если там нет пользовательских обработчиков сигналов, то никакие сигналы не будут перехвачены, поэтому любые системные вызовы не будут прерваны. Я прав в этом? - person Rick; 29.03.2019
comment
Ах, этот ответчик ответил мне. Тогда я не буду вас беспокоить. Еще раз спасибо @Ctx - person Rick; 29.03.2019

wait и waitpid, как и любая другая блокирующая функция, могут быть прерваны с помощью errno, установленного на EINTR - это точно потому, что обработчик сигнала может сделать очень мало - в основном установить некоторые флаги. Теперь, если блокирующая функция не возвращает значение EINTR, как вы можете каким-либо образом реагировать на сигнал?!

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

person Antti Haapala    schedule 29.03.2019