Введение в именованные каналы

http://www.linuxjournal.com/article/2156?page=0,0

Introduction to Named Pipes #41
September 1997
Sep 01, 1997 By Andy Vaught in SysAdmin

Весьма полезная особенность Linux, это возможность использовать именованные каналы в качестве одного из механизмов межпроцессного взаимодействия. "Пайпы" имеются во всех UNIX-подобных системах. Они позволяют различным процессам обмениваться данными, даже если программы, выполняющиеся в этих процессах, изначально не были написаны для взаимодействия с другими программами. Это позволяет комбинировать различные утилиты в весьма причудливые конструкции.

Первое знакомство

Простейший пример использования канала:

ls | grep x

Когда интерпретатор bash проверяет ввод и находит символ вертикальной черты, |, которая разделяет две команды, он запускает обе команды, соединяя вывод одной со вводом другой. Команда ls выводит список файлов в текущем каталоге, в то время как утилита grep проверяет строки этого списка и находит среди них только такие, которые содержат символ х.

Это был пример работы неименованного канала. Этот канал существует только в ядре и к нему нет доступа из процесса, который его породил, в данном случае, из экземпляра bash. Для тех, кто еще не знает, в UNIX новый процесс порождается уже существующим, в примере выше bash породил два дочерних процесса для выполнения программ ls и grep.

Другой вид каналов - это именованные каналы, которые иногда называют FIFO (поток данных в неименованном канале тоже обрабатывается по процедуре FIFO - прим. перев.). Имя именованного канала есть ни что иное, как имя файла на файловой системе. Именованные каналы показываются командой ls как обычные файлы с парой отличий:

% ls -l fifo1
prw-r--r--   1 andy  users    0 Jan 22 23:11 fifo1|

"р" в самой левой колонке показывает, что fifo1 - канал. Права на доступ к fifo1 по умолчанию создаются такими же, как в простом вновь созданном файле. В ls тех систем, где включена подсветка вывода каналы обычно отмечаются красным.

Стандартная утилита для создания именованного канала - это mkfifo. Она принимает одно или более имя в качестве аргументов и создает каналы под этими именами. Например, для создания именованного канала под именем pipe1 следует отдать команду:

mkfifo pipe1

Простейший метод объяснения, как работают именованные каналы, это разобрать пример. Мы создали канал в примере выше. Теперь в первой виртуальной консоли командуем:

ls -l > pipe1

В свою очередь во второй виртуальной консоли введем следующую команду:

cat < pipe1

Вуаля! Вывод команды в первой консоли отобразится во второй консоли. Заметьте, что порядок выполнения команд значения не имеет. Также обратите внимание на то, что первая команда ничего не выводит. Это происходит потому, что процесс блокируется в ожидании события соединения выхода канала со входом в какой-нибудь другой процесс.

Одним из наиболее употребимых свойств каналов является возможность обеспечения коммуникации различных программ. Например, программа, обрабатывающая какие-либо запросы (на печать, обращения к базе данных, и тому подобные) может открыть канал на чтение. Далее, другой процесс может создавать запросы путем открытия канала на запись и записывания в него запроса. Похоже на клиент-серверное взаимодействие, не так ли? Если "клиент" ничего не пишет в канал, ничего не происходит. Также ничего не будет происходить, если "сервер" не слушает с другой стороны канала.

Канальный беспредел

Создайте два именованных канала, pipe1 и pipe2. Запустите команду:

echo -n x | cat - pipe1 > pipe2 &
cat <pipe2 > pipe1

На экране вы не увидите ничего, но если запустите утилиту top для просмотра статусов процессов, вы увидите обе команды cat, которые с маниакальным упорством копируют символ х туда и сюда в бесконечном цикле.

Если нажать ctrl-C для выхода из цикла, можно увидеть сообщение “broken pipe”. Эта ошибка возникает, когда процесс, пишущий в канал, видит, что процесс, читающий из канала, закрыл свою сторону канала. С момента, когда "читатель" перестает читать, данные перестают отправляться. В нормальной ситуации "писатель" должен первым закрыть входное соединение, тогда "читатель" получает EOF (конец файла) и завершает запрос.

Возникнет или нет ошибка “broken pipe”, зависит от событий в точный момент нажатия ctrl-C. Если второй cat в это время читает х, ctrl-C останавливает второй cat, канал 1 закрывается и первый cat завершается молча, то есть без сообщений. Если же второй cat ожидал записи первым х в канал, ctrl-C завершает второй cat перед тем, как первый сможет записать в канал, и возникает сообщение об ошибке. Такого рода рандомная конкуренция обычно обозначается термином “race condition”.

Командная подстановка
Bash использует именованные каналы в очень изящной манере. Когда мы завершаем ввод команды в родительском процессе, она фактически запускается в подоболочке, то есть в клоне-потомке той оболочки, из которой мы набрали команду. С момента, когда исходная оболочка выполнит команду, вывод полной последовательности команд может быть перенаправлен, как вывод одной команды. Например:

(ls -l; ls -l) >ls.out

выведет две копии списка файлов текущего каталога в файл ls.out. Подстановка команд появляется тогда, когда мы ставим < или > у левой команды. Введите, например:

cat <(ls -l)

Результат ls -l, которая выполняется в подоболочке, как обычно, но направляет свой вывод во временный именованный канал, который создала bash. Когда cat прочитает информацию из этого временного канала, он уничтожается. Таким образом, cat имеет регулярное имя файла, которое она может использовать для чтения из него. Подобным образом, задание в командной строке последовательности >(commands) породит временный именованный канал, из которого bash прочитает информацию для подачи ее на ввод последовательности commands.

Другой пример, если вы хотите посмотреть, есть ли в двух каталогах одинаковые файлы, введите последовательность:

cmp <(ls /dir1) <(ls /dir2)

Утилита cmp сравнит два списка и выведет не уникальные имена.

Подстановка команд также используется при работе утилиты tee, с помощью которой можно одновременно просматривать вывод команд и сохранять его в файл, при этом один поток данных может быть прочтен многими "читателями" без специальных усилий, bash все сама сделает для вас.

ls | tee >(grep foo | wc >foo.count) >(grep bar | wc >bar.count) | grep baz | wc >baz.count

подсчитывает количество строк, слов и символов в файлах foo, bar и baz и складывает эти данный в файлы foo.count, bar.count и baz.count.

Подстановка команд может быть даже такой:

cat <(cat <(cat <(ls -l))))

Это будет работать также, как просто вывод списка текущего каталога с помощью ls.

Как мы увидели, в отличие от неименованных каналов, которые позволяют объединять команды вводом-выводом, именованные каналы позволяют создавать целые логические деревья обработки вывода, ограниченные в размерах только вашей фантазией.

Andy Vaught is currently a PhD candidate in computational physics at Arizona State University and has been running Linux since 1.1. He enjoys flying with the Civil Air Patrol as well as skiing. He can be reached at andy@maxwell.la.asu.edu.



Назад в тематический каталог
Назад на страницу переводов из Linux Journal