Podle manuálové stránky open(2) můžete předat O_RDONLY|O_NONBLOCK
nebo O_WRONLY|O_NONBLOCK
abyste se vyhnuli open
syscall bude zablokován (dostanete errno == ENXIO
v tom případě)
Jak jsem uvedl, přečtěte si také manuálové stránky fifo(7) a mkfifo(3).
Nejprve několik přípravných zápasů:
Pomocí O_NONBLOCK
a poll()
je běžná praxe - ne naopak. Chcete-li úspěšně pracovat, musíte si být jisti, že zvládnete všechny poll()
a read()
návratové stavy správně:
read()
návratová hodnota0
znamená EOF -- druhá strana uzavřela spojení. To odpovídá (obvykle, ale ne ve všech operačních systémech)poll()
vracíPOLLHUP
obviňovat. Možná budete chtít zkontrolovatPOLLHUP
před pokusem oread()
, ale odread()
to není nezbytně nutné zaručeně vrátí0
po uzavření strany pro psaní.- Pokud zavoláte na číslo
read()
než se připojí zapisovač a vy máteO_RDONLY | O_NONBLOCK
, získáte EOF (read()
vrací0
) opakovaně, jak jste si všimli. Pokud však použijetepoll()
čekat naPOLLIN
událost před volánímread()
, počká, až se zapisovač připojí, a nevytvoří EOF. read()
návratová hodnota-1
obvykle znamená chybu. Pokud všakerrno == EAGAIN
, to jednoduše znamená, že momentálně nejsou k dispozici žádná další data a neblokujete, takže se můžete vrátit napoll()
v případě, že je třeba manipulovat s jinými zařízeními. Pokuderrno == EINTR
a potéread()
bylo přerušeno před čtením jakýchkoli dat a můžete se buď vrátit napoll()
nebo jednoduše zavolejteread()
okamžitě znovu.
Nyní pro Linux:
- Pokud otevřete na straně čtení pomocí
O_RDONLY
, pak:open()
se zablokuje, dokud nebude otevřen odpovídající zapisovač.poll()
dáPOLLIN
revent, když jsou data připravena ke čtení nebo dojde k EOF.read()
se zablokuje, dokud nebude načten požadovaný počet bajtů, spojení bude uzavřeno (vrátí 0), nebude přerušeno signálem nebo dojde k nějaké fatální IO chybě. Toto blokování poněkud maří účel použitípoll()
, a protopoll()
téměř vždy se používá sO_NONBLOCK
. Můžete použítalarm()
abyste se probudili zread()
po určité době, ale to je příliš komplikované.- Pokud se zapisovač zavře, čtečka obdrží
poll()
POLLHUP
revent aread()
vrátí0
poté na neurčito. V tomto okamžiku musí čtenář zavřít popisovač souborů a znovu jej otevřít.
- Pokud otevřete na straně čtení pomocí
O_RDONLY | O_NONBLOCK
, pak:open()
nebude blokovat.poll()
dáPOLLIN
revent, když jsou data připravena ke čtení, nebo dojde k EOF.poll()
bude také blokovat, dokud nebude dostupný zapisovač, pokud žádný není přítomen.- Po přečtení všech aktuálně dostupných dat
read()
buď vrátí -1 a nastavíerrno == EAGAIN
pokud je připojení stále otevřené, nebo vrátí0
pokud je spojení uzavřeno (EOF) nebo ještě není otevřeno zapisovatelem . Kdyžerrno == EAGAIN
, to znamená, že je čas vrátit se kpoll()
, protože připojení je otevřené, ale nejsou k dispozici žádná další data. Kdyžerrno == EINTR
,read()
dosud nepřečetl žádné bajty a byl přerušen signálem, takže jej lze restartovat. - Pokud se zapisovač zavře, čtečka obdrží
poll()
POLLHUP
revent aread()
vrátí0
poté na neurčito. V tomto okamžiku musí čtenář zavřít svůj popisovač souborů a znovu jej otevřít.
- (specifické pro Linux:) Pokud otevřete na straně čtení pomocí
O_RDWR
, pak:open()
nebude blokovat.poll()
dáPOLLIN
revent, když jsou data připravena ke čtení. U pojmenovaných kanálů však EOF nezpůsobíPOLLIN
neboPOLLHUP
revenuje.read()
se zablokuje, dokud není přečten požadovaný počet bajtů, není přerušen signálem nebo se neobjeví jiná závažná IO chyba. U pojmenovaných kanálů nevrátíerrno == EAGAIN
, ani nevrátí0
jeden z. Bude tam jen sedět, dokud se nepřečte přesný počet požadovaných bajtů nebo dokud nepřijme signál (v takovém případě vrátí počet dosud přečtených bajtů, nebo vrátí -1 a nastavíerrno == EINTR
pokud nebyly dosud načteny žádné bajty).- Pokud se zapisovač zavře, čtenář neztratí možnost číst pojmenované kanály později, pokud jiný zapisovač pojmenované kanály otevře, ale čtenář také neobdrží žádné upozornění.
- (specifické pro Linux:) Pokud otevřete na straně čtení pomocí
O_RDWR | O_NONBLOCK
, pak:- Číslo
open()
nebude blokovat. poll()
dáPOLLIN
revent, když jsou data připravena ke čtení. EOF však nezpůsobíPOLLIN
neboPOLLHUP
reventy na pojmenovaných kanálech.- Po přečtení všech aktuálně dostupných dat
read()
vrátí-1
a nastavteerrno == EAGAIN
. Nyní je čas vrátit se napoll()
čekat na další data, případně z jiných streamů. - Pokud se zapisovač zavře, čtenář neztratí možnost číst pojmenované kanály později, pokud pojmenované kanály otevře jiný zapisovač. Připojení je trvalé.
- Číslo
Jak se správně obáváte, pomocí O_RDWR
s trubkami není standardní, POSIX nebo jinde.
Protože se však zdá, že se tato otázka objevuje často, nejlepší způsob, jak v Linuxu vytvořit „odolné pojmenované kanály“, které zůstanou naživu, i když se jedna strana zavře, a které nezpůsobí POLLHUP
odvrátí nebo vrátí 0
pro read()
, je použít O_RDWR | O_NONBLOCK
.
Vidím tři hlavní způsoby zpracování pojmenovaných kanálů v Linuxu:
-
(Přenosné.) Bez
poll()
a s jedinou rourou:open(pipe, O_RDONLY);
- Hlavní smyčka:
read()
tolik dat, kolik je potřeba, případně smyčkování naread()
hovory.- Pokud
read() == -1
aerrno == EINTR
,read()
všechno znovu. - Pokud
read() == 0
, spojení se ukončí a všechna data byla přijata.
- Pokud
-
(Přenosné.) S
poll()
a s očekáváním, že kanály, a to i pojmenované, se otevřou pouze jednou a že jakmile jsou uzavřeny, musí je znovu otevřít jak čtenář, tak spisovatel, čímž se vytvoří nový kanál:open(pipe, O_RDONLY | O_NONBLOCK);
- Hlavní smyčka:
poll()
proPOLLIN
události, případně na více potrubích najednou. (Poznámka:Toto zabráníread()
od získání více EOF předtím, než se spisovatel připojí.)read()
tolik dat, kolik je potřeba, případně smyčkování naread()
hovory.- Pokud
read() == -1
aerrno == EAGAIN
, přejděte zpět napoll()
krok. - Pokud
read() == -1
aerrno == EINTR
,read()
všechno znovu. - Pokud
read() == 0
, připojení je uzavřeno a musíte ukončit nebo uzavřít a znovu otevřít potrubí.
- Pokud
-
(Nepřenosné, specifické pro Linux.) S
poll()
a s očekáváním, že pojmenované kanály nikdy neskončí a mohou být připojeny a odpojeny několikrát:open(pipe, O_RDWR | O_NONBLOCK);
- Hlavní smyčka:
poll()
proPOLLIN
události, případně na více potrubích najednou.read()
tolik dat, kolik je potřeba, případně smyčkování naread()
hovory.- Pokud
read() == -1
aerrno == EAGAIN
, přejděte zpět napoll()
krok. - Pokud
read() == -1
aerrno == EINTR
,read()
všechno znovu. - Pokud
read() == 0
, něco je špatně -- sO_RDWR
by se to nemělo stávat na pojmenovaných kanálech, ale pouze sO_RDONLY
nebo nepojmenované dýmky; označuje uzavřené potrubí, které musí být uzavřeno a znovu otevřeno. Pokud smícháte pojmenované a nepojmenované kanály ve stejnémpoll()
smyčka zpracování událostí, může být tento případ stále potřeba zpracovat.
- Pokud