Книга: Linux программирование в примерах
10.4.5. Состояния гонок и sig_atomic_t (ISO C)
10.4.5. Состояния гонок и sig_atomic_t
(ISO C)
Пока обработка одного сигнала за раз выглядит просто: установка обработчика сигнала в main()
и (не обязательная) переустановка самого себя обработчиком сигнала (или установка действия SIG_IGN
) в качестве первого действия обработчика.
Но что произойдет, если возникнут два идентичных сигнала, один за другим? В частности, что, если ваша система восстановит действие по умолчанию для вашего сигнала, а второй сигнал появится после вызова обработчика, но до того, как он себя восстановит?
Или предположим, что вы используете bsd_signal()
, так что обработчик остается установленным, но второй сигнал отличается от первого? Обычно обработчику первого сигнала нужно завершить свою работу до того, как запускается второй, а каждый обработчик сигнала не должен временно игнорировать все прочие возможные сигналы!
Оба случая относятся к состоянию гонки. Одним решением для этих проблем является как можно большее упрощение обработчиков сигналов. Это можно сделать, создав флаговые переменные, указывающие на появление сигнала. Обработчик сигнала устанавливает переменную в true и возвращается. Затем основная логика проверяет флаговую переменную в стратегических местах:
int sig_int_flag = 0; /* обработчик сигнала устанавливает в true */
void int_handler(int signum) {
sig_int_flag = 1;
}
int main(int argc, char **argv) {
bsd_signal(SIGINT, int_handler);
/* ...программа продолжается... */
if (sig_int_flag) {
/* возник SIGINT, обработать его */
}
/* ...оставшаяся логика... */
}
(Обратите внимание, что эта стратегия уменьшает окно уязвимости, но не устраняет его).
Стандарт С вводит специальный тип — sig_atomic_t
— для использования с такими флаговыми переменными. Идея, скрывающаяся за этим именем, в том, что присвоение значений переменным этого типа является атомарной операцией: т.е. они совершаются как одно делимое действие. Например, на большинстве машин присвоение значения int
осуществляется атомарно, тогда как инициализация значений в структуре осуществляется либо путем копирования всех байтов в (сгенерированном компилятором) цикле, либо с помощью инструкции «блочного копирования», которая может быть прервана. Поскольку присвоение значения sig_atomic_t
является атомарным, раз начавшись, оно завершается до того, как может появиться другой сигнал и прервать его.
Наличие особого типа является лишь частью истории. Переменные sig_atomic_t
должны быть также объявлены как volatile
:
volatile sig_atomic_t sig_int_flag = 0; /* обработчик сигнала устанавливает в true */
/* ...оставшаяся часть кода как раньше... */
Ключевое слово volatile
сообщает компилятору, что переменная может быть изменена извне, за спиной компилятора, так сказать. Это не позволяет компилятору применить оптимизацию, которая могла бы в противном случае повлиять на правильность кода
Структурирование приложения исключительно вокруг переменных sig_atomic_t
ненадежно. Правильный способ обращения с сигналами показан далее, в разделе 10.7 «Сигналы для межпроцессного взаимодействия».
- LOCK SIGNAL
- Open Source Insight and Discussion
- Internet Service Providers who use assigned IP addresses
- 20.5. Ситуация гонок
- 6.6.4 sigwait and sigwaitinfo
- 6.6.5 SIGEV_THREAD
- Я переписал ISO-образ загрузочного диска на CD-RW и выставил в BIOS загрузку с CD-ROM. Но при перезапуске компьютера нач...
- Не могу открыть файлы ISO. Что и где мне надо скачать?
- 10.6.4. Перехват сигналов: sigaction()
- Assigning Permissions
- Comparison Operators
- 3.2.3 Using mutexes for atomicity