• Мультиплицирующие вызовы ввода-вывода poll(), ppoll(), select() и pselect(). (В стандарте SUSv3 явно указывается, что поведение вызовов select() и pselect() в случае прерывания обработчиком сигнала не устанавливается вне зависимости от флага SA_RESTART).

• Специфичные системные вызовы Linux epoll_wait() и epoll_pwait().

• Специфичный системный вызов Linux io_getevents().

• Блокирующие системные вызовы, использовавшиеся с очередями сообщений и семафорами System V: semop(), semtimedop(), msgrcv() и msgsnd(). (Несмотря на то что изначально в System V не был реализован автоматический перезапуск системных вызовов, в некоторых реализациях UNIX эти системные вызовы перезапускаются при установке флага SA_RESTART.)

• Вызов read() из файлового дескриптора inotify.

• Системные вызовы и библиотечные функции, созданные специально для ожидания доставки сигнала: pause(), sigsuspend(), sigtimedwait() и sigwaitinfo().

Изменение флага SA_RESTART сигнала

Функция siginterrupt() изменяет установку SA_RESTART конкретного сигнала.

#include

int siginterrupt(int sig, int flag);

Возвращает 0 при успешном завершении и –1 при ошибке

Если параметр flag истинен (1), значит, обработчик сигнала sig будет прерывать блокирующие системные вызовы. Если flag ложен (0), то блокирующие системные вызовы будут перезапущены после выполнения обработчика сигнала sig.

Функция siginterrupt() в своей работе использует функцию sigaction() для получения копии текущей диспозиции сигнала, изменяет значение флага SA_RESTART, возвращаемого структурой oldact, а затем вызывает функцию sigaction() еще раз, чтобы обновить диспозицию сигнала.

В стандарте SUSv4 функция siginterrupt() помечена как устаревшая с рекомендацией использовать для этих целей функцию sigaction().

Необрабатываемые сигналы остановки могут сгенерировать ошибку EINTR для некоторых системных вызовов Linux

В Linux некоторые блокирующие системные вызовы могут возвращать ошибку EINTR даже при отсутствии обработчика сигнала. Это может случиться, если системный вызов заблокирован и процесс сначала остановлен сигналом (SIGSTOP, SIGTSTP, SIGTTIN и SIGTTOU), а затем возобновлен сигналом SIGCONT.

Так ведут себя следующие системные вызовы: epoll_pwait(), epoll_wait(), read() из файлового дескриптора inotify, semop(), semtimedop(), sigtimedwait() и sigwaitinfo().

В ядрах, предшествовавших версии 2.6.24, так вел себя вызов poll(), в ядрах старше версии 2.6.22 — вызовы sem_wait(), sem_timedwait(), futex(FUTEX_WAIT), вызовы msgrcv(), msgsnd() в ядрах до 2.6.9 и nanosleep() в Linux 2.4 и более ранних.

В Linux 2.4 и более ранних версиях вызов sleep() также мог быть прерван подобным образом, но вместо возврата ошибки он возвращал количество оставшихся секунд сна.

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

21.6. Резюме

В этой главе мы рассмотрели целый набор факторов, влияющих на работу и проектирование обработчиков сигналов.

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

Кроме выполнения возврата управления, обработчик сигнала может быть завершен одним из нескольких способов, в том числе вызов функции _exit(), путем отправки сигнала (kill(), raise() или abort()) либо выполнения нелокального перехода. Использование функций sigsetjmp() и siglongjmp() предоставляет программе явный контроль над обработкой сигнальной маски процесса при выполнении нелокального перехода.

Мы можем использовать функцию signalstack() для определения альтернативного сигнального стека процесса. Это участок памяти, который будет задействоваться вместо стандартного стека процесса при активации обработчика сигнала. Альтернативный стек может потребоваться в ситуациях, когда ресурсы стандартного разросшегося стека израсходованы (в этом случае ядро направляет процессу сигнал SIGSEGV).

Флаг SA_SIGINFO функции sigaction() позволяет установить обработчик сигнала, получающий дополнительную информацию о сигнале. Эта информация предоставляется через структуру siginfo_t, адрес которой передается обработчику сигнала в качестве аргумента.

Перейти на страницу:

Похожие книги