Linux программирование в примерах
/* Обработка прерываний и отсоединений. Упрощена для представления */static void sighandler(int sig) {signal(sig, SIG_IGN); /* Отныне этот сигнал игнорировать */cleanup(); /* Очистка после себя */signal(sig, SIG_DFL); /* Восстановление действия по умолчанию */raise(sig); /* Повторно отправить сигнал */}Установка действия
гарантирует, что все последующие появляющиеся сигналыSIG_IGNне повлияют на продолжающийся процесс очистки. Когда функцияSIGINTзавершит работу, восстановление действияcleanup()позволяет системе сделать снимок образа процесса, если это нужно возникшему сигналу. ВызовSIG_DFLвосстанавливает сигнал. Затем восстановленный сигнал вызывает действие по умолчанию, которое, скорее всего, завершит программу. (Далее в этой главе мы полностью покажем обработчик сигналаraise().)sort.c10.4.4. Системные вызовы, допускающие повторный запуск
Значение
дляEINTR(см. раздел 4.3 «Определение ошибок») указывает, что системный вызов был прерван. Хотя с этим значением ошибки может завершаться большое количество системных вызовов, двумя наиболее значительными являютсяerrnoиread(). Рассмотрите следующий код:write()void handler(int signal) { /* обработка сигналов */ }int main(int argc, char **argv) {signal(SIGINT, handler);...while ((count = read(fd, buf, sizeof buf)) > 0) {/* Обработка буфера */}if (count == 0)/* конец файла, очистка и т.п. */else if (count == -1)/* ошибка */...}Предположим, что система успешно прочла (и заполнила) часть буфера, когда возник
. Системный вызовSIGINTеще не вернулся из ядра в программу, но ядро решает, что оно может доставить сигнал. Вызываетсяread(), запускается и возвращается в серединуhandler(). Что возвратитread()?read()В былые времена (V7, более ранние системы System V)
возвратила бы -1 и установила быread()равнымerrno. Не было способа сообщить, что данные были переданы. В данном случае V7 и System V действуют, как если бы ничего не случилось: не было перемещений данных в и из буфера пользователя, и смещение файла не было изменено. BSD 4.2 изменила это. Были два случая:EINTRМедленные устройства
«Медленное устройство» является в сущности терминалом или почти всяким другим устройством, кроме обычного файла. В этом случае
могла завершиться с ошибкойread(), лишь если не было передано никаких данных, когда появился сигнал. В противном случае системный вызов был бы запущен повторно, иEINTRвозвратилась бы нормально.read()Обычные файлы
Системный вызов был бы запущен повторно В этом случае
вернулась бы нормально; возвращенное значение могло быть либо числом запрошенных байтов, либо числом действительно прочитанных байтов (как в случае чтения вблизи конца файла).read()Поведение BSD несомненно полезно; вы всегда можете сказать, сколько данных было прочитано.
Поведение POSIX сходно, но не идентично первоначальному поведению BSD. POSIX указывает, что
[108] завершается с ошибкойread()лишь в случае появления сигнала до начала перемещения данных. Хотя POSIX ничего не говорит о «медленных устройствах», на практике это условие проявляется именно на них.EINTRВ противном случае, если сигнал прерывает частично выполненную
, возвращенное значение является числом уже прочитанных байтов. По этой причине (а также для возможности обработки коротких файлов) всегда следует проверять возвращаемоеread()значение и никогда не предполагать, что прочитано все запрошенное количество байтов. (Функция POSIX APIread(), описанная позже, позволяет при желании получить поведение повторно вызываемых системных вызовов BSD.)sigaction()10.4.4.1. Пример: GNU Coreutils
иsafe_read()safe_write()Для обработки случая EINTR в традиционных системах GNU Coreutils использует две функции,
иsafe_read(). Код несколько запутан из-за того, что один и тот же файл за счет включения #include и макросов реализует обе функции. Из файлаsafe_write()в дистрибутиве Coreutils:lib/safe-read.c1 /* Интерфейс read и write для .повторных запусков после прерываний.2 Copyright (С) 1993, 1994, 1998, 2002 Free Software Foundation, Inc./* ... куча шаблонного материала опущена... */5657 #ifdef SAFE_WRITE58 # include "safe-write.h"59 # define safe_rw safe_write /* Создание safe_write() */60 # define rw write /* Использование системного вызова write() */61 #else62 # include "safe-read.h"63 # define safe_rw safe_read /* Создание safe_read() */64 # define rw read /* Использование системного вызова read() */65 # undef const66 # define const /* пусто */67 #endif6869 /* Прочесть (записать) вплоть до COUNT байтов в BUF из(в) дескриптора FD, повторно запуская вызов при