10.4.4.1. Пример: GNU Coreutils safe_read() и safe_write()

10.4.4.1. Пример: GNU Coreutils safe_read() и safe_write()

Для обработки случая EINTR в традиционных системах GNU Coreutils использует две функции, safe_read() и safe_write(). Код несколько запутан из-за того, что один и тот же файл за счет включения #include и макросов реализует обе функции. Из файла lib/safe-read.c в дистрибутиве Coreutils:

1  /* Интерфейс read и write для .повторных запусков после прерываний.

2     Copyright (С) 1993, 1994, 1998, 2002 Free Software Foundation, Inc.

   /* ... куча шаблонного материала опущена... */

56

57 #ifdef SAFE_WRITE

58 # include "safe-write.h"

59 # define safe_rw safe_write /* Создание safe_write() */

60 # define rw write /* Использование системного вызова write() */

61 #else

62 # include "safe-read.h"

63 # define safe_rw safe_read /* Создание safe_read() */

64 # define rw read /* Использование системного вызова read() */

65 # undef const

66 # define const /* пусто */

67 #endif

68

69 /* Прочесть (записать) вплоть до COUNT байтов в BUF из(в) дескриптора FD, повторно запуская вызов при

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.с.