Linux программирование в примерах
Открытие FIFO с установленным или сброшенным
демонстрирует следующее поведение:O_NONBLOCKopen("/fifо/file", O_RDONLY, mode)Блокируется до открытия FIFO для записи.
open("/fifo/file", O_RDONLY | O_NONBLOCK, mode)Открывает файл, возвращаясь немедленно.
open("/fifo/file", O_WRONLY, mode)Блокирует до открытия FIFO для чтения.
open("/fifo/file", O_WRONLY | O_NONBLOCK, mode)Если FIFO был открыт для чтения, открывает FIFO и немедленно возвращается. В противном случае возвращает ошибку (возвращаемое значение -1 и
установлен вerrno).ENXIOКак описано для обычных каналов, вызов
для FIFO, который больше не открыт для чтения, возвращает конец файла (возвращаемое значение 0). Флагread()в данном случае неуместен. Для пустого канала или FIFO (все еще открытых для записи, но не содержащих данных) все становится интереснее:O_NONBLOCKread(fd, buf, count) и сброшенный O_NONBLOCKФункция
блокируется до тех пор, пока в канал или FIFO не поступят данные.read()read(fd, buf, count) и установленный O_NONBLOCKФункция
немедленно возвращает -1 с установленным вread().errno EAGAINВ заключение, поведение
более сложно. Для обсуждения этого нам нужно сначала представить концепцию атомарной записи. Атомарная запись — это такая запись, при которой все данные записываются целиком, не чередуясь с данными от других записей. POSIX определяет вwrite()константу<unistd.h>. Запись в канал или FIFO данных размером менее или равнымPIPE_BUFбайтов либо успешно завершается, либо блокируется в соответствии с подробностями, которые мы скоро приведем. Минимальным значением дляPIPE_BUFявляетсяPIPE_BUF, что равняется 512. Само значение_POSIX_PIPE_BUFможет быть больше; современные системы GLIBC определяют ее размер в 4096, но в любом случае следует использовать эту именованную константу и не ожидать, чтоPIPE_BUFбудет иметь то же значение на разных системах.PIPE_BUFВо всех случаях для каналов и FIFO
добавляет данные в конец канала. Это происходит от того факта, что у каналов нет файловых смещений: в них нельзя осуществлять поиск.write()Также во всех случаях, как упоминалось, записи размером вплоть до
являются атомарными: данные не перемежаются с данными от других записей. Данные записи размером болееPIPE_BUFбайтов могут перемежаться с данными других записей в произвольных границах. Это последнее означает, что вы не можете ожидать, что каждая порция размеромPIPE_BUFбольшого набора данных будет записана атомарно. УстановкаPIPE_BUFне влияет на это правило.O_NONBLOCKКак и в случае с
, когдаread()не установлен,O_NONBLOCKблокируется до тех пор, пока все данные не будут записаны.write()Наиболее все усложняется, когда установлен
. Канал или FIFO ведут себя следующим образом:O_NONBLOCK
размер ≥ 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• Всегда можно отличить конец файла:
возвращает 0 байтов.read()• Если нет доступных для чтения данных,
либо завершается успешно, либо возвращает указание «нет данных для чтения»:read(), что означает «попытайтесь снова позже».EAGAIN• Если для записи нет места,
либо блокируется до успешного завершения (write()сброшен), либо завершается неудачей с ошибкой «в данный момент нет места для записи»:O_NONBLOCK.EAGAIN• Когда место есть, будет записано столько данных, сколько возможно, так что в конечном счете все данные будут переписаны.
Подводя итог, если вы собираетесь использовать неблокирующий ввод/вывод, любой код, который использует
, должен быть способен обработать укороченную запись, когда успешно записан меньший объем данных, чем было затребовано. Устойчивый код в любом случае должен быть написан таким способом: даже в случае обычного файла диск может оказаться заполненным иwrite()сможет записать лишь часть данных.write()Более того, вы должны быть готовы обработать
, понимая, что в этом случае неудачаEAGAINне обязательно означает фатальную ошибку. То же верно для кода, использующего для чтения неблокирующий ввод/вывод: признайте, что и здесьwrite()не является фатальным. (Однако, может стоит подсчитывать число таких отказов, оставив попытки, когда их слишком много.)EAGAINНеблокирующий ввод/вывод действительно усложняет вашу жизнь, в этом нет никакого сомнения. Но для многих приложений он является необходимостью, позволяющей выполнить задание. Снова рассмотрите спулер печати. Демон спулера не может позволить себе находиться в блокирующем
для файла FIFO, которому представлены входящие задания. Он должен иметь также возможность отслеживать запущенные задания и, возможно, периодически проверять состояние печатающих устройств (например, убедиться, что не заело бумагу).read()