Linux программирование в примерах
70 прерывании. Вернуть число действительно прочитанных (записанных) байтов, 0 для EOF71 или в случае ошибки SAFE_READ_ERROR(SAFE_WRITE_ERROR). */72 size_t73 safe_rw(int fd, void const *buf, size_t count)74 {75 ssize_t result;7677 /* 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;8586 do87 {88 result = rw(fd, buf, count);89 }90 while (result < 0 && IS_EINTR(errno));9192 return (size_t) result;93 }Строки 57–67 обрабатывают определения, создавая соответствующим образом
иsafe_read()(см. нижеsafe_write()).safe_write.cСтроки 77–84 указывают на разновидность осложнений, возникающих при чтении. Здесь один особый вариант Unix не может обработать значения, превышающие
, поэтому строки 83–84 выполняют сразу две операции: уменьшают значение числа, чтобы оно не превышалоINT_MAX, и сохраняют его кратным 8192. Последняя операция служит эффективности дисковых операций: выполнение ввода/вывода с кратным основному размеру дискового блока объемом данных более эффективно, чем со случайными размерами данных. Как отмечено в комментарии, код сохраняет семантикуINT_MAXиread(), где возвращенное число байтов может быть меньше затребованного.write()Обратите внимание, что параметр
может и в самом деле быть большеcount, поскольку count представляет типINT_MAX, который является беззнаковым (unsigned).size_tявляется чистымINT_MAX, который на всех современных системах является знаковым.intСтроки 86–90 представляют действительный цикл, повторно осуществляющий операцию, пока она завершается ошибкой
. МакросEINTRне показан, но он обрабатывает случай в системах, на которыхIS_EINTR()не определен. (Должен быть по крайней мере один такой случай, иначе код не будет возиться с установкой макроса; возможно, это было сделано для эмуляции Unix или POSIX в не-Unix системе.) ВотEINTR:safe_write.c1 /* Интерфейс write для повторного запуска после прерываний.2 Copyright (С) 2002 Free Software Foundation, Inc./* ...куча шаблонного материала опущена... */1718 #define SAFE_WRITE19 #include "safe-read.с"В строке 18
определяет#define; это связано со строками 57–60 вSAFE_WRITE.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. Состояния гонок и
(ISO C)sig_atomic_tПока обработка одного сигнала за раз выглядит просто: установка обработчика сигнала в
и (не обязательная) переустановка самого себя обработчиком сигнала (или установка действияmain()) в качестве первого действия обработчика.SIG_IGNНо что произойдет, если возникнут два идентичных сигнала, один за другим? В частности, что, если ваша система восстановит действие по умолчанию для вашего сигнала, а второй сигнал появится после вызова обработчика, но до того, как он себя восстановит?
Или предположим, что вы используете
, так что обработчик остается установленным, но второй сигнал отличается от первого? Обычно обработчику первого сигнала нужно завершить свою работу до того, как запускается второй, а каждый обработчик сигнала не должен временно игнорировать все прочие возможные сигналы!bsd_signal()