Ограничение PIPE_BUF определяет то, когда именно данные будут переданы в канал. При записи не более PIPE_BUF байт вызов write() при необходимости может заблокироваться, пока в канале не освободится достаточно места для автоматического выполнения этой операции. Если записываемый блок данных превышает PIPE_BUF байт, то вызов write() передает ту их часть, которая полностью заполнит канал, и затем блокируется до тех пор, пока данные не будут удалены из канала каким-нибудь считывающим процессом. При прерывании такой операции обработчиком сигнала вызов write() возобновляет работу и возвращает количество успешно переданных байтов (которое будет меньше запрашиваемого объема — так называемая
Канал — это всего лишь буфер, находящийся в памяти ядра. И его максимальная вместимость ограничена. Если канал заполнен, то все дальнейшие операции записи в него блокируются, пока считывающий процесс не удалит из него часть данных.
В стандарте SUSv3 нет никаких требований относительно вместимости каналов. В версиях ядра Linux, предшествовавших 2.6.11, вместимость была равна размеру страницы памяти в системе (например, 4096 байт на платформе x86-32); но, начиная с версии 2.6.11, канал может хранить в себе до 55 536 байт. Это ограничение отличается в других реализациях UNIX.
В общем случае приложению не нужно знать точную вместимость канала. Если нужно предотвратить блокировку записывающих процессов, то процессы, считывающие данные, должны изымать содержимое канала по мере его поступления.
Теоретически нет никаких причин, по которым канал не мог бы работать с меньшей вместимостью, даже если это однобайтный буфер. Увеличение размера продиктовано требованиями к производительности: каждый раз, когда записывающий процесс заполняет канал, ядро вынуждено переключать контекст, чтобы считывающий процесс мог извлечь данные из канала. Чем больше буфер, тем реже приходится переключать контекст.
Начиная с Linux 2.6.35, вместимость канала можно менять. Вызов fcntl(fd, F_SETPIPE_SZ, size) (доступен только в Linux) назначает каналу fd размер size байт. Непривилегированный процесс может установить для канала любую вместимость в диапазоне от размера страницы памяти и до значения, хранящегося в файле /proc/sys/fs/pipe-max-size. По умолчанию значение pipe-max-size равно 1 048 576 байт. Привилегированный процесс (CAP_SYS_RESOURCE) может переопределить это ограничение. Выделяя место для канала, Linux может округлить size до значения, которое система находит более подходящим. Вызов fcntl(fd, F_GETPIPE_SZ) возвращает фактический размер выделенного канала.
Системный вызов pipe() создает новый канал.
include
int pipe(int
Возвращает 0 при успешном завершении или -1 при ошибке
Успешный вызов pipe() возвращает массив filedes с двумя открытыми файловыми дескрипторами: один для чтения (filedes[0]), второй — для записи (filedes[1]).
Как и в случае с любыми другими дескрипторами, для выполнения ввода/вывода в контексте канала можно использовать системные вызовы read() и write(). Данные, записанные в канал, немедленно становятся доступными для чтения на другом его конце. Операция чтения извлекает из канала количество байтов, не больше запрошенного, но не меньше того, которое сейчас доступно (если канал пустой, операция блокируется).
Для работы с каналом также можно применять функции стандартного ввода/вывода (printf(), scanf() и т. д.), предварительно получив файловый поток из одного из дескрипторов в массиве filedes (см. раздел 13.7), задействуя вызов fdopen(). Но при этом следует учитывать потенциальные проблемы с буферизацией ввода/вывода, описанные в разделе 44.6.
Вызов ioctl(fd, FIONREAD, &cnt) возвращает количество непрочитанных байтов в канале или очереди FIFO, на которые ссылается дескриптор fd. Он доступен и в ряде других реализаций, но не описан в стандарте SUSv3.
На рис. 44.2 изображен канал, созданный с помощью вызова pipe(). Каждый конец данного канала доступен вызывающему процессу в виде файловых дескрипторов.
Рис. 44.2.
В рамках одного процесса канал можно применять несколькими способами (один из них будет рассмотрен в подразделе 59.5.2). Обычно канал используется для обеспечения взаимодействия двух процессов. Чтобы их соединить с помощью канала, вслед за pipe() делается вызов fork(). Во время его выполнения потомок наследует копии файловых дескрипторов родителя (см. раздел 24.2.1), что приводит к ситуации, представленной на рис. 44.3,