background image

 

 

11. PAKIET IPC

Narzędzia z pakietu IPC 

(InterProcess Communication)

 służą do koordynacji 

procesów wykonywa-

nych na jednym komputerze (nie są przeznaczone do komunikacji sieciowej). W 
skład tego pakietu

wchodzą biblioteki funkcji obsługujących kolejki komunikatów 

(message 

queue)

pamięć dzieloną

(shared memory)

 i semafory 

(semaphore)

. Z wszystkimi trzema rodzajami 

obiektów związane są

odpowiednie struktury danych tworzone przez jądro systemu, do których dostęp 
jest możliwy jedynie

poprzez wywoływanie przeznaczonych do tego funkcji systemowych.

Zestawienie głównych funkcji pakietu IPC (wg W.R. Stevensa):

                                                                                Kolejki komun.          Semafory   
        Pamięć dziel.

Plik nagłówkowy                                                    < sys/msg.h >          < 
sys/sem.h>        < sys/shm.h>

Funkcja systemowa tworzenia lub otwierania            msgget                    semget    
              shmget

Funkcja systemowa operacji sterujących                    msgctl                     semctl     
             shmctl

Funkcje systemowe przesyłania                                  msgsnd                   semop      
             shmat

                                                                                     msgrcv                                    
             shmdt

background image

 

 

Ze względu na to, że struktury kontrolne obiektów pakietu IPC są przechowywane 
w jądrze systemu

i mogą być widoczne dla wszystkich procesów, muszą mieć klucze unikalne w 
obrębie całego systemu.

Zalecane jest stosowanie funkcji ftok generującej unikalne klucze na podstawie 
ścieżek dostępu do

plików z programami wykonywanymi przez procesy tworzące obiekty pakietu IPC. 
Dopuszczalny

zakres kluczy zależy od ustawień systemowych - odpowiada mu typ key_t 
zdefiniowany w nagłówku

< sys/types.h >.

Kolejki komunikatów

Kolejki komunikatów nie są kolejkami prostymi (komunikaty mogą być z nich 
wybierane w innej

kolejności, niż zostały umieszczone). Komunikaty posiadają pewną strukturę (nie 
są tylko ciągami

bajtów, jak w przypadku łącz):

struct msgbuf { long mtype ;

                          char mtext[1] ; }

Uwaga. Typ char zawartości komunikatu jest zdefiniowany tylko pro forma - może 
być rzutowany.

background image

 

 

int msgget (key_t klucz, int flagi);

Zwraca: identyfikator kolejki w przypadku sukcesu ;

              -1 w przypadku błędu.

Flagi określają prawa dostępu, oraz czy ma być zwrócony błąd, jeśli kolejka o 
danym kluczu już istnieje.

Działanie: tworzy kolejkę o podanym kluczu, jeśli taka kolejka jeszcze nie istnieje.

int msgsnd (int ident, struct msgbuf kom, int rozmiar, int flagi) ;

Zwraca: 0 w przypadku sukcesu ;

              -1 w przypadku błędu.

ident - identyfikator kolejki (zwrócony przez msgget)

kom - wskaźnik do struktury przechowującej typ komunikatu i sam komunikat 
(bufora komunikatu)

rozmiar - rozmiar komunikatu „netto” (nie licząc typu)

flagi - 0 lub IPC_NOWAIT (decydują, czy w sytuacji przepełnienia kolejki proces ma 
być zawieszony)

Działanie: wstawia komunikat wraz z podanym typem na koniec kolejki.

background image

 

 

int msgrcv (int ident; struct msgbuf kom, int rozmiar, long mtype, int flagi);

Zwraca: liczbę faktycznie pobranych bajtów z kolejki w przypadku sukcesu ;

              -1 w przypadku błędu.

ident - identyfikator kolejki

kom - wskaźnik do bufora komunikatu

rozmiar - rozmiar struktury komunikatu (nie licząc typu)

mtype - typ komunikatu, jaki chcemy pobrać z kolejki (może być 0)

flagi - można ustawić IPC_NOWAIT i / lub MSG_NOERROR (powoduje 
odpowiednie zachowanie,

           jeśli komunikat jest większy, niż przewiduje rozmiar)

Działanie: pobiera z kolejki najdawniej wstawiony komunikat o danym typie (jeśli 
istnieje), zaś

                  jeśli został podany typ 0, pobiera najdawniej wstawiony komunikat (o 
dowolnym typie).

background image

 

 

int msgctl (int ident, int polecenie, struct msgqid_ds struktura);

Zwraca: 0 w przypadku sukcesu ;

              -1 w przypadku błędu.

ident - identyfikator kolejki

polecenie - kod czynności do wykonania na strukturze kontrolnej kolejki

struktura - wskaźnik do bufora struktury kontrolnej

Działanie: może wykonywać mnóstwo różnych czynności (w tym również takich, 
które mogą 

                 pozbawić programistę kontroli nad kolejką) - zmieniać prawa dostępu, 
odczytywać

                 informacje o ostatnio wykonanej operacji na kolejce itp. Najczęściej jest 
wykorzystywana

                do usunięcia kolejki:

                                                      msgctl (ident, IPC_RMID, 0);

background image

 

 

Kolejki komunikatów są narzędziem bardziej skomplikowanym w użyciu, a 
jednocześnie oferującym

bogatsze możliwości, niż kolejki FIFO. W typowym zastosowaniu - implementacji 
par programów

typu klient / serwer - stosując kolejki FIFO musimy otworzyć oddzielną kolejkę dla 
serwera i oddzielne

dla wszystkich klientów (gdyż nie ma możliwości testowania danych 
umieszczonych w jednej kolejce

dla wielu odbiorców).

klient 1

     kolejka 
klienta 1

Dane od klientów powinny na początku zawierać informację o swojej długości oraz 
adres zwrotny

(to jest adres kolejki odbiorczej klienta).

serw
er

    kolejka 
serwera

 klient 
2

   kolejka klienta 
2

klient 
n

   kolejka 
klienta n

background image

 

 

W przypadku stosowania kolejek komunikatów wystarczają dwie takie kolejki 
(związane z serwerem),

gdyż umożliwiają demultipleksowanie odpowiedzi serwera do różnych klientów.

serwe
r

       odpowiedzi

          pytania

Klienci muszą mieć unikalne adresy kodowane jako typy komunikatów. 
Odpowiedzi serwera są

opatrywane tymi samymi typami, jakie miały przysyłane przez klientów pytania - 
klienci mogą je

selektywnie wybierać z kolejki zwrotnej.

Uwaga. Jest również możliwe rozwiązanie przy użyciu tylko jednej kolejki (dla 
pytań i odpowiedzi).

klient 
1

klient 
2

klient 
n

background image

 

 

Pamięć dzielona

Poza własnym segmentem danych przydzielonym w momencie utworzenia, 
proces może mieć

przydzielony dynamicznie (w trakcie wykonywania) jeden lub więcej segmentów 
pamięci z ogólnych

zasobów systemowych. Takie segmenty są dołączane do przestrzeni adresowej 
procesu i można na

nich operować bezpośrednio (na przykład wykonując operacje przypisania). Jeśli 
programista ustanowi

odpowiednie prawa dostępu, segmenty takie mogą być niezależnie przydzielane 
wielu procesom

jednocześnie. Komunikowanie się przez pamięć dzieloną jest zdecydowanie 
najszybszym sposobem

komunikowania się procesów (choć najtrudniejszym do synchronizacji).

Uwaga.

1) Podobnie jak w przypadku plików i dowiązań do nich, segment pamięci 
dzielonej jest zwracany do

    puli wolnych zasobów systemowych dopiero wtedy, gdy ostatni z użytkujących 
go procesów

    zrzeknie się jego dalszego używania (odłączy go od swojej przestrzeni 
adresowej).

2) Nie ma możliwości operowania w segmencie pamięci dzielonej inaczej, niż za 
pomocą zmiennych

    dynamicznych (wskaźników). W gruncie rzeczy segmenty pamięci dzielonej są 
widziane przez

    programy jako dodatkowe (współdzielone) sterty.

background image

 

 

3) Jeden i ten sam segment pamięci dzielonej może być dołączony do przestrzeni 
adresowej procesu 

    w wielu różnych miejscach. W ten sposób możemy dysponować wieloma 
kopiami jednej i tej 

    samej zmiennej, zmiana wartości jednej kopii jest natychmiast widoczna w 
pozostałych miejscach.

4) Proces potomny dziedziczy utworzony (i przyłączony) segment pamięci 
dzielonej.

int shmget (key_t klucz, int rozmiar, int flagi);

Zwraca: identyfikator segmentu w przypadku sukcesu;

              -1 w przypadku błędu.

klucz, flagi - pełnią podobną rolę, jak dla kolejek komunikatów

rozmiar - rozmiar tworzonego segmentu w bajtach (argument nieistotny, jeśli 
segment już istnieje)

Działanie: tworzy nową pozycję w tablicy segmentów, jeśli segment wcześniej nie 
istniał.

background image

 

 

int shmat (int ident, char adres, int flagi);

Zwraca: wskaźnik do miejsca, gdzie rzeczywiście został dołączony segment, w 
przypadku sukcesu;

              -1 w przypadku błędu.

ident - identyfikator segmentu (zwrócony przez funkcję shmget)

adres - wskaźnik do miejsca, gdzie programista proponuje dołączyć segment 
(może być 0)

flagi - rozmaite role ( na przykład mogą nakazać dołączenie segmentu tylko do 
odczytu)

Działanie: dołącza segment (i zwiększa jego licznik dowiązań o 1) pod podanym 
adresem (w miarę

                 możności) jeśli adres jest większy od 0, zaś pod adresem wybranym 
przez system, jeśli

                 podany adres jest równy 0 (najczęściej stosowane i najbardziej 
zalecane postępowanie).

background image

 

 

int shmdt (char *adres);

Zwraca: 0 w przypadku sukcesu;

              -1 w przypadku błędu.

adres - adres, pod którym był dołączony (przez funkcję shmat) segment pamięci 
dzielonej

Działanie: segment jest odłączany od przestrzeni adresowej procesu, a jego licznik 
dowiązań zmniej-

                 szany o 1. Jeżeli stan licznika dowiązań zmniejszył się w wyniku tego do 
0, a segment był

                 oznaczony do usunięcia, w tym momencie następuje jego usunięcie z 
tablicy segmentów.

background image

 

 

int shmctl (int ident, int polecenie, struct shmid_ds struktura);

Zwraca: 0 w przypadku sukcesu;

              -1 w przypadku błędu.

ident - identyfikator segmentu (zwrócony przez funkcję shmget)

polecenie - kod polecenia do wykonania na strukturze zarządzającej segmentem

struktura - wskaźnik do bufora struktury zarządzającej segmentem

Działanie: podobnie, jak w przypadku kolejek komunikatów, może wykonywać 
wiele różnych

                 czynności, a najczęsciej wykonywaną jest oznaczenie segmentu do 
usunięcia:

                                                 shmctl (ident, IPC_RMID, 0);

Uwaga. Zalecane jest odłączenie segmentu (wykonanie funkcji shmdt) przed 
oznaczeniem segmentu

              do usunięcia.

background image

 

 

Semafory

Semafory są uważane za najbardziej skomplikowane w użyciu obiekty pakietu 
IPC. Ich najbardziej

typowym zastosowaniem jest synchronizacja dostępu różnych procesów do 
zmiennych w pamięci

dzielonej. Semafory zaimplementowane w pakiecie IPC są podobne do 
semaforów Agerwali.

Występują nie jako pojedyncze obiekty, ale jako elementy tablic semaforów, na 
których można

wykonywać jednocześnie (niepodzielne) operacje. Maksymalny rozmiar tablicy 
semaforów zależy od

ustawień systemowych. Zakres wartości przyjmowanych przez pojedynczy 
semafor jest zakresem

wartości typu ushort (czyli od 0 do 255).

               0             1             2              3                              
       n-1

sem. 0    sem. 1    sem. 2     sem. 3                            
sem. n-1

background image

 

 

int semget (key_t klucz, int liczbasem, int flagi);

Zwraca: identyfikator tablicy semaforów w przypadku sukcesu;

              -1 w przypadku błędu.

klucz, flagi - jak dla kolejek komunikatów i pamięci dzielonej

liczbasem - liczba semaforów w tablicy (argument nieistotny, jeśli tablica już 
istnieje)

Działanie: tworzy nową tablicę semaforów, jeśli wcześniej nie istniała.

background image

 

 

int semop (int ident, struct sembuf oper, unsigned liczbaoper);

gdzie        struct sembuf

                     { ushort sem_num;                         numer semafora w tablicy

                        short sem_op;                              operacja na semaforze

                        short sem_flg;  }                          flagi operacji

Zwraca: 0 w przypadku sukcesu;

              -1 w przypadku błędu.

ident - identyfikator tablicy semaforów (zwrócony przez funkcję semget)

oper - wskaźnik do początku tablicy operacji (tablicy struktur sembuf)

liczbaoper - liczba elementów w tablicy wskazywanej przez oper

Działanie: system wykonuje niepodzielnie wszystkie operacje nakazane w tablicy 
struktur wskazywanej

przez oper - albo nie wykonuje żadnej, jeśli choć jedna z nich jest w danej chwili 
niemożliwa.

background image

 

 

Pojedyncza operacja na pojedynczym semaforze wygląda następująco:

- jeżeli wartość sem_op jest dodatnia, wartość semafora zwiększa się o nią 
(zatem, w przeciwieństwie

  do tego, co jest podane w klasycznej definicji semafora, wartość semafora może 
wzrosnąć o więcej,

  niż 1), a jednocześnie jest budzona odpowiednia liczba procesów śpiących pod 
tym semaforem (jeśli

  są takie);

- jeżeli wartość sem_op jest ujemna, wartość semafora odpowiednio się 
zmniejsza, jeśli to możliwe,

  a jeśli niemożliwe, zmniejszenie nie jest wykonywane, a proces albo zasypia 
czekając na zaistnienie 

  takiej możliwości, albo od razu następuje powrót z funkcji z błędem (w 
zależności od ustawienia

  flagi IPC_NOWAIT);

- jeżeli wartość sem_op wynosi zero, proces zasypia, jeśli wartość semafora nie 
jest zerem (lub wraca

  od razu z błędem, jeśli jest ustawiona flaga IPC_NOWAIT) i budzi się dopiero po 
osiągnięciu

  wartości zero przez semafor.

background image

 

 

int semctl (int ident, int numer, int polecenie, union semun argument);

gdzie       union semun

                     {  int val;                                     do ustawienia wartości pojedynczego 
semafora

                         struct semid_ds buf;              bufor struktury zarządzającej tablicą 

semaforów

                         ushort array;                          wskaźnik do tablicy ustawień 

wartości całej tablicy sem.

                         struct seminfo __buf;            specyficzne dla Linuxa, używane przez
                         void __pad;  }                        jądro systemu operacyjnego

Zwraca: liczbę dodatnią będącą wynikiem wykonania polecenia - w przypadku 
sukcesu

              -1 w przypadku błędu

ident - identyfikator tablicy semaforów (zwrócony przez funkcję semget)

numer - numer semafora w tablicy (istotny w przypadku, gdy polecenie dotyczy 
pojedynczego semafora)

polecenie - kod polecenia do wykonania

argument - argument jednego z typów wchodzących w skład unii, zależnego od 
polecenia

background image

 

 

Działanie: może wykonywać mnóstwo różnych czynności (najwięcej z wszystkich 
funkcji IPC) na

                 pojedynczych semaforach, na całej ich tablicy lub na strukturze 
zarządzającej. Najczęściej

                 używanymi poleceniami są:

                 IPC_RMID                      usunięcie tablicy semaforów

                 GETALL                         odczytanie wartości wszystkich semaforów w 
tablicy

                 SETALL                          nadanie wartości wszystkim semaforom w 
tablicy

                 GETVAL                         odczytanie wartości pojedynczego semafora

                 SETVAL                          nadanie wartości pojedynczemu semaforowi

Uwaga. Operacje nadania lub odczytania wartości semaforów nie wiążą się z 
możliwością wstrzymania

              procesu wykonującego daną operację.


Document Outline