read(fd, buf, count) и сброшенный O_NONBLOCK
Функция read() блокируется до тех пор, пока в канал или FIFO не поступят данные.
read(fd, buf, count) и установленный O_NONBLOCK
Функция read() немедленно возвращает -1 с установленным в errno EAGAIN.
В заключение, поведение write() более сложно. Для обсуждения этого нам нужно сначала представить концепцию атомарной записи. Атомарная запись — это такая запись, при которой все данные записываются целиком, не чередуясь с данными от других записей. POSIX определяет в <unistd.h> константу PIPE_BUF. Запись в канал или FIFO данных размером менее или равным PIPE_BUF байтов либо успешно завершается, либо блокируется в соответствии с подробностями, которые мы скоро приведем. Минимальным значением для PIPE_BUF является _POSIX_PIPE_BUF, что равняется 512. Само значение PIPE_BUF может быть больше; современные системы GLIBC определяют ее размер в 4096, но в любом случае следует использовать эту именованную константу и не ожидать, что PIPE_BUF будет иметь то же значение на разных системах.
Во всех случаях для каналов и FIFO write() добавляет данные в конец канала. Это происходит от того факта, что у каналов нет файловых смещений: в них нельзя осуществлять поиск.
Также во всех случаях, как упоминалось, записи размером вплоть до PIPE_BUF являются атомарными: данные не перемежаются с данными от других записей. Данные записи размером более PIPE_BUF байтов могут перемежаться с данными других записей в произвольных границах. Это последнее означает, что вы не можете ожидать, что каждая порция размером PIPE_BUF большого набора данных будет записана атомарно. Установка O_NONBLOCK не влияет на это правило.
Как и в случае с read(), когда O_NONBLOCK не установлен, write() блокируется до тех пор, пока все данные не будут записаны.
Наиболее все усложняется, когда установлен O_NONBLOCK. Канал или FIFO ведут себя следующим образом:
| размер ≥ nbytes | размер < abytes | |
|---|---|---|
nbytes ≤ PIPE_BUF |
write() успешна |
write() возвращает (-1)/EAGAIN |
| размер > 0 | размер = 0 | |
nbytes > PIPE_BUF |
write() записывает, что может |
write() возвращает (-1)/EAGAIN |
Для файлов, не являющихся каналами и FIFO и к которым может быть применен O_NONBLOCK, поведение следующее:
размер > 0 write() записывает, что может
размер = 0 write() возвращает -1/EAGAIN
Хотя есть ряд сбивающих с толку изменений поведения в зависимости от того, канал это или не канал, установлен O_NONBLOCK или сброшен, есть в канале место для записи или нет, а также в зависимости от размера предполагаемой записи, эти правила предназначены для упрощения программирования:
• Всегда можно отличить конец файла: read() возвращает 0 байтов.
• Если нет доступных для чтения данных, read() либо завершается успешно, либо возвращает указание «нет данных для чтения»: EAGAIN, что означает «попытайтесь снова позже».
• Если для записи нет места, write() либо блокируется до успешного завершения (O_NONBLOCK сброшен), либо завершается неудачей с ошибкой «в данный момент нет места для записи»: EAGAIN.
• Когда место есть, будет записано столько данных, сколько возможно, так что в конечном счете все данные будут переписаны.
Подводя итог, если вы собираетесь использовать неблокирующий ввод/вывод, любой код, который использует write(), должен быть способен обработать укороченную запись, когда успешно записан меньший объем данных, чем было затребовано. Устойчивый код в любом случае должен быть написан таким способом: даже в случае обычного файла диск может оказаться заполненным и write() сможет записать лишь часть данных.
Более того, вы должны быть готовы обработать EAGAIN, понимая, что в этом случае неудача write() не обязательно означает фатальную ошибку. То же верно для кода, использующего для чтения неблокирующий ввод/вывод: признайте, что и здесь EAGAIN не является фатальным. (Однако, может стоит подсчитывать число таких отказов, оставив попытки, когда их слишком много.)
Неблокирующий ввод/вывод действительно усложняет вашу жизнь, в этом нет никакого сомнения. Но для многих приложений он является необходимостью, позволяющей выполнить задание. Снова рассмотрите спулер печати. Демон спулера не может позволить себе находиться в блокирующем read() для файла FIFO, которому представлены входящие задания. Он должен иметь также возможность отслеживать запущенные задания и, возможно, периодически проверять состояние печатающих устройств (например, убедиться, что не заело бумагу).
9.4.3.5. Сводка fcntl()
Сводка для системного вызова fcntl() приведена в табл. 9.5.
Таблица 9.5. Сводка fcntl()
Значение cmd |
Значение arg |
Возвращает |
|---|---|---|
F_DUPFD |
Наименьший новый дескриптор | Дублирует аргумент fd |
F_GETFD |
Получает флаги дескриптора файла (close-on-exec) | |
F_SETFD |
Новое значение флага | Устанавливает флаги дескриптора файла (close-on-exec) |
F_GETFL |
Получает флаги основного файла | |
F_SETFL |
Новое значение флага | Устанавливает флаги основного файла |
Флаги создания, статуса и прав доступа файла копируются, когда дескриптор файла дублируется. Флаг close-on-exec не копируется.
9.5. Пример: двусторонние каналы в gawk