Читаем Linux программирование в примерах полностью

Строки 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()

Файл GLIBC определяет макрос TEMP_FAILURE_RETRY(), который вы можете использовать для инкапсулирования любого системного вызова, который может при неудачном вызове установить errno в EINTR. Его «объявление» следующее:

#include /* 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(), так что обработчик остается установленным, но второй сигнал отличается от первого? Обычно обработчику первого сигнала нужно завершить свою работу до того, как запускается второй, а каждый обработчик сигнала не должен временно игнорировать все прочие возможные сигналы!

Перейти на страницу:

Похожие книги

C++ Primer Plus
C++ Primer Plus

C++ Primer Plus is a carefully crafted, complete tutorial on one of the most significant and widely used programming languages today. An accessible and easy-to-use self-study guide, this book is appropriate for both serious students of programming as well as developers already proficient in other languages.The sixth edition of C++ Primer Plus has been updated and expanded to cover the latest developments in C++, including a detailed look at the new C++11 standard.Author and educator Stephen Prata has created an introduction to C++ that is instructive, clear, and insightful. Fundamental programming concepts are explained along with details of the C++ language. Many short, practical examples illustrate just one or two concepts at a time, encouraging readers to master new topics by immediately putting them to use.Review questions and programming exercises at the end of each chapter help readers zero in on the most critical information and digest the most difficult concepts.In C++ Primer Plus, you'll find depth, breadth, and a variety of teaching techniques and tools to enhance your learning:• A new detailed chapter on the changes and additional capabilities introduced in the C++11 standard• Complete, integrated discussion of both basic C language and additional C++ features• Clear guidance about when and why to use a feature• Hands-on learning with concise and simple examples that develop your understanding a concept or two at a time• Hundreds of practical sample programs• Review questions and programming exercises at the end of each chapter to test your understanding• Coverage of generic C++ gives you the greatest possible flexibility• Teaches the ISO standard, including discussions of templates, the Standard Template Library, the string class, exceptions, RTTI, and namespaces

Стивен Прата

Программирование, программы, базы данных