Категории
Самые читаемые
ChitatKnigi.com » 🟢Компьютеры и Интернет » Интернет » UNIX — универсальная среда программирования - Керниган Брайан Уилсон

UNIX — универсальная среда программирования - Керниган Брайан Уилсон

Читать онлайн UNIX — универсальная среда программирования - Керниган Брайан Уилсон
1 ... 94 95 96 97 98 99 100 101 102 ... 187
Перейти на страницу:

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать

Решение, показанное выше, состоит в том, чтобы проверить состояние обработки прерываний, если они игнорировались ранее. Функции программы в том виде, в каком она написана, зависят от возвращаемого 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("nInterruptn");

 longjmp(sjbuf, 0); /* вернуться

                       в сохраненное состояние */

}

Файл <setjmp.h> описывает тип jmp_buf как объект, в котором сохраняется позиция стека; sjbuf считается таким объектом. Функция setjmp(3) сохраняет запись о том, где выполняется программа. Значения переменных не сохраняются. Когда происходит прерывание, выполняется обращение к подпрограмме onintr, которая может печатать сообщения, устанавливать флаги и т.д. Функция longjmp берет в качестве аргумента объект, сохраненный setjmp, и возвращает управление в ячейку после вызова setjmp. Поэтому управление (и значение уровня стека) будет возвращено обратно в основную программу — ко входу в головной цикл.

Отметим, что после прерывания сигнал вновь настраивается на onintr. Это обусловлено тем, что когда сигналы возникают, они автоматически настраиваются на реакцию по умолчанию.

Некоторые программы, которые "хотят" обнаружить сигналы, просто не могут быть остановлены в произвольный момент, например в середине обновления сложных составных данных. Решение состоит в том, что подпрограмма обработки прерывания должна установить флаг и вернуться к месту вызова exit или longjmp. Выполнение программы продолжится точно с того места, где оно было прервано, а флаг прерывания будет проверен позднее.

С этим подходом связана одна трудность. Предположим, что, когда посылается сигнал прерывания, программа читается с терминала. Описанная подпрограмма непременно вызывается; она устанавливает свой флаг и возвращается. Если бы, как отмечалось выше, было верно то, что выполнение возобновляется точно с того места, где оно прервалось, программа продолжала бы чтение с терминала до ввода пользователем другой строки. Однако здесь возникает недоразумение, поскольку пользователь может не знать, что программа читает, и предположительно предпочел бы, чтобы сигнал сразу оказал действие. Для разрешения проблемы система должна закончить read, но с сообщением об ошибке, указывающим, что произошло: errno присваивается EINTR, определенное в заголовке <errno.h>, чтобы обозначить прерванный системный вызов.

Так, программы, которые "ловят" сигналы и продолжают после этого свою работу, должны быть готовы к появлению ошибок, вызванных прерванными системными вызовами. (Следует остерегаться системных вызовов read — чтение с терминала, wait, pause). Такая программа при чтении стандартного входного потока могла бы использовать фрагмент, подобный следующему:

#include <errno.h>

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); /* пока потомок не завершился */

signal(SIGINT, onintr); /* восстанавливает прерывания */

Почему? Сигналы посылаются всем вашим процессам. Предположим, программа, которую вы вызвали, ловит свои собственные сигналы прерывания, как это делает редактор. Если вы прервете выполнение подпрограммы, она получит сигнал, вернется к своему главному циклу и, возможно, начнет читать с вашего терминала. Но вызывающая программа также перейдет от wait к подпрограмме и будет читать с терминала. Два процесса, читающие с вашего терминала, создадут трудную ситуацию, так как в результате системе придется гадать, к кому попадет та или иная строка входного потока. Решение состоит в том, чтобы родительская программа игнорировала прерывания, пока не завершился процесс-потомок. Это решение нашло свое отражение при обработке сигнала в system:

1 ... 94 95 96 97 98 99 100 101 102 ... 187
Перейти на страницу:
Открыть боковую панель
Комментарии
Jonna
Jonna 02.01.2025 - 01:03
Страстно🔥 очень страстно
Ксения
Ксения 20.12.2024 - 00:16
Через чур правильный герой. Поэтому и остался один
Настя
Настя 08.12.2024 - 03:18
Прочла с удовольствием. Необычный сюжет с замечательной концовкой
Марина
Марина 08.12.2024 - 02:13
Не могу понять, где продолжение... Очень интересная история, хочется прочесть далее
Мприна
Мприна 08.12.2024 - 01:05
Эх, а где же продолжение?