Именованные каналы, или FIFO
Каналы являются изящным и мощным механизмом межпроцессного взаимодействия. Тем не менее они имеют ряд недостатков.
Первый, и наиболее серьезный из них, заключается в том, что каналы могут использоваться только для связи процессов, имеющих общее происхождение, таких как родительский процесс и его потомок. Это ограничение становится видным при попытке разработать настоящую «серверную» программу, которая выполняется постоянно, обеспечивая системный сервис. Примерами таких программ являются серверы управления сетью и спулеры печати. В идеале клиентские процессы должны иметь возможность стартовать, подключаться к не связанному с ними серверному процессу при помощи канала, а затем снова отключаться от него. К сожалению, такую модель при помощи обычных каналов реализовать нельзя.
Второй недостаток каналов заключается в том, что они не могут существовать постоянно. Они каждый раз должны создаваться заново, а после завершения обращающегося к ним процесса уничтожаются.
Для восполнения этих недостатков существует разновидность канала, называемая именованным каналом, или файлом типа FIFO
(сокращение от first-in first-out, то есть «первый вошел/первым вышел»). В отношении вызовов fdread и fdwrite именованные каналы идентичны обычным. Тем не менее, в отличие от обычных каналов, именованные каналы являются постоянными и им присвоено имя файла системы UNIX. Именованный канал также имеет владельца, размер и связанные с ним права доступа. Он может быть открыт, закрыт и удален, как и любой файл UNIX, но при чтении или записи ведет себя аналогично каналу.
Прежде чем рассматривать применение каналов FIFO на программном уровне, рассмотрим их использование на уровне команд. Для создания именованного канала используется команда mknod:
$ /etc/mknod channel p
Первый аргумент channel является именем канала FIFO (в качестве него можно задать любое допустимое имя UNIX). Параметр р
команды mknod указывает, что нужно создать именованный канал. Этот параметр необходим, так как команда mknod также используется для создания файлов устройств.
Некоторые атрибуты вновь созданного канала FIFO можно вывести при помощи команды ls:
$ ls -l channel
prw-rw-r- 1 ben usr 0 Aug 1 21:05 channel
Символ р
в первой колонке обозначает, что channel является файлом типа FIFO. Обратите внимание на права доступа к именованному каналу channel (чтение/запись для владельца и группы владельца, только чтение для всех остальных пользователей); владельца и группу владельца (ben, usr); размер (0 байт, то есть в настоящий момент канал пуст) и время создания.
При помощи стандартных команд
UNIX можно выполнять чтение из канала FIFO и запись в него, например:
$ cat < channel
Если выполнить эту команду сразу же после создания именованного канала channel, то она «зависнет». Это происходит из-за того, что процесс, открывающий канал FIFO на чтение, по умолчанию будет блокирован до тех пор, пока другой процесс не попытается открыть канал FIFO для записи. Аналогично процесс, пытающийся открыть канал FIFO для записи, будет блокирован до тех пор, пока другой процесс не попытается открыть его для чтения. Это благоразумный подход, так как он экономит системные ресурсы и облегчает координацию работы программы. Вследствие этого, при необходимости создания одновременно как записывающего, так и читающего процессов, потребуется запустить один из них в фоновом режиме (или с другого терминала, или псевдотерминала xterm графического интерфейса), например:
$ cat < channel &
102
$ ls -l > channel; wait
total 17
prw-rw-r- 1 ben usr 0 Aug 1 21:05 channel
-rw-rw-r- 1 ben usr 0 Aug 1 21:06 f
-rw-rw-r- 1 ben usr 937 Jul 27 22:30 fifos
-rw-rw-r- 1 ben usr 7152 Jul 27 22:11 pipes.cont
Проанализируем подробнее этот результат. Содержимое каталога вначале выводится при помощи команды ls, а затем записывается в канал FIFO. Ожидающая команда cat затем считывает данные из канала FIFO и выводит их на экран. После этого процесс, выполняющий команду cat, завершает работу. Это происходит из-за того, что канал FIFO больше не открыт для записи, чтение из него будет безуспешным, как и для обычного канала, что команда cat понимает как достижение конца файла. Команда же wait заставляет командный интерпретатор ждать завершения команды cat перед тем, как снова вывести приглашение командной строки.