ГлавнаяarrowСистемное программированиеarrow12. Каналы в UNIX

12. Каналы в UNIX

Система UNIX обеспечивает конструкцию, кото­рая называется программными каналами (pipe). Программный канал (или просто канал) служит для установления односторонний связи, соеди­няющей один процесс с другим, и является еще одним видом обобщенного ввода/вывода системы UNIX. Процесс может посылать данные в ка­нал при помощи системного вызова write, а другой процесс может принимать данные из канала при помощи системного вызова read.
Каналы создаются в программе при помощи системного вызова pipe. В случае удачного завершения вызов сообщает два дескриптора файла: один для записи в канал, а другой для чтения из него.
int pipe(int filedes[2]);
Переменная filedes является массивом из двух целых чисел, который будет содержать дескрипторы файлов, обозначающие канал. После успешного вызова filedes[0] будет открыт для чтения из канала, a filedes[1] для записи в канал.В случае неудачи вызов pipe вернет значение -1.
После создания канала с ним можно работать просто при помощи вызовов read и write.
Сообщения считываются в том же порядке, в каком они были записаны. Каналы обращаются с данными в порядке «первый вошел - первым вышел» (first-in first-out, или сокращенно FIFO). lseek не работает с каналами.
Размеры блоков при записи в канал и чтении из него необязательно должны быть одинаковыми.
Настоящее значение каналов проявляется при использовании вместе с системным вызовом fork, тогда можно воспользоваться тем фактом, что файловые дескрипторы остаются открытыми в обоих процессах.
В таком случае канал соединяет два процесса. И в родительском, и в дочернем процес­сах открыто по два дескриптора файла, позволяя выполнять запись в канал и чте­ние из него
Важно за­метить, что размер буфера канала конечен. Только определенное число байтов может находиться в канале, прежде чем следующий вызов write будет заблокирован. При программировании важно знать максимальный размер канала для системы, так как он влияет на оба вызова write и read. Если вызов write выполняется для канала, в котором есть свободное место, то данные посыла­ются в канал, и немедленно происходит возврат из вызова. Если же в момент вызо­ва write происходит переполнение канала, то выполнение процесса обычно при­останавливается до тех пор, пока место не освободится в результате выполнения другим процессом чтения из канала.
Обычно вызов write для канала выполняется неделимыми порциями (atomically), и дан­ные передаются ядром за одну непрерываемую операцию.
Взаимодействие вызова read с каналами является более простым. При вы­полнении вызова read система проверяет, является ли канал пустым. Если он пуст, то вызов read будет заблокирован до тех пор, пока другой процесс не за­пишет в канал какие-либо данные. При наличии в канале данных произойдет возврат из вызова read, даже если запрашивается больший объем данных, чем находится в канале.
Для простых приложений применение неблокирующих операций чтения и запи­си работает прекрасно. Для работы с множеством каналов одновременно существует другое решение, которое заключается в использовании системного вызова select.
Родительский процесс может выступить в качестве сер­верного процесса и может иметь произвольное число связанных с ним клиентских (дочерних) процессов. В этом случае серверный процесс должен как-то справляться с ситуацией, когда одновременно в нескольких каналах может находиться информация, ожидающая обработки. Кроме того, если ни в одном из каналов нет ожидающих данных, то мо­жет иметь смысл приостановить работу серверного процесса до их появления, а не опрашивать постоянно каналы. Если информация поступает более чем по одному каналу, то серверный процесс должен знать обо всех таких каналах для того, чтобы работать с ними в правильном порядке (например, согласно их приоритетам).
Это можно сделать при помощи системного вызова select. Системный вызов select используется не только для каналов, но и для обычных файлов, именованных каналов и сокетов. Сис­темный вызов select показывает, какие дескрипторы файлов из заданных наборов готовы для чтения, записи или ожидают обработки ошибок. Иногда серверный про­цесс не должен совсем прекращать работу, даже если не происходит никаких собы­тий, поэтому в вызове select также можно задать предельное время ожидания.
Вызов select будет заблокирован до тех пор, пока не произойдет интересующее процесс событие. Если в структуре timeout задано нулевое время, то вызов select завершится немедленно (без блокирования). Если структура timeout содержит не­нулевое значение, то возврат из вызова select произойдет через заданное число секунд или микросекунд, если файловые дескрипторы неактивны.
Возвращаемое вызовом select значение равно -1 в случае ошибки, нулю - после истечения временного интервала или целому числу, равному числу «инте­ресующих» программу дескрипторов файлов. Следует сделать предостережение: при возврате из вызова select он переустанавливает битовые маски, на которые указывают переменные readfds, writefds или errorfds, сбрасывая маску и сно­ва задавая в ней дескрипторы файлов, содержащие искомую информацию. Поэто­му необходимо сохранять копию исходных масок.

Именованные каналы



Каналы имеют ряд недостатков. Каналы могут использоваться только для связи процессов, имеющих общее происхождение, та­ких как родительский процесс и его потомок. Они не могут существовать постоянно. Каналы каждый раз должны создаваться заново, а после завершения об­ращающегося к ним процесса уничтожаются.
Для восполнения этих недостатков существует разновидность канала, называ­емая именованным каналом, или файлом типа FIFO. В отношении вызовов read и write именованные каналы идентичны обычным. Именованные каналы являются постоянными и им присвоено имя файла системы UNIX. Именованный канал также имеет владельца, размер и связанные с ним права доступа. Он может быть открыт, закрыт и удален, как и любой файл UNIX, но при чтении или записи ведет себя аналогично каналу.
Программирование при помощи каналов FIFO, в основном, идентично про­граммированию с использованием обычных каналов. Единственное существенное различие заключается в их инициализации. Вместо использования вызова pipe канал FIFO создается при помощи вызова mkfifо.
Описание
int mkfifo(const char *pathname, mode_t mode);
Системный вызов mkfifо создает файл FIFO с именем, заданным первым па­раметром pathname. Канал FIFO будет иметь права доступа, заданные парамет­ром mode и измененные в соответствии со значением umask процесса.
После создания канала FIFO он должен быть открыт при помощи вызова open.
Например:
mkfifo(“/tmp/fifo”, 0666);

fd = open(“/tmp/fifo”, O_WRONLY);
Вызов open будет заблокирован до тех пор, пока другой процесс не откроет канал FIFO для чтения.
Можно выполнить неблокирующий вызов open для канала FIFO. Для этого во время вызова должен быть установлен флаг O_NONBLOCK и один из флагов O_WRONLY или O_RDONLY.
 

Hosted by uCoz