Do spisu tresci tematu 3

3.1.1 Lacza nienazwane



Spis tresci


Wprowadzenie

Lacza komunikacyjne sa najprostszym sposobem do przekazywania danych miedzy procesami. Dane przekazywane sa zgodnie z zasada kolejki prostej (FIFO). Lacza dzielimy na dwa rodzaje: nazwane (kolejki FIFO) i nienazwane (ang. pipe). Dostep do laczy nienazwanych moga wspoldzielic tylko procesy pokrewne, tzn. Potomkowie procesu, ktory wywolal funkcje pipe wlacznie z nim samym (dzieki dziedziczeniu deskryptorow).
Typowy scenariusz korzystania z laczy nienazwanych wyglada nastepujaco:


Struktury danych

Wprowadzenie

Lacze nienazwane przechowywane jest w zwyklym i-wezle. Roznica jest jedynie wartosc pola i_pipe ustawiona na 1 oraz u , w ktorym przechowywana jest struktura pipe_inode_info (znajduja sie w niej wszystkie informacje potrzebne do korzystania z lacza oraz bufor na dane, dzieki temu rozne procesy korzystaja z tych samych wskaznikow do lacza tzn. miejsce, w ktorym zaczynaja sie dane w buforze lacza).

W pliku include/linux/limits.h zdefiniowane jest ograniczenie na wielkosc bufora na przechowywane dane:

#define PIPE_BUF 4096    /* max rozmiar bufora dla lacza (w bajtach) */


Struktura pipe_inode_info

Oto dokladna definicja struktury pipe_inode_info z pliku include/linux/pipe_fs_i.h:

struct pipe_inode_info {
        struct wait_queue * wait;   /* kolejka dla procesow,ktore chca 
                                     pisac do pelnego lacza oraz tych,
                                     ktore chca czytac z pustego lacza */
        char * base;                /* wskaznik do bufora danych */
        unsigned int start;         /* poczatek danych w buforze */
        unsigned int len;           /* dlugosc danych (w bajtach)*/
        unsigned int lock;          /* blokada lacza */
        unsigned int rd_openers;    /* ilosc czytelnikow, ktorzy otworzyli
                                      lacze (tylko dla lacza nazwanego) */ 
        unsigned int wr_openers;    /* ilosc pisarzy, ktorzy otworzyli
                                      lacze (tylko dla lacza nazwanego) */
        unsigned int readers;       /* ilosc czytelnikow */
        unsigned int writers;       /* ilosc pisarzy */
};



Funkcje i ich implementacja

Wprowadzenie

Do obslugi laczy procesy uzywaja zwyklych funkcji stosowanych do plikow, takich jak read, write i close. Aby utworzyc lacze nienazwane i tym samym je otworzyc nalezy uzyc funkcji pipe.

Funkcja pipe()

- sluzy do tworzenia lacza nienazwanego.

Przed wywolaniem funkcji nalezy wczesniej zarezerwowac miejsce dla argumentu fd.

DEFINICJA: int pipe(int fd[2])
    WYNIK: 0 w przypadku sukcesu  oraz w tablicy dwuelementowej fd 
           pierwszym elementem jest deskryptor do czytania a drugim
           deskryptor do pisania 
           -1, gdy blad: errno = EMFILE (za duzo deskryptorow jest w uzyciu
                                         przez proces)
                                 ENFILE (brakuje miejsca w systemowa 
                                         tablica plikow)
                                 EFAULT  (zly argument)


A oto jak wyglada algorytm:

{
   sprawdzenie poprawnosci parametru fd;
   przydziel nowy i-wezel z urzadzenia do przechowywania laczy
      (za pomoca funkcji get_pipe_inode);
   znajdz dwie wolne pozycje w tablicy plikow i przydziel pierwsza do
      czytania a druga do pisania (za pomoca funkcji get_empty_filp);
   znajdz dwie wolne pozycje w tablicy deskryptorow procesu i zainicjuj
      je, tak by wskazywaly na odpowiednie pozycje w tablicy plikow, 
      podstawiajac od razu odpowiednie wartosci w argumencie fd
      (fd[0]= deskryptor do czytania, fd[1]= deskryptor do pisania);
   zainicjuj znalezione pozycje w tablicy plikow, by wskazywaly na nowy
      i-wezel;
   ustaw flagi i wskaznik do struktury z operacjami w znalezionych pozycjach
      w tablicy plikow odpowiednio do czytania i pisania;

}


Funkcja read()

- sluzy do czytania lacza nienazwanego.

Raz przeczytanej informacji nie mozna powtornie odczytac.

DEFINICJA: int read(int fd, char *buf, size_t count)
    WYNIK: w przypadku sukcesu liczba przeczytanych bajtow
           (0 jesli nie ma pisarzy i puste lacze) 
           -1, gdy blad: errno = EINTR (funkcja zostala przerwana przez sygnal)
                                 EAGAIN (zostala uzyta flaga O_NONBLOCK i 
                                         lacze bylo puste)
                                 EISDIR  (fd jest katalogiem)
                                 EBADF  (fd nie jest deskryptorem)
                                 EINVAL  (fd nie jest deskryptorem, z ktorego
                                          mozna czytac)
                                 EFAULT  (wskaznik buf jest poza przestrzenia
                                          adresowa procesu)

Czytanie danych z lacza przebiega zgodnie z ponizszymi regulami:

Bardziej szczegolowe informacje dotyczace algorytmu mozna znalezc w temacie Kolejki FIFO .

Funkcja write()

- sluzy do pisania do lacza nienazwanego.

DEFINICJA: size_t read(int fd,const char *buf, size_t count)
    WYNIK: w przypadku sukcesu liczba bajtow zapisanych do lacza 
           -1, gdy blad: errno = EINTR (funkcja zostala przerwana przez sygnal)
                                 EAGAIN (zostala uzyta flaga O_NONBLOCK i 
                                         nie bylo miejsca na wpisanie wszystkich
                                        danych)
                                 EBADF  (fd nie jest deskryptorem)
                                 EINVAL  (fd nie jest deskryptorem, do ktorego
                                          mozna pisac)
                                 EFAULT  (wskaznik buf jest poza przestrzenia
                                          adresowa procesu)
                                 EPIPE  (nie ma czytelnikow i lacze jest
                                         zablokowane lub nie ma w nim miejsca)

Pisanie danych do lacza przebiega zgodnie z ponizszymi regulami:


Jezeli proces chce zapisac wieksza liczbe bajtow niz wynosi ilosc wolnego miejsca i nie jest ustawiona flaga O_NONBLOCK, to operacja pisania moze nie byc niepodzielna (po czesci danych od jednego procesu moze zostac umieszczona czesc danych od drugiego procesu). W pozostalych przypadkach operacja pisania jest podzielna.
Jesli komus nie wystarczaja te informacje, to bardziej szczegolowy algorytm znajdzie w temacie Kolejki FIFO.


Bibliografia

  1. Pliki zrodlowe Linuxa:
  2. Maurice J. Bach: Budowa systemu operacyjnego Unix - rozdzial 5.12 o laczach
  3. W. Richard Stevens: Programowanie zastosowan sieciowych w systemie Unix - rozdzial 3.4 o laczach komunikaccyjnych


Autor: : Marcin Stopyra