table of contents
- bookworm-backports 1:4.24.0-2~bpo12+1
- testing 1:4.24.0-2
- unstable 1:4.24.0-2
pipe(7) | Miscellaneous Information Manual | pipe(7) |
NAZWA¶
pipe - przegląd potoków i FIFO
OPIS¶
Potoki i FIFO (zwane również potokami nazwanymi lub łączami nazwanymi) udostępniają jednokierunkowy kanał komunikacji międzyprocesowej. Potok ma koniec do odczytu oraz koniec do zapisu. Dane zapisane do końca do zapisu mogą być odczytane z końca do odczytu potoku.
Potok tworzy się za pomocą pipe(2), które tworzy nowy potok i zwraca dwa deskryptory plików, jeden odnoszący się do końca do odczytu potoku, a drugi odnoszący się do końca do zapisu. Potoków można użyć do utworzenia kanału komunikacji pomiędzy powiązanymi procesami; zob. pipe(2) aby zapoznać się z przykładem.
FIFO (skrót od ang. First In First Out – pierwszy na wejściu, pierwszy na wyjściu) posiada nazwę w systemie plików (utworzoną za pomocą mkfifo(3)) i jest otwierany przy użyciu open(2). Każdy proces może otworzyć FIFO, o ile uprawnienia pliku na to zezwalają. Koniec do odczytu otwiera się za pomocą znacznika O_RDONLY; koniec do zapisu, za pomocą O_WRONLY. Więcej szczegółów w podręczniku fifo(7). Uwaga: Choć FIFO mają ścieżkę w systemie plików, to wejście/wyjście na FIFO nie przeprowadza operacji na podległym urządzeniu (jeśli takie istnieje).
Wejście/wyjście na potokach i FIFO¶
Jedyna różnica pomiędzy potokami i FIFO polega na sposobie ich tworzenia i otwierania. Po ukończeniu tych zadań, wejście/wyjście na potokach i FIFO korzysta z dokładnie tej samej semantyki.
Gdy proces spróbuje odczytać z pustego potoku, read(2) zablokuje do momentu pojawienia się danych. Gdy proces spróbuje zapisać do pełnego potoku (zob. niżej), write(2) zablokuje do momentu odczytania wystarczającej ilości danych z potoku, umożliwiającej poprawne przeprowadzenie zapisu.
Nieblokujące wejście/wyjście można uzyskać za pomocą operacji F_SETFL fcntl(2), w celu włączenia znacznika statusu otwartego pliku O_NONBLOCK lub otwierając fifo(7) z O_NONBLOCK. Jeśli jakiś proces otworzy potok do zapisu, odczyt zawiedzie z błędem EAGAIN; w innych przypadkach, przy braku potencjalnych zapisujących, odczyt powiedzie się i powróci pusty.
Kanał udostępniany przez potok jest strumienien bajtów: nie występuje koncept granic komunikatów.
Jeśli wszystkie deskryptory pliku odnoszące się do końca do zapisu potoku zostaną zamknięte, to próba odczytu za pomocą read(2) z potoku przyniesie koniec-pliku (read(2) zwróci 0). Jeśli wszystkie deskryptory pliku odnoszące się do końca do odczytu zostaną zamknięte, to zapis za pomocą write(2) spowoduje wygenerowanie sygnału SIGPIPE dla wywołującego procesu. Jeśli proces wywołujący zignoruje ten sygnał, to write(2) zawiedzie z błędem EPIPE. Aplikacje używające pipe(2) i fork(2) powinny korzystać z odpowiednich wywołań close(2), aby zamykać niepotrzebnie zduplikowane deskryptory pliku; to zapewni, że koniec-pliku i SIGPIPE/EPIPE są dostarczane tam, gdzie to konieczne.
Do potoku nie da się zastosować lseek(2).
Pojemność potoku¶
Potok ma ograniczoną pojemność. Po zapełnieniu potoku, write(2) zablokuje lub zawiedzie, w zależności od tego, czy znacznik O_NONBLOCK jest ustawiony (zob. niżej). Różne implementacje posiadają odmienne limity pojemności potoku. Aplikacje nie powinny zależeć od jakiejś określonej pojemności, lecz należy je zaprojektować tak, aby konsumowały dane tak wcześnie jak to możliwe, w celu uniknięcia blokowania procesu zapisującego.
Przed Linuksem 2.6.11, pojemność potoku była taka sama jak systemowy rozmiar strony (np. 4096 bajtów na architekturze i386). Od Linuksa 2.6.11, pojemność potoku wynosi 16 stron (tj. 65 536 bajtów w systemie o rozmiarze strony 4096 bajtów). Od Linuksa 2.6.35, domyślny rozmiar potoku wynosi 16 stron, ale można go odpytać i ustawić za pomocą operacji F_GETPIPE_SZ i F_SETPIPE_SZ fcntl(2). Więcej szczegółów w podręczniku fcntl(2).
Następująca operacja ioctl(2), którą można zastosować do deskryptora pliku, odnoszącego się do dowolnego końca potoku, umieszcza licznik nieodczytanych bajtów w potoku, w buforze int wskazanym ostatnim argumentem wywołania:
ioctl(fd, FIONREAD, &nbytes);
Operacja FIONREAD nie jest przewidziana żadnym standardem, ale udostępnia ją wiele implementacji.
Pliki /proc¶
W Linuksie, następujące pliki kontrolują wielkość pamięci, jaką można przeznaczyć potokom:
- /proc/sys/fs/pipe-max-pages (tylko w Linuksie 2.6.34)
- Górny limit pojemności, podany w stronach, jaką nieuprzywilejowany użytkownik (nieposiadający przywileju (ang. capability) CAP_SYS_RESOURCE), może ustawić dla potoku.
- Domyślna wartość tego limitu to szesnastokrotność domyślnego rozmiaru potoku (zob. wyżej); dolny limit to dwie strony.
- Interfejs ten usunięto w Linuksie 2.6.35, na korzyść /proc/sys/fs/pipe-max-size.
- /proc/sys/fs/pipe-max-size (od Linuksa 2.6.35)
- Maksymalny rozmiar (w bajtach) poszczególnych potoków, jaki może być ustawiany przez użytkowników bez przywileju CAP_SYS_RESOURCE. Wartość przypisana do tego pliku może być zaokrąglona w górę, w uwzględnieniu wartości użytej faktycznie, ze względu na wygodę implementacji. Aby sprawdzić wartość zaokrągloną, należy wyświetlić wartość tego pliku, po przypisaniu mu wartości.
- Domyślną wartością pliku jest 1048576 (1 MiB). Minimalną wartością, jaką można przypisać do tego pliku, jest systemowy rozmiar strony. Próby ograniczenia limitu poniżej rozmiaru strony spowodują niepowodzenie write(2) z błędem EINVAL.
- Od Linuksa 4.9, wartość pliku działa również jako górny, domyślny limit pojemności nowego potoku lub nowo otwartego FIFO.
- /proc/sys/fs/pipe-user-pages-hard (od Linuksa 4.5)
- Bezwzględny limit całkowitego rozmiaru (w stronach) wszystkich potoków utworzonych lub ustawionych przez pojedynczego nieuprzywilejowanego użytkownika (tzn. nieposiadającego przywilejów CAP_SYS_RESOURCE ani CAP_SYS_ADMIN). Gdy całkowita liczba stron przypisanych do buforów potoku dla danego użytkownika osiągnie tej limit, próby tworzenia nowych potoków będą odmawiane, a także próby zwiększenia rozmiaru potoku będą odmawiane.
- Gdy wartość limitu wynosi zero (tak jest domyślnie), bezwzględny limit nie obowiązuje.
- /proc/sys/fs/pipe-user-pages-soft (od Linuksa 4.5)
- Miękki limit całkowitego rozmiaru (w stronach) wszystkich potoków utworzonych lub ustawionych przez pojedynczego nieuprzywilejowanego użytkownika (tzn. nieposiadającego przywilejów CAP_SYS_RESOURCE ani CAP_SYS_ADMIN). Gdy całkowita liczba stron przypisanych do buforów potoku dla danego użytkownika osiągnie tej limit, poszczególne potoki tworzone przez użytkownika będą ograniczone do jednej strony, a próby zwiększenia rozmiaru potoku będą odmawiane.
- Gdy wartość limitu wynosi zero, miękki limit nie obowiązuje. Domyślną wartością tego pliku jest 16384, co pozwala na tworzenie do 1024 potoków o domyślnym rozmiarze.
Przed Linuksem 4.9 pewne błędy wpływały na obsługę limitów pipe-user-pages-soft i pipe-user-pages-hard; zob. USTERKI.
PIPE_BUF¶
POSIX.1 określa, że zapis mniej niż PIPE_BUF bajtów musi być niepodzielny: dane wyjściowe są zapisywane do potoku jako ciągła sekwencja. Zapis więcej niż PIPE_BUF nie musi być niepodzielny: jądro może przeplatać dane, z danymi zapisywanymi przez inne procesy. POSIX.1 wymaga, aby PIPE_BUF miał co najmniej 512 bajtów (w Linuksie PIPE_BUF ma 4096 bajtów). Dokładna semantyka zależy od tego, czy deskryptor pliku jest nieblokujący (O_NONBLOCK), czy występuje wiele zapisów do potoku oraz od n, liczby bajtów do zapisania:
- O_NONBLOCK wyłączone, n <= PIPE_BUF
- Wszystkie n bajtów jest zapisane niepodzielnie; write(2) może zablokować, jeśli brak miejsca do natychmiastowego zapisu n bajtów
- O_NONBLOCK włączone, n <= PIPE_BUF
- Jeśli jest miejsce na zapisanie n bajtów do potoku, to write(2) natychmiast powiedzie się, zapisując wszystkie n bajtów; w przeciwnym wypadku write(2) zawodzi, z errno ustawionym na EAGAIN.
- O_NONBLOCK wyłączone, n > PIPE_BUF
- Zapis jest podzielny: dane przekazane do write(2) mogą ulec przepleceniu z write(2) innych procesów; write(2) blokuje do momentu zapisania n bajtów.
- O_NONBLOCK włączone, n > PIPE_BUF
- Gdy potok jest pełny, write(2) zawiedzie z errno ustawionym na EAGAIN. W przeciwnym wypadku, zapisanych może ulec od 1 do n bajtów (tzn. może wystąpić „częściowy zapis”, wywołujący powinien sprawdzić wartość zwróconą przez write(2), aby przekonać się, jak wiele bajtów faktycznie zapisano), bajty te mogą być przeplatane z zapisami z innych procesów.
Znaczniki statusu otwartego pliku¶
Jedyne znaczniki statusu otwartego pliku, jakie można sensownie zastosować do potoku lub FIFO to O_NONBLOCK i O_ASYNC.
Ustawienie znacznika O_ASYNC na końcu do odczytu potoku powoduje wygenerowanie sygnału (domyślnie SIGIO), gdy nowe wejście stanie się dostępne na potoku. Cel dostarczenia sygnałów należy ustawić za pomocą polecenia F_SETOWN fcntl(2). W Linuksie O_ASYNC jest obsługiwane w przypadku potoków i FIFO dopiero od Linuksa 2.6.
Uwagi dotyczące przenośności¶
W niektórych systemach (ale nie w Linuksie), potoki są dwukierunkowe: dane mogą być transmitowane w obu kierunkach pomiędzy węzłami końcowymi. POSIX.1 wymaga jedynie potoków jednokierunkowych. Przenośne aplikacje powinny unikać polegania na semantyce potoków dwukierunkowych.
USTERKI¶
Przed Linuksem 4.9 występowały pewne błędy dotyczące obsługi limitów pipe-user-pages-soft i pipe-user-pages-hard przy używaniu operacji F_SETPIPE_SZ fcntl(2) do zmiany rozmiaru potoku:
- (a)
- Przy zwiększaniu rozmiaru potoku, sprawdzenia dotyczące limitów miękkich i bezwzględnych, czynione były wobec istniejącej zajętości i z wyłączeniem pamięci wymaganej do zwiększenia pojemności potoku. Nowo powiększona pojemność potoku mogła wykroczyć (nawet znacznie) poza całkowitą pamięć używaną przez potoki użytkownika (mogło to również wyzwolić problem opisany jako następny).
- Od Linuksa 4.9, sprawdzenia limitów wliczają pamięć potrzebną do nowej pojemności potoku.
- (b)
- Sprawdzenia limitów dokonywano nawet wówczas, gdy nowa pojemność potoków była niższa niż istniejąca. Mogło to prowadzić do problemów, gdy użytkownik ustawił znaczną pojemność potoku, a następnie limity ograniczono, co powodowało, że użytkownik nie mógł już zmniejszyć pojemności potoku.
- Od Linuksa 4.9, sprawdzenia limitów następują tylko przy zwiększaniu pojemności potoku; użytkownik nieuprzywilejowany może zawsze zmniejszyć pojemność potoku.
- (c)
- Wyliczanie i sprawdzanie limitów odbywało się w następujący sposób:
- (1)
- Sprawdzenie, czy użytkownik przekroczył limit.
- (2)
- Utworzenie nowego przydzielenia buforu potoku.
- (3)
- Wyliczenie nowego przydzielenia wobec limitów.
- Była to sytuacja sprzyjająca hazardowi. Punkt (1) mogło przekroczyć jednocześnie wiele procesów, a przydzielone następnie bufory potoku były wyliczane jedynie w kroku (3), co mogło prowadzić do przekroczenia limitu przez przydzielony bufor potoku użytkownika.
- Od Linuksa 4.9, krok wyliczania jest dokonywany przed przydzieleniem, a operacja zawodzi, gdy limit miałby być przekroczony.
Przed Linuksem 4.9, błędy podobne do opisanych w punktach (a) i (c) mogły występować również przy przydzielaniu przez jądro pamięci buforowi nowego potoku tj. przy wywoływaniu pipe(2) i przy otwieraniu uprzednio nieotwartego FIFO.
ZOBACZ TAKŻE¶
mkfifo(1), dup(2), fcntl(2), open(2), pipe(2), poll(2), select(2), socketpair(2), splice(2), stat(2), tee(2), vmsplice(2), mkfifo(3), epoll(7), fifo(7)
TŁUMACZENIE¶
Autorami polskiego tłumaczenia niniejszej strony podręcznika są: Michał Kułach <michal.kulach@gmail.com>
Niniejsze tłumaczenie jest wolną dokumentacją. Bliższe informacje o warunkach licencji można uzyskać zapoznając się z GNU General Public License w wersji 3 lub nowszej. Nie przyjmuje się ŻADNEJ ODPOWIEDZIALNOŚCI.
Błędy w tłumaczeniu strony podręcznika prosimy zgłaszać na adres listy dyskusyjnej manpages-pl-list@lists.sourceforge.net.
2 maja 2024 r. | Linux man-pages 6.8 |