Programowanie współbieżne i rozproszone/PWR Ćwiczenia 4: Różnice pomiędzy wersjami

Z Studia Informatyczne
Przejdź do nawigacjiPrzejdź do wyszukiwania
Mengel (dyskusja | edycje)
Nie podano opisu zmian
Mengel (dyskusja | edycje)
Nie podano opisu zmian
Linia 51: Linia 51:
Załóżmy, że w pliku <tt>test.txt</tt> znajdują się po kolei znaki <tt>abcd</tt>.  
Załóżmy, że w pliku <tt>test.txt</tt> znajdują się po kolei znaki <tt>abcd</tt>.  
Zmienne tego procesu mają następujące wartości:
Zmienne tego procesu mają następujące wartości:
Takie współdzielenie deskryptorów jest także możliwe w pojedynczym procesie. Aby stworzyć kopię deskryptora pliku używamy funkcji:
:<tt>int dup(int oldfd);</tt>
:<tt>int dup2(int oldfd, int newfd);</tt>
Funkcja <tt>dup</tt> tworzy kopię deskryptora, która odnosi się do ''tego samego'' miejsca w tablicy otwartych plików, co oryginał. Jest to zatem zupełnie inna sytuacja niż przy wielokrotnym otwarciu tego samego pliku.
Funkcja <tt>dup</tt> duplikuje deskryptor <tt>oldfd</tt> i przekazuje wartość nowego deskryptora. Wiadomo przy tym, że przekazywany deskryptor jest deskryptorem o najmniejszej wartości spośród aktualnie dostępnych.
Funkcja <tt>dup2</tt> zamyka deskryptor <tt>newfd</tt> jeśli był on otwarty i duplikuje <tt>oldfd</tt> na tę właśnie wartość. Wynikiem
<tt>dup2</tt> jest numer nowego deskryptora (czyli <tt>newfd</tt>). W wypadku błędu obie funkcje dają w wyniku wartość -1.
; Rysunek
; Test wyboru
Przypuśćmy, że proces wykonuje następujący ciąg operacji:
Załóżmy, że w pliku <tt>test.txt</tt> znajdują się po kolei znaki <tt>abcd</tt>.
Zmienne tego procesu mają następujące wartości:
; Zadanie
:Przeczytaj man dup


Wykonanie funkcji systemowej <tt>exec()</tt> nie ma wpływu na postać tablicy deskryptorów.Po wykonaniu funkcji <tt>exec()</tt> proces  zachowuje otwarte pliki (choć zapewne nie zna już wartości ich deskryptorów, bo zmienne, które przechowywały te wartości przestały istnieć w chwili wykonania funkcji <tt>exec()</tt>).
Wykonanie funkcji systemowej <tt>exec()</tt> nie ma wpływu na postać tablicy deskryptorów.Po wykonaniu funkcji <tt>exec()</tt> proces  zachowuje otwarte pliki (choć zapewne nie zna już wartości ich deskryptorów, bo zmienne, które przechowywały te wartości przestały istnieć w chwili wykonania funkcji <tt>exec()</tt>).
Linia 61: Linia 79:
:Przeczytaj man read i man 2 write
:Przeczytaj man read i man 2 write


<!--
Po uruchomieniu każdego procesu rezerwowane są dla niego trzy specjalne deskryptory o numerach 0, 1 i 2. Są to deskryptory standardowego
  Współdzielenie deskryptorów jest także możliwe w pojedynczym procesie.
wejścia, standardowego wyjścia i standardowego wyjścia błędów procesu. Domyślnie są one związane z konsolą, czyli odczyt po standardowym deskryptorze wejściowym powoduje czytania klawiatury, zapis po standardowym deskryptorze wyjściowym i diagnostycznym powoduje zapis na ekran. Wejście, wyjście i wyjście błędów można przekierowywać z poziomu interpretera poleceń za pomocą operatorów <tt><</tt> <tt>></tt>
  Aby stworzyć kopie deskryptora pliku używamy funkcji:
<tt>>></tt>, <tt>2></tt> oraz  <tt>&></tt>. Na przykład:
      int dup(int oldfd);
:<tt>cat plik > kopia.pliku</tt>  "wyświetli" plik nie na ekran lecz do pliku <tt>kopia.pliku</tt>
      int dup2(int oldfd, int newfd);
  Zwróć uwagę, że dup tworzy kopię deskryptora, która odnosi się
  do TEGO SAMEGO miejsca w tablicy otwartych plików, co oryginał.
  Jest to zatem zupełnie inna sytuacja niż przy wielokrotnym
  otwarciu tego samego pliku.
  Uwaga! Wynikiem funkcji dup i dup2 jest numer nowego deskryptora,
  a w wypadku błędu wartość -1.
* Przeczytaj man dup


=== Łącza nienazwane ===


==== Zasada działania ====


Łącza nienazwane to rodzaj plików istniejących tylko wewnątrz jądra systemu operacyjnego - nie można ich znaleźć na dysku twardym. Służą  one do komunikacji między procesami pokrewnymi. Dwa procesy nazwiemy ''pokrewnymi'', jeśli mają wspólnego przodka (ojca, dziadka itd.) lub  jeden jest przodkiem drugiego. Do łącza można zapisywać dane i odczytywać je za pomocą tych samych funkcji systemowych co w przypadku    plików (<tt>read</tt> oraz <tt>write</tt>). W praktyce o łączu nienazwanym można myśleć jak o buforze utrzymywanym przez system operacyjny. Można do niego zapisywać dane i odczytywać, przy czym dane są odczytywane w kolejności ich zapisywania. Taki bufor jest oczywiście skończony, ale nie znamy jego pojemności. Wiadomo jedynie, że zmieści się w nim co najmniej 4KB danych.


=== Łącza nienazwane
W przypadku próby odczytu danych z pustego łącza proces jest (domyślnie) wstrzymywany, choć zachowanie to można zmienić. Łącza zachowują się zatem bardzo podobnie do abstrakcyjnego mechanizmu <tt>SendMessage</tt> i <tt>GetMessage</tt> przedstawionego na poprzednich ćwiczeniach. Jest to mechanizm asynchroniczny.


==== Zasada działania ====
==== Tworzenie ====
==== Tworzenie ====
==== Zapis ====
Do tworzenia łączy nienazwanych służy funkcja systemowa
==== Odczyt ====
:<tt>int pipe(int filedes[2]);</tt>
Funkcja ta tworzy nowe łącze nienazwane oraz umieszcza w tablicy podanej jako parametr, wartości dwóch deskryptorów. Pierwszy deskryptor służy do czytania, a drugi do pisania do utworzonego łącza.
 
; Rysunek
 
; Zadanie
: Przeczytaj man pipe
 
Oczywiście informacje o łączu trafiają również do systemowej tablicy otwartych plików, więc rozważania przedstawione we wstępie pozostają w mocy także w odniesieniu do łączy.
 
==== Zapis do łącza ====
 
Jeśli <tt>fd</tt> jest deskryptorem służącym do zapisu do pewnego łącza, to funkcja systemowa
:<tt>write (fd, buf, count)</tt>
próbuje zapisuje do łącza o deskryptorze <tt>fd</tt> <tt>count</tt> bajtów znajdujących się w tablicy <tt>buf</tt>. Ale łącza mają ograniczona pojemność. Proces, który próbuje zapisać do łącza, w którym nie ma miejsca na całą zapisywaną porcję jest wstrzymany do
czasu, aż z łącza zostanie odczytana taka ilość danych by znalazło się miejsce na wszystkie zapisywane dane. Zwróćmy uwagę: write zapisze ''wszystko'' albo ''nic''. Jest jednak wyjątek od tej reguły. Gdy próbujemy na raz zapisać do łącza więcej niż jego pojemność, to proces zapisuje do łącza tyle danych ile może i jest wstrzymywany domomentu aż znowu będzie mógł coś do łącza wpisać.
Wynikiem funkcji <tt>write</tt> jest liczba zapisanych bajtów lub -1, jeśli nastąpił błąd. Jeśli nie próbujemy pisać więcej niż pojemność łącza, to wynikiem powinna być wartość <tt>count</tt>.
 
Zapis do łącza jest możliwy tylko wtedy, gdy jest ono otwarte (przez ten sam lub inny proces) do czytania. Jeśli proces spróbuje zapisać coś do łącza, które nie jest przez żaden proces otwarte do czytania, to zostanie przerwany sygnałem <tt>SIGPIPE</tt> (więcej o sygnałach wkrótce). Ten błąd najczęściej objawia się komunikatem <tt>broken pipe</tt> z poziomu interpretera poleceń.
 
==== Odczyt z łącza ====
Jeśli <tt>fd</tt> jest deskryptorem służącym do odczytu z pewnego łącza, to funkcja systemowa:
:<tt>ssize_t read(int fd, void *buf, size_t count);</tt>
odczyta z łącza co najwyżej <tt>count</tt> bajtów i zapisze je pod adresem <ttbuf></tt>. Jeśli w łączu znajduje się mniej bajtów niż <tt>count</tt> (ale łącze nie jest puste), to funkcja <tt>read</tt> odczyta to, co jest w łączu i kończy się pomyślnie. Jej wynikiem jest wówczas liczba faktycznie odczytanych bajtów. Próba odczytu z pustego łącza wstrzymuje proces do czasu pojawienia się w łączu jakichkolwiek danych lub do chwili, gdy łącze nie będzie otwarte do zapisu. Kolejność odczytu jest zgodna z kolejnością zapisu (łącza są kolejkami prostymi).
 
Jeśli proces próbuje odczytać z łącza, które nie jest przez nikogo otwarte do zapisu, to funkcja <tt>read</tt> kończy się natychmiast przekazując w wyniku 0. Ta własność jest wykorzystywana do sprawdzania, czy jest coś jeszcze do odczytu.
<!---
==== Zamykanie ====
==== Zamykanie ====


Linia 103: Linia 141:




  Przy starcie każdego procesu rezerwowane są trzy specjalne deskryptory
  o numerach 0, 1 i 2. Są to deskryptory standardowego
  wejścia, wyjścia i standardowego wyjścia błędów procesu.
  Wejście, wyjście i wyjście błędów można przekierowywać z poziomu
  interpretatora poleceń (shella) za pomocą operatorów '<' '>' '>>'
  '2>' oraz  '&>'.
  Na przykład: cat plik > kopia.pliku  "wyświetli" plik nie na ekran
  lecz do pliku 'kopia.pliku'.
2. PIPE - czyli łącza nienazwane
  -----------------------------
  Łącza nienazwane to rodzaj plików istniejących tylko wewnątrz jądra
  systemu operacyjnego - nie można ich znaleźć na dysku twardym. Służą
  one do komunikacji między procesami pokrewnymi. Dwa procesy nazwiemy
  pokrewnymi jeśli maja wspólnego przodka (ojca, dziadka, itd.) lub
  jeden jest przodkiem drugiego. Do łącza można zapisywać dane i
  odczytywać je za pomocą tych samych funkcji systemowych co
  pliki (read oraz write). Łącza są jednokierunkowe, zatem aby
  uzyskać komunikacje w dwie strony należy utworzyć parę łącz
  nienazwanych.
  Do tworzenia łączy nienazwanych służy funkcja systemowa
        int pipe(int filedes[2]);
* Przeczytaj man pipe


   Funkcja ta tworzy nowe łącze nienazwane oraz umieszcza w tablicy podanej
   Funkcja ta tworzy nowe łącze nienazwane oraz umieszcza w tablicy podanej
Linia 136: Linia 147:




3. Jak pisać do łącza ?
  --------------------
  Funkcja write (fd, buf, count) zapisuje do otwartego łącza o
  deskryptorze fd nie więcej niż count bajtów znajdujących się w tablicy buf.
  Łącza mają ograniczona pojemność (nie mniejszą niż 4Kb).
  Proces, który próbuje zapisać do łącza, w którym nie ma miejsca
  na całą zapisywaną porcję jest wstrzymany do
  czasu, aż z łącza zostanie odczytana taka ilość danych by znalazło
  się miejsce na wszystkie zapisywane dane. Zwróćmy uwagę: write
  zapisze wszystko albo nic. Jedyny wyjątek od tej reguły jest wtedy,
  gdy próbujemy na raz zapisać do łącza więcej niż jego pojemność.
  Wtedy i proces zapisuje do łącza ile może i jest wstrzymywany do
  momentu aż znowu będzie mógł coś do łącza wpisać.
  Wynikiem funkcji write jest liczba zapisanych bajtów lub
  -1, jeśli nastąpił błąd.
  UWAGA
  -----
  Zapis do łącza jest możliwy tylko wtedy, gdy jest ono otwarte
  (przez ten sam lub inny proces) do czytania.
  Jeśli proces spróbuje pisać do łącza, które nie jest przez żaden
  proces otwarte do czytania, zostanie przerwany sygnałem SIGPIPE
  (więcej o sygnałach wkrótce). Ten błąd najczęściej ujawnia się
  komunikatem 'broken pipe' z poziomu interpretera poleceń.
4. Jak czytać z łącza
  -----------------
  Do odczytu z łącza używamy funkcji systemowej
      ssize_t read(int fd, void *buf, size_t count);
* Przeczytaj man read
  Tak jak w przypadku zwykłych plików funkcja odczytuje z łącza
  o deskryptorze fd nie więcej niż count bajtów do bufora znajdującego się
  pod adresem buf. Jeśli w łączu znajduje się mniej bajtów niż count (ale
  łącze nie jest puste), to funkcja read odczytuje to, co jest w łączu i
  kończy się pomyślnie.
  Odczyt z pustego łącza wstrzymuje proces odczytujący do czasu pojawienia
  się w łączu jakichkolwiek danych.
  Kolejność odczytu jest zgodna z kolejnością zapisu (łącza są kolejkami
  prostymi).
  Wynikiem funkcji read jest liczba faktycznie przeczytanych bajtów.
  Zwróć uwagę, że zgodnie z powyższym opisem, nie powinno to nigdy być
  zero.
  Jednak zero może także być wynikiem funkcji read.  Dzieje się tak
  przy próbie odczytu z łącza, które nie jest przez żaden proces otwarte


  do zapisu.


   Typowy scenariusz użycia łącza jest zatem następujący.
   Typowy scenariusz użycia łącza jest zatem następujący.

Wersja z 08:39, 13 cze 2006

Tematyka laboratorium

  1. Łącza nienazwane w systemie Unix: deskryptory plików, tworzenie i zamykanie łączy, duplikowanie łączy, zapis i odczyt z łącza
  2. Łącza nienazwane jako realizacja komunikacji asynchronicznej

Literatura uzupełniająca

  1. M. Rochkind, Programowanie w systemie Unix dla zaawansowanych, rozdz. 6.2 i 6.3
  2. W. R. Stevens, Programowanie zastowań sieciowych w systemie UNI, rozdz. 2.3 i 3.4
  3. M. J. Bach, Budowa systemu operacyjnego UNIX, rozdz. 7.1
  4. man do poszczególnych funkcji systemowych

Pliki z programami przykładowymi

  • Makefile
  • err.h plik nagłówkowy biblioteki obsługującej błędy
  • err.c biblioteka do obsługi błędów
  • parent_pipe.c program ilustrujący komunikację za pomocą łącza. Współpracuje z child_pipe
  • child_pipe.c program ilustrujący komunikację za pomocą łącza. Współpracuje z parent_pipe
  • parent_dup.c program ilustrujący duplikację łącza

Scenariusz zajęć

=== Deskryptory, otwarte pliki Gdy proces otwiera plik, informacje o tym pliku (m.in.: tryb otwarcia, położenie na dysku, wskaźnik pliku, czyli pozycja w pliku, której będzie dotyczyć kolejna operacja wejścia/wyjścia itp.) są zapisywane w tablicy otwartych plików utrzymywanej przez system operacyjny. W całym systemie jest jedna taka tablica. Z kolei informacja o położeniu danych o pliku w systemowej tablicy plików otwartych (oraz pewne dodatkowe informacje) jest zapamiętywana w tablicy deskryptorów tego procesu, który plik otworzył. Każdy proces utrzymuje tablicę deskryptorów --- jest ona lokalna dla procesu, znajduje się w jego danych systemowych (a więc jest kopiowana przy wykonywaniu funkcji fork()). Indeks w tej tablicy to właśnie deskryptor pliku.

Rysunek

Do otwierania pliku służy funkcja systemowa

open.

W wyniku przekazuje ona deskryptor otwartego pliku lub informacje o błędzie.

Zadanie
Przeczytaj stronę man dotyczącą funkcji open

W wyniku otwarcia pliku, tworzy się nowa pozycja w tablicy otwartych plików. Oznacza to, że jeśli proces dwukrotnie otworzy ten sam plik, to w tablicy otwartych plików będą dwie różne pozycje dotyczące tego pliku. Proces będzie miał również dwa różne deskryptory odnoszące się do tego samego pliku. Ponieważ wskaźnik pliku jest pamiętany w systemowej tablicy otwartych plików, więc operacje wejścia/wyjścia wykonywane za pomocą różnych deskryptorów będą od siebie niezależne (wykonanie operacji odczytu za pośrednictwem jednego deskryptora nie ma wpływu na pozycję, z której odczytamy kolejne dane z pliku za pośrednictwem drugiego deskryptora).

Test wyboru

Przypuśćmy, że proces wykonuje następujący ciąg operacji: Załóżmy, że w pliku test.txt znajdują się po kolei znaki abcd. Zmienne tego procesu mają następujące wartości:



Gdy proces wykonuje funkcję systemową fork() tablica deskryptorów jest kopiowana do procesu potomnego. Zatem potomek "dziedziczy" po procesie macierzystym wszystkie otwarte pliki. Co więcej, deskryptory o tych samych wartościach w procesie potomnym i macierzystym odnoszą się do tej samej pozycji w systemowej tablicy otwartych plików.

Rysunek

Wynika z tego, że rodzic i potomek współdzielą otwarte pliki, tj. każda operacja wejścia/wyjścia w procesie macierzystym przesuwa wskaźnik pliku i powoduje, że kolejna operacja wejścia/wyjścia w procesie potomnym będzie dotyczyć kolejnej porcji danych z pliku.

Test wyboru

Przypuśćmy, że proces wykonuje następujący ciąg operacji: Załóżmy, że w pliku test.txt znajdują się po kolei znaki abcd. Zmienne tego procesu mają następujące wartości:


Takie współdzielenie deskryptorów jest także możliwe w pojedynczym procesie. Aby stworzyć kopię deskryptora pliku używamy funkcji:

int dup(int oldfd);
int dup2(int oldfd, int newfd);

Funkcja dup tworzy kopię deskryptora, która odnosi się do tego samego miejsca w tablicy otwartych plików, co oryginał. Jest to zatem zupełnie inna sytuacja niż przy wielokrotnym otwarciu tego samego pliku. Funkcja dup duplikuje deskryptor oldfd i przekazuje wartość nowego deskryptora. Wiadomo przy tym, że przekazywany deskryptor jest deskryptorem o najmniejszej wartości spośród aktualnie dostępnych. Funkcja dup2 zamyka deskryptor newfd jeśli był on otwarty i duplikuje oldfd na tę właśnie wartość. Wynikiem dup2 jest numer nowego deskryptora (czyli newfd). W wypadku błędu obie funkcje dają w wyniku wartość -1.

Rysunek
Test wyboru

Przypuśćmy, że proces wykonuje następujący ciąg operacji: Załóżmy, że w pliku test.txt znajdują się po kolei znaki abcd. Zmienne tego procesu mają następujące wartości:

Zadanie
Przeczytaj man dup

Wykonanie funkcji systemowej exec() nie ma wpływu na postać tablicy deskryptorów.Po wykonaniu funkcji exec() proces zachowuje otwarte pliki (choć zapewne nie zna już wartości ich deskryptorów, bo zmienne, które przechowywały te wartości przestały istnieć w chwili wykonania funkcji exec()).

Do czytania z pliku służy funkcja systemowa read, a do zapisu funkcja systemowa write. Funkcje te są niepodzielne. Oznacza to, że operacje read i/lub write wykonywane na tym samym pliku "jednocześnie" nie będą się przeplatać: druga rozpocznie się po zakończeniu pierwszej.

Zadanie
Przeczytaj man read i man 2 write

Po uruchomieniu każdego procesu rezerwowane są dla niego trzy specjalne deskryptory o numerach 0, 1 i 2. Są to deskryptory standardowego wejścia, standardowego wyjścia i standardowego wyjścia błędów procesu. Domyślnie są one związane z konsolą, czyli odczyt po standardowym deskryptorze wejściowym powoduje czytania klawiatury, zapis po standardowym deskryptorze wyjściowym i diagnostycznym powoduje zapis na ekran. Wejście, wyjście i wyjście błędów można przekierowywać z poziomu interpretera poleceń za pomocą operatorów < > >>, 2> oraz &>. Na przykład:

cat plik > kopia.pliku "wyświetli" plik nie na ekran lecz do pliku kopia.pliku

Łącza nienazwane

Zasada działania

Łącza nienazwane to rodzaj plików istniejących tylko wewnątrz jądra systemu operacyjnego - nie można ich znaleźć na dysku twardym. Służą one do komunikacji między procesami pokrewnymi. Dwa procesy nazwiemy pokrewnymi, jeśli mają wspólnego przodka (ojca, dziadka itd.) lub jeden jest przodkiem drugiego. Do łącza można zapisywać dane i odczytywać je za pomocą tych samych funkcji systemowych co w przypadku plików (read oraz write). W praktyce o łączu nienazwanym można myśleć jak o buforze utrzymywanym przez system operacyjny. Można do niego zapisywać dane i odczytywać, przy czym dane są odczytywane w kolejności ich zapisywania. Taki bufor jest oczywiście skończony, ale nie znamy jego pojemności. Wiadomo jedynie, że zmieści się w nim co najmniej 4KB danych.

W przypadku próby odczytu danych z pustego łącza proces jest (domyślnie) wstrzymywany, choć zachowanie to można zmienić. Łącza zachowują się zatem bardzo podobnie do abstrakcyjnego mechanizmu SendMessage i GetMessage przedstawionego na poprzednich ćwiczeniach. Jest to mechanizm asynchroniczny.

Tworzenie

Do tworzenia łączy nienazwanych służy funkcja systemowa

int pipe(int filedes[2]);

Funkcja ta tworzy nowe łącze nienazwane oraz umieszcza w tablicy podanej jako parametr, wartości dwóch deskryptorów. Pierwszy deskryptor służy do czytania, a drugi do pisania do utworzonego łącza.

Rysunek
Zadanie
Przeczytaj man pipe

Oczywiście informacje o łączu trafiają również do systemowej tablicy otwartych plików, więc rozważania przedstawione we wstępie pozostają w mocy także w odniesieniu do łączy.

Zapis do łącza

Jeśli fd jest deskryptorem służącym do zapisu do pewnego łącza, to funkcja systemowa

write (fd, buf, count)

próbuje zapisuje do łącza o deskryptorze fd count bajtów znajdujących się w tablicy buf. Ale łącza mają ograniczona pojemność. Proces, który próbuje zapisać do łącza, w którym nie ma miejsca na całą zapisywaną porcję jest wstrzymany do czasu, aż z łącza zostanie odczytana taka ilość danych by znalazło się miejsce na wszystkie zapisywane dane. Zwróćmy uwagę: write zapisze wszystko albo nic. Jest jednak wyjątek od tej reguły. Gdy próbujemy na raz zapisać do łącza więcej niż jego pojemność, to proces zapisuje do łącza tyle danych ile może i jest wstrzymywany domomentu aż znowu będzie mógł coś do łącza wpisać. Wynikiem funkcji write jest liczba zapisanych bajtów lub -1, jeśli nastąpił błąd. Jeśli nie próbujemy pisać więcej niż pojemność łącza, to wynikiem powinna być wartość count.

Zapis do łącza jest możliwy tylko wtedy, gdy jest ono otwarte (przez ten sam lub inny proces) do czytania. Jeśli proces spróbuje zapisać coś do łącza, które nie jest przez żaden proces otwarte do czytania, to zostanie przerwany sygnałem SIGPIPE (więcej o sygnałach wkrótce). Ten błąd najczęściej objawia się komunikatem broken pipe z poziomu interpretera poleceń.

Odczyt z łącza

Jeśli fd jest deskryptorem służącym do odczytu z pewnego łącza, to funkcja systemowa:

ssize_t read(int fd, void *buf, size_t count);

odczyta z łącza co najwyżej count bajtów i zapisze je pod adresem <ttbuf>. Jeśli w łączu znajduje się mniej bajtów niż count (ale łącze nie jest puste), to funkcja read odczyta to, co jest w łączu i kończy się pomyślnie. Jej wynikiem jest wówczas liczba faktycznie odczytanych bajtów. Próba odczytu z pustego łącza wstrzymuje proces do czasu pojawienia się w łączu jakichkolwiek danych lub do chwili, gdy łącze nie będzie otwarte do zapisu. Kolejność odczytu jest zgodna z kolejnością zapisu (łącza są kolejkami prostymi).

Jeśli proces próbuje odczytać z łącza, które nie jest przez nikogo otwarte do zapisu, to funkcja read kończy się natychmiast przekazując w wyniku 0. Ta własność jest wykorzystywana do sprawdzania, czy jest coś jeszcze do odczytu.