Linux программирование в примерах - Роббинс Арнольд
Шрифт:
Интервал:
Закладка:
70 прерывании. Вернуть число действительно прочитанных (записанных) байтов, 0 для EOF
71 или в случае ошибки SAFE_READ_ERROR(SAFE_WRITE_ERROR). */
72 size_t
73 safe_rw(int fd, void const *buf, size_t count)
74 {
75 ssize_t result;
76
77 /* POSIX ограничивает COUNT значением SSIZE_MAX, но мы еще больше ограничиваем его, требуя,
78 чтобы COUNT <= INT_MAX, для избежания ошибки в Tru64 5.1.
79 При уменьшении COUNT сохраняйте указатель файла выровненным по размеру блока.
80 Обратите внимание, что read (write) может быть успешным в любом случае, даже если прочитано (записано)
81 менее COUNT байтов, поэтому вызывающий должен быть готов обработать
82 частичные результаты. */
83 if (count > INT_MAX)
84 count = INT_MAX & -8191;
85
86 do
87 {
88 result = rw(fd, buf, count);
89 }
90 while (result < 0 && IS_EINTR(errno));
91
92 return (size_t) result;
93 }
Строки 57–67 обрабатывают определения, создавая соответствующим образом safe_read() и safe_write() (см. ниже safe_write.c).
Строки 77–84 указывают на разновидность осложнений, возникающих при чтении. Здесь один особый вариант Unix не может обработать значения, превышающие INT_MAX, поэтому строки 83–84 выполняют сразу две операции: уменьшают значение числа, чтобы оно не превышало INT_MAX, и сохраняют его кратным 8192. Последняя операция служит эффективности дисковых операций: выполнение ввода/вывода с кратным основному размеру дискового блока объемом данных более эффективно, чем со случайными размерами данных. Как отмечено в комментарии, код сохраняет семантику read() и write(), где возвращенное число байтов может быть меньше затребованного.
Обратите внимание, что параметр count может и в самом деле быть больше INT_MAX, поскольку count представляет тип size_t, который является беззнаковым (unsigned). INT_MAX является чистым int, который на всех современных системах является знаковым.
Строки 86–90 представляют действительный цикл, повторно осуществляющий операцию, пока она завершается ошибкой EINTR. Макрос IS_EINTR() не показан, но он обрабатывает случай в системах, на которых EINTR не определен. (Должен быть по крайней мере один такой случай, иначе код не будет возиться с установкой макроса; возможно, это было сделано для эмуляции Unix или POSIX в не-Unix системе.) Вот safe_write.c:
1 /* Интерфейс write для повторного запуска после прерываний.
2 Copyright (С) 2002 Free Software Foundation, Inc.
/* ...куча шаблонного материала опущена... */
17
18 #define SAFE_WRITE
19 #include "safe-read.с"
В строке 18 #define определяет SAFE_WRITE; это связано со строками 57–60 в safe_read.с.
10.4.4.2. Только GLIBC: TEMP_FAILURE_RETRY()
Файл <unistd.h> GLIBC определяет макрос TEMP_FAILURE_RETRY(), который вы можете использовать для инкапсулирования любого системного вызова, который может при неудачном вызове установить errno в EINTR. Его «объявление» следующее:
#include <unistd.h> /* GLIBC */
long int TEMP_FAILURE_RETRY(expression);
Вот определение макроса:
/* Оценить EXPRESSION и повторять, пока оно возвращает -1 с 'errno',
установленным в EINTR. */
# define TEMP_FAILURE_RETRY(expression)
(__extension__
({ long int __result;
do __result = (long int)(expression);
while (__result == -1L && errno == EINTR);
__result; }))
Макрос использует расширение GCC к языку С (как обозначено ключевым словом __extension__), которое допускает заключенным в фигурные скобки внутри обычных скобок выражениям возвращать значение, действуя таким образом подобно простому выражению.
Используя этот макрос, мы могли бы переписать safe_read() следующим образом:
size_t safe_read(int fd, void const *buf, size_t count) {
ssize_t result;
/* Ограничить count, как в ранее приведенном комментарии. */
if (count > INT_MAX)
count = INT_MAX & ~8191;
result = TEMP_FAILURE_RETRY(read(fd, buf, count));
return (size_t)result;
}
10.4.5. Состояния гонок и sig_atomic_t (ISO C)
Пока обработка одного сигнала за раз выглядит просто: установка обработчика сигнала в main() и (не обязательная) переустановка самого себя обработчиком сигнала (или установка действия SIG_IGN) в качестве первого действия обработчика.
Но что произойдет, если возникнут два идентичных сигнала, один за другим? В частности, что, если ваша система восстановит действие по умолчанию для вашего сигнала, а второй сигнал появится после вызова обработчика, но до того, как он себя восстановит?
Или предположим, что вы используете bsd_signal(), так что обработчик остается установленным, но второй сигнал отличается от первого? Обычно обработчику первого сигнала нужно завершить свою работу до того, как запускается второй, а каждый обработчик сигнала не должен временно игнорировать все прочие возможные сигналы!