Давайте вернемся назад к примеру с каналами, поскольку он весьма интересен, а
также является хорошей иллюстрацией для понимания ссылок. Когда вы в
командной строке используете канал, shell создает для вас канал и
работает так, что команда перед каналом выполняет в него запись, а команда
после канала выполняет из него чтение. Все каналы, будь они анонимными
(как те, что используются в
shell'ах) или именованными
(смотрите ниже), работают согласно принципу простой очереди FIFO (First In,
First Out, «первым пришел - первым обслужен»). Мы уже видели
примеры использования каналов в shell'е, но давайте взглянем еще на
один пример для демонстрации этого принципа:
$ ls -d /proc/[0-9] | head -5 /proc/1/ /proc/2/ /proc/3/ /proc/4/ /proc/5/
Одно обстоятельство, которое вы не заметите в этом примере (потому что это
происходит слишком быстро), состоит в блокировке записей в каналы. Это
означает, что когда команда ls выполняет запись в канал,
он блокируется до тех пор, пока процесс выполняет чтение на другом конце.
Чтобы увидеть этот эффект, вы можете создать именованные каналы, которые, в
отличие от каналов, используемых shell'ами, имеют имена (т.е. они
являются связанными, в то время как каналы shell'а - нет)[26]. Команда для создания именованного канала -
mkfifo:
$ mkfifo a_pipe
$ ls -il
total 0
169 prw-rw-r-- 1 queen queen 0 Dec 10 14:12 a_pipe|
#
# Вы можете видеть, что счетчик ссылок равен 1,
# а файл является каналом ('p').
#
# Вы также можете использовать здесь ln:
#
$ ln a_pipe the_same_pipe
$ ls -il
total 0
169 prw-rw-r-- 2 queen queen 0 Dec 10 15:37 a_pipe|
169 prw-rw-r-- 2 queen queen 0 Dec 10 15:37 the_same_pipe|
$ ls -d /proc/[0-9] >a_pipe
#
# Процесс заблокирован, т.к. на другом конце нет считывающей программы.
# Нажмите Control Z, чтобы приостановить процесс...
#
[1]+ Stopped ls -d /proc/[0-9] >a_pipe
#
# ...Затем отправьте его в фоновый режим:
#
$ bg
[1]+ ls -d /proc/[0-9] >a_pipe &
#
# теперь выполняем чтение из канала...
#
$ head -5 <the_same_pipe
#
# ...процесс записи завершается....
#
/proc/1/
/proc/2/
/proc/3/
/proc/4/
/proc/5/
[1]+ Done ls -d /proc/[0-9] >a_pipe
$Аналогичным образом чтение тоже блокируется. Если мы выполним приведенные выше команды в обратном порядке, мы увидим что команда head блокируется, ожидая, чтобы какой-либо процесс дал ей что-нибудь прочитать:
$ head -5 <a_pipe # # Программа заблокировалась, приостановите ее: C-z # [1]+ Stopped head -5 <a_pipe # # Отправляем ее в фоновый режим... # $ bg [1]+ head -5 <a_pipe & # # ...И скармливаем ей что-нибудь :) # $ ls -d /proc/[0-9] >the_same_pipe /proc/1/ /proc/2/ /proc/3/ /proc/4/ /proc/5/ $
Вы также можете увидеть нежелательный эффект в предыдущем примере: команда ls завершилась до того, как вступила в действие команда head. В результате вы немедленно возвратились в приглашение консоли, а head выполнилась позже, и вы увидели ее вывод только после возвращения.
[26] Существуют и другие различия между этими двумя типами каналов, но это выходит за рамки данной книги.