Чтение онлайн

ЖАНРЫ

Linux программирование в примерах
Шрифт:

В действительности человек, ожидающий опустения или заполнения сушилки, не должен просто неподвижно стоять. [101] Вместо этого незанятый супруг мог бы пойти и найти другую работу по кухне (такую, как подметание всех крошек за детьми на полу), пока сушилка снова не будет готова.

На языке Unix/POSIX эта концепция обозначается термином неблокирующий ввод/вывод, т.е. запрошенный ввод/вывод либо завершается, либо возвращает значение ошибки, указывающее на отсутствие данных (для читающего) или отсутствие места (для записывающего). Неблокирующий ввод/вывод применяется к каналам и FIFO, а не к обычным файлам на диске. Он может применяться также и к определенным устройствам, таким как терминалы, и к сетевым соединениям, обе эти темы выходят за рамки данной книги.

101

Ну,

мы игнорируем мысль, что два супруга могли бы хотеть поговорить друг с другом и насладиться компанией — Примеч. автора.

С функцией

open
может использоваться флаг
O_NONBLOCK
для указания неблокирующего ввода/вывода, он может быть установлен и сброшен с помощью
fcntl
. Для
open
и
read
неблокирующий ввод/вывод прост.

Открытие FIFO с установленным или сброшенным

O_NONBLOCK
демонстрирует следующее поведение:

open("/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
).

Как описано для обычных каналов, вызов

read
для FIFO, который больше не открыт для чтения, возвращает конец файла (возвращаемое значение 0). Флаг
O_NONBLOCK
в данном случае неуместен. Для пустого канала или FIFO (все еще открытых для записи, но не содержащих данных) все становится интереснее:

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)