Решение, показанное выше, состоит в том, чтобы проверить состояние обработки прерываний, если они игнорировались ранее. Функции программы в том виде, в каком она написана, зависят от возвращаемого signal
предыдущего состояния конкретного сигнала. Если сигналы уже игнорировались, процесс должен продолжить это дело; в противном случае их следует перехватывать.
Более сложная программа может перехватить прерывание и интерпретировать его как запрос на прекращение своих действий и возврат к основному циклу обработки команд. Подумаем о текстовом редакторе: прерывание длинного вывода на печать не должно вызывать завершения редактирования и потерю уже отредактированного текста. Программа для такого случая может быть написана следующим образом:
#include
#include
jmp_buf sjbuf;
main() {
int onintr();
if(signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, onintr);
setjmp(sjbuf);
/* сохранить текущую позицию стека */
for(;;) {
/* главный рабочий цикл */
}
...
}
onintr() { /* установить если прервано */
signal(SIGINT, onintr); /* установить
для следующего прерывания */
printf("\nInterrupt\n");
longjmp(sjbuf, 0); /* вернуться
в сохраненное состояние */
}
Файл описывает тип jmp_buf
как объект, в котором сохраняется позиция стека; sjbuf
считается таким объектом. Функция setjmp(3)
сохраняет запись о том, где выполняется программа. Значения переменных не сохраняются . Когда происходит прерывание, выполняется обращение к подпрограмме onintr
, которая может печатать сообщения, устанавливать флаги и т.д. Функция longjmp
берет в качестве аргумента объект, сохраненный setjmp
, и возвращает управление в ячейку после вызова setjmp
. Поэтому управление (и значение уровня стека) будет возвращено обратно в основную программу — ко входу в головной цикл.
Отметим, что после прерывания сигнал вновь настраивается на onintr
. Это обусловлено тем, что когда сигналы возникают, они автоматически настраиваются на реакцию по умолчанию.
Некоторые программы, которые "хотят" обнаружить сигналы, просто не могут быть остановлены в произвольный момент, например в середине обновления сложных составных данных. Решение состоит в том, что подпрограмма обработки прерывания должна установить флаг и вернуться к месту вызова exit
или longjmp
. Выполнение программы продолжится точно с того места, где оно было прервано, а флаг прерывания будет проверен позднее.
С этим подходом связана одна трудность. Предположим, что, когда посылается сигнал прерывания, программа читается с терминала. Описанная подпрограмма непременно вызывается; она устанавливает свой флаг и возвращается. Если бы, как отмечалось выше, было верно то, что выполнение возобновляется точно с того места, где оно прервалось, программа продолжала бы чтение с терминала до ввода пользователем другой строки. Однако здесь возникает недоразумение, поскольку пользователь может не знать, что программа читает, и предположительно предпочел бы, чтобы сигнал сразу оказал действие. Для разрешения проблемы система должна закончить read
, но с сообщением об ошибке, указывающим, что произошло: errno
присваивается EINTR
, определенное в заголовке , чтобы обозначить прерванный системный вызов.
Так, программы, которые "ловят" сигналы и продолжают после этого свою работу, должны быть готовы к появлению ошибок, вызванных прерванными системными вызовами. (Следует остерегаться системных вызовов read
— чтение с терминала, wait
, pause
). Такая программа при чтении стандартного входного потока могла бы использовать фрагмент, подобный следующему:
#include
extern int errno;
...
if (read(0, &c, 1) <= 0) /* EOF или прерывание */
if (errno == EINTR) { /* EOF, вызванный прерыванием */
errno = 0; /* устанавливается для следующего раза */
} else { /* настоящий конец файла */
...
}
Очень сложно постоянно следить за тем, как реакция на сигнал комбинируется с выполнением других программ. Предположим, программа ловит сигналы прерывания и располагает средствами (типа " !
"в ed
) для выполнения других программ. Тогда программа могла бы выглядеть так:
if (fork() == 0)
execlp(...);
signal(SIGINT, SIG_IGN); /* родитель игнорирует прерывание */
wait(&status); /* пока потомок не завершился */
Читать дальше
Конец ознакомительного отрывка
Купить книгу