Linux программирование в примерах
Оба случая относятся к состоянию гонки. Одним решением для этих проблем является как можно большее упрощение обработчиков сигналов. Это можно сделать, создав флаговые переменные, указывающие на появление сигнала. Обработчик сигнала устанавливает переменную в 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:volatilevolatile sig_atomic_t sig_int_flag = 0; /* обработчик сигнала устанавливает в true *//* ...оставшаяся часть кода как раньше... */Ключевое слово
сообщает компилятору, что переменная может быть изменена извне, за спиной компилятора, так сказать. Это не позволяет компилятору применить оптимизацию, которая могла бы в противном случае повлиять на правильность кодаvolatileСтруктурирование приложения исключительно вокруг переменных
ненадежно. Правильный способ обращения с сигналами показан далее, в разделе 10.7 «Сигналы для межпроцессного взаимодействия».sig_atomic_t10.4.6. Дополнительные предостережения
Стандарт POSIX предусматривает для обработчиков сигналов несколько предостережений:
• Что случается, когда возвращаются обработчики для
,SIGFPE,SIGILLили любых других сигналов, представляющих «вычислительные исключения», не определено.SIGSEGV• Если обработчик был вызван в результате вызова
,abort()илиraise(), он не может вызватьkill().raise()описана в разделе 12.4 «Совершение самоубийства:abort()», aabort()описана далее в этой главе. (Описанная далее функция APIkill()с обработчиком сигнала, принимающая три аргумента, дает возможность сообщить об этом, если это имеет место.)sigaction()• Обработчики сигналов могут вызвать лишь функции из табл. 10.2. В частности, они должны избегать функций
. Проблема в том, что во время работы функции<stdio.h>может возникнуть прерывание, когда внутреннее состояние библиотечной функции находится в середине процесса обновления. Дальнейшие вызовы функций<stdio.h>могут повредить это внутреннее состояние.<stdio.h>Список в табл. 10.2 происходит из раздела 2.4 тома System Interfaces (Системные интерфейсы) стандарта POSIX 2001. Многие из этих функций относятся к сложному API и больше не рассматриваются в данной книге.
Таблица 10.2. Функции, которые могут быть вызваны из обработчика сигнала
_Exit()fpathconf()raise()sigqueue()_exit()fstat()read()sigset()accept()fsync()readlink()sigsuspend()access()ftruncate()recv()sleep()aio_error()getegid()recvfrom()socket()aio_return()geteuid()recvmsg()socketpair()aio_suspend()getgid()rename()stat()alarm()getgroups()rmdir()sysmlink()bind()getpeername()select()sysconf()cfgetispeed()getpgrp()sem_post()tcdrain()cfgetospeed()getpid()send()tcflow()cfsetispeed()getppid()sendmsg()tcflush()cfsetospeed()getsockname()sendto()tcgetattr()chdir()getsockopt()setgid()tcgetpgrp()chmod()getuid()setpgid()tcsendbreak()chown()kill()setsid()tcsetattr()clock_gettime()link()setsockopt()tcsetpgrp()close()listen()setuid()time()connect()lseek()shutdown()timer_getoverrun()creat()lstat()sigaction()timer_gettime()dup()mkdir()sigaddset()timer_settime()dup2()mkfifo()sigdelset()times()execle()open()sigemptyset()umask()execve()pathconf()sigfillset()uname()fchmod()pause()sigismember()unlink()fchown()pipe()signal()utime()fcntl()poll()sigpause()wait()fdatasync()posix_trace_event()sigpending()waitpid()fork()pselect()sigprocmask()write()