Решение, показанное выше, состоит в том, чтобы проверить состояние обработки прерываний, если они игнорировались ранее. Функции программы в том виде, в каком она написана, зависят от возвращаемого
signalБолее сложная программа может перехватить прерывание и интерпретировать его как запрос на прекращение своих действий и возврат к основному циклу обработки команд. Подумаем о текстовом редакторе: прерывание длинного вывода на печать не должно вызывать завершения редактирования и потерю уже отредактированного текста. Программа для такого случая может быть написана следующим образом:
#include <signal.h>#include <setjmp.h>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); /* вернуться в сохраненное состояние */}Файл
<setjmp.h>jmp_bufsjbufsetjmp(3)onintrlongjmpsetjmpsetjmpОтметим, что после прерывания сигнал вновь настраивается на
onintrНекоторые программы, которые "хотят" обнаружить сигналы, просто не могут быть остановлены в произвольный момент, например в середине обновления сложных составных данных. Решение состоит в том, что подпрограмма обработки прерывания должна установить флаг и вернуться к месту вызова
exitlongjmpС этим подходом связана одна трудность. Предположим, что, когда посылается сигнал прерывания, программа читается с терминала. Описанная подпрограмма непременно вызывается; она устанавливает свой флаг и возвращается. Если бы, как отмечалось выше, было верно то, что выполнение возобновляется точно с того места, где оно прервалось, программа продолжала бы чтение с терминала до ввода пользователем другой строки. Однако здесь возникает недоразумение, поскольку пользователь может не знать, что программа читает, и предположительно предпочел бы, чтобы сигнал сразу оказал действие. Для разрешения проблемы система должна закончить
readerrnoEINTR<errno.h>Так, программы, которые "ловят" сигналы и продолжают после этого свою работу, должны быть готовы к появлению ошибок, вызванных прерванными системными вызовами. (Следует остерегаться системных вызовов
readwaitpause#include <errno.h>extern int errno;...if (read(0, &c, 1) <= 0) /* EOF или прерывание */ if (errno == EINTR) { /* EOF, вызванный прерыванием */ errno = 0; /* устанавливается для следующего раза */ } else { /* настоящий конец файла */ ... }Очень сложно постоянно следить за тем, как реакция на сигнал комбинируется с выполнением других программ. Предположим, программа ловит сигналы прерывания и располагает средствами (типа "
!edif (fork() == 0) execlp(...);signal(SIGINT, SIG_IGN); /* родитель игнорирует прерывание */wait(&status); /* пока потомок не завершился */signal(SIGINT, onintr); /* восстанавливает прерывания */Почему? Сигналы посылаются всем вашим процессам. Предположим, программа, которую вы вызвали, ловит свои собственные сигналы прерывания, как это делает редактор. Если вы прервете выполнение подпрограммы, она получит сигнал, вернется к своему главному циклу и, возможно, начнет читать с вашего терминала. Но вызывающая программа также перейдет от
waitsystem