9.4.3.4. Неблокирующий ввод/вывод для каналов и FIFO

9.4.3.4. Неблокирующий ввод/вывод для каналов и FIFO

Ранее для описания способа работы каналов мы использовали сравнение с двумя людьми, моющими и вытирающими тарелки с использованием сушилки; когда сушилка заполняется, останавливается моющий, а когда она пустеет, останавливается вытирающий. Это блокирующее поведение: производитель или потребитель блокируются в вызове write() или read(), ожидая либо освобождения канала, либо появления в нем данных.

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

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

С функцией 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, которому представлены входящие задания. Он должен иметь также возможность отслеживать запущенные задания и, возможно, периодически проверять состояние печатающих устройств (например, убедиться, что не заело бумагу).

Поделитесь на страничке

Следующая глава >

Похожие главы из других книг

Ввод/Вывод

Из книги Давайте создадим компилятор! автора Креншоу Джек


2.2.3.2 Асинхронный ввод-вывод

Из книги Руководство администратора баз данных Informix. автора Кустов Виктор

2.2.3.2 Асинхронный ввод-вывод Для ускорения операций ввода-вывода сервер использует собственный пакет асинхронного ввода-вывода (AIO) или пакет асинхронного ввода-вывода ядра ОС (KAIO), если он доступен. Пользовательские запросы на ввод-вывод обрабатываются асинхронно,


Ввод/вывод

Из книги Microsoft Visual C++ и MFC. Программирование для Windows 95 и Windows NT автора Фролов Александр Вячеславович

Ввод/вывод Как вы знаете, операторы << и >> выполняют сдвиг числового значения влево и вправо на опеределенное число бит. В программах, приведенных в нашей книге, эти операторы также используются для ввода информации с клавиатуры и вывода на экран.Если с левой стороны


Ввод и вывод

Из книги Основы объектно-ориентированного программирования автора Мейер Бертран

Ввод и вывод Два класса библиотеки KERNEL обеспечивают основные средства ввода и вывода: FILE и STD_FILES.Среди операций, определенных для объекта f типа FILE, есть следующие:create f.make ("name") -- Связывает f с файлом по имени name.f.open_write -- Открытие f для записиf.open_read -- Открытие f для


4.11. Ограничения программных каналов и FIFO

Из книги UNIX: взаимодействие процессов автора Стивенс Уильям Ричард

4.11. Ограничения программных каналов и FIFO На программные каналы и каналы FIFO системой накладываются всего два ограничения:? OPEN_MAX — максимальное количество дескрипторов, которые могут быть одновременно открыты некоторым процессом (Posix устанавливает для этой величины


Перекрывающийся ввод/вывод

Из книги Системное программирование в среде Windows автора Харт Джонсон М

Перекрывающийся ввод/вывод Первое, что необходимо сделать для организации асинхронного ввода/вывода, будь то перекрывающегося или расширенного, — это установить атрибут перекрывания (overlapped attribute) для файлового или иного дескриптора. Для этого при вызове CreateFile или иной


Глава 6 Ввод и вывод

Из книги Программирование на языке Пролог для искусственного интеллекта автора Братко Иван

Глава 6 Ввод и вывод В этой главе мы рассмотрим некоторые встроенные средства для записи данных в файл и считывания их из файла. Такие средства можно также применять и для форматирования объектов данных программы, чтобы получить желаемую форму их внешнего представления.


Ввод и вывод

Из книги Язык программирования Си для персонального компьютера автора Бочков C. О.

Ввод и вывод Функции ввода и вывода в стандартной библиотеке Си позволяют читать данные из файлов или получать их с устройств ввода (например, с клавиатуры) и записывать данные в файлы, или выводить их на различные устройства (например, на принтер).Функции ввода/вывода


4.4. Ввод и вывод

Из книги Linux программирование в примерах автора Роббинс Арнольд

4.4. Ввод и вывод Все операции Linux по вводу/выводу осуществляются посредством дескрипторов файлов. Данный раздел знакомит с дескрипторами файлов, описывает, как их получать и освобождать, и объясняет, как выполнять с их помощью


2.1.4. Стандартный ввод-вывод

Из книги Программирование для Linux. Профессиональный подход автора Митчелл Марк

2.1.4. Стандартный ввод-вывод В стандартной библиотеке языка С определены готовые потоки ввода и вывода (stdin и stdout соответственно). Они используются функциями scanf(), printf() и целым рядом других библиотечных функций. Согласно идеологии UNIX, стандартные потоки можно


11. ввод/вывод

Из книги Основы программирования на Java автора Сухов С. А.


17.5.1. Виртуальный ввод/вывод

Из книги C++ для начинающих автора Липпман Стенли

17.5.1. Виртуальный ввод/вывод Первая виртуальная операция, которую мы хотели реализовать, - это печать запроса на стандартный вывод либо в файл:ostream& print( ostream &os = cout ) const;Функцию print() следует объявить виртуальной, поскольку ее реализации зависят от типа, но нам нужно


20.6. Файловый ввод/вывод

Из книги Идеальный программист. Как стать профессионалом разработки ПО автора Мартин Роберт С.


Ввод и вывод

Из книги автора

Ввод и вывод Также мне кажется очень важным, чтобы мои результаты подпитывались соответствующим «вводом». Написание программного кода – творческая работа. Обычно мои творческие способности в наибольшей степени проявляются тогда, когда я сталкиваюсь с творческим