Брайан Керниган - UNIX — универсальная среда программирования
- Название:UNIX — универсальная среда программирования
- Автор:
- Жанр:
- Издательство:Финансы и статистика
- Год:1992
- Город:Москва
- ISBN:5-289-00253-4
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Брайан Керниган - UNIX — универсальная среда программирования краткое содержание
В книге американских авторов — разработчиков операционной системы UNIX — блестяще решена проблема автоматизации деятельности программиста, системной поддержки его творчества, выходящей за рамки языков программирования. Профессионалам открыт богатый "встроенный" арсенал системы UNIX. Многочисленными примерами иллюстрировано использование языка управления заданиями shell.
Для программистов-пользователей операционной системы UNIX.
UNIX — универсальная среда программирования - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
#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); /* пока потомок не завершился */
signal(SIGINT, onintr); /* восстанавливает прерывания */
Почему? Сигналы посылаются всем вашим процессам. Предположим, программа, которую вы вызвали, ловит свои собственные сигналы прерывания, как это делает редактор. Если вы прервете выполнение подпрограммы, она получит сигнал, вернется к своему главному циклу и, возможно, начнет читать с вашего терминала. Но вызывающая программа также перейдет от wait
к подпрограмме и будет читать с терминала. Два процесса, читающие с вашего терминала, создадут трудную ситуацию, так как в результате системе придется гадать, к кому попадет та или иная строка входного потока. Решение состоит в том, чтобы родительская программа игнорировала прерывания, пока не завершился процесс-потомок. Это решение нашло свое отражение при обработке сигнала в system
:
#include
system(s) /* run command line s */
char *s;
{
int status, pid, w, tty;
int (*istat)(), (*qstat)();
...
if ((pid = fork()) == 0) {
...
execlp("sh", "sh", "-c", s, (char*)0);
exit(127);
}
...
istat = signal(SIGINT, SIG_IGN);
qstat = signal(SIGQUIT, SIG_IGN);
while ((w = wait(&status)) != pid && w != -1);
if (w == -1)
status = -1;
signal(SIGINT, istat);
signal(SIGQUIT, qstat);
return status;
}
Несколько слов по поводу описаний: функция signal
, очевидно, имеет довольно странный второй аргумент. Фактически он представляет собой указатель на функцию, поставляющую целое значение, и в то же время это тип самой подпрограммы сигнала. Две величины, SIG_IGN
и SIG_DFL
, имеют правильный тип, но выбраны так, что не совпадают ни с одной из существующих функции. Для любознательных покажем, как они определены для PDP-11 и VAX: определения, видимо, достаточно "неуклюжи", чтобы стимулировать использование .
#define SIG_DFL (int(*)())0
#define SIG_IGM (int(*)())1
Системный вызов alarm(n)
обеспечивает посылку сигнала SIGALRM
вашему процессу через n секунд. Сигнал будильника может быть использован для того, чтобы удостовериться в возникновении каких-то событий за соответствующее время. Если что-нибудь произошло, сигнал будильника может быть выключен; в противном случае процесс может получить управление, перехватив этот сигнал.
Для иллюстрации приведем программу timeout
, которая запускает другую команду; если команда не закончила свое выполнение за определенное время, она будет завершена по звонку будильника. Например, вспомните команду watchfor
из гл. 5. Вместо того чтобы запускать ее без ограничения времени работы, установите ограничение в часах:
$ timeout -3600 watchfor dmg &
Программа timeout
демонстрирует почти все возможности, которые мы обсуждали в последних двух разделах. Создан процесс-потомок, родительский процесс устанавливает будильник и затем ждет, пока потомок завершит работу. Если будильник "зазвенел" первым, потомок уничтожается. Делается попытка вернуть состояние потомка при выходе.
/* timeout: set time limit on a process */
Интервал:
Закладка: