Named Pipes (FIFO) в Unix с несколькими читателями - PullRequest
32 голосов
/ 28 октября 2009

У меня есть две программы, Writer и Reader.

У меня есть FIFO из Writer в Reader, поэтому, когда я пишу что-то в stdin в Writer, оно выводится в стандартный вывод из Reader.

Я попытался сделать это с двумя открытыми программами чтения, и я получил вывод на стандартный вывод только из одной из двух программ чтения. Какая из программ чтения Unix выбирает для печати стандартный вывод, казалось, произвольный каждый раз, когда я запускаю эту программу, но как только она выбирает одну из программ, каждый вывод на стандартный вывод печатается из одной и той же программы чтения.

Кто-нибудь знает, почему это происходит?

Если у меня есть две программы WRITER, они обе пишут в одну и ту же трубу.

Ответы [ 5 ]

26 голосов
/ 28 октября 2009

O в FIF O означает «вне». Как только ваши данные «вышли», они исчезли. :-) Так что, естественно, если появляется другой процесс, и кто-то уже выпустил чтение, данные не будут там дважды.

Чтобы выполнить то, что вы предлагаете, вам нужно заглянуть в доменные сокеты Unix. Manpage здесь . Вы можете написать сервер, который может писать в клиентские процессы, привязываясь к пути файловой системы. См. Также socket(), bind(), listen(), accept(), connect(), все из которых вы хотите использовать с PF_UNIX, AF_UNIX и struct sockaddr_un.

10 голосов
/ 28 октября 2009

Linux tee () может удовлетворить ваши потребности.
смотри здесь тройник

ПРИМЕЧАНИЕ: эта функция специфична для Linux.

2 голосов
/ 28 октября 2009

Я не думаю, что наблюдаемое вами поведение более чем случайно. Рассмотрим эту трассировку, которая использует «sed» в качестве двух читателей и цикл в качестве автора:

Osiris JL: mkdir fifo
Osiris JL: cd fifo
Osiris JL: mkfifo fifo
Osiris JL: sed 's/^/1: /' < fifo &
[1] 4235
Osiris JL: sed 's/^/2: /' < fifo &
[2] 4237
Osiris JL: while read line ; do echo $line; done > fifo < /etc/passwd
1: ##
1: # User Database
1: #
1: # Note that this file is consulted directly only when the system is running
1: # in single-user mode. At other times this information is provided by
1: # Open Directory.
1: #
1: # This file will not be consulted for authentication unless the BSD local node
1: # is enabled via /Applications/Utilities/Directory Utility.app
1: #
1: # See the DirectoryService(8) man page for additional information about
1: # Open Directory.
1: ##
1: nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
1: root:*:0:0:System Administrator:/var/root:/bin/sh
1: daemon:*:1:1:System Services:/var/root:/usr/bin/false
1: _uucp:*:4:4:Unix to Unix Copy Protocol:/var/spool/uucp:/usr/sbin/uucico
1: _lp:*:26:26:Printing Services:/var/spool/cups:/usr/bin/false
2: _postfix:*:27:27:Postfix Mail Server:/var/spool/postfix:/usr/bin/false
2: _mcxalr:*:54:54:MCX AppLaunch:/var/empty:/usr/bin/false
2: _pcastagent:*:55:55:Podcast Producer Agent:/var/pcast/agent:/usr/bin/false
2: _pcastserver:*:56:56:Podcast Producer Server:/var/pcast/server:/usr/bin/false
2: _serialnumberd:*:58:58:Serial Number Daemon:/var/empty:/usr/bin/false
2: _devdocs:*:59:59:Developer Documentation:/var/empty:/usr/bin/false
2: _sandbox:*:60:60:Seatbelt:/var/empty:/usr/bin/false
2: _mdnsresponder:*:65:65:mDNSResponder:/var/empty:/usr/bin/false
2: _ard:*:67:67:Apple Remote Desktop:/var/empty:/usr/bin/false
2: _www:*:70:70:World Wide Web Server:/Library/WebServer:/usr/bin/false
2: _eppc:*:71:71:Apple Events User:/var/empty:/usr/bin/false
2: _cvs:*:72:72:CVS Server:/var/empty:/usr/bin/false
2: _svn:*:73:73:SVN Server:/var/empty:/usr/bin/false
2: _mysql:*:74:74:MySQL Server:/var/empty:/usr/bin/false
2: _sshd:*:75:75:sshd Privilege separation:/var/empty:/usr/bin/false
2: _qtss:*:76:76:QuickTime Streaming Server:/var/empty:/usr/bin/false
2: _cyrus:*:77:6:Cyrus Administrator:/var/imap:/usr/bin/false
2: _mailman:*:78:78:Mailman List Server:/var/empty:/usr/bin/false
2: _appserver:*:79:79:Application Server:/var/empty:/usr/bin/false
2: _clamav:*:82:82:ClamAV Daemon:/var/virusmails:/usr/bin/false
2: _amavisd:*:83:83:AMaViS Daemon:/var/virusmails:/usr/bin/false
2: _jabber:*:84:84:Jabber XMPP Server:/var/empty:/usr/bin/false
2: _xgridcontroller:*:85:85:Xgrid Controller:/var/xgrid/controller:/usr/bin/false
2: _xgridagent:*:86:86:Xgrid Agent:/var/xgrid/agent:/usr/bin/false
2: _appowner:*:87:87:Application Owner:/var/empty:/usr/bin/false
2: _windowserver:*:88:88:WindowServer:/var/empty:/usr/bin/false
2: _spotlight:*:89:89:Spotlight:/var/empty:/usr/bin/false
2: _tokend:*:91:91:Token Daemon:/var/empty:/usr/bin/false
2: _securityagent:*:92:92:SecurityAgent:/var/empty:/usr/bin/false
2: _calendar:*:93:93:Calendar:/var/empty:/usr/bin/false
2: _teamsserver:*:94:94:TeamsServer:/var/teamsserver:/usr/bin/false
2: _update_sharing:*:95:-2:Update Sharing:/var/empty:/usr/bin/false
2: _installer:*:96:-2:Installer:/var/empty:/usr/bin/false
2: _atsserver:*:97:97:ATS Server:/var/empty:/usr/bin/false
2: _unknown:*:99:99:Unknown User:/var/empty:/usr/bin/false
Osiris JL:  jobs
[1]-  Running                 sed 's/^/1: /' < fifo &
[2]+  Done                    sed 's/^/2: /' < fifo
Osiris JL: echo > fifo
1: 
Osiris JL: jobs
[1]+  Done                    sed 's/^/1: /' < fifo
Osiris JL: 

Как видите, оба читателя должны прочитать некоторые данные. Выбор читателя в любое время зависел от прихоти о / с. Обратите внимание, что я тщательно использовал эхо для печати каждой строки файла; это были атомарные записи, которые были прочитаны атомарно.

Если бы я использовал скрипт Perl, например, с задержкой после чтения и повторения строки, то я вполне мог бы видеть более определенное поведение (обычно) с двумя строками из Reader 1 для каждой 1 строки из Reader 2.

perl -n -e 'while(<>){ print "1: $_"; sleep 1; }' < fifo &
perl -n -e 'while(<>){ print "2: $_"; sleep 2; }' < fifo &

Эксперименты проведены на MacOS X 10.5.8 (Leopard) - но, вероятно, будут похожи в большинстве мест.

1 голос
/ 16 января 2013

Я хотел бы добавить к вышеприведенным объяснениям, что записи (и предположительные чтения, хотя я не могу подтвердить это из страниц руководства) в каналы имеют атомарный размер до определенного размера (4 КБ в Linux). Итак, предположим, что мы начинаем с пустого канала, а пишущий записывает данные <= 4KiB в канал. Вот что я думаю происходит: </p>

а) Автор записывает все данные за один раз. Пока это происходит, ни у одного другого процесса нет возможности прочитать (или записать) канал.

b) Один из читателей должен сделать это I / O.

c) Выбранный считыватель считывает все данные из канала за один раз и через некоторое время выводит их на свой стандартный вывод.

Я думаю, что это может объяснить, пока вы видите результаты только одного из читателей. Попробуйте писать небольшими порциями и, возможно, спать после каждой записи.

Конечно, другие ответили, почему каждый элемент данных читается только процессом.

0 голосов
/ 07 июня 2019

Решение для сокетов работает, но усложняется в случае сбоя сервера. Чтобы разрешить любому процессу быть сервером, я использую блокировки записей в конце временного файла, который содержит местоположение / длину / изменения данных в данном файле. Я использую временный именованный канал для передачи запросов на добавление к любому процессу, имеющему блокировку записи в конце временного файла.

...