GNU/Linux >> Znalost Linux >  >> Linux

O_RDWR na pojmenovaných kanálech s poll()

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á hodnota 0 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 zkontrolovat POLLHUP před pokusem o read() , ale od read() 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áte O_RDONLY | O_NONBLOCK , získáte EOF (read() vrací 0 ) opakovaně, jak jste si všimli. Pokud však použijete poll() čekat na POLLIN událost před voláním read() , počká, až se zapisovač připojí, a nevytvoří EOF.
  • read() návratová hodnota -1 obvykle znamená chybu. Pokud však errno == EAGAIN , to jednoduše znamená, že momentálně nejsou k dispozici žádná další data a neblokujete, takže se můžete vrátit na poll() v případě, že je třeba manipulovat s jinými zařízeními. Pokud errno == EINTR a poté read() bylo přerušeno před čtením jakýchkoli dat a můžete se buď vrátit na poll() nebo jednoduše zavolejte read() 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()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 proto poll() téměř vždy se používá s O_NONBLOCK . Můžete použít alarm() abyste se probudili z read() po určité době, ale to je příliš komplikované.
    • Pokud se zapisovač zavře, čtečka obdrží poll() POLLHUP revent a read() 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()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 k poll() , 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 a read() 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()POLLIN revent, když jsou data připravena ke čtení. U pojmenovaných kanálů však EOF nezpůsobí POLLIN nebo POLLHUP 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()POLLIN revent, když jsou data připravena ke čtení. EOF však nezpůsobí POLLIN nebo POLLHUP reventy na pojmenovaných kanálech.
    • Po přečtení všech aktuálně dostupných dat read() vrátí -1 a nastavte errno == EAGAIN . Nyní je čas vrátit se na poll() č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é.

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:

  1. (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í na read() hovory.
        • Pokud read() == -1 a errno == EINTR , read() všechno znovu.
        • Pokud read() == 0 , spojení se ukončí a všechna data byla přijata.

  2. (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() pro POLLIN 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í na read() hovory.
        • Pokud read() == -1 a errno == EAGAIN , přejděte zpět na poll() krok.
        • Pokud read() == -1 a errno == 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í.

  3. (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() pro POLLIN události, případně na více potrubích najednou.
      • read() tolik dat, kolik je potřeba, případně smyčkování na read() hovory.
        • Pokud read() == -1 a errno == EAGAIN , přejděte zpět na poll() krok.
        • Pokud read() == -1 a errno == EINTR , read() všechno znovu.
        • Pokud read() == 0 , něco je špatně -- s O_RDWR by se to nemělo stávat na pojmenovaných kanálech, ale pouze s O_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ém poll() smyčka zpracování událostí, může být tento případ stále potřeba zpracovat.

Linux
  1. Práce s rourami na příkazovém řádku Linuxu

  2. Simulovat vadné blokové zařízení s chybami čtení?

  3. Linux:existuje čtení nebo recv ze socketu s časovým limitem?

  1. Jak používat příkaz `subprocess` s rourami

  2. Čtení v Pythonu s názvem PIPE

  3. Jak zkopíruji soubor s názvem začínající tečkou?

  1. Příkaz Linux/Unix pro spojení N řádků vstupu s oddělovači?

  2. Proč není hlasování nahrazeno epollem?

  3. Podmíněné předávání DNS s názvem na Linuxu