Литмир - Электронная Библиотека
A
A

Оба случая относятся к состоянию гонки. Одним решением для этих проблем является как можно большее упрощение обработчиков сигналов. Это можно сделать, создав флаговые переменные, указывающие на появление сигнала. Обработчик сигнала устанавливает переменную в 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 «Сигналы для межпроцессного взаимодействия».

10.4.6. Дополнительные предостережения

Стандарт POSIX предусматривает для обработчиков сигналов несколько предостережений:

• Что случается, когда возвращаются обработчики для

SIGFPE
,
SIGILL
,
SIGSEGV
или любых других сигналов, представляющих «вычислительные исключения», не определено.

• Если обработчик был вызван в результате вызова

abort()
,
raise()
или
kill()
, он не может вызвать
raise()
.
abort()
описана в разделе 12.4 «Совершение самоубийства:
abort()
», a
kill()
описана далее в этой главе. (Описанная далее функция API
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()
140
{"b":"576259","o":1}