opracowanie zagadnienia 2007

background image

1


ZAGADNIENIA NA KOLOKWIUM Z QNX’a - 31.05.2007

(na podstawie PDF-ów dr Ułasiewicza)



1. Podstawowe pojęcia współbieżności: bezpieczeństwo, żywotność, bloka-
da, zagłodzenie.

Bezpieczeństwo - aplikacja jest bezpieczna, jezeli utrzymuje system w pozadanym
stanie. W odniesieniu do modelu klient–serwer bezpieczenstwo oznacza ze klienci sa
obslugiwani w zadowalający sposób:

1. Serwer nie zaprzestal obslugi zlecen.
2. Na zlecenia odpowiadal w prawidlowy sposób.

Pierwszy z warunków nie bedzie spelniony gdy wystapi blokada serwera (ang.
dealock
). Drugi warunek bedzie zagrozony gdy nie zostanie zachowany warunek
wzajemnego wykluczania procesów przy dostepie do niepodzielnego z natury za-
sobu. Wzajemne wykluczanie musi byc zapewnione gdy kilka procesów ma dostep
do wspólnego obszaru pamieci i przynajmniej jeden z nich modyfikuje ten obszar.

Zywotnosc - aplikacja jest zywotna, jezeli kazde pozadane zdarzenie w koncu zaj-
dzie. W modelu klient–serwer zywotnosc oznacza ze kazdy klient zostanie w koncu
obsluzony.

Uczciwosc - Aplikacja jest uczciwa, jezeli zadajace obslugi procesy sa traktowane
jednakowo lub zgodnie ze swoimi priorytetami.
W modelu klient–serwer uczciwosc oznacza ze kazdy klient zostanie obsluzony
zgodnie z kolejnoscia zgloszen lub priorytetem.
Wyróznia sie nastepujace rodzaje uczciwosci:

1. Uczciwosc mocna – jesli proces zglasza zadanie nieskonczenie wiele razy to w

koncu zostanie ono obsluzone.

2. Uczciwosc liniowa – jesli proces zglasza zadanie bedzie ono obsluzone zanim do-

wolny inny proces bedzie obsluzony wiecej niz raz.

3. Uczciwosc typu FIFO – zadania procesów sa obslugiwane zgodnie z kolejnoscia

ich zglaszania. (FIFO – ang. First-In First-Out)

Blokada - Z blokada mamy do czynienia gdy kazdy z zablokowanych procesów
oczekuje na zdarzenie które moze byc wygenerowane tylko przez którys z zabloko-
wanych procesów. Blokada zwana tez zakleszczeniem jest typowym zagrozeniem
aplikacji wspólbieznych.

Zagłodzenie - sytuacja, w której dany proces nie jest w stanie zakończyć działania,
ponieważ nie ma dostępu do procesora lub innego współdzielonego zasobu. Wystę-
puje najczęściej na skutek niewłaściwej pracy algorytmu szeregowania, którego za-
daniem jest sprawiedliwy przydział zasobów, lub nadmiernego obciążenia systemu.

background image

2

2. Powody stosowania aplikacji współbieżnych.

Korzysci wynikajace z zastosowania wspólbieznosci:

1. Polepszenie wykorzystania zasobów. Gdy jakis proces czeka na niedostepny w

danej chwili zasób, procesor moze wykonywac inny proces.

2. Podzial zadania na procesy umozliwia wykonywanie ich na oddzielnych maszy-

nach. Prowadzi to do zrównoleglenia przetwarzania.

3. Podzial duzego zadanie na wiele mniejszych komunikujacych sie procesów pro-

wadzi do dekompozycji problemu. Ulatwia to ich implementacje, uruchamianie i
testowanie przez wielu niezaleznych programistów.

Trudnosci powstajace przy implementacji aplikacji wspólbieznych:
1. problem sekcji krytycznej
2. problem synchronizacji procesów
3. problem zakleszczenia

3. Architektura komputera klasy PC. Metody ochrony pamięci (segmenta-
cja, stronicowanie), metody ochrony procesora (tryby system / użytkow-
nik).

Podstawowe architektury systemów komputerowych

1. Komputery jednoprocesorowe
2. Komputery wieloprocesorowe ze wspólna pamiecia.
3. Komputery równolegle
4. Systemy rozproszone

Systemy jednoprocesorowe
Wiekszosc tradycyjnych komputerów posiada wlasnie taka architekture. System
sklada sie z procesora, pamieci i róznych urzadzen wejscia / wyjscia (dysków, nape-
dów dyskietek, kart sieciowych, portów szeregowych i równoleglych itd.). Urzadze-
nia wejscia / wyjscia sa podlaczone do szyny komputera za pomoca kontrolerów
tych urzadzen.

background image

3


Komputery wieloprocesorowe ze wspólna pamiecia
 Procesy wykonywane na róznych procesorach komunikuja sie przez wspólna pa-

miec.

 Mozliwosc przetwarzania równoleglego.
 Wydajnosc ograniczaja konflikty przy dostepie do wspólnej pamieci.
 Liczba procesorów jest zwykle niewielka.



Systemy rozproszone
 System rozproszony sklada sie z wielu niezaleznych komputerów polaczonych za

pomoca sieci.

 Pracujace na nich aplikacje komunikuja sie poprzez siec.

background image

4

4. Pojęcie procesu, stany procesów, graf stanu procesów, fazy wykonania

procesów, dziedziczenie atrybutów.

Proces - wykonujacy sie program. Proces jest wiec aktywna struktura dynamiczna
istniejaca tylko w srodowisku dzialajacego komputera. Aby proces mógl sie wyko-
nywac potrzebne sa co najmniej nastepujace zasoby:
 procesor
 pamiec operacyjna
 urzadzenia wejscia / wyjscia

Rodzaje procesów

Procesy sekwencyjne (ang. Sequential processes)
Procesy sa sekwencyjne jezeli nastepny proces ze zbioru procesów rozpoczyna sie
po zakonczeniu procesu poprzedniego.
Procesy wspólbiezne (ang. Concurrent processes)
Dwa procesy sa wspólbiezne jezeli jeden z nich rozpoczyna sie przed zakonczeniem
drugiego.
Procesy równolegle (ang.Paralell processes)
Dwa procesy sa równolegle jezeli jeden z nich rozpoczyna sie przed zakonczeniem
drugiego i wykonywane sa jednoczesnie na oddzielnych procesorach.

Kanoniczne stany procesów
Proces moze byc w jednym z trzech podstawowych stanów:
- wykonywany (ang. Running),
- gotowy (ang. Ready)
- zablokowany (ang. Blocked).

Pokazane na rysunku przejscia maja miejsca w nastepujacych sytuacjach.
1. Proces zada zasobu który nie jest dostepny.
2. Wystapilo przerwanie (proces zostal wywlaszczony) lub tez proces dobrowolnie
zwolnil procesor.
3. Procedura szeregujaca zdecydowala ze ten proces ma byc wykonywany.
4. Zasób, którego brakowalo do kontynuacji procesu stal sie dostepny. Przejscie zo-
stalo zainicjowane przez przerwanie od urzadzenia wejscia / wyjscia lub tez proces
aktualnie wykonywany.

Atrybuty procesu sa to informacje wykorzystywane przez system do zarzadzania
procesami a wiec do ich identyfikacji, szeregowania, utrzymywania bezpieczenstwa i
uruchamiania. Najwazniejsze atrybuty procesu:

background image

5


- PID - identyfikator procesu,
- PPID - PID procesu macierzystego,
- UID - identyfikator uzytkownika
- GID - identyfikator grupy do której nalezy uzytkownik
- SID - identyfikatory sesji
- PGRP - identyfikatory grupy procesów,
- priorytet procesu,
- CWD - katalog biezacym
- katalog glówny
- otoczenie procesu

Fazy wykonania procesu

1. Tworzenie :
- Alokacja deskryptora procesu, przydzial PID
- Ustalenie zmiennych otoczenia – zwykle dziedziczone z procesu macierzystego
2. Ladowanie
Zaladowanie segmentu kodu i danych oraz inicjacji stosu. Ladowanie wykonywane
jest przez oddzielny watek ladujacy aby nie blokowac administratora procesu Proc.
3. Faza wykonania
Po zaladowaniu nowy proces jest umieszczany w kolejce procesów gotowych, uru-
chamiany i zaczyna wspólzawodniczyc o zasoby.
4. Faza zakonczenia
Zakonczenie procesu moze byc zainicjowane przez sam proces – gdy wykona on
funkcje exit, lub poprzez wyslany z zewnatrz sygnal. Zakonczenie sklada sie z
dwu etapów.
a) Zwolnienie zasobów - proces zwalnia wszystkie zajmowane zasoby jak pamiec,
nazwy, itd. oraz likwiduje interakcje z innymi procesami. Po wykonaniu tej fazy zaj-
muje tylko deskryptor.
b) Zawiadomienie procesu macierzystego o zakonczeniu. Dopóki proces macierzysty
nie wykona funkcji wait lub waitpid konczony proces pozostaje w stanie „zombie”.
Aby uniknac pozostawania procesów w stanie „zombie” mozna ustawic reakcje na
sygnal SIGCHLD w funkcji signal na SIG_IGN.

Stany procesu w systemie QNX

READY
Proces posiada wszystkie potrzebne zasoby oprócz procesora.
BLOCKED
Proces zablokowany na pewnej operacji komunikacji miedzyprocesowej:
SEND_BLOCKED, RECEIVE_BLOCKED, REPLY_BLOCKED, SIGNAL_BLOCKED.
HELD
Proces otrzymal sygnal SIGSTOP I przebywa w stanie HELD. Gdy otrzyma sygnal
SIGCONT bedzie wznowiony a gdy inny sygnal zakonczy sie.
WAIT
Proces wykonal funkcje wait ale zaden z procesów potomnych nie zakonczyl sie.
DEAD
Stan zwany tez „zombie”. Proces sie zakonczyl ale jego proces macierzysty nie wy-
konal funkcji wait.

background image

6


Przejscia pomiedzy stanami procesu w systemie QNX

Wykonanie funkcji exec powoduje zastapienie starego segmentu kodu, danych i
stosu nowymi. Nowy proces dziedziczy ze starego PID, PPID, priorytet, srodowisko,
katalog biezacy.

5. Tworzenie procesów, deskryptor procesu, funkcje fork, exec, wait, wait-
pid, exit. Makra WEXITSTATUS, WTERMSIG, WIFEXITED, WIFSIGNAL.

Utworzenie procesu
Operacja powoduje utworzenie deskryptora i alokacje pamieci niezbednej dla proce-
su.
Deskryptor (ang. descriptor) - struktura definiująca określony obiekt w pamięci.
Deskryptor procesu zawiera informacje o zmiennych dzielonych wykorzystywanych
przez proces.

Tworzenie kopii procesu biezacego – funkcja fork()
Funkcja ta tworzy kopie procesu biezacego czyli tego procesu który wykonuje funk-
cje fork(). Utworzony proces potomny rózni sie od macierzystego pod nastepujacy-
mi wzgledami:
1. Ma inny PID.
2 .Ma inny PID procesu macierzystego (ang. parent PID).
3. Proces potomny zachowuje otwarte pliki procesu macierzystego ale tworzy wla-
sne kopie ich deskryptorów.

Dzialanie funkcji fork – procesy macierzysty i potomny wykonywane sa wspólbieznie.
Funkcja fork tworzy deskryptor nowego procesu oraz kopie segmentu danych i stosu
procesu macierzystego.
1. Wartosci zmiennych w procesie potomnym sa takie jak w procesie macierzystym
bezposrednio przed wykonaniem funkcji fork.

background image

7

2. Modyfikacje zmiennych danych dokonywane w procesie macierzystym nie sa widocz-
ne w procesie potomnym (i odwrotnie) gdyz kazdy z procesów posiada wlasna kopie
segmentu danych.

Funkcja exec
Kazda funkcja z rodziny exec przeksztalca biezacy proces w nowy proces tworzony
z pliku wykonywalnego bedacego jednym z parametrów funkcji exec. Wykonanie
funkcji exec powoduje zastapienie starego segmentu kodu, danych i stosu nowymi
Nowy proces dziedziczy ze starego PID, PPID, priorytet, srodowisko, katalog bieza-
cy.

Funkcja exit
Wykonanie funkcji exit(x) powoduje zakonczenie sie procesu biezacego.Wszystkie
zasoby zajmowane przez proces z wyjatkiem jego deskryptora sa zwalniane. Dodat-
kowo wykonywane sa nastepujace akcje:
1. Otwarte pliki i strumienie sa zamykane.
2. Najmlodszy bajt (8 bitów) z kodu powrotu x jest przekazywane do zmiennej sta-
tus odczytywanej przez funkcje

wait() wykonana w procesie macierzystym. Kod

powrotu przechowywany jest w deskryptorze procesu.
3. Gdy proces macierzysty wykonal wczesniej funkcje wait() albo waitpid() i jest
zablokowany, nastepuje jego odblokowanie i usuniecie deskryptora.
4. Gdy proces macierzysty nie wykonal wczesniej funkcje wait() albo waitpid()
kod powrotu przechowywany jest w deskryptorze procesu a proces przechodzi do
stanu „zoombie”.
5. Jezeli konczony proces posiada jakies procesy potomne, wysylany jest do nich
sygnal SIGHUP. Procesy te sa adoptowane przez inny proces. Zwykle jest to naj-
wyzszy proces w hierarchii.
6. Do procesu macierzystego wysylany jest sygnal SIGCHLD.

Funkcja wait
Funkcja wait() powoduje ze proces macierzysty bedzie czekal na zakończenie pro-
cesu potomnego. Dzialanie funkcji wait jest nastepujace:
1. Gdy proces potomny nie zakonczyl sie funkcja wait powoduje zablokowanie pro-
cesu macierzystego az do zakonczenia sie procesu potomnego. Gdy ten się zakon-
czy zwracany jest jego PID oraz status.
2. Gdy proces potomny zakonczyl sie zanim wykonano funkcje wait nie wystepuje
blokada procesu macierzystego. Funkcja zwraca PID zakonczonego procesu oraz
jego status.
3. Gdy brak jakichkolwiek procesów potomnych funkcja wait zwraca –1,

Funkcja waitpid
Funkcja waitpid wstrzymuje proces wywołujący do momentu, aż potomek określony
przez pid przestanie działać.

Testowanie statusu zakonczonego procesu.
Status zakonczonego procesu udostepniany jest przez funkcje wait
pid = wait(&status)

Wartosc zmiennej status zalezy od:
1. Systemu operacyjnego, który umieszcza tam informacje o przyczynach i sposobie

background image

8


zakonczenie procesu.
2. Zakonczonego procesu potomnego, który umieszcza tam wartosc kodu powrotu –
jest to parametr funkcji exit.

Makra WIFEXITED, WEXITSTATUS, WIFSIGNALED, WTERMSIG

Inicjowanie zakonczenia procesu
Zakonczenie sie procesu nastepuje w podanych nizej przypadkach:
1. W dowolnym miejscu kodu procesu wykonana zostanie funkcja

exit

.

2. Funkcja

main

procesu wykona instrukcje

return

.

3. Funkcja main procesu wykona ostatnia instrukcje kodu.
4. Proces zostanie zakonczony przez system operacyjny lub inny proces.
Preferowanym sposobem zakonczenia procesu jest wykonanie funkcji exit

6. Tworzenie procesów za pomocą funkcji spawn. Tworzenie procesów na
innym węźle.

Tworzenie nowego procesu za pomoca funkcji spawn
Kazda funkcja z rodziny spawn tworzy nowy proces potomny na podstawie pliku
wykonywalnego okreslonego w jednym z parametrów funkcji .
Srodowisko (ang. Enviroment) jest dziedziczone z procesu macierzystego. Funkcja
zwraca:
> 0 - pid utworzonego procesu potomnego
- 1 - blad gdy proces nie moze byc utworzony

Tryby wykonania procesu:

background image

9



Uruchamianie procesów na innym wezle niz biezacy.
Aby uruchomic proces na innym wezle nalezy zmodyfikowac zmienna globalna
qnx_spawn_options typu _qnx_spawn_globs. Zmienna i jej typ zdefiniowane
sa w plikach naglówkowych <sys/qnx_glob.h> i <sys/qnx_types.h>. O szczególach
uruchamianego procesu decyduja pola zmiennej qnx_spawn_options .

struct _qnx_spawn_globs {
nid_t node; // Numer wezla na którym uruchamiamy proces
char priority; // Priorytet procesu

char sched_alg; // Algorytm szeregowania
char flags; // Flagi


} qnx_spawn_options ;

7. Łacza nienazwane (unnamed pipes) i ich wykorzystanie. Funkcje pipe,
open, read, write, close.

Łącza nienazwane (ang. Unnamed Pipes) i nazwane (ang. Unnamed Pipes) - jedna z
historycznie pierwszych metod komunikacji międzyprocesowej. Wywodzą się z sys-
temu UNIX.
Łącze nienazwane (ang. Pipe) można wyobrazić sobie jako rodzaj „rury bitowej” łą-
czącej dwa procesy. Łącze nienazwane implementowane jest jako bufor cykliczny.

background image

10

Do pisania i czytania z łącza używa się mechanizmu plików i standardowych funkcji
read i write. Plików nie otwiera się za pomocą funkcji open. Stosownych uchwytów
dostarcza funkcja

pipe w tablicy fildes.

Własności łącz nienazwanych:
1. Kanał jest jednokierunkowy dla danego procesu i nieużywany w tym procesie plik
powinien być zamknięty.
2. Metoda komunikacji może być użyta tylko dla procesów związanych – będących w
relacji macierzysty / potomny.
3. Jako że łącze jest buforem typu FIFO utrzymywanym w pamięci operacyjnej ma
ono ograniczoną pojemność.
4. Operacje zapisu odczytu do łącza są operacjami atomowymi.

Ł

ą

cze nienazwane tworzy si

ę

poprzez wykonanie funkcji pipe:

int pipe(int fildes[2]);
fildes Tablica dwuelementowa na uchwyty plików do odczytu I zapisu
Funkcja tworzy ł

ą

cze nienazwane i umieszcza w tablicy fildes uchwyty plików:

fildes[0] – uchwyt pliku do odczytu.
fildes[1] – uchwyt pliku do zapisu.
Funkcja zwraca: 0 – sukces, -1 – bł

ą

d.


Odczyt z pliku – funkcja read
int read(int fdes, void *bufor, int nbytes)
fdes Uchwyt do pliku zwracany przez funkcję open
bufor Bufor w którym umieszczane są przeczytane bajty
nbytes Liczba bajtów którą chcemy przeczytać.
Funkcja powoduje odczyt z pliku identyfikowanego przez fdes, nbytes bajtów i u-
mieszczenie ich w buforze. Funkcja zwraca:
> 0 – liczbę rzeczywiście przeczytanych bajtów,
- 1 – gdy błąd.


Zapis do pliku – funkcja write
int write(int fdes, void *bufor, int nbytes)
fdes Uchwyt do pliku zwracany przez funkcję open
bufor Bufor w którym umieszczane są bajty przeznaczone do zapisu
nbytes Liczba bajtów którą chcemy zapisać
Funkcja powoduje zapis do pliku identyfikowanego przez fdes nbytes bajtów znajdu-
jących buforze. Funkcja zwraca:
> 0 – liczbę rzeczywiście zapisanych bajtów,
- 1 – gdy błąd.

Zamknięcie pliku – funkcja close
int close(int fdes)
fdes Uchwyt do pliku zwracany przez funkcję open
Funkcja powoduje zamknięcie pliku identyfikowanego przez fdes. Należy ją wyko-
nać, gdy nie będą już wykonywane operacje na danym pliku .

background image

11

Zamykanie łącz

Co stanie si

ę

gdy deskryptor reprezentuj

ą

cy ł

ą

cze zostanie zamkni

ę

ty?

1. Zamkni

ę

cie deskryptora pliku do zapisu. Gdy istniej

ą

inne procesy które maj

ą

otwarte

te ł

ą

cze dla zapisu nie dzieje si

ę

nic. Gdy nie gdy nie w ł

ą

czu danych procesy zabloko-

wane na odczycie zwracaj

ą

zero.

2. Zamkni

ę

cie deskryptora pliku do odczytu. Gdy istniej

ą

inne procesy które maj

ą

otwar-

te te ł

ą

cze dla odczytu nie dzieje si

ę

nic. Gdy nie do wszystkich procesów zablokowa-

nych na zapisie wysłany zostanie sygnał

SIGPIPE.

8. Łacza nazwane (ang. Named Pipes) i ich wykorzystanie. Funkcja mkfifo.

Pliki FIFO:
- tworzone są w pamięci operacyjnej,
- widziane są jednak w przestrzeni nazw plików
- posiadają zwykłe atrybuty pliku w tym prawa dostępu.

Plik FIFO tworzy się przy pomocy funkcji:
int mkfifo(char * path, mode_t mode)
path
Nazwa pliku FIFO (ze ścieżką)
mode Prawa dostępu do pliku .
Funkcja zwraca: 0 – sukces, -1 – błąd.

Aby proces mógł użyć pliku FIFO należy:
1. Utworzyć plik FIFO za pomocą funkcji mkfifo o ile wcześniej nie został utworzo-
ny.
2. Otworzyć plik FIFO za pomocą funkcji open.
3. Pisać lub czytać do / z pliku używając funkcji read lub write.
4. Zamknąć plik przy pomocy funkcji close.

Własności plików FIFO.
1. Pliki FIFO są plikami specjalnymi tworzonymi w pamięci operacyjnej ale widzia-
nymi w systemie plików komputera. Stąd procesy mające dostęp do tego samego
systemu plików mogą się komunikować przez pliki FIFO.
2. Operacje zapisu odczytu do / z pliku FIFO są operacjami atomowymi.
3. Bajty odczytane z pliku FIFO są stamtąd usuwane.
4. Zachowanie się procesu przy próbie odczytu z pustego pliku FIFO lub zapisu do
pełnego zależą od flagi O_NONBLOCK.
5. Informacje w pliku FIFO są pozbawione struktury.
6. Plik FIFO i jego zawartość ginie przy wyłączeniu komputera.

9. Model komunikujących się procesów, komunikaty synchroniczne i asyn-
chroniczne, buforowanie, adresowanie, obsługa błędów, rola systemu ope-
racyjnego.

Model procesów procesów komunikatów
Model procesów i komunikatów skonstruowany jest w oparciu o następujące reguły:
 Aplikacja składa się ze zbioru procesów sekwencyjnych.
 Procesy mogą być wykonywane równolegle. Proces wykonuje się sekwencyjnie i

używa swej pamięci lokalnej.

background image

12

 Proces komunikuje się z otoczeniem za pomocą komunikatów. Są dwie podsta-

wowe operacje komunikacyjne: wysłanie komunikatu i odbiór komunikatu.

 Procesy mogą być przydzielone do procesorów w różny sposób. Poprawność

działania aplikacji nie powinna zależeć od tego podziału.

Komunikaty
Możliwość przekazywania komunikatów pomiędzy procesami jest fundamentalną
własnością większości systemów operacyjnych. Jeśli pomiędzy dwoma maszynami
istnieje jakikolwiek sposób komunikacji (sieć lokalna, sieć rozległa, bezpośrednie
łącze, wspólna pamięć, itd.) na pewno daje się przesłać komunikat pomiędzy proce-
sami wykonywanymi na tych maszynach. Przesłanie komunikatu pomiędzy proce-
sami jest przesłaniem pomiędzy nimi pewnej liczby bajtów według ustalonego pro-
tokołu. Przesłanie komunikatu jest operacją atomową. Z przesyłaniem komunikatów
wiążą się następujące problemy:
 problem synchronizacji nadawcy i odbiorcy (kto i kiedy czeka),
 problem adresowania (jaki system adresacji),
 problem identyfikacji (czy procesy znają swoje identyfikatory),
 problem przepływu danych (w jedną czy dwie strony),
 problem zapewnienia niezawodnego przesłania przez zawodny kanał komunika-

cyjny.

Do zapewnienia komunikacji potrzebne są przynajmniej dwie funkcje interfejsowe –
wysyłająca (send) i odbierająca komunikat (receive).

Komunikacja synchroniczna
 Proces wysyłający jest blokowany do czasu otrzymania potwierdzenia że proces

docelowy otrzymał wysyłany komunikat

 Gdy w momencie wykonania funkcji receive brak jest oczekującego komunikatu,

proces odbierający jest wstrzymywany do czasu nadejścia jakiegoś komunikatu.
Gdy jakiś komunikat oczekuje, proces odbierający nie jest blokowany.

Komunikacja synchroniczna pomiędzy procesami P1 i P2

background image

13


Komunikacja asynchroniczna
 Proces wysyłający komunikat nie jest blokowany.
 Proces odbierający jest wstrzymywany do czasu nadejścia komunikatu (wersja

blokująca) lub też nie jest wstrzymywany (wersja nie blokująca). Informacja czy
odebrano komunikat czy też nie, przekazywana jest jako kod powrotu funkcji
odbierającej.

Komunikacja asynchroniczna pomiędzy procesami P1 i P2


Porównanie komunikacji synchronicznej i asynchronicznej

Buforowanie
Przy transmisji asynchronicznej konieczne jest buforowanie po stronie wysyłającej.
Postępowanie w przypadku przepełnienia bufora:
 Zablokować proces wysyłający.
 Funkcja wysyłająca komunikat kończy się błędem.

background image

14


Adresowanie
Do adresowania procesu docelowego stosuje się następujące rozwiązania:
1. PID procesu
2. IP maszyny + numer portu
3. Nazwy symboliczne procesów
Aby zastosować nazwy symboliczne konieczny jest serwer nazw (ang. name server).
Skąd znana jest lokalizacja serwera nazw?
1. Lokalizacja serwerów nazw jest ustalona i podana jako parametr instalacyjny sys-
temu
2. Serwery nazw rozgłaszają swą lokalizację innym węzłom.

Rola systemu operacyjnego
Przesyłanie komunikatów realizowane jest przez system operacyjny. Funkcje syste-
mu operacyjnego:
1. Zapewnienie transmisji komunikatu pomiędzy komputerami i ukrycie szczegółów
tej transmisji.
2. Wykrywanie i korygowanie błędów transmisji.
3. Składanie i rozkładanie komunikatów w pakiety transmitowane przez sieć.

10. Przesyłanie komunikatów w systemie QNX. Funkcje Send, Receive, Re-
ply, Creceive. Administrowanie

nazwami, funkcje qnx_name_attach,

qnx_name_detach,

Przesyłanie komunikatów
Przesłanie komunikatu pomiędzy procesami jest przesłaniem pewnej liczby bajtów
pomiędzy tymi procesami według ustalonego protokołu. Przesłanie komunikatu jest
operacją atomową. Możliwość przekazywania komunikatów pomiędzy procesami jest
fundamentalną własnością systemu QNX.

W systemie QNX wyróżniamy następujące akcje związane z przesyłaniem komunika-
tów pomiędzy procesami P1 i P2:
1) Wysłanie komunikatu przez P1 do P2
2) Odbiór komunikatu przez P2
3) Przesłanie odpowiedzi zwrotnej od P2 do P1.

Wysłanie komunikatu
Komunikat do procesu P wysyła się wykonując funkcję Send.

int Send(pid_t pid, void * smsg, void * rmsg, unsigned
sbytes,unsigned rbytes)

Znaczenie parametrów powyższej funkcji jest następujące:
pid PID procesu docelowego P
smsg adres bufora danych wysyłanych
rmsg adres bufora danych odbieranych
sbytes liczba bajtów wysyłanych
rbytes max. liczba bajtów odbieranych

Działanie funkcji Send jest następujące:

background image

15

1) Komunikat smsg wysyłany jest do procesu docelowego po czym proces bieżący
ulega zablokowaniu.
2) Proces wysyłający jest zablokowany do czasu gdy proces docelowy nie odbierze
wysłanego komunikatu i nie prześle odpowiedzi.
3) Po otrzymaniu odpowiedzi jest ona umieszczana w buforze rmsg i proces bieżący
jest wznawiany.

Odbiór komunikatu
Do odbioru komunikatów przez proces P służy funkcja Receive. Gdy zachodzi po-
trzeba odbioru komunikatu należy wykonać funkcję Receive.

pid_t Receive(pid_t pid, void * msg, unsigned rbytes)
Znaczenie parametrów powyższej funkcji Receive jest następujące:
pid PID procesu nadającego lub 0 – gdy odbiór od wszystkich procesów
rmsg adres bufora danych odbieranych
rbytes max. liczba bajtów odbieranych
Funkcja zwraca: > 0 – PID procesu który nadał komunikat, - 1 - błąd

Zwykle jako parametr pid w funkcji Receive wstawiamy 0 co oznacza że odbierane
będą komunikaty od wszystkich procesów. Działanie funkcji Receive zależy od tego
czy istnieją komunikaty które zostały wcześniej wysłane do procesu P przez inne
procesy. Jeśli tak było to muszą one oczekiwać w kolejce Q nieodebranych komuni-
katów. Działanie funkcji jest następujące:
1. Gdy w kolejce Q są nieodebrane komunikaty pierwszy z nich usuwany jest z ko-
lejki Q i umieszczany w buforze msg. Funkcja zwraca PID procesu który wysłał ten
komunikat. System powoduje odblokowanie procesu wysyłającego komunikat. Pro-
ces bieżący nie ulega zablokowaniu.
2. Gdy kolejka Q jest pusta proces P ulega zablokowaniu do czasu nadejścia takiego
komunikatu. Zablokowany proces jest w stanie RECEIVE_BLOCKED.
Istnieje możliwość zmiany uporządkowania tej kolejki na uporządkowanie według
priorytetów procesów przysyłających komunikaty. Należy w tym przypadku użyć
funkcji qnx_pflags(…). Priorytetowa organizacja kolejki pociąga za sobą możliwość
zagło

dzenia procesów.

Wysłanie odpowiedzi na komunikat
pid_t Reply(pid_t pid, void * msg, unsigned sbytes)
pid
PID procesu nadającego lub 0 – odbiór od wszystkich
msg adres bufora danych wysyłanych
sbytes maksymalna liczba bajtów wysyłanych
Funkcja zwraca: > 0 – PID procesu który nadał komunikat, 1 - błąd

Warunkowy odbiór komunikatu
pid_t Creceive(pid_t pid, void * msg, unsigned rbytes)
Funkcja nie powoduje zablokowania procesu bieżącego gdy brak
komunikatów.
pid > 0 - PID procesu nadającego
0 - odbiór od wszystkich procesów
rmsg adres bufora danych odbieranych
rbytes max. liczba bajtów odbieranych

background image

16


Funkcja zwraca:
> 0 – PID procesu który nadał komunikat
- 1 - błąd

Wymiana komunikatów
Na poniższym rysunku za pomocą sieci Petriego przedstawiono wymianę komunika-
tów pomiędzy procesami P1 i P1.




















Nazwy
Proces P1 może wysłać komunikat do P2 o ile zna jego PID. Bez dodatkowych me-
chanizmów tylko procesy będące w relacji macierzysty / potomny mogą się w ten
sposób komunikować. Aby umożliwić komunikowanie się procesów niezwiązanych, w
systemie QNX wprowadzono mechanizm nazw. Procesy mogą rejestrować swoje na-
zwy w systemie. Nazwy są zwykłymi łańcuchami. Inne procesy mogą zwrócić się do
systemu o PID procesu podając jego nazwę. Mechanizm ten działa także przez sieć i
nosi nazwę lokalizacji.

Rejestracja nazwy
Proces rejestruje się poprzez wykonanie funkcji:
int qnx_name_attach(nid_t nid, char * name)
nid Identyfikator węzła na którym nazwa jest rejestrowana (0 – węzeł bieżący)
name Nazwa rejestrowanego procesu
Gdy nazwa zaczyna się od znaku / jest widoczna w całej sieci. Nazwa może być też
zarejestrowana na określonym węźle. Funkcja zwraca:
> 0 - identyfikator nazwy (ang. name ID) – liczba typu int używana w innych
funkcjach.
-1 - gdy rejestracja się nie udała.

Lokalizacja nazwy
PID procesu o znanej nazwie uzyskuje się poprzez wywołanie funkcji:
pid_t qnx_name_locate(nid_t nid, char * name, unsigned
size, NULL)

nid Identyfikator węzła na którym nazwa jest rejestrowana (0 – węzeł bieżący)
name Nazwa lokalizowanego procesu

background image

17

size Długość komunikatu który będzie przesyłany pomiędzy procesami
Funkcja zwraca:
> 0 - identyfikator procesu
-1 - gdy lokalizacja się nie udała.
Gdy nid = 0 proces będzie lokalizowany najpierw na bieżącym węźle a potem w sie-
ci. Gdy nid # 0 proces będzie lokalizowany tylko na węźle o numerze nid. Gdy loka-
lizowany proces leży na innym węźle automatycznie tworzone jest połączenie wirtu-
alne (ang. Virtual Circuit) pomiędzy węzłami.

Wyrejestrowanie nazwy
Nazwa kasowana jest się poprzez wywołanie funkcji:
int qnx_name_detach(nid_t nid, int name_id)
nid Identyfikator węzła na którym nazwa jest kasowana (0 – węzeł bieżący)
name_id Identyfikator kasowanej nazwy – liczba zwracana przez funkcję
qnx_name_attach
Funkcja powoduje usuniecie nazwy o identyfikatorze name_id z bazy nazw.
Funkcja zwraca:
0 - gdy wyrejestrowanie się udało
-1 - gdy operacja się nie udała.

11. Mechanizm

przesyłania depozytów

w systemie

QNX.

Funkcje

qnx_proxy_attach, Trigger. Depozyt jako mechanizmu reagowania na zda-
rzenia z wielu źródeł.

Depozyty
Depozyty są rodzajem komunikatów o ustalonej treści (zwykle zerowej) nie wyma-
gającym potwierdzenia. W aplikacjach współbieżnych potrzebny jest często nieblo-
kujący mechanizm powiadamiania procesów o pewnych zdarzeniach. W systemie
QNX taki mechanizm jest zaimplementowany i nosi tam nazwę depozytu (ang.
proxy).
Depozyty mogą być przesyłane przez sieć. Depozyty są stosowane w nastę-
pujących sytuacjach:
1. Proces chce powiadomić inny proces o zdarzeniu, ale nie może sobie pozwolić na
zablokowanie się.
2. Proces chce powiadomić inny proces o zdarzeniu, ale nie potrzebuje informacji
zwrotnej.
3. Używane są w procedurach obsługi przerwań (ang. interrupt handler).

Depozyt tworzony jest za pomocą funkcji:
int qnx_proxy_attach(pid_t pid, char * data, int nbytes,
int priority)

pid PID procesu będącego właścicielem depozytu (0 gdy jest to proces wykonujący
funkcję).
data Zawartość depozytu – zwykle 0.
nbytes Długość informacji zawartej w depozycie – zwykle 0;
priority Priorytet depozytu (gdy –1 – będzie on taki jak priorytet procesu wykonu-
jącego funkcję).
Funkcja zwraca:
> 0 - identyfikator depozytu
-1 - błąd

background image

18

Wysłanie depozytu:
Depozyt wysyłany jest za pomocą funkcji:
pid_t Trigger(pid_t proxy)
proxy Identyfikator depozytu – wartość zwracana przez funkcję
qnx_proxy_attach.
Wykonanie funkcji Trigger powoduje wysłanie depozytu proxy do procesu który jest
jego właścicielem.
Funkcja zwraca:
> 0 - identyfikator procesu który jest właścicielem depozytu
-1 - błąd

Odbiór depozytu:
Odbiór depozytu następuje poprzez wykonanie funkcji Receive. Jeżeli proces jest
zablokowany na funkcji Receive i do procesu tego zostanie dostarczony depozyt,
proces ulega odblokowaniu i funkcja Receive zwraca identyfikator depozytu.

Kasowanie depozytu
Depozyt jest kasowany za pomocą funkcji:
int qnx_proxy_detach(pid_t proxy)
proxy Identyfikator kasowanego depozytu – wartość zwracana przez funkcję
qnx_proxy_attach.
Funkcja zwraca:
0 – sukces
-1 błąd

Własności depozytów:
1. Wysłane depozyty nie wymagają potwierdzenia.
2. Nieodebrane depozyty są kolejkowane (w ilości do 65535). Zostaną odebrane
przy kolejnym wywołaniu funkcji Receive.
3. Każdy depozyt należy do procesu który go utworzył.
4. Depozyt może być wysłany z dowolnego procesu, ale zawsze trafia do procesu
który jest jego właścicielem.

12. Tworzenie aplikacji klient/serwer, serwer stanowy–bezstanowy, se-
kwencyjny-współbieżny.

Architektura klient / serwer to sposób przetwarzania informacji gdzie proces żą-
dający usług i proces dostarczający usług są wyodrębnione. Jest ona szczególnie
wygodna w systemach sieciowych i rozproszonych.
Klient – proces potrzebujący pewnej usługi i zlecający ja serwerowi.
Serwer – proces dostarczający usługi zlecanej przez klienta.
Procesy klientów i serwerów mogą być zlokalizowane na tych samych bądź innych
komputerach. Proces serwera może być klientem serwera innej usługi.

Rodzaje architektury klient serwer:
1. Serwer połączeniowy / bezpołączeniowy
2. Serwer sekwencyjny / współbieżny
3. Serwer stanowy / bezstanowy

background image

19


Ilustracja architektury klient–serwer

Serwer bezpołączeniowy

Serwer połączeniowy

background image

20


Prosty serwer sekwencyjny w systemie QNX
Serwer sekwencyjny to serwer składający się z jednego tylko procesu. W danej
chwili może on obsługiwać tylko jednego klienta.

Kroki klienta:
1. Lokalizacja serwera
2. Utworzenie komunikatu specyfikującego żądanie
3. Wysłanie komunikatu do procesu serwera
4. Odbiór odpowiedzi.
5. Wykorzystanie wyniku.

Kroki serwera:
1. Rejestracja nazwy własnej w serwerze nazw.
2. Odbiór zlecenia.
3. Identyfikacja zlecenia
4. Realizacja zlecenia.
5. Wysłanie odpowiedzi do klienta

Zlecenia do serwera musza być kolejkowane.

Metody kolejkowania nie obsłużonych klientów.















Serwer iteracyjny / współbieżny

Serwer iteracyjny
Serwer iteracyjny – serwer zdolny do obsługi jednego zlecenia klienta w danym od-
cinku czasu
t – czas obsługi jednego zlecenia
N – liczba zleceń klientów
T - Czas obsługi N zleceń

Serwer współbieżny
Serwer współbieżny – serwer zdolny do współbieżnej obsługi zleceń wielu
klientów. Składa się z pewnej liczby procesów lub wątków realizujących
zlecenia klientów.

background image

21

Działanie serwera iteracyjnego

Działanie serwera współbieżnego

Zasada działania serwera współbieżnego:
1. Proces główny serwera tworzy L procesów usługowych (być może na oddzielnych
maszynach).
2. Procesy usługowe wykonują operację Send do procesu głównego i są zablokowa-
ne na operacji Reply gdyż nie udzielono im odpowiedzi. Ich identyfikatory PID są
umieszczone w kolejce SQ – wolnych procesów usługowych.
3. Gdy przychodzą zlecenia od klientów to odbiera je proces główny SG. Wykonuje on
następujące działania:
- Z kolejki SQ pobierany jest wolny proces usługowy Sj i przydziela mu się do ob-
sługi klienta Ki.
- Proces usługowy odblokowuje się udzielając mu odpowiedzi Reply . W parametrach
odpowiedzi przekazywane są parametry zlecenia.

background image

22

- PID nie obsłużonego klienta umieszcza się w kolejce KQ.
4. Gdy do procesu SG przychodzi komunikat od procesu usługowego Sj że zakończył
on obsługę klienta Ki to:
- PID procesu obsługowego Sj dodawany jest do kolejki SQ.
- PID procesu klienta Ki usuwany jest z kolejki KQ.
- Wynik dostarczony przez proces usługowy odbierany jest z bufora funkcji Receive.
- Procesowi klienta Ki udzielana jest odpowiedź za pomocą funkcji Reply a wyniki
przekazywane jako parametry .
- Proces klienta Ki jest odblokowany na czym kończy się jego obsługa.

Serwer bezstanowy / stanowy

Serwer bezstanowy (ang. stateless server) - serwer, który nie przechowuje żad-
nych informacji o kliencie. Zwracając się do serwera bezstanowego, klient musi każ-
dorazowo określić komplet informacji dotyczących usługi.
Serwer stanowy (ang. Stateful server) – serwer przechowuje informacje o klien-
cie. Są dwa typy informacji przechowywanej przez serwer:
- Informacja globalna
- Informacja o stanie sesji

Informacja globalna – informacja przechowywana przez cały okres aktywności
serwera.
Informacja o stanie sesji (ang. Session state information)
Dla pewnych protokołów i aplikacji serwer musi przechowywać pewne informacje o
stanie sesji. Informacja o stanie sesji obejmuje:
1. Nazwę pliku,
2. bieżący numer bloku,
3. rodzaj akcji (pobieranie, wysyłanie)
W serwerze bezstanowym informacja o stanie sesji przechowywana jest u klienta. W
serwerze stanowym informacja o stanie sesji przechowywana jest w serwerze.

Protokół FTP – serwer bezstanowy

background image

23

Protokół FTP – serwer stanowy






















13. Komunikacja przez wspólną pamięć w systemie Unix System V i POSIX.

Komunikacja przez pamięć dzieloną
Metoda komunikacji przez wspólną pamięć może być użyta gdy procesy wykonywa-
ne są na maszynie jednoprocesorowej lub wieloprocesorowej ze wspólną pamięcią.

Procesy
Procesy mają rozdzielone segmenty danych - modyfikacje wykonane na danych w
jednym procesie w żaden sposób nie przenoszą się do procesu drugiego. Aby proce-
sy mogły mieć wspólny dostęp do tych samych danych należy:
1. Utworzyć oddzielny segment pamięci.
2. Udostępnić dostęp do segmentu zainteresowanym procesom.
Gdy procesy komunikują się przez wspólną pamięć spełniony powinien być warunek
wzajemnego wykluczania procesów. Aby warunek ten był spełniony należy zastoso-
wać dodatkowe metody synchronizacji procesów korzystających ze wspólnej pamię-
ci.

Wątki
Wątki z natury dzielą obszar danych. Zmienne zadeklarowane jako zmienne
globalne będą dostępne dla wątków.

background image

24

Funkcje operujące na wspólnej pamięci – IPC UNIX System V

Klucze mechanizmów IPC
Mechanizmy komunikacji międzyprocesowej (kolejki komunikatów, semafory, seg-
menty pamięci dzielonej) wymagają identyfikacji tych obiektów w obrębie poje-
dynczego komputera. Identyfikacja ta odbywa się za pomocą kluczy (ang.key).

Do otrzymywania unikalnych kluczy generowanych na podstawie ścieżki do pliku
służy funkcja ftok.
key_t ftok(char *path, int id);
Gdzie:
path Scieżka do pliku
id Numer identyfikacyjny
Funkcja zwraca klucz zależny od parametrów path, id.

Tworzenie segmentu wspólnej pamięci
Jeżeli mamy korzystać z segmentu pamięci wspólnej należy go najpierw utworzyć.
Segment tworzy się przy pomocy funkcji shmget.
int shmget(key_t key, size_t size, int flags)
key Klucz identyfikujący semafor
size Minimalny rozmiar obszaru wspólnego
flags Flagi specyfikujące tryb tworzenia i prawa dostępu (IP_CREAT,
IPC_EXCL)
Funkcja zwraca:
> 0 identyfikator segmentu wspólnego
-1 błąd

Dołączenie segmentu pamięci
Po utworzeniu segment pamięci wspólnej musi być udostępniony w przestrzeni ad-
resowej procesu który ma go używać. Udostępnienie to następuje przez wykonanie
funkcji shmat.
void *shmat(int id, void addr , int flags)
Gdzie:
Id identyfikator segmentu zwracany przez funkcję shmget
addr Zmienna specyfikująca adres obszaru wspólnego
flags Flagi specyfikujące tryb dostępu i przydzielania adresu
Funkcja zwraca:
> 0 - adres przydzielonego segmentu wspólnego
-1 - błąd

Gdy wartość zmiennej addr jest równa NULL system sam nada wartość adresowi po
którym widziany jest dołączany segment pamięci. Gdy nie jest równy zeru działanie
funkcji zależy od ustawienia flag. Gdy flaga SHM_RND jest ustawiona system przyj-
mie adres pod którym ma być widziany segment wspólny jako addr ale zaokrągli go
do najbliższej strony. Gdy flaga SHM_RND jest wyzerowana system ustali adres pod
którym ma być widziany segment wspólny jako dokładna wartość addr. Inną flagą
mającą tu zastosowanie jest flaga SHM_RDONLY. Gdy jest ona ustawiona segment
wspólny nadaje się tylko do odczytu.

background image

25

Odwzorowanie segmentu pamięci dzielonej w przestrzeni adresowej proce-
su

Dołączony za pomocą funkcji shmat segment pamięci wspólnej można odłączyć
używając funkcji shmdt.
int shmdt(void *addr)
Parametr addr jest wartością zwróconą przez funkcję shmat.
Funkcja shmdt zwraca:
0 gdy wykona się poprawnie
–1 w gdy wystąpi błąd.

Funkcje operujące na wspólnej pamięci – standard Posil

Standard Posix 1003.4 - funkcje pozwalające na tworzenie i udostępnianie segmen-
tów pamięci: shm_open, ltrunc, mmap, munmap ,mprotect, shm_unlink.

Tworzenie segmentu pamięci
Tworzenie segmentu pamięci podobne jest do tworzenia pliku – segment jest pli-
kiem specjalnym.
int shm_open(char *name, int oflag, mode_t mode )
name Nazwa segmentu pamięci
oflag Flaga specyfikująca tryb utworzenia (jak dla plików), np. O_RDONLY,
O_RDWR, O_CREAT
mode Specyfikacja trybu dostępu (jak dla plików).
Gdy funkcja zwraca liczbę nieujemną jest to uchwyt identyfikujący segment w pro-
cesie. Segment widziany jest jako plik specjalny w katalogu /dev/shmem.

Ustalanie rozmiaru segmentu pamięci
off_t ltrunc(int fdes, off_t offset, int whence)
fdes Uchwyt segmentu zwracany przez poprzednią funkcję shm_open.
offset Wielkość segmentu w bajtach.
whence W tym przypadku stała SEEK_SET
Funkcja zwraca wielkość segmentu lub –1 gdy błąd.

Odwzorowanie segmentu pamięci wspólnej w obszar procesu,
void *mmap(void * addr, size_t len, int prot, int flags,
int fdes, off_t off)

addr Zmienna wskaźnikowa w procesie której wartość będzie przez funkcję

background image

26

zainicjowana. Może być 0.
len Wielkość odwzorowywanego obszaru.
prot Specyfikacja dostępu do obszaru opisana w <sys/mman.h>. Może być
PROT_READ | PROT_WRITE
flags Specyfikacja użycia segmentu, np. MAP_SHARED.
fdes Uchwyt segmentu wspólnej pamięci.
off Początek obszaru we wspólnej pamięci (musi to być wielokrotność strony 4K)
Funkcja zwraca adres odwzorowanego obszaru lub –1 gdy błąd.

Odłączenie się od segmentu pamięci
shm_unlink(char *name)
name Nazwa segmentu pamięci.
Każde wywołanie tej funkcji zmniejsza licznik udostępnień segmentu. Gdy osiągnie
on wartość 0 czyli segment nie jest używany już przez żaden proces, segment jest
kasowany.

Schemat utworzenia i udostępnienia segmentu

Sposób wykorzystania wspólnej pamięci do komunikacji między procesami

Proces macierzysty P1:
1. Deklaruje zmienną wskaźnikową buf.
2. Tworzy segment pamięci o nazwie „Bufor” - funkcja shm_open.
3. Ustala jego wielkość na B_SIZE - funkcją ltrunc.

background image

27

4. Udostępnia segment w przestrzeni adresowej inicjując zmienną buf – funkcja
mmap.
5. Tworzy proces potomny P2 – funkcja fork.
6. Czyta znaki z bufora buf.
7. Odłącza się od segmentu – funkcja shm_unlink.

Proces potomny P2:
1. Korzysta z utworzonego, udostępnionego i odwzorowanego jako buf segmentu
pamięci.
2. Pisze znaki do bufora buf.

Rozwiązanie problemu producenta i konsumenta – semafory nienazwane

Rozwiązanie problemu producenta i konsumenta – semafory nazwane

background image

28


14. Wzajemne wykluczanie, sekcja krytyczna, niesystemowe i systemowe

metody ochrony sekcji krytycznej.

Problem wzajemnego wykluczania i warunki jego rozwiązania

Operacja atomowa
Sekwencja jednego lub wielu działań elementarnych które nie mogą być przerwane.
Wykonuje się w całości albo wcale. Działania pośrednie nie mogą być obserwowane
przez inny proces.
Operacja atomowa drobnoziarnista (ang. fine grained)
Operacja wykonywana przez pojedynczą atomową instrukcję kodu maszynowego.
Operacja atomowa gruboziarnista (ang. coarse grained)
Sekwencja operacji drobnoziarnistych której zapewniono niepodzielność innymi me-
todami.

Zakładamy że:
- Odczyt z pamięci komórki o adresie X jest operacją atomową
- Zapis do pamięci komórki o adresie X jest operacją atomową

Instrukcje procesów P1 i P2 wykonywane w trybie przeplotu

 Nie możemy poczynić żadnych założeń dotyczących momentów przełączenia pro-

cesów P1 i P2

 Nie da się określić wyniku działania powyższych procesów.

Wynik działania aplikacji współbieżnej nie może być uzależniony od sposobu przełą-
czania procesów. Musi być prawidłowy dla wszystkich możliwych przeplotów. Gdy
procesy współbieżne do wzajemnej komunikacji używają wspólnej pamięci, wyniki
takiej komunikacji mogą okazać się przypadkowe. Prawidłowa komunikacja współ-
bieżnych procesów przez wspólny obszar pamięci wymaga dotrzymania warunku
wzajemnego wykluczania.

Wzajemne wykluczanie - wymaganie aby ciąg operacji na pewnym zasobie (zwy-
kle pamięci) był wykonany w trybie wyłącznym przez tylko jeden z potencjalnie wie-
lu procesów.

background image

29

Sekcja krytyczna – ciąg operacji na pewnym zasobie (zwykle pamięci) który musi
wykonany w trybie wyłącznym przez tylko jeden z potencjalnie wielu procesów. Przy
wejściu do sekcji proces wykonuje

protokół wejścia w którym sprawdza czy może

wejść do sekcji krytycznej. Po wyjściu z sekcji wykonuje protokół wyjścia aby po-
informować inne procesy że opuścił już sekcję krytyczną i inny proces może ją za-
jąć. W danej chwili w sekcji krytycznej może przebywać tylko jeden proces.

Model programowania z sekcją lokalną i sekcją krytyczną

Rozwiązanie problemu wzajemnego wykluczania musi spełniać następujące warunki:
1. W sekcji krytycznej może być tylko jeden proces to znaczy instrukcje z sekcji kry-
tycznej nie mogą być przeplatane.
2. Nie można czynić żadnych założeń co do względnych szybkości wykonywania pro-
cesów.
3. Proces może się zatrzymać w sekcji lokalnej nie może natomiast w sekcji kry-
tycznej. Zatrzymanie procesu w sekcji lokalnej nie może blokować innym procesom
wejścia do sekcji krytycznej.
4. Każdy z procesów musi w końcu wejść do sekcji krytycznej.

Niesystemowe metody wzajemnego wykluczania

Blokowanie przerwań
Metoda zapewnienia wzajemnego wykluczania poprzez blokowanie przerwań opiera
się na fakcie że proces może być przełączony przez:
1. Przerwanie które aktywuje procedurę szeregującą
2. Wywołanie wprost procedury szeregującej lub innego wywołania systemowego
powodującego przełączenie procesów.
Gdy żaden z powyższych czynników nie zachodzi procesy nie mogą być przełączane.

Metoda ochrony sekcji krytycznej poprzez blokowanie przerwań opiera się na nastę-
pujących zasadach:
1 Protokół wejścia do sekcji – następuje zablokowanie przerwań.
2. Protokół wyjścia z sekcji – następuje odblokowanie przerwań.

background image

30

3. Wewnątrz sekcji krytycznej nie wolno używać wywołań systemowych mogących
spowodować przełączenie procesów.

Ochrona sekcji krytycznej przez blokowanie przerwań
Wady metody:
1. Przełączanie wszystkich procesów jest zablokowane.
2. System nie reaguje na zdarzenia zewnętrzne co może spowodować utratę da-
nych.
3. Skuteczne w maszynach jednoprocesorowych
Zastosowanie metody:
Wewnątrz systemu operacyjnego do ochrony wewnętrznych sekcji krytycznych.

Metoda zmiennej blokującej (nieprawidłowa)
Metoda polega na użyciu zmiennej o nazwie lock. Gdy zmienna lock = 0 sekcja jest
wolna, gdy lock = 1 sekcja jest zajęta. Proces przy wejściu testuje wartość tej
zmiennej. Gdy wynosi ona 1 to czeka, gdy zmieni się na 0 wchodzi do sekcji usta-
wiając wartość zmiennej lock na 1. Metoda jest niepoprawna, gdyż operacja testo-
wania wartości zmiennej lock i ustawiania jej na 1 może być przerwana (nie jest
niepodzielna). Dodatkową wadą metody jest angażowanie procesora w procedurze
aktywnego czekania.

Wykorzystania wsparcia sprzętowego do ochrony sekcji krytycznej
Wiele mikroprocesorów zawiera instrukcje wspierające sprzętowo
wzajemne wykluczanie. Są to instrukcje typu
1. Sprawdź i Przypisz - (ang. TAS -Test And Set)
2. Sprawdź i Zamień - (ang. CAS – Compare And Swap)
3. Zamień - (ang. EXCH - Exchange).
Pozwalają one wykonać kilka operacji w sposób nieprzerywal

Sprzętowe wsparcie ochrony sekcji krytycznej w systemach multiproceso-
rowych

Gdy wiele procesorów odczytuje w pętli tę samą lokację pamięci zapewne znajduje
się ona w pamięci podręcznej. Sprzętowy mechanizm kontroli pamięci musi dbać o
spójność pamięci podręcznej i głównej.
Pamięć przepisywalna (ang. Write Througt)
1. Gdy procesor czyta dane słowo z pamięci operacyjnej, strona zostaje pobrana
również do pamięci podręcznej.
2. Gdy procesor zapisuje słowo znajdujące się w pamięci podręcznej to:
- następuje zapis w pamięci głównej
- pamięci podręczne przechowujące zapisywaną lokację unieważniają zapis.
Pamięć podglądająca (ang Snooping Cache)
Jednostka sterująca pamięcią podręczną podgląda magistrale. Gdy stwierdzi że do-
konano zapisu do pamięci operacyjnej lokacji pamiętanej w tej pamięci to aktualizu-
je swój zapis.

Programowe metody zapewnienia wzajemnego wykluczania
We wczesnych procesorach nie było wsparcia sprzętowego dla wzajemnego wyklu-
czania. Stąd wzajemne wykluczanie realizowano w sposób wyłącznie programowy.
Obecnie metody te mają znaczenie tylko teoretyczne i historyczne. Wymienić można

background image

31


tu algorytm Dekkera, algorytm Petersona, algorytm piekarniczy.

Systemowe metody zapewnienia wzajemnego wykluczania

Niesystemowe metody stosowane są rzadko i ich znaczenie jest raczej teoretyczne.
Powody:
1. Prawie zawsze tworzymy aplikacje działające w środowisku systemu operacyjne- go
który z reguły dostarcza mechanizmów zapewnienia wzajemnego wykluczania.
2. Realizacja metod wzajemnego wykluczania polega na zawieszeniu pewnych pro-
cesów a wznowieniu innych. System operacyjny w naturalny sposób zapewnia takie
mechanizmy. Proces zawieszony nie wykonuje czekania aktywnego a zatem nie zu-
żywa czasu procesora.
3. Metody systemowe są znacznie prostsze i powiązane z innymi mechanizmami i
zabezpieczeniami. Przykładowo awaryjne zakończenie się procesu w sekcji krytycz-
nej odblokowuje tę sekcję. Można też narzucić maksymalny limit czasowy oczekiwa-
nia na wejście do sekcji krytycznej (ang. Timeout).
Z niesystemowych metod wzajemnego wykluczania praktycznie stosowane są meto-
dy:
1. Wirujące blokady (ang. Spin Locks) wykorzystujące sprzętowe wsparcie w postaci
instrukcji sprawdź i przypisz oraz zamień. Stosuje się je do synchronizacji wątków
ze względu na mały narzut operacji systemowych.
2. Blokowanie przerwań – do ochrony wewnętrznych sekcji krytycznych systemu
operacyjnego.

Wzajemne wykluczanie poprzez obiekty typu mutex
Mechanizm zapewniających wzajemne wykluczanie zaimplementowny jest w wielu
systemach operacyjnych. W systemach standard Posix mechanizm ten nosi nazwę
mutex. Jest to skrót od angielskiego terminu Mutual Exclusion. Poniżej opisano im-
plementację dotyczącą wątków POSIX z biblioteki Pthreads.

Tworzenie obiektu typu mutex
int mutex_init(mutex_t*mutex, mutexattr_t* attr )
mutex Obiekt typu mutex
attr Atrybuty określające zachowanie obiektu. Gdy NULL atrybuty zostaną przyjęte
domyślne Wykonanie funkcji pozostawia zmienną mutex w stanie niezablokowanym.

Zablokowanie sekcji krytycznej
int mutex_lock( mutex_t* mutex )
mutex Obiekt typu mutex zainicjowany poprzednio przez funkcję mutex_init
Gdy przynajmniej jeden proces wykonał wcześniej funkcję mutex_lock zmienna
mutex oznaczona będzie jako zajęta. Proces bieżący wykonujący tę funkcję zosta-
nie wstrzymany

Zwolnienie sekcji krytycznej
Proces opuszczający sekcję krytyczną powinien poinformować o
tym system (wykonać protokół wyjścia).
int mutex_unlock( pthread_mutex_t* mutex )
mutex Obiekt typu mutex

background image

32

Gdy jakieś procesy czekają na wejście do sekcji to jeden z nich będzie odblokowany
i wejdzie do sekcji. Gdy brak takich procesów to sekcja zostanie oznaczona jako
wolna.

Skasowanie obiektu typu mutex
int mutex_destroy( pthread_mutex_t* mutex );


Podstawowy schemat ochrony sekcji krytycznej przy użyciu zmiennej mutex:

mutex_t mutex ; // Deklaracja zmiennej mutex
mutex_init(&mutex,NULL); // Inicjalizacja zmiennej
do {

............
// Zablokowanie sekcji krytycznej
mutex_lock( &mutex );

Sekcja krytyczna
// Zwolnienie sekcji krytycznej
unlock( &mutex );

} while(1),

Ochrona sekcji krytycznej przez obiekt typu mutex

15. Semafory i ich zastosowanie. Semafory Unix System V i POSIX. Sposoby
implementacji semaforów.

Semafory
Semafor jest obiektem abstrakcyjnym służącym do kontrolowania dostępu do ogra-
niczonego zasobu. Semafory są szczególnie przydatne w środowisku gdzie wiele
procesów lub wątków komunikuje się przez wspólną pamięć.
Definicja semafora
Semafor S jest obiektem abstrakcyjnym z którym związany jest licznik L zasobu
przyjmujący wartości nieujemne. Na semaforze zdefiniowane są atomowe operacje
sem_init, sem_wait i sem_post. Podano je w poniższej tabeli.

background image

33

Inicjacja semafora - procedura sem_init
Operacja inicjacji semafora S - sem_init(S,N):
1. Alokacja pamięci na strukturę semafora.
2. Inicjacja pól struktury *S.
3. Inicjacja licznika L semafora na wartość początkową N.
Procedura sem_wait
Kroki procedury sem_wait(S)podane są poniżej:
1. Zablokować przerwania.
2. Zmniejsz licznik L semafora S o 1.
3. Gdy licznik L < 0 to:
- usunąć proces bieżący z kolejki procesów gotowych
- umieścić deskryptor usuniętego procesu w kolejce S->Sem_waiting semafora S
- przekazać sterowanie do systemu operacyjnego
4. Odblokować przerwania.
Operacja musi sem_wait być atomowa. Zablokowanie przerwań jest metodą ochro-
ny sekcji krytycznej.
Procedura sem_post
Kroki procedury sem_post(S)podane są poniżej:
1. Zablokować przerwania.
2. Zwiększ licznik L semafora S o 1.
3. Gdy licznik L <= 0 to:
- Usunąć pierwszy proces z kolejki semafora
- Umieścić deskryptor usuniętego procesu w kolejce procesów gotowych.
- Przekazać sterowanie do procedury szeregującej systemu operacyjnego
4. Odblokować przerwania.
Operacja musi sem_post być atomowa.

Istotne cechy semafora:
1. Semafor nie jest liczbą całkowitą na której można wykonywać operacje arytme-
tyczne.
2. Operacje na semaforach są operacjami atomowymi.

Niezmiennik semafora
Aktualna warto

ść

licznika L semafora S spełnia nast

ę

puj

ą

ce warunki:

1. Jest nieujemna czyli: L >= 0
2. Jego warto

ść

wynosi: L= N - Liczba_operacji(sem_wait) + Liczba_operacji(sem_post).

(N jest warto

ś

ci

ą

pocz

ą

tkow

ą

licznika).

background image

34


Semafor binarny
W semaforze binarnym wartość licznika przyjmuje tylko dwie wartości: 0 i 1.
Rodzaje semaforów
1. Semafor ze zbiorem procesów oczekujących (ang. Blocked- set Semaphore) – Nie
jest określone który z oczekujących procesów ma być wznowiony.
2. Semafor z kolejką procesów oczekujących (ang. Blocked- queue Semaphore) –
Procesy oczekujące na semaforze umieszczone są w kolejce FIFO.
Uwaga!
Pierwszy typ semafora nie zapewnia spełnienia warunku zagłodzenia.

Przykład zastosowania semafora do ochrony sekcji krytycznej

Implementacja semaforów - kanoniczne operacje synchronizacyjne
Kanoniczne operacje synchronizacyjne Suspend i Resume:

Suspend(Queue K) – zawieszenie procesu bieżącego w kolejce K
Kroki procedury Suspend
Stan procesu bieżącego zapamiętywany jest w jego deskryptorze.
1. Deskryptor usuwany jest z kolejki procesów gotowych i umieszczany w kolejce K.
2. Uruchamiana jest procedura szeregująca która wybiera do wykonania pewien
proces z kolejki procesów gotowych.

Resume(Queue K) - Wznowienie pierwszego procesu z kolejki K
Kroki procedury Resume
1. Deskryptor pierwszego procesu z kolejki K jest przenoszony z tej kolejki do kolej-
ki procesów gotowych.
2. Uruchamiana jest procedura szeregująca która wybiera do wykonania pewien
proces z kolejki procesów gotowych.

Nieraz niezbędna jest procedura Resume(K,P) – wznowienie procesu P (P –
PID procesu) z kolejki K.

Semafory w IPC UNIX System V
Semafory w IPC UNIX System V tworzą tablicę. Funkcje dotyczące semaforów ope-

background image

35


rują na całych tablicach. Z semaforem związane są następujące liczniki:

semval

Wartośc licznika semafora

sempid

PID procesu który ostatnio wykonywał operację na semaforze

semncnt

Liczba procesów czekających aż semafor osiągnie wartość
większą niż jego bieżąca wartość

semzcnt

Liczba procesów czekających aż semafor osiągnie wartość zero

Tablica semaforów w IPC UNIX System V

Klucze mechanizmów IPC
Mechanizmy komunikacji międzyprocesowej (kolejki komunikatów, semafory, seg-
menty pamięci dzielonej) wymagają identyfikacji tych obiektów w obrębie poje-
dynczego komputera. Identyfikacja ta odbywa się za pomocą kluczy (ang. key). Do
otrzymywania unikalnych kluczy generowanych na podstawie ścieżki do pliku służy
funkcja ftok (opisana w pytaniu nr 13).

Tworzenie semafora
int semget(key_t klucz, int ile, int flagi)

Klucz

Klucz identyfikujący semafor

Ile

Liczba semaforów w tablicy

Flagi

Flagi specyfikujące tryb tworzenia i prawa dostępu

Funkcja zwraca:
> 0 identyfikator semafora -1 błąd
Funkcja sprawdza czy semafor o danym kluczu istnieje. Gdy nie to go tworzy. Gdy
istnieje to dalsze działanie zależy od flag.
IPC_CREAT Utwórz semafor gdy nie istnieje. Gdy istnieje zwraca identyfikator
semafora.
IPC_EXCL Gdy semafor istnieje funkcja kończy się błędem.
Flagi zawierają informacje o:
1. Sposobie tworzenia semafora (IPC_CREAT, IPC_EXCL)
2. Prawa dostępu dla właściciela, grupy i innych

Ustawianie własności semafora
int semctrl(int semid, int num_sem, int command, semun ctl_arg )
Gdzie:

semid

Identyfikator semafora zwracany przez funkcję semget

background image

36

semid

Identyfikator semafora zwracany przez funkcjęsemget

op

Struktura specyfikująca operację

num_sem

Liczba elementów w tablicy semaforów

num_sem

Numer semafora do którego odnosi się operacja

command

Polecenie specyfikujące działanie na semaforze

sem_un

Bufor z argumentami. Postać zależy od pola command

Funkcja zwraca:
>= 0 – sukces
-1 – błąd

Operacje semaforowe
int semop(int semid, struct sembuf * op, size_n num_sem)
Gdzie:




Funkcja zwraca:
>= 0 – sukces
-1 – błąd
Flagi:
IPC_NOWAIT operacja nie będzie blokująca

SEM_UNDO po zako

ń

czeniu procesu warto

ść

licznika wszelkich operacji semaforowych

b

ę

dzie zniwelowana

Operacje które maj

ą

by

ć

wykonane na semaforze zale

żą

od warto

ś

ci pola sem_op w

sposób nast

ę

puj

ą

cy:

Pole sem_op ujemne

Gdy pole sem_op jest < 0 to operacja odpowiada operacji sem_wait. Z polem
sem_op porównywany jest licznik semafora semval a działanie zgodne jest z poniż-
szą tabelą:
Gdy: semval + sem_op >= 0 semval = semval -sem_op
Gdy: semval + sem_op < 0 Proces bieżący jest blokowany do czasu gdy semval +
sem_op >= 0
Pole sem_op dodatnie
Gdy pole sem_op jest > 0 to operacja odpowiada operacji sem_post. Wartość pola
semval jest modyfikowana semval = semval + sem_op.
Gdy żaden proces nie czeka na semaforze to nic sięnie dzieje.
Gdy są procesy czekające na semaforze to sprawdzane jest czy nowa wartość pola
semval jest wystarczająca do wznowienia jakiegoś czekającego procesu. Gdy tak to
jest on wznawiany.
Pole sem_op równe zero
Gdy pole sem_op jest = 0 działanie semafora zależy od wartości pola semval.
Gdy semval jest równe zeru to nic się nie dzieje.
Gdy semval jest różne od zera to proces bieżący blokuje się do czasu gdy pole
semval będzie równe zeru.

Należy zauważyć że funkcja semop(...) działa na tablicy semaforów. Pozwala ona
niepodzielne wykonanie grupy operacji semaforowych zgodnie z zasadą„wszystko
albo nic”. Proces będzie odblokowany gdy pozwalają na to warunki na wszystkich
semaforach.

background image

37


Semafory w standardzie POSIX
Wyróżnione są tu dwa typy semaforów:
1. Semafory nienazwane
2. Semafory nazwane
Semafory nienazwane nadają się do synchronizacji wątków w obrębie jednego pro-
cesu. Dostęp do semafora nienazwanego następuje poprzez jego adres. Może on
być także użyty do synchronizacji procesów o ile jest umieszczony w pamięci dzielo-
nej. Dostęp do semaforów nazwanych następuje poprzez nazwę. Ten typ semaforów
bardziej nadaje się synchronizacji procesów niż wątków. Semafory nienazwane dzia-
łają szybciej niż nazwane.

Semafory nienazwane
Dostęp do semafora nienazwanego następuje po adresie semafora. Stąd nazwa se-
mafor nienazwany.

Deklaracja semafora
Przed użyciem semafora musi on być zadeklarowany jako obiekt typu sem_t a pa-
mięć używana przez ten semafor musi zostać mu jawnie przydzielona.
O ile semafor ma być użyty w różnych procesach powinien być umieszczony w wcze-
śniej zaalokowanej pamięci dzielonej.
1. Utworzyć segment pamięci za pomocą funkcji shm_open.
2. Określić wymiar segmentu używając funkcji ltrunc.
3. Odwzorować obszar pamięci wspólnej w przestrzeni danych procesu - mmap.
Inicjacja semafora - funkcja sem_init
Przed użyciem semafor powinien być zainicjowany.
int sem_init(sem_t *sem, int pshared, unsigned value)
sem Identyfikator semafora (wskaźnik na strukturę w pamięci )
pshared Gdy wartość nie jest zerem semafor może być umieszczony w pamięci
dzielonej i dostępny w wielu procesach value Początkowa wartość semafora
Funkcja zwraca 0 gdy sukces, -1 gdy błąd.
Pobranie zasobu – funkcja sem_wait
Funkcja ta odpowiada omawianej wcześniej operacji sem_wait(S). Prototyp
funkcji jest następujący:
int sem_wait(sem_t *sem)
sem Identyfikator semafora
Gdy licznik semafora jest nieujemny funkcja zmniejsza go o 1. W przeciwnym
przypadku proces bieżący jest zawieszany. Zawieszony proces może być od-
blokowany przez procedurę sem_sem_post wykonaną w innym procesie lub
sygnał. Funkcja zwraca 0 gdy sukces, -1 gdy błąd.
Zwrot zasobu – funkcja sem_post
Funkcja ta odpowiada omawianej wcześniej operacji sem_post(S). Prototyp
funkcji jest następujący:

background image

38


int sem_post(sem_t *sem)
sem Identyfikator semafora
Jeśli jakikolwiek proces jest zablokowany na tym semaforze przez wykonanie
funkcji

sem_wait zostanie on odblokowany. Gdy brak procesów zablokowa-

nych licznik semafora zwiększany jest o 1.
Funkcja zwraca 0 gdy sukces, -1 gdy błąd.
Kasowanie semafora – funkcja sem_destroy
Semafor kasuje sięprzy pomocy funkcji:
int sem_destroy(sem_t *sem) sem Identyfikator semafora Gdy jakieś procesy
są zablokowane na tym semaforze, zostaną one odblokowane i operacja
sem_destroy zakończy się błędem.


Semafory nazwane
Semafory nazwane identyfikowane są w procesach poprzez ich nazwę. Na semafo-
rze nazwanym operuje się tak samo jak na semaforze nienazwanym z wyjątkiem
funkcji otwarcia i zamknięcia semafora.

Otwarcie semafora – funkcja sem_open
Aby użyć semafora nazwanego należy uzyskać do niego dostęp poprzez funkcję:
sem_t * sem_open(const char *sem_name, int oflags, ... )
sem_name Nazwa semafora, powinna się zaczynać od znaku „/”.
oflags Flagi trybu tworzenia i otwarcia: O_RDONLY, O_RDWR, O_WRONLY. Gdy
semafor jest tworzony należy użyć flagi O_CREAT
mode Prawa dostępu do semafora – takie jak do plików. Parametr jest opcjonalny.
value Początkowa wartość semafora. Parametr jest opcjonalny. Funkcja zwraca
identyfikator semafora. Semafor widoczny jest w katalogu /dev/sem. Funkcja two-
rzy semafor gdy nie był on wcześniej utworzony i otwiera go.
Zamykanie semafora nazwanego – funkcja sem_close
Semafor zamyka się poprzez wykonanie funkcji:
int sem_close(sem_t * sem)
Funkcja zwalnia zasoby semafora i odblokowuje procesy które są na nim zabloko-
wane. Funkcja zwraca 0 gdy sukces, -1 gdy błąd.

16. Monitory i ich zastosowanie

Monitory
Semafor nie jest mechanizmem strukturalnym. Aplikacje pisane z użyciem semafo-
rów są podatne na błędy, np. brak operacji sem_post blokuje aplikację. Monitor jest
strukturalnym narzędziem synchronizacji.
Zmienne i działające na nich procedury zebrane sąw jednym module. Dostęp do
zmiennych monitora możliwy jest tylko za pomocą procedur monitora. W danej
chwili tylko jeden proces wykonywać może procedury monitora. Gdy inny proces
wywoła procedurę monitora będzie on zablokowany do chwili opuszczenia monitora
przez pierwszy proces.
Istnieje możliwość wstrzymania i wznowienia procedur monitora za pomocą zmien-
nych warunkowych (ang. conditional variables). Na zmiennych warunkowych można
wykonywać operacje Wait i Signal.

background image

39

Wait(c) -Wstrzymanie procesu bieżącego wykonującego procedurę monitora i wsta-
wienie go na koniec kolejki związanej ze zmienną warunkową c. Jeżeli jakieś proce- sy
czekają na wejście do monitora to jeden z nich będzie wpuszczony.
Signal(c) – Odblokowanie jednego z procesów czekających na zmiennej warunko-
wej c. Gdy brak czekających procesów operacja nie daje efektów.
Jeśli nie jest to ostatnia instrukcja procedury monitora to proces wykonujący tę ope-
rację jest wstrzymany do chwili gdy wznowiony przezeń proces zwolni monitor.
Wstrzymany tak proces może przebywać w:
1. wejściowej kolejce procesów oczekujących na wejście do monitora
2. kolejce uprzywilejowanej Które rozwiązanie zastosowano zależne jest od

implementacji.

Notempty(c) – Funkcja zwraca true gdy kolejka c jest niepusta, false gdy pusta.

Operacja signal jest bezpamięciowa (nie posiada licznika).

Kolejki monitora

Rozwiązanie problemu wzajemnego wykluczania za pomocą monitorów

monitor int z1,z2;
Pisz(int x1, int x2){

z1 = x1;
z2 = x2;

};
Czytaj(int *x1, int *x2){

*x1 := z1;
*x2 := z2;

};

Procedury Czytaj lub pisz wykonane będą w sposób wyłączny co wynika z definicji
monitora.

background image

40

Monitory z powiadamianiem i rozgłaszaniem
W omawianych poprzednio monitorach Hoara’a wystąpienie operacji Signal(c) musi
spowodować natychmiastowe wznowienie procesu oczekującego na zmiennej wa-
runkowej c.
Monitory z powiadamianiem
Inne rozwiązanie stosowane jest w monitorach z powiadamianiem.
Notify(c) - odblokowanie jednego z procesów związanej ze zmienną warunkową c
ale proces ten nie musi być natychmiast zaszeregowany.
Monitory z rozgłaszaniem
NotifyAll(c) - odblokowanie wszystkich z procesów związanej ze zmienną warun-
kową c. Proces te będą konkurowały o dostęp do monitora. Będą zaszeregowane
gdy będzie to możliwe. W monitorach z rozgłaszaniem należy powtórnie sprawdzić
warunek powodujący zablokowanie.

17. Kolejki komunikatów Unix System V i POSIX. Zastosowanie kolejek ko-
munikatów.

Kolejki komunikatów
Kolejka komunikatów Q posiada następujące własności:
1. Posiada określoną pojemność N komunikatów (długość bufora komunikatów).
2. Posiada nazwę którą procesy mogą zidentyfikować.
3. Więcej niż jeden proces może czytać lub pisać z/do kolejki.

Przebieg operacji zapisu i odczytu zależy od liczby n komunikatów w kolejce i od jej
pojemności Max.

Liczba komunikatów
n w kolejce Q

Wysłanie komunikatu
Send(Q,buf,size)

Odbiór komunikatu
Receive(Q,buf,size)

n = Max

Blokada lub sygnaliza-
cja błędu

Bez blokady

0< n < Max

Bez blokady

Bez blokady

n = 0

Bez blokady

Blokada lub sygnalizacja błędu

Tworzenie kolejki komunikatów
W IPC Unix System V kolejki komunikatów służą do komunikacji lokalnej. Identyfi-
kowane są przez klucz (podobnie jak w przypadku pamięci dzielonej i semaforów).
int msgget(key_t key, int flags)

background image

41

Key Klucz identyfikujący semafor
Flags Flagi specyfikują:
- tryb tworzenia kolejki (IP_CREAT, IPC_EXCL)
-prawa dostępu (9 mniej znaczących bitów)

Wysyłanie komunikatów
Komunikaty wysyła się przy pomocy funkcji msgsnd zdefiniowanej jak poniżej.
int msgsnd(int msgid, msgbuf* msg, size_t size, int flags)

msgid

Identyfikator kolejki (zwracany przez funkcję msgget)

msg

Adres bufora z wysyłanym komunikatem

size

Długość wysyłanego komunikatu

flags

Dopuszczalna tylko flaga IPC_NOWAIT

Funkcja zwraca:
0 - sukces
-1 - błąd

Komunikat powinien być strukturą zdefiniowaną poniżej:

typedef struc {

int typ; // Typ komunikatu
char tekst[SIZE]; // Treść komunikatu

} msgbuf

 Pole typ używane jest w funkcji odbierającej do selekcji komunikatu.
 Pole tekst może być dowolną strukturą dowolnego rozmiaru.

Działanie funkcji msgsnd:
1. Gdy jest miejsce w kolejce komunikat msg jest dołączany do tej kolejki.
2. Gdy brak miejsca w kolejce proces bieżący blokowany jest do czasu gdy miejsce

takie się pojawi. Gdy ustawiona jest flaga IPC_NOWAIT proces nie jest blokowa-
ny a wywołanie kończy się błędem.

Przy wysyłaniu sprawdzane są prawa dostępu do kolejki komunikatów. Gdy proces
nie ma stosownych praw do użycia kolejki wywołanie kończy się błędem.

Odbiór komunikatów
Komunikaty odbiera się przy pomocy funkcji msgrcv zdefiniowanej jak poniżej.
int msgrcv(int msgid, msgbuf* msg, size_t size, long mtyp, int flags)

msgid

Identyfikator kolejki (zwracany przez funkcję msgget)

msg

Adres bufora na odbierany komunikat

size

Maksymalna długość odbieranego komunikatu

mtyp

Typ komunikatu

flags

Dopuszczalne są flagi IPC_NOWAIT, MSG_NOERROR

Funkcja zwraca: > 0 -liczba odebranych bajtów -1- błąd

Działanie funkcji msgrcv zależy od:
1. Parametru mtyp – od parametru tego zależy które komunikaty z kolejki będą

background image

42


odebrane.

2. Stanu kolejki i flag – czynniki te decydują o synchronizacji procesów.

Zależność od parametru mtyp:
1. Gdy mtyp > 0 z kolejki odbierane są komunikaty danego typu (typ = mtyp).
2. Gdy mtyp = 0 to odbierane są wszystkie komunikaty
3. Gdy mtyp < 0 odbierane są komunikaty których typ jest mniejszy od wartości

bezwzględnej parametru mtyp. Komunikaty odbierane są w kolejności od naj-
mniejszego pola typ do największego.

Zależność od stanu kolejki:
1. Gdy w kolejce jest komunikat żądanego typu proces bieżący kopiuje ten komuni-

kat do bufora msg, usuwa go z kolejki i biegnie dalej.

2. Gdy w kolejce brak komunikatu żądanego typu proces bieżący ulega zablokowa-

niu do czasu gdy taki proces się pojawi. Gdy ustawiona jest flaga IPC_NOWAIT
proces nie jest blokowany a wywołanie kończy się błędem.

Gdy w kolejce znajduje się komunikat którego długość przekracza parametr size to
działanie zależy od flagi MSG_NOERROR. Gdy flaga będzie ustawiona komunikat zo-
stanie obcięty. Gdy flaga nie jest ustawiona wywołanie skończy się błędem.

Testowanie i ustawianie parametrów kolejki
Do testowania i ustawiania parametrów kolejki służy funkcja msgctl. Najważniejsze
polecenia to testowanie statusu kolejki, ustawianie jej parametrów oraz usunięcie
całej kolejki z systemu.
int msgctl(int msgid, int command, struct msqid_ds *stat)

msgid

Identyfikator kolejki (zwracany przez funkcję msgget)

command

Polecenie

stat

Bufor na dane lub polecenia

Funkcja zwraca: > 0 -sukces -1- błąd

Testowanie statusu kolejki – polecenie IPC_STAT
Gdy pole command przyjmuje wartość IPC_STAT do struktury stat system kopiuje
informacje o stanie kolejki.

Ustawianie parametrów kolejki – polecenie IPC_SET
Aby ustawić parametry kolejki komunikatów używa siępolecenia IPC_SET. Zmieniać
można:
 prawa dostępu (można je też ustawić przy tworzeniu)
 maksymalną długość kolejki.
Następuje to poprzez ustawienia pola msg_qbytes struktury stat. Domyślnie wartość
tego pola wynosi 16 KB.

Kasowanie kolejki komunikatów
Aby skasować kolejkę komunikatów używa się polecenia IPC_RMID. Polecenie to
może być wykonane przez administratora i właściciela kolejki.

background image

43


Kolejki komunikatów - POSIX
Dwa procesy piszą do jednej kolejki:

Podstawowe cechy kolejek komunikatów:
1. W przypadku komunikatów komunikacja zachodzi bezpośrednio pomiędzy proce-

sami. Kolejki komunikatów są pośrednim obiektem komunikacyjnym widzianym
jako plik specjalny. Komunikujące się procesy nie muszą znać swoich identyfika-
torów.

2. Komunikaty odczytywane z kolejki zachowują strukturę– są separowane. W ko-

lejce mogą znajdować się komunikaty różnej długości. Własności tej nie mają ko-
lejki FIFO.

3. Można zadać maksymalną długość kolejki komunikatów. Gdy zostanie ona prze-

kroczona, proces piszący do kolejki komunikatów będzie zablokowany.

4. Kolejka widziana jest w systemie plików jako plik specjalny. Operacje zapisu /

odczytu mogą być zabezpieczane prawami dostępu tak jak w przypadku plików
regularnych.

5. Można testować status kolejki (np. liczbę komunikatów w kolejce). Nie jest to

możliwe w przypadku kolejek FIFO.

6. Komunikatom można nadać priorytet. Komunikaty wyższym priorytecie będą

umieszczane na początku kolejki.

Zastosowanie kolejki komunikatów jest wygodnym rozwiązaniem w następujących
przypadkach:
1. Proces wysyłający komunikaty nie może być wstrzymany.
2. Proces wysyłający komunikaty nie potrzebuje szybkiej informacji zwrotnej o tym

czy komunikat dotarł do adresata.

3. Zachodzi potrzeba przekazywania danych z procesu w którym one powsta-

ją(producent) do procesu w którym są one przetwarzane (konsument)

Podstawowe typy i plik nagłówkowy
Kolejka komunikatów jest typu mqd_t. Typ ten jest zdefiniowany w pliku nagłów-
kowym <mqueue.h>. Modyfikowalne atrybuty kolejki komunikatów zdefiniowane są
w strukturze mq_attr.

struct mq_attr {

long mq_maxmsg; // Maksymalna liczba komunikatów w kolejce long
mq_msgsize; // Maksymalna wielkość pojedynczego komunikatu long

background image

44


mq_curmsg; // Aktualna liczba komunikatów w kolejce long mq_flags; // Flagi
long mq_sendwait; // Liczba procesów zablok. na operacji zapisu long
mq_recvwait; // Liczba procesów zablok. na operacji odczytu

}

Utworzenie i otwarcie kolejki komunikatów
Kolejkę komunikatów tworzy się za pomocą funkcji:
mqd_t mq_open(char *name,int oflag,int mode,mq_attr*attr)
name Łańcuch identyfikujący kolejkę komunikatów. Kolejki tworzone są w katalogu
/dev/mqueue
oflag Tryb tworzenia kolejki. Tryby te są analogiczne jak w zwykłej funkcji open. mode

Prawa dostępu do kolejki (r -odczyt, w -zapis) dla właściciela pliku, grupy i innych,
analogicznie jak w przypadku plików regularnych. Atrybut x -wykonanie jest
ignorowany.

attr Atrybuty kolejki
Funkcja mq_open zwraca:
1. W przypadku pomyślnego wykonania wynik jest nieujemny – jest to identyfikator

kolejki komunikatów

2. W przypadku błędu funkcja zwraca –1.

Wysłanie komunikatu do kolejki
Wysłanie komunikatu do kolejki komunikatów odbywa się za pomocą funkcji:
int mq_send(mqd_t mq, char *msg, size_t len, unsigned int mprio)
Znaczenie parametrów:
mq identyfikator kolejki komunikatów,
*msg adres bufora wysyłanego komunikatu,
len długość wysyłanego komunikatu,
mprio priorytet komunikatu (od 0 do MQ_PRIORITY_MAX).
Wywołanie funkcji powoduje przekazanie komunikatu z bufora msg do kolejki mq.
Można wyróżnić dwa zasadnicze przypadki:
1. W kolejce jest miejsce na komunikaty. Wtedy wykonanie funkcji nie spowoduje

zablokowania procesu bieżącego.

2. W kolejce brak miejsca na komunikaty. Wtedy wykonanie funkcji spowoduje za-

blokowania procesu bieżącego. Proces ulegnie odblokowaniu gdy zwolni się miej-
sce w kolejce.

Zachowanie się funkcji uzależnione jest od stanu flagi O_NONBLOCK. Flaga ta jest
domyślnie wyzerowana. W ogólności funkcja zwraca: 0 Sukces -1 Błąd

Pobieranie komunikatu z kolejki
Pobieranie komunikatu z kolejki komunikatów odbywa się za pomocą funkcji
mq_receive.
int mq_receive(mqd_t mq, char *msg, size_t len, unsigned int *mprio)
Znaczenie parametrów:
mq identyfikator kolejki komunikatów,
*msg Adres bufora na odbierany komunikat,
len maksymalna długość odbieranego komunikatu,
mprio priorytet odebranego komunikatu.

1. Gdy w kolejce znajduje się przynajmniej jeden komunikat wywołanie funkcji

background image

45

mq_receive nie spowoduje zablokowania procesu bieżącego.

2. Gdy w kolejce brak komunikatów wywołanie funkcji mq_receive spowoduje za-

blokowania procesu bieżącego. Proces ulegnie odblokowaniu gdy w kolejce poja-
wi się jakiś komunikat.

W przypadku gdy więcej niż jeden proces czeka na komunikat – odblokowany bę-
dzie proces który najdłużej czekał. Zachowanie się funkcji uzależnione jest także od
stanu flagi O_NONBLOCK. Funkcja mq_receive zwraca:
>0 Rozmiar odebranego komunikatu gdy wynik jest większy od 0.
–1 Gdy wystąpił błąd.

Testowanie statusu kolejki komunikatów
Testowanie statusu kolejki komunikatów odbywa się poprzez wykonanie funkcji:
int mq_getattr(mqd_t mq, struct mq_attr *attr)
Znaczenie parametrów:
mq Iidentyfikator kolejki komunikatów,
*attr Adres bufora ze strukturą zawierającą atrybuty kolejki komunikatów
Użyteczne elementy struktury atrybutów:
mq_curmsg Aktualna liczba komunikatów w kolejce
mq_sendwait Liczba procesów zablokowanych na operacji zapisu
mq_recvwait Liczba procesów zablokowanych na operacji odczytu

Zawiadamianie procesu o pojawieniu się komunikatu w kolejce
Można spowodować aby pojawienie się komunikatu w pustej kolejce (a wiec zmiana
stanu kolejki z „pusta” na „niepusta”) powodowało zawiadomienie procesu bieżące-
go. Zawiadomienie może mieć postać sygnału lub depozytu (ang. Proxy).
int mq_notify(mqd_t mq, struct sigevent *notif)
Znaczenie parametrów:
mq Identyfikator kolejki komunikatów,

*notif Adres struktury typu sigevent specyfikuj

ą

cego sposób zawiadomienia.

 Gdy pole sigev_signo > 0 interpretowane jest ono jako numer sygnału który

będzie wysłany gdy w kolejce pojawi się komunikat. W procesie należy zdefinio-
wać sposób obsługi tego sygnału.

 Gdy pole sigev_signo < 0 jego wartość bezwzględna interpretowana jest jako

numer depozytu generowanego gdy w kolejce pojawi się komunikat.

Zamknięcie i skasowanie kolejki komunikatów
Gdy proces przestanie korzystać z kolejki komunikatów powinien ją zamknąć. Do
tego celu służy funkcja:
int mq_close(mqd_t mq)
Kolejkę kasuje się za pomocą polecenia:
int mq_unlink(char *name)

18. Problem producenta / konsumenta, czytelników i pisarzy i ich rozwią-
zanie za pomocą komunikatów, semaforów, monitorów, kolejek komunika-
tów.

Problem producenta i konsumenta

background image

46

Zagadnienie kontroli użycia jednostek zasobu
W systemie istnieje pula N jednostek zasobu pewnego typu. Procesy mogą pobierać
z puli zasoby i je zwracać. Gdy brak jest zasobu a pewien proces będzie próbował go
pobrać ulega on zablokowaniu. Proces zostanie odblokowany gdy inny proces zwróci
jednostkę zasobu.
1. W systemie istnieją dwie grupy procesów – producenci i konsumenci oraz bufor
na elementy.
2. Producenci produkują pewne elementy i umieszczają je w buforze.
3. Konsumenci pobierają elementy z bufora i je konsumują.
4. Producenci i konsumenci przejawiają swą aktywność w nie dających się określić
momentach czasu.
5. Bufor ma pojemność na N elementów.

Należy prawidłowo zorganizować pracę systemu.
1. Gdy są wolne miejsca w buforze producent może tam umieścić swój element.
Gdy w buforze brak miejsca na elementy producent musi czekać. Gdy wolne miejsca
się pojawią producent zostanie odblokowany.
2. Gdy w buforze są jakieś elementy konsument je pobiera. Gdy brak elementów w
buforze konsument musi czekać. Gdy jakiś element się pojawi, konsument zostanie
odblokowany.
Bufor zorganizowany może być na różnych zasadach.
1. Kolejka FIFO (bufor cykliczny).
2. Kolejka LIFO (stos).
Umieszczanie / pobieranie elementu z bufora jest sekcją krytyczną.

Problem czytelników i pisarzy
W systemie istnieją dwie grupy procesów – czytelnicy i pisarze. Czytanie może się
odbywać współbieżnie z innymi procesami natomiast pisanie, w celu zapewnienia
spójności danych, musi się odbywać na zasadzie wyłączności.

background image

47

 Czytelnik może wejść do czytelni gdy jest ona pusta lub gdy są tam inni czytelni-

cy

 Pisarz może wejść do czytelni gdy jest ona pusta

Problem jest uogólnieniem problemu dostępu wielu procesów do bazy danych.

Rozwiązanie problemu producenta i konsumenta za pomocą komunikatów
Struktura rozwiązania:
1. Zarządzanie buforami leży w gestii procesu administratora
2. Procesy producentów i konsumentów komunikują się z procesem administratora.

Administrator zasobu
Problemy:
1. Jak wstrzymać procesy producentów gdy brak miejsca w buforze?
2. Jak wstrzymać procesy konsumentów gdy brak elementów w buforze?
Rozwiązanie:
1. Procesom które mają być wstrzymane nie udziela się odpowiedzi Reply.
2. Aby można było tak wstrzymany proces potem odblokować należy przechować
jego pid.

Rozwi

ą

zanie problem czytelników i pisarzy za pomoc

ą

semaforów

Rozwiązanie z możliwością zagłodzenia pisarzy
- Czytelnik może wejść do czytelni gdy jest ona pusta lub gdy są tam inni czytelnicy
- Pisarz może wejść do czytelni gdy jest ona pusta
Może się tak zdarzyć że zawsze jakiś czytelnik jest w czytelni co doprowadzi do za-
głodzenia pisarzy.
Rozwiązanie z możliwością zagłodzenia czytelników
- Czytelnik musi czekać gdy są w czytelni lub czekają jacykolwiek pisarze
Rozwiązanie poprawne
- Wpuszczać na przemian czytelników i pisarzy
- Gdy wchodzi jeden z czytelników, to wpuszcza on wszystkich czekających czytelni-
ków
Rozwiązanie poprawne nie dopuszcza do zagłodzenia czy to czytelników czy też pi-
sarzy.

#define PLACES 8 // Liczba wolnych miejsc w czytelni
semaphore wolne ; // Liczba wolnych miejsc w czytelni

background image

48

semaphore wr; // Kontrola dostępu do czytelni
Reader(){

while (TRUE) {
sem_wait(wolne); // Czekanie na miejsca w czytelni
read_db(); // Czytelnik w czytelni - czyta
sem_post(wolne); // Zwolnienie miejsca w czytelni

}
Writer() {

while (TRUE) {

create_data(); // Pisarz zastanawia się
sem_wait(wr); // Zablokowanie dostępu dla pisarzy
// Wypieranie czytelników z czytelni
for(j=1;j<=places;j++)
sem_wait(wolne);

write_db(); // Pisarz w czytelni – pisze
// Wpuszczenie czytelników
for(j=1;j<= PLACES;j++)
sem_post(wolne);

sem_post(wr); // Odblokowanie pisarzy

}

}
main() {

sem_init(wolne,PLACES);
sem_init(wr,1);

....

}
Rozwiązanie problemu producenta i konsumenta – semafory nienazwane

Rozwiązanie problemu producenta i konsumenta za pomocą monitorów

background image

49

Rozwiązanie problemu czytelników i pisarzy za pomocą monitorów

monitor CzytelnicyPisarzecondition Pisanie, Czytanie;// Kolejka pisarzy i czytelników
int ReaderCount = 0; // Liczba czytelników w czytelni

Boolean zajety = false; // true gdy w czytelni jest pisarz
procedure StartRead() {

if (zajety) // Gdy czytelnia zajęta - blokada
Wait(Czytanie);ReaderCount++;Signal(Czytanie); // Wpuść następnego
czytelnika

}
EndRead() {

ReaderCount-- ;if(ReaderCount == 0) // Ostatni Czyt. wpuszcza pisarzy
Signal(Pisanie);

}
StartWrite() {// Czekaj gdy w czytelni jest pisarz lub czytelnicyif(zajety

ReaderCount != 0)

Wait(Pisanie);
zajety = true;

}
EndWrite() {

zajety = false;// Gdy czekają czytelnicy to ich wpuść

// Gdy czytelnicy nie czekają – wpuść pisarzy

if (Notempty(Czytanie)) Signal(Czytanie);
else Signal(Pisanie);

}
Reader() {

while (true) {

StartRead();
Czytaj(); // Wykonaj procedurę czytaniaEndRead();

}

}

Writer() {

while (true) {

StartWrite();
Pisz(); // Wykonaj procedurę pisaniaEndWrite();

}

}

19. Obsługa zdarzeń asynchronicznych. Sygnały i ich obsługa. Funkcje sig-
nal, kill, raise, alarm, raise, pause. Zabezpieczanie operacji blokujących sy-
gnałami.

Zdarzenia i ich obsługa
Istnieją dwie metody pozyskania informacji o zdarzeniach:
1. Cykliczne odpytywanie urządzenia czy zdarzenie zaszło (ang. polling).
2. Wykorzystanie przerwań generowanych przez zdarzenia (ang. interrupt).
Maksymalny czas reakcji na zdarzenia powinien być mierzony dla najmniej korzyst-
nych warunków. Stąd mamy pewność że w rzeczywistej sytuacji czas reakcji na

background image

50

zdarzenie nie będzie dłuższy od Tmax. Należy tak konstruować systemy aby mak-
symalny czas reakcji na zdarzenia był jak najkrótszy.

Metoda odpytywania posiada następujące cechy.
1. Długi czas reakcji na zdarzenie – maksymalnie suma czasów obsługi wszystkich
zdarzeń.
2. Trudności w uszeregowaniu obsługi zdarzeń według priorytetów.
3. Przy braku zdarzeń utrata czasu procesora na wykonanie jałowych czynności
4. Prostota implementacji – nie jest wymagany specjalny sprzęt.

Sygnał – mechanizm asynchronicznego powiadamiania procesów o zdarzeniach –
zwykle awaryjnych. Metoda powiadamiania procesów za pomocą sygnałów wywodzi
się z systemu UNIX.

Sygnały mogą być generowane przez:
1. System operacyjny, zwykle po wykonaniu nieprawidłowej operacji.
2. Z konsoli operatorskiej poprzez polecenia kill i slay.
3. Z programu aplikacyjnego poprzez funkcje kill, raise, abort, alarm oraz timery.

Proces może zareagować na sygnały w sposób następujący:
1. Obsłużyć sygnał czyli wykonać funkcję dostarczoną poprzez programistę.
2. Zignorować sygnał– nie każdy sygnał daje się zignorować.
3. Zablokować sygnał to znaczy odłożyć jego obsługę na później.
4. Zakończyć się po otrzymaniu sygnału.

Reakcja procesu na sygnałw zależności od stanu w jakim znajduje się proces.
1. Gdy proces jest wykonywany lub gotowy to następuje przerwanie sekwencji

wykonania i skok do procedury obsługi sygnału.

2. Gdy proces jest zablokowany to następuje jego odblokowanie i wykonanie

procedury obsługi tego sygnału.


Obsługa sygnału dla przypadków gdy proces jest gotowy i zablokowany

background image

51

Zestawienie ważniejszych sygnałów

Sygnał

Opis sygnału

Akcja
domyślna

SIGABRT

Sygnał przerwania procesu (ang. Abort). Sygnał może
być wygenerowany poprzez wykonanie funkcji abort w
procesie bieżącym. Powoduje że proces przed zakoń-
czeniem zapisuje na dysku swój obraz (ang. core dump)

ABRT,
DMP

SIGALRM

Sygnał alarmu (ang. Alarm) wskazujący że upłynął za-
dany czas. Generacja może być spowodowana poprzez
wykonanie funkcji alarm lub czasomierze (ang. Timers).

ABRT

SIGBUS

Sygnał wysyłany przez system operacyjny gdy ten
stwierdzi błąd magistrali (ang. Bus error).

ABRT

SIGCHLD

Przesyłany do procesu macierzystego gdy proces po-
tomny (ang. Child) kończy się.

IGN

SIGSTOP

Powoduje że proces który otrzymał ten sygnał ulega
zablokowaniu do czasu gdy nie otrzyma sygnału
SIGCONT

SIGCONT

Powoduje wznowienie procesu zawieszonego sygnałem
SIGCONT

SIGFPE

Generowany przez system gdy nastąpił błąd operacji
zmiennoprzecinkowej (ang. Floating point exception).

ABRT,
DMP

SIGHUP

Generowany gdy następuje zamknięcie terminala (ang.
Hangup
). Sygnał otrzymują procesy dla których jest to
terminal kontrolny.

ABRT

SIGILL

Generowany gdy proces próbuje wykonać nielegalną
instrukcję (ang. Illegal instruction).

ABRT

SIGINT

Przerwanie procesu (ang. Interrupt). Sygnał wysyłany
do wszystkich procesów związanych z danym termina-
lem gdy tam naciśnięto Ctrl+Break lub Ctrl+C.

SIGKILL

Sygnał wysyłany w celu zakończenia procesu. Nie może
być przechwycony ani zignorowany.

ABRT

SIGPIPE

Generowany przy próbie zapisu do łącza (ang. Pipe) lub
gniazdka gdy proces odbiorcy zakończył się.

ABRT

SIGPOLL

Sygnał generowany przez system gdy na otwarty plik
stał się gotowy do zapisu lub odczytu.

ABRT

SIGQUIT

Próba zakończenia procesu (ang. Quit). Sygnał wysyła-
ny do wszystkich procesów związanych z danym termi-
nalem gdy tam naciśnięto Ctrl+\.

ABRT,
DMP

SIGSEGV

Wysyłany przez system gdy proces naruszył mechanizm
ochrony pamięci (ang. Segment Violation)

ABRT

SIGTERM

Sygnał wysyłany w celu zakończenia procesu. Nie może
być przechwycony ani zignorowany.

ABRT

SIGPWR

Generowany przez system operacyjny gdy ten stwierdzi
upadek zasilania (ang. Power Failure) sygnalizowany
przez układ dozoru zasilania.

ABRT

background image

52

SIGUSR1

Sygnał może być wykorzystany przez użytkownika do
własnych potrzeb.

ABRT

SIGUSR2

Sygnał może być wykorzystany przez użytkownika do
własnych potrzeb.

ABRT

Wysyłanie sygnałów z programu

Użycie funkcji kill
Funkcja kill posiada następujący prototyp:
int kill(pid_t pid, int sig)
pid PID procesu do którego wysyłany jest sygnał
sig Numer sygnału.
Funkcja kill powoduje wysłanie sygnału sig do procesu pid.
Funkcja zwraca 0 gdy sukces, -1 gdy błąd.
Aby proces bieżący mógł wysłać sygnał do innego procesu musi być spełniony jeden
z warunków:
1. Efektywny identyfikator użytkownika EUID (ang. Effective User ID) procesu wy-

syłającego sygnał i procesu docelowego muszą być zgodne.

2. Rzeczywisty identyfikator użytkownika UID (ang. User ID) procesu wysyłającego

sygnał i procesu docelowego muszą być zgodne.

3. Proces wysyłający sygnał ma prawa administratora (ang. root).
Specjalne znaczenie parametru pid:
1. Gdy pid = 0 to sygnał będzie wysyłany do wszystkich procesów należących do tej

samej grupy co nadawca.

2. Gdy pid < 0 to sygnał będzie wysyłany do wszystkich procesów należących do

grupy o numerze id = |pid|.

Użycie funkcji raise
Funkcja raise posiada następujący prototyp:
int raise(int sig)
sig Numer sygnału.
Funkcja raise powoduje wysłanie sygnału sig do procesu który tę funkcję wykonał.
Gdy sig = 0 to następuje wysłanie sygnału do wszystkich procesów należących do
grupy procesów. Funkcja zwraca 0 gdy sukces, -1 gdy błąd.

Użycie funkcji alarm
Funkcja alarm posiada następujący prototyp:
int alarm(int seconds)
Funkcja alarm powoduje wygenerowanie sygnału SIGALRM po upływie liczby se-
kund wyspecyfikowanej jako parametr. Sygnał wysyłany jest do procesu który funk-
cję wywołał.
Funkcja zwraca:
> 0 to wynik jest liczbą sekund pozostałych do wysłania sygnału.
0 znaczy że alarm nie był wcześniej ustawiany
-1 Błąd

Wysyłanie sygnału z konsoli
Do wysłania sygnału z konsoli użyćmożna polecenia kill lub slay.

background image

53

Polecenie kill ma postać:
kill [-nazwa_sygnału | -numer_sygnału] pid
pid
PID procesu do którego wysyłany jest sygnał
numer_sygnału Numeryczne określenie sygnału
nazwa_sygnału Symboliczne określenie sygnału – może być uzyskane przez pole-
cenie: kill –l
Uwagi:
1. Gdy pid = 0 to sygnał będzie wysyłany do wszystkich procesów należących do

tej samej grupy co użytkownik.

2. Gdy pid < 0 to sygnał będzie wysyłany do wszystkich procesów należących do

grupy o numerze

id = |pid|.

Polecenie slay umożliwia wysłanie sygnału do procesu bez znajomości jego PID. Ja-
ko parametr podaje się nazwę procesu.
slay [-numer_sygnału] nazwa
nazwa nazwa procesu do którego wysyłany jest sygnał
numer_sygnału Numeryczne określenie sygnału – domyślnie SIGTERM

Obsługa sygnałów
Ustalenie reakcji procesu na sygnał odbywa się za pomocą funkcji signal. Ma ona
następujący prototyp:
void(*signal(int sig, void(*func)(int)))(int)
sig Numer lub symbol sygnału który ma być obsłużony
func Nazwa funkcji która ma być wykonana gdy proces odbierze sygnał sig.
Możliwe są trzy typy akcji podejmowanych w reakcji na nadejście sygnału:
1. Zignorowanie sygnału
2. Wykonanie akcji domyślnej -działanie określone przez OS – zwykle zakończenie

procesu.

3. Wykonanie funkcji dostarczonej przez programistę.
Nie jest możliwe obsłużenie sygnałów:
 SIGSTOP
 SIGKILL
Funkcja obsługi sygnału powinna być zdefiniowana w programie. Funkcja zwraca
wskaźnik na poprzednią funkcję obsługi sygnału. Istnieją dwie pierwotnie zdefinio-
wane funkcje obsługi sygnałów:
SIG_IGN Funkcja powodująca zignorowanie sygnału.
SIG_DFL Domyślna reakcja na sygnał-zakończenie procesu lub zignorowanie sy-
gnału.

Funkcja pause powoduje zablokowanie procesu aż do chwili nadejścia sygnału. Aby
proces się nie zakończył sygnał musi być obsługiwany.

Odporny interfejs sygnałowy
Funkcje sigaction pozwala na lepsze kontrolowanie obsługi sygnału niż zesta-
wy sygnałów.

struct sigaction {

void (*sa_handler)(int) ; // Funkcja obsługi sygnału
sigset_t sa_mask; // Sygnały blok. podczas obsługi

background image

54

int sa_flags; // Flagi modyfikacji działania

}

Pole sa_mask definiuje sygnały blokowane podczas obsługi danego sygnału. Będą
one zgłoszone później. Pole sa_handler może mieć jedną z trzech wartości:
 SIG_IGN
 SIG_DFL
 Adres handlera obsługi sygnału

Funkcja sigaction:
int sigaction(int signo, struct sigaction *act, structsigaction *oldact);
signo Numer sygnału dla którego definiowana jest akcja act.
act Definicja działania które ma być podjęte gdy przyjdzie sygnał.
oldact Definicja poprzedniej akcji lub ULL
Funkcja sigaction definiuje sposób obsługi sygnału signo.

Uwagi o obsłudze sygnałów.
1. Blokada sygnałów.
Podczas obsługi sygnału dostarczanie innych sygnałów jest zablokowane.
2. Sygnały i komunikaty.
Gdy proces jest zablokowany na funkcji Send lub Receive reakcja na sygnał jest na-
stępująca:

 Proces jest odblokowywany
 Sygnał jest obsługiwany
 Funkcja Send lub Receive kończy się błędem: kod powrotu –1 i zmienna errno

= EINTR.

3. Sygnały i funkcje systemowe.
W większości przypadków w czasie wykonania funkcji systemowych sygnały są za-
blokowane. Wyjątek stanowią:

 Funkcje read, write, open w odniesieniu do terminali.
 Funkcje wait, pause, sigsuspendFunkcje te będą przerywane przez sygnał.

Możliwe jest ustawienie flagi S.A._RESTART aby przerwane funkcje kontynu-
ować.

4. Kolejkowanie sygnałów.
Sygnały nie są kolejkowane

background image

55

20. Pojęcie wątku, synchronizacja wątków, muteksy, zmienne warunkowe,

biblioteka Pthreads.

Proces – pojemnik na zasoby w ramach którego wykonują się watki.
Wątek – elementarna jednostka szeregowania korzystająca z zasobów procesu.
Wątki wykonywane w ramach jednego procesu dzielą jego przestrzeń adresową i
inne zasoby procesu.
W ramach jednego procesu może się wykonywać wiele wątków

Atrybuty i zasoby własne wątku
1. Identyfikator wątka TID (ang.Thread Identifier) - każdy watek ma unikalny w
ramach procesu identyfikator. Jest to liczba całkowita. Pierwszy wątek ma TID 1,
następny 2 itd.
2. Zestaw rejestrów (ang. Register set) - każdy wątek posiada własny obszar pa-
mięci w którym pamiętany jest zestaw rejestrów procesora (tak zwany kontekst
procesora). Gdy watek jest wywłaszczany lub blokowany w obszarze tym pamiętane
są rejestry procesora. Gdy watek będzie wznowiony obszar ten jest kopiowany do
rejestrów procesora.
3. Stos (ang. Stack) - każdy wątek ma swój własny stos umieszczony w przestrzeni
adresowej zawierającego go procesu. Na stosie tym pamiętane są zmienne lokalne
wątku.
4. Maska sygnałów (ang. Signal mask) - każdy wątek ma swą własną maskę sygna-
łów. Maska sygnałów specyfikuje które sygnały mają być obsługiwane a które blo-
kowane. Początkowa maska jest dziedziczona z procesu macierzystego.
5. Obszar TLS wątku (ang. Thread Local Storage) – każdy wątek ma pewien obszar
pamięci przeznaczony na utrzymywanie różnych danych administracyjnych takich
jak TID, PID, początek stosu, kod ewentualnego błędu errno i inne dane. Obszar
TLS jest odpowiednikiem deskryptora procesu.
6. Procedura zakończenia (ang. Cancellation Handler) - gdy wątek się kończy wyko-
nywana jest procedura zakończenia w ramach której zwalniane są zasoby wątku.

Wątek dzieli ze swym procesem macierzystym następujące zasoby:
 Dane statyczne (segment danych)
 Pliki
 Środowisko
 Katalog macierzysty
 Sesję, użytkownika, grupę

Własności wątków
 Koszt utworzenia i przełączania wątku jest mniejszy niż procesu.
 Dane statyczne procesu są dla wątków działających w ramach jednego procesu

wzajemnie widoczne.

 Wykonanie każdego wątku przebiega sekwencyjnie, każdy wątek ma swój licznik

rozkazów.

 Wątki mogą być wykonywane na oddzielnych procesorach co umożliwia przyspie-

szenie obliczeń.

 Ponieważ wątki dzielą wspólne dane konieczna jest synchronizacja dostępu do

tych wspólnych danych.

background image

56

Rodzaje wątków
Wyróżniane są następujące typy wątków:
 Wątki poziomu jądra KLT - (ang. Kernel Level Tthreads).
 Wątki poziomu użytkownika ULT - (ang. User Level Threads).
 Rozwiązania mieszane

Kanoniczne stany wątku
 Wykonywany - Właśnie wykonywany przez procesor
 Gotowy - Posiadający wszyskie zasoby oprócz procesora
 Zablokowany - Brak pewnych zasobów
 Zombie - Zakończony ale nie dołączony do wątku macierzystego (wątek dołą-

czalny)

 Nieistniejący - Nie utworzony lub zakończony

Diagram stanów kanonicznych w

ą

tków

Podstawowe operacje na w

ą

tkach

Tworzenie wątku
Nowy wątek tworzy się przy pomocy funkcji pthread_create. Funkcja ta tworzy
wątek, którego kod znajduje się w funkcji podanej jako argument func. Wątek jest
uruchamiany z parametrem arg, a informacja o nim jest umieszczana w strukturze
thread.

background image

57


int pthread_create( pthread_t *thread, pthread_attr_t *attr, void (*
func)(void *), *arg)
thread identyfikator wątku – wartość nadawana przez funkcję
attr atrybuty wątku, gdy NULL przyjęte domyślne
func procedura zawierająca kod wątku
arg argument przekazywany do wątku
Funkcja zwraca: 0 – sukces, -1 – błąd.

Kończenie wątku
Wątek może być zakończony w następujące sposoby:
 Następuje powrót z procedury określającej kod wątku.
 Wątek wykonuje funkcję pthread_exit().
 Wątek jest kasowany przez inny wątek.
 Następuje zakończenie procesu macierzystego wątku.
Jawne zakończenie wątku następuje poprzez wywołanie procedury:
pthread_exit(void * status)
status Kod powrotu wątku
Możliwe są dwa sposoby postępowania z kończonymi wątkami:
1. Z chwilą zakończenia się wątku zwalniane są wszystkie jego zasoby.
2. Zasoby zwalniane są z chwilą dołączenia wątku bieżącego do innego wątku (wy-
konującego funkcję pthread_join).
Postępowanie to uzależnione jest od ustawienia atrybutu
PTHREAD_CREATE_JOINABLE który ustalany jest podczas tworzenia wątku.
1. Gdy atrybut ten nie jest ustawiony, wątek zwalnia swe zasoby zaraz po zakoń-
czeniu.
2. Gdy atrybut jest ustawiony, wątek zwalnia zasoby po dołączeniu do innego wąt-
ku.

Oczekiwanie na zakończenie wątku.
Proces bieżący może czekać na zakończenie innego wątku poprzez wywołanie
funkcji pthread_join.
int pthread_join( pthread_t *thread, void *status)
thread identyfikator wątku – wartość nadawana przez funkcję
status Kod powrotu zwracany przez zakończony wątek
Gdy wskazany jako parametr wątek nie zakończył się jeszcze, wątek bieżący jest
wstrzymywany.
Funkcja zwraca: 0 – sukces, -1 – błąd.

Inne atrybuty wątków
Oprócz sposobu zakończenia wątków można ustawiać także inne atrybuty wątków
jak np. stategia szeregowania (SCHED_RR, SCHED_FIFO,SCHED_OTHER).

Uzyskanie własnego identyfikatora
Wątek może uzyskać własny identyfikator poprzez wywołanie funkcji:
pthread_t pthread_self(void)

Zwolnienie procesora
Wywołanie funkcji pthread_yield powoduje przekazanie sterowania do procedury
szeregującej która wybierze następny wątek do wykonania.

background image

58


int pthread_yield(void)

Dostęp do wspólnych danych
Wątki dzielą wspólny obszar danych. Stąd współbieżny dostęp do danych może na-
ruszyć ich integralność. Należy zapewnić synchronizację dostępu do wspólnych da-
nych. W bibliotece pthreads do zapewnienia wyłączności dostępu do danych stosuje
się mechanizm muteksu (ang. mutex). Nazwa ta pochodzi od słów Mutual exclusion
c
zyli wzajemne wykluczanie.

Deklaracja muteksu

Muteks jest obiektem abstrakcyjnym który może być w dwu stanach: wolny i zajęty. Na

muteksie wykonuje się dwie podstawowe operacje: zajęcie i zwolnienie. Bibliote-

ka pthreads definiuje muteks jako typ pthread_mutex_t. Przed użyciem obiekt
typu muteks musi być zadeklarowany. Przykładowo muteks o nazwie blokada dekla-
ruje się jak poniżej.
pthread_mutex_t blokada;

Inicjacja muteksu
Przed użyciem muteks musi być zainicjowany. Inicjacja następuje poprzez wykona-
nie funkcji:
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t
*attr)
mutex Zadeklarowana wcześniej zmienna typu pthread_mutex_t.
attr Atrybuty muteksu. Gdy attr jest równe NULL przyjęte będą wartości domyślne.
Funkcja zwraca: 0 – sukces, -1 – błąd.
Zainicjowany muteks pozostaje w stanie odblokowania.

Zablokowanie dostępu do zasobu
Przed dostępem do zasobu należy zapewnić sobie wyłączność w korzystaniu z tego
zasobu. W tym celu wątek wykonuje funkcję:
int pthread_mutex_lock(pthread_mutex_t *mutex);
mutex
Zadeklarowana wcześniej i zainicjowana zmienna typu
pthread_mutex_t
Działanie funkcji zależy od stanu w jakim znajduje się muteks.
1. Gdy muteks jest wolny, następuje jego zablokowanie.
2. Gdy muteks jest zajęty, próba jego powtórnego zajęcia powoduje zablokowanie
się wątku który tę próbę podejmuje.

Odblokowanie dostępu do zasobu
Użyty i zablokowany wcześniej zasób powinien być zwolniony. Zwolnienie zasobu
odbywa się poprzez wywołanie funkcji:
int pthread_mutex_unlock(pthread_mutex_t *mutex)

background image

59

mutex Zadeklarowana wcześniej i zainicjowana zmienna typu
pthread_mutex_t
Działanie funkcji zależy od tego czy inne wątki czekają zablokowane na muteksie.
1. Brak wątków zablokowanych na muteksie – stan muteksu zostaje zmieniony na
wolny.
2. Są wątki zablokowane na muteksie – jeden z czekających wątków zostaje odblo-
kowany.

Kasowanie muteksu
Kasowanie muteksu odbywa się poprzez wywołanie funkcji:
int pthread_mutex_destroy(pthread_mutex_t *mutex)
System zwalnia zasoby zajęte przez muteks. Muteks musi być wolny gdyż w prze-
ciwnym razie nie będzie skasowany i funkcja zwróci kod błedu.

Gdy dwa lub więcej wątki o różnych priorytetach używają wspólnego muteksu może
dojść zjawiska nazywanego inwersją priorytetów.
Inwersja priorytetów – zjawisko polegające na wykonywaniu się wątku o niższym
priorytecie mimo że wątek o wyższym priorytecie pozostaje gotowy. Inwersja prio-
rytetów może się pojawić gdy wątki o różnych priorytetach używają wspólnego mu-
teksu lub podobnego mechanizmu synchronizacyjnego.
Podbicie priorytetu – tymczasowe zwiększenie priorytetu wątku przebywającego
w sekcji krytycznej do priorytetu wątku który oczekuje na wejście do sekcji krytycz-
nej.
Ilustracja zjawiska inwersji priorytetów

Zmienne warunkowe
Zmienna warunkowa jest narzędziem do blokowania wątku wewnątrz sekcji krytycz-
nej aż do momentu gdy pewien warunek zostanie spełniony. Warunek ten może być
dowolny i niezależny od zmiennej warunkowej. Zmienna warunkowa musi być użyta
w połączeniu z muteksem o ile konstrukcja ma zapewnić własności monitora. Przed
użyciem zmienna warunkowa musi być zadeklarowana jako zmienna typu pthre-
ads_cond_t.
Najważniejsze operacje związane ze zmiennymi warunkowymi są dane poniżej.

background image

60

Inicjacja zmiennej warunkowej
int pthread_cond_init(pthreads_cond_t *zw, pthreads_condattr_t attr)
zw Zadeklarowana wcześniej zmienna typu pthread_cond_t.
attr Atrybuty zmiennej warunkowej. Gdy attr jest równe NULL przyjęte będą war-
tości domyślne.

Zawieszenie wątku w oczekiwaniu na sygnalizację
int pthread_cond_wait(pthreads_cond_t *zw, pthread_mutex_t *mutex)
zw Zadeklarowana i zainicjowana zmienna typu pthread_cond_t.
mutex Zadeklarowana i zainicjowana zmienna typu pthread_mutex_t.
Funkcja powoduje zawieszenie bieżącego wątku w kolejce związanej ze zmienną
warunkową zw. Jednocześnie blokada mutex zostaje zwolniona. Obie operacje są
wykonane w sposób atomowy. Gdy inny wątek wykona operację pthre-
ads_cond_signal(&zw)
zablokowany wątek zostanie odblokowany a blokada mu-
tex zwolniona.

Wznowienie zawieszonego wątku
int pthread_cond_signal(pthreads_cond_t *zw)
zw Zadeklarowana i zainicjowana zmienna typu pthread_cond_t.
Jeden z wątków zablokowanych na zmiennej warunkowej zw zostanie zwolniony.

Wznowienie wszystkich zawieszonych wątków
int pthread_cond_brodcast(pthreads_cond_t *zw)
zw Zadeklarowana i zainicjowana zmienna typu pthread_cond_t.
Wszystkie wątki zablokowane na zmiennej warunkowej zw zostaną zwolnione.

Biblioteka pthreads
Zestaw funkcji dotyczący wątków zdefiniowany został przez normę POSIX P1003.4a
i nosi nazwę pthreads (skrót od Posix threads). Implementacja pakietu istnieje mię-
dzy innymi w systemie Linux, QNX6, DEC OSF1. Obecnie wątki są elementem biblio-
teki glibc (Od wersji 2).

21. Problem zakleszczenia, definicja i przykłady (zastój meksykański, pro-
blem 5 filozofów). Warunki konieczne blokady, metody wykrywania i uni-
kania blokad, graf przydziału zasobów.

W systemach w których wykonywane jest wiele współbieżnych procesów które ope-
rują na wspólnych zasobach może dojść do niezamierzonego wstrzymania pracy
pewnych procesów. Mówi się że procesy mogą ulec zakleszczeniu.

background image

61


Zakleszczenie
Zbiór procesów jest w stanie zakleszczenia jeżeli każdy proces z tego zbioru czeka
na zdarzenie które może być spowodowane tylko przez inny proces z tego samego
zbioru.

Prosty przykład zakleszczenia - zastój meksykański (ang. Mexican stan-
doff)




















Problem pięciu ucztujących filozofów
Przy okrągłym stole siedzi pięciu filozofów. Zajmują się oni naprzemiennie tylko
dwoma czynnościami - myśleniem i jedzeniem. Do jedzenia filozof potrzebuje dwu
widelców. Gdy filozof otrzyma dwa widelce je, a następnie odkłada obydwa widelce.
Problem polega na takim zorganizowaniu pracy filozofów aby spełnione były warun-
ki:
- Filozof je wtedy gdy zdobędzie dwa widelce.
- Dwu filozofów nie może trzymać tego samego widelca.
- Każdy z filozofów musi się w końcu najeść (nie zostanie zagłodzony).
Dodatkowym wymaganiem jest efektywność rozwiązania. Znaczy to że nie należy
blokować aktywności filozofa gdy nie jest to konieczne dla spełnienia poprzednich
warunków.

A. Rozwiązanie z możliwością blokady:
1. Filozof czeka aż będzie wolny lewy widelec i podnosi go.
2. Filozof czeka aż będzie wolny prawy widelec i podnosi go.
3. Filozof je.

background image

62

4. Filozof odkłada obydwa widelce.
5. Filozof myśli.
Jeżeli w pewnej chwili każdy z filozofów podniesie lewy widelec i będzie czekał na
prawy nigdy go nie otrzyma gdyż algorytm przewiduje zwolnienie widelców po za-
kończeniu jedzenia. Nastąpi zakleszczenie.

B. Rozwiązanie z możliwością zagłodzenia:
1. Filozof czeka aż będą wolne oba widelce i podnosi je (musi to być operacja niepo-
dzielna).
2. Filozof je.
3. Filozof odkłada obydwa widelce.
4. Filozof myśli.
Może się tak zdarzyć że filozof będzie miał bardzo żarłocznych sąsiadów z których w
każdej chwili jeden z nich je. W takim przypadku filozof zostanie zagłodzony.

C. Rozwiązanie poprawne
1. Potrzebny jest arbiter zewnętrzny (nazywany lokajem) który dba o to aby jednej
chwili najwyżej czterech filozofów konkurowało o widelce.
2. Dalej postępujemy jak w przypadku A.

Rozwiązanie problemu pięciu filozofów za pomocą semaforów

Semaphore Widelec[5]; // Tablica pięciu semaforów
Semaphore Lokaj; // Semafor reprezentujący lokaja
// Kod wątku filozofa i, i=0,1,2,3,4
Filozof(int i){

do { // Myślenie

sem_wait(Lokaj); // Dopuszczenie najwyżej 4 procesów
sem_wait(Widelec[i]); // Czekaj na widelec lewy
sem_wait(Widelec[(i+1)%5]);// Czekaj na widelec prawy

// Jedzenie
sem_post(Widelec[(i+1)%5]); // Oddaj widelec prawy
sem_post(Widelec[i]); // Oddaj na widelec lewy
sem_post(Lokaj);

} while(1);

}
main(void) {

int i;
for(i=0;i<5;i++) {

// Inicjacja semaforów widelec
sem_init(Widelec[i],1);

}
sem_init(Lokaj,4);
for(i=0;i<5;i++) {

// Utworzenie wątków odpowiadających filozofom
...
}

}

background image

63


Warunki Cofmanna
Warunki konieczne zakleszczenia podane zostały przez Coffmana. Do zakleszczenia
może dochodzić gdy spełnione są jednocześnie cztery warunki:
1. Wzajemne wykluczanie (ang. Mutual exclusion condition ) – Każdy z zasobów jest
albo wolny albo zajęty przez dokładnie jeden proces.
2. Przetrzymywanie i oczekiwanie (ang. Wait and hold condition) – Proces posiada-
jący jakiś zasób może żądać innego zasobu.
3. Brak wywłaszczeń (ang. No prememption condition) – Zasoby nie podlegają wy-
właszczeniu, czyli zasób zajęty przez jakiś proces może być zwolniony tylko przez
ten proces.
4. Czekanie cykliczne (ang. Circular wait condition) – Musi istnieć zamknięty łańcuch
procesów P = {P1, P2,...,Pm} w którym Pi czeka na zasób zajęty przez Pi+1 dla
i=1,m-1 oraz Pm czeka na na zasób zajęty przez P1.

Metody postępowania z zakleszczeniami
1. Zignorowanie problemu - nie zajmować się problemami zakleszczeń na poziomie
systemu operacyjnego
2. Zapobieganie zakleszczeniom (ang. deadlock prevention) - stosować taki protokół
zamawiania zasobów aby któryś z warunków Coffmana nie był spełniony i tym sa-
mym nie doszło do zakleszczenia.
3. Unikanie zakleszczeń (ang. deadlock avoidance) - stosować taki protokół zama-
wiania zasobów aby liczba liczba wolnych zasobów zawsze umożliwiała zakończenie
rozpoczętych zadań.
4. Dopuszczać do możliwości zakleszczenia ale wykrywać je a następnie usuwać.

Zapobieganie zakleszczeniom
Zapobieganie zakleszczeniom (ang. deadlock prevention) opiera się na niedopusz-
czeniu do spełnienia jednego z warunków Coffmana.
Negacja warunku wzajemnego wykluczania
Nie jest możliwe uniknięcie tego warunku gdyż pewne zasoby są z natury niepo-
dzielne.
Negacja warunku przetrzymywania i oczekiwania
Podejście polega na unikaniu sytuacji gdy proces posiadający już zasoby zamawia
inne zasoby.
1. Proces może zająć zasoby tylko wtedy gdy wszystkie z nich może otrzymać.
2. Proces może zamówić jakieś zasoby tylko wtedy gdy nie posiada zajętych innych
zasobów. Strategia nazywa się wszystko albo nic (ang. One-shot allocation). Zasto-
sowanie strategii wszystko albo nic do problemu 5-ciu filozofów.
 Filozof może otrzymać albo dwa widelce albo żadnego.
 Filozof 1 i 3 otrzymują widelce – reszta czeka.
Rozwiązanie nie powoduje zakleszczenia ale może prowadzić do zagłodzenia.
Wady:
1. Małe wykorzystanie zasobów gdyż być może nie wszystkie naraz są potrzebne do
wykonania zadania.
2. Możliwość zagłodzenia pewnych procesów poprzez ciągłe zajęcie często używa-
nych zasobów.
Negacja warunku braku wywłaszczeń.
Możliwe jest zastosowanie następujących algorytmów:

background image

64

1) Gdy proces żąda zasobu który nie jest dostępny sprawdza się czy proces przy-
trzymujący te zasoby czeka na inne zasoby. Gdy tak jest odbiera się mu te zasoby.
Implementacja takiej metody wymaga aby program był napisany w języku obsługu-
jącym wyjątki.
2) Gdy proces posiadający jakieś zasoby żąda innych zasobów które nie mogą być
przydzielone – traci przydzielone zasoby.
Negacja warunku czekania cyklicznego
Aby zapobiec czekaniu cyklicznemu należy ponumerować zasoby i żądać aby każdy
proces zamawiał zasoby we wzrastającym porządku numeracji. Gdy tak będzie nie
wystąpi warunek czekania cyklicznego.

Graf przydziału zasobów dla problemu pięciu filozofów – rozwiązanie symetryczne
Istnieje zamknięty cykl procesów:

Graf przydziału zasobów dla problemu pi

ę

ciu filozofów – rozwi

ą

zanie asymetryczne

background image

65

Unikanie zakleszczeń (ang. deadlock avoidance)

System bezpieczny i zagrożony
Podejście ma zastosowanie gdy:
1. Wymagana jest aprioryczna informacja jakich zasobów (i w jakiej ilości) będzie
używał proces.
2. W trakcie pracy system decyduje czy zrealizować zamówienie czy też je wstrzy-
mać.
Założenia - realizowalność przydziału
1. Proces nie może żądać więcej zasobu niż całkowita ilość zasobu w systemie
2. Proces nie może żądać więcej zasobu niż deklarował.
3. Suma przydzielonych zasobów nie może być większa niż dostępna.
Stan bezpieczny
Dany stan przydział zasobu jest bezpieczny jeżeli nie może prowadzić do zaklesz-
czenia. Stan systemu jest bezpieczny jeżeli istnieje ciąg bezpieczny
Ciąg bezpieczny.
Ciąg <P1,P2,...Pn> jest bezpieczny jeżeli dla każdego procesu Pj jego maksymalne
zapotrzebowanie na zasoby może być zaspokojone przez zasoby aktualnie wolne i
utrzymywane przez wszystkie procesy Pj gdzie i < j (procesy wcześniejsze w ciągu).

Stan zagrożenia - nie istnieje ciąg bezpieczny.

Wykrywanie zakleszczeń
Zakleszczenia mogą być wykryte na podstawie analizy grafu oczekiwania (ang. wait-
for graph
). Graf oczekiwania można uzyskać z grafu przydziału zasobów przez usu-
nięcie wierzchołków odpowiadających zasobom.
Graf oczekiwania:
1. Wierzchołki – procesy {P1,P2,...Pn}
2. Luki - od Pi do Pj wtedy proces Pi czeka na zasoby zajęte przez proces Pj.

Otrzymywanie grafu oczekiwania z grafu przydziału zasobów

background image

66

Gdy w grafie oczekiwania wystąpi cykl to system jest w stanie zakleszczenia. Aby na
bieżąco wykrywać zakleszczenia system musi:
1. Utrzymywać aktualny graf oczekiwania
2. Dla grafu oczekiwania wykonywać algorytm wykrywania cyklu

Likwidowanie zakleszczenia
Gdy zakleszczenie wystąpi i zostanie wykryte należy je zlikwidować. Możliwe
podejścia:
Zakończenie procesów
 Wszystkich
 Niektórych aby zakleszczenie ustąpiło
Występuje problem które procesy zakończyć. Należy minimalizować koszty zakoń-
czenia przedwczesnego procesów. Decyzja zależy od:
 bezpieczeństwo
 priorytet procesu
 czas wykonywania procesu
 łatwość wywłaszczenie z zasobu
Wywłaszczenia z zasobów
Przy wywłaszczaniu zasobów należy rozważyć następujące kwestie
 Który z zasobów ma być wywłaszczony
 Jak przeprowadzić wycofanie
 Jak zapewnić aby nie doszło do zagłodzenia procesu

22. Systemy rozproszone, sprzęt (wieloprocesory, multikomputery), kryte-
ria projektowe, zagadnienia projektowe (komunikacja, nazewnictwo, spój-
ność, struktura), typy.

Czynniki które umożliwiły powstanie systemów rozproszonych:
 Rozpowszechnienie tanich i wydajnych komputerów
 Rozwój sieci komputerowych
Naturalna jest tendencja aby dokonać ściślejszego połączenia komputerów co pro-
wadzi do systemów rozproszonych.
Definicja:
System rozproszony jest to układ niezależnych komputerów który sprawia na jego
użytkowniku wrażenie że jest jednym komputerem.
Główna idea systemów rozproszonych:
Integracja zasobów sprzętowych i programowych wielu komputerów za pomocą sieci
w celu wzajemnego udostępnienia ich użytkownikom.
Własności:
 Komputery połączone mniej lub bardziej ściśle
 Na wszystkich komputerach wykonywany jednego typu system operacyjny
 Komputery mogą być różnego typu
 Jednolity mechanizm komunikacji pomiędzy procesami
 System plików wszędzie wygląda jednakowo

Sprzęt systemów rozproszonych
Taksonomia Tanneubauma:
Bierze pod uwagę komunikację pomiędzy procesorami. Może ona być przez:
 wspólną pamięć

background image

67

 system wejścia wyjścia
Inny aspekt systemów – szybkość komunikacji
 systemy ściśle powiązane (ang. tightly coupled)
 systemy luźno powiązane (ang. tightly coupled)
Podział systemów komputerowych wg. Tanneubauma

Multiprocesor
Jedna przestrzeń adresowa dzielona pomiędzy wiele procesorów. Występują nastę-
pujące rodzaje multiprocesorów:
 Wieloprocesory szynowe
 Wieloprocesory przełączane
 Maszyny NUMA
Ogólny schemat Multiprocesora:

W wieloprocesorach szynowych wszystkie procesory korzystają ze wspólnej
amięci. Dostęp do pamięci staje się wąskim gardłem systemu. Dlemat szybkości
pamięci:
 Duże pamięci są wolne

background image

68

 Małe pamięci są drogie
Rozwiązaniem jest zastosowanie szybkiej pamięci podręcznej (ang. cache).
Wieloprocesory przełączane – sieć z przełącznicą krzyżową
Własności:
 Brak blokad - połączenie dowolnego procesora z dowolnym modułem pamięci nie

blokuje połączeń innych procesorów z pozostałymi modułami.

 Duży koszt – liczba elementów przełączających p2 (p – liczba procesorów)
 Stosowane wewnątrz superwęzłów (do 8 procesorów)
Maszyny NUMA - Maszyny z niejednolitym czasem dostępu do pamięci. (ang.Non
Uniform Memory Access
). Czas dostępu do pamięci zależy od jej lokalizacji. Dla pa-
mięci lokalnej czas jest krótszy, dla pamięci odległej dłuższy. Stosunek tych czasów
może być 1:10 lub więcej. Każdy z procesorów ma własną pamięć lokalną ale ma
także dostęp do pamięci lokalnych innych jednostek. W maszynach NUMA istnieje
jedna wspólna wirtualna przestrzeń adresowa.
Własności maszyny NUMA
1. Możliwy jest dostęp tak do pamięci lokalnej jak i odległej
2. Dostęp do pamięci odległego węzła jest wolniejszy od dostępu do pamięci lokal-
nej
3. Czas dostępu do pamięci odległej nie jest ukryty przez pamięć podręczną.


Multikomputer
Własności:
 Każdy z procesorów ma swoją lokalną pamięć.
 Komunikacja procesów odbywa się przez system wejścia / wyjścia przy

wykorzystaniu abstrakcji komunikatów.

Wyróżniamy następujące typy multikomputerów:
 multikomputery szynowe
 multikomputery przełączane
Najczęściej stosuje się multikomputery szynowe chociaż są też architektury typu
kostka wielowymiarowa czy krata. Sprzęt może być powiązany ściśle lub luźno. Ar-
chitektura ta nosi też nazwę klastra.
Schemat multikomputera:

background image

69


Multikomputery szynowe
Jako szyna bywa stosowana sieć lokalna 10Mbit/s – 1000 Mbit/s. Architektura mul-
tikomputera szynowego jest podobna do sieci lokalnej.
Multikomputery przełączane
Wymiana danych pomiędzy jednostkami przetwarzającymi odbywa się za pośrednic-
twem sieci komunikacyjnej o określonej topologii. Typowe topologie takiej sieci to:
 pierścień (ang. ring),
 siatka (ang. mesh) dwu- lub trójwymiarowa, otwarta, zamknięta
 drzewo (ang. tree),
 gwiazda (ang. star),
 hipersześcian (ang. hypercube).
Sieć przełączająca – łączy procesory ze sobą albo procesory i moduły pamięci.

Porównanie multiprocesorów i multikomputerów

Problemy projektowania systemów rozproszonych
Kryteria projektowe które należy spełnić przy budowie systemów rozproszonych:
 Otwartość
 Skalowalność
 Wydajność
 Przeźroczystość
 Tolerowanie uszkodzeń
Problemy projektowe:
 Nazewnictwo
 Komunikacja
 Struktura oprogramowania
 Przydzielanie obciążeń
 Zapewnianie spójności

Kryteria projektowe

Otwartość
Otwartość – zdolność do rozszerzania różnymi sposobami. Możliwość dodawania
składników sprzętowych i programowych beznaruszenia stanu istniejącego. Otwar-
tość osiągana przez określanie, dokumentowanie i upowszechnienie zasadniczych
interfejsów. Pożądane cechy otwartości: zupełna i neutralna specyfikacja interfej-
sów.

background image

70

Zupełność – kompletny opis interfejsu
Neutralność - niepreferowanie określonego sposobu implementacji
Przeźroczystość
W prawdziwym systemie rozproszonym użytkownicy nie muszą się troszczy o lokali-
zację, ochronę i optymalizację użycia zasobów. Wyrożniamy przezroczystość dostę-
pu, położenia, wędrówki, współbieżności, awarii, równoległości.
Niezawodność
Jednym z pierwotnych celów budowy systemów rozproszonych było zwiększenie nie-
zawodności całego systemu. Aby to osiągnąć stosuje się:
 Zwielokrotnienie sprzętu i oprogramowania
 Przechowywanie danych w wielu miejscach
 Tolerowanie awarii
 Specjalne mechanizmy ochrony przed nieautoryzowanym użyciem
Wydajność
Jednym z pierwotnych celów budowy systemów rozproszonych było zwiększenie wy-
dajności całego systemu. Aby to osiągnąć należy wziąć pod uwagę ziarnistość obli-
czeń:
 Rozpraszanie zadań składających się z wielu drobnych obliczeń wymagających

wielu interakcji jest nieopłacalne ze względu na koszt komunikacji i koordynacji.

 Rozpraszanie zadań zawierających wiele obliczeń które nie wymagają wielu

koordynacji jest opłacalne.

Skalowalność
Pożądane jest aby po dodaniu dodatkowych węzłów do systemu następował wzrost
jego mocy przetwarzania. Nie byłoby wymagane tu przeprojektowywanie systemu.

Podstawowe zagadnienia projektowe

Usługi nazewnicze
W systemie rozproszonym dostęp do zasobu następuje poprzez jego nazwę która
jest niezależna od fizycznego położenia zasobu.
Tłumaczenie nazwy – przekształcenie na postać za pomocą której można spowodo-
wać działanie na obiekcie lub zasobie. W systemach rozproszonych dostęp do zaso-
bów następuje na podstawie identyfikatorów komunikacyjnych (np. adres IP i numer
portu). Projektując usługi nazewnicze (ang. Name Service) należy:
1. Wybrać przestrzeń adresową dla danego zasobu
2. Przełożyć nazwę zasobu na identyfikator komunikacyjny
Przestrzeń adresowa może być strukturalna (np. hierarchiczna) lub płaska
Usługi nazewnicze – przekształcanie identyfikatorów z jednej przestrzeni na inną. W
ich zakres wchodzi:
 Rejestrowanie nowych nazw
 Usuwanie nieaktualnych nazw
 Dokonywanie przekładu jednej nazwy na inną
Jest to zarządzanie bazą danych nazw Tłumaczenie nazwy może być wielostopniowe
(jak w QNX6).
Komunikacja
Składowe systemu rozproszonego są rozdzielone a zatem muszą się komunikować.
Zakłada się że komunikacja następuje pomiędzy parami procesów. Wydajność sys-
temów rozproszonych w decydujący sposób zalezy od jakości systemu komunikacji.
Komunikacja przenosi:

background image

71

 Dane
 Synchronizację
 Specyfikację działań
Podstawowa konstrukcja – przekazywanie komunikatów (synchronicznych, asyn-
chronicznych). W komunikacji występują dwa podstawowe schematy:
 Model klient–serwer
 Model rozsyłania grupowego
Przekazywanie działań – wysyłanie pomiędzy procesami specyfikacji działań które
mają być wykonane (PosScript, Java).
Struktura oprogramowania
W systemach rozproszonych wyróżnia się następujące składniki oprogramowania:
 Usługi jądra systemu - zarządzanie pamięcią, urządzeniami, komunikacja,

abstrakcja procesu, komunikacja.

 Zaplecze programowania rozproszonego – zdalne wywoływanie procedur, komu-

nikacja grupowa

 Usługi otwarte – dostęp do zasobów: pliki, urządzenia, tranzakcje, bazy danych,

nazwy

 Aplikacje
Struktura oprogramowania systemu rozproszonego:

Utrzymywanie spójności
Rozproszenie zasobów powoduje trudność w utrzymaniu ich spójności. Wyróżniamy
następujące rodzaje spójności:
 Spójność aktualizacji
 Spójność zwielokrotnienia
 Spójność pamięci podręcznej
 Spójność czasu
 Spójność awarii


Budowa systemu rozproszonego
 System ściśle powiązany
 System luźno powiązany

System ściśle powiązany (ang. tightly coupled)
System globalnie zarządza wszystkimi zasobami systemu rozproszonego. Ten typ
projektowany zwykle dla wieloprocesorów i wielokomputerów homogenicznych.
 Każdy komputer posiada lokalny system operacyjny z własnym jądrem.

background image

72

 Powyżej jądra znajduje się warstwa odpowiedzialna za współdzielenie zasobów.
 Na górze warstwa aplikacji rozproszonej.

System luźno powiązany (ang. losely coupled)
 Zbiór komputerów z lokalnymi systemami operacyjnymi (sieciowymi) które

współpracują ze sobą.

 System udostępnia lokalne zasoby dla zdalnych klientów.
 Dostęp do zasobów poszczególnych węzłów troszczy się aplikacja rozproszona.
 Komputery mogą być różne ale OS jednolity

Aby ułatwić pisanie aplikacji rozproszonych opartych na sieciowym systemie opera-
cyjnym korzysta się z oprogramowania warstwy pośredniej (ang. middleware).

background image

73

Usługi warstwy pośredniej:
 Komunikacja
 Usługi nazewnicze
 Jednolity dostęp do zasobów

23. Komunikacja grupowa grupy, adresowanie grupowe, synchroniczność

wirtualna, algorytm BCAST.

Rodzaje komunikacji
Składowe systemu rozproszonego są rozdzielone tak logicznie jak fizycznie. Aby
mogły one współdziałać muszą się komunikować. Istnieją dwa powszechnie stoso-
wane schematy komunikacji:
 Punkt - punkt
 Rozsyłanie grupowe

Grupa – zbiór procesów działających wspólnie w sposób określony poprzez system
lub użytkownika.
 Komunikacja punkt – punkt jest powszechnie stosowana w modelu klient-serwer
 Komunikacja grupowa stosowana w systemach rozproszonych (tolerowanie

uszkodzeń, przetwarzanie równoległe, wyszukiwanie informacji, zwielokrotnione
aktualizacje)

Komunikacja klient serwer
Zorientowany na dostarczanie usług. Wyróżnia się następujące etapy:
1. Klient wysyła do serwera komunikat z zamówieniem
2. Serwer realizuje zamówienie
3. Odpowiedź przesyłana jest do klienta
Komunikacja między para procesów obejmuje działania po stronie procesu
nadawczego i odbiorczego. Są to:
 Przenoszenie danych
 Synchronizacja
Rodzaje komunikacji ze względu na ciągłość danych:
 Dwukierunkowy strumień (ang. stream) – brak separacji danych, przykład: TCP
 Komunikat (ang. message) – dane są separowane, przykład: UDP

background image

74


Adresowanie
W systemach rozproszonych dąży się do uzyskania przeźroczystości położenia adre-
sów. Adresowanie odbywa się w sposób symboliczny. Odwzorowanie adresu symbo-
licznego na fizyczny wykonywane jest przez odpowiednie warstwy oprogramowania.
Usługi mogą być przemieszczane bez powiadamiania klientów.

Komunikacja połączeniowa i bezpołączeniowa

Komunikacja połączeniowa
1. Dwa procesy ustanawiają połączenie w którym występuje informacja adresowa
2. Wymieniają dane używając tylko identyfikatora połączenia
3. Rozłączają się
Często połączenie jest kontrolowane w sensie:
 Możliwości przesyłania danych
 Poprawności danych
Komunikacja bezpołączeniowa
W każdym przesłaniu występuje pełna informacja adresowa

24. Komunikacja rozproszona poprzez zdalne wywoływanie procedur RPC,
zasady, łącznik, adresowanie, jezyk XDR, tworzenie aplikacji przy pomocy
makrogeneratora rpcgen.

RPC – ZDALNE WYWOŁYWANIE PROCEDUR (ang. Remote Procedure Calls)

Koncepcja
Model klient serwer pozwala na rozwiązanie szerokiej klasy problemów, posiada
jednak ograniczenia:
 Odwołanie się do wejścia / wyjścia (receive / send)
 Występuje problem reprezentacji danych (systemy heterogeniczne)
 Model zorientowany na dane

background image

75

Lokalne wywołanie procedury

Zdalne wywołanie procedury

Właściwości zdalnych wywołań procedur:
 Definicja procedury powinna określić które parametry są wejściowe, które wyj-

ściowe a które przekazują dane w obydwu kierunkach. W języku C par. przeka-
zywane przez odniesienie tego nie specyfikują.

 Procedura wywołująca i wywoływana działają na różnych maszynach w różnych

przestrzeniach adresowych. Występuje problem z użyciem wskaźników. Zmienne
globalne nie mogą być wykorzystane.

 Maszyny mogą mieć różne systemy reprezentacji danych – należy dokonać

konwersji i serializacji danych.

 Występuje problem obsługi sytuacji awaryjnych
Łącznikami aplikacji klienta i serwera są
 Stopka klienta (ang. client stub) - reprezentuje serwer po stronie klienta
 Stopka serwera (ang. server stub) - reprezentuje klienta po stronie serwera
Zdalne wywołanie procedury odbywa się w krokach:
1. Aplikacja klienta wywołuje stopkę klienta
2. Stopka klienta buduje komunikat i przechodzi do jądra OS
3. Jądro przesyła komunikat do jądra maszyny odległej
4. Stopka serwera rozpakowuje parametry i wywołuje serwer
5. Serwer wykonuje zdalną procedurę i zwraca wynik stopce serwera
6. Stopka pakuje wyniki w komunikat i przesyła do maszyny klienta
7. Jądro klienta przekazuje komunikat stopce klienta
8. Stopka klienta rozpakowuje wynik i zwraca go klientowi

background image

76

Przebieg zdalnego wywołania procedury

Obsługa sytuacji wyjątkowych
System RPC jest systemem działającym w środowisku sieciowym a zatem należy się
liczyć z trudnościami komunikacyjnymi. Ponieważ dowolne wywołanie RPC może się
skończyć niepowodzeniem wymagane jest informowanie strony wywołującej o błę-
dach i obsługa sytuacji wyjątkowych. Obsługa może być oparta o:
 Zwracanie kodu błędów przez procedury
 Obsługę wyjątków ( konstrukcje jak np. try {...} catch {...} )
Gdy zdalna procedura ma zwrócić jakiś wynik powstaje problem jak odróżnić ten
wynik od kodu powrotu. Należy zastosować mechanizm wyjatku.
Typy awarii:
1. Klient nie może zlokalizować serwera
2. Zaginął komunikat zamawiający od klienta do serwera
3. Zaginęła odpowiedź od serwera do klienta
4. Serwer uległ awarii po otrzymaniu zamówienia
5. Klient uległ awarii po wysłaniu zamówienia

Wiązanie dynamiczne
Przy zdalnym wywoływaniu procedur powstaje pytanie jak klient ma zlokalizować
procedury serwera.
Wiązanie (ang. binding) – odwzorowanie nazwy (procedury RPC) w konkretny
obiekt określony identyfikatorem komunikacyjnym. Postać identyfikatora zależy od
systemu (np. adres gniazdka - IP, port).
Łącznik (ang. binder) – specjalna usługa RPC utrzymująca tablicę odwzorowań
nazw usług (procedur RPC) na porty serwerów tych usług. Łącznik utrzymywany jest
przez serwery które udostępniają identyfikatory portów swoim klientom.
Funkcje interfejsowe łącznika

background image

77

Funkcje interfejsowe łącznika:
 void Rejestruj(NazwaUsługi, PortSerwera, Wersja)
 void Usuń(NazwaUsługi, PortSerwera, Wersja)
 int Szukaj(NazwaUsługi, PortSerwera, Wersja)

Od usług łącznika zależą wszystkie inne usługi. Dlatego łączniki tworzy się tak aby
tolerowały awarie. Np. tablice odwzorowań zapisuje się w pliku z którego mogą być
one wczytane w przypadku awarii.
Lokalizowanie łącznika
Zanim klient RPC zrobi cokolwiek musi skontaktować się z łącznikiem. Skąd ma znać
jego lokalizację? Stosowane są rozwiązania:
1. Łącznik działa na komputerze którego adres jest dobrze znany. Gdy jego lokaliza-
cja się zmieni wymagana jest rekompilacja klientów.
2. Za dostarczanie aktualnego adresu łącznika odpowiada system operacyjny kom-
putera klienta i serwera. Można go utrzymywać w postaci zmiennej środowiskowej.
3. Rozpoczynając swoje działanie programy klienta lub serwera lokalizuje łącznik za
pomocą rozgłaszania. Komunikat rozgłaszający zawierać może numer portu łącznika
a łącznik odpowiada adresem IP komputera na którym się znajduje.

Standard XDR - przekazywanie parametrów
ile = read(int fd, char * bufor, int ile)
Przekazywanie parametrów w języku C:
 Przez wartość - wartość zmiennej kopiowana na stos
 Przez odniesienie - adres zmiennej kopiowany na stos
Maszyny mogą się różnić sposobem reprezentacji danych (big endian, little endian,
ASCII, EBDIC, Unicode). Stąd konieczność konwersji do postaci kanonicznej. Pakowanie
parametrów do komunikatu – przetaczanie danych (ang. Parameters Marshalling).
Jedną z metod rozwiązania problemu jest opracowany w firmie Sun system XDR
(ang. eXternal Data Reprezentation).
Własności:
 XDR umożliwia reprezentację danych w sposób niezależny od komputera.
 Jest to język opisu danych i narzędzie do ich konwersji.
 Nie jest zależny od żadnego szczególnego języka.

background image

78


XDR stosuje konwencje:
 Liczby całkowite kodowane jako „big Indian” – starsze bajty mają niższe adresy
 Liczby rzeczywiste kodowane w formacie IEEE
 Typy danych mają zawsze wielokrotność 4 bajtów (krótsze wypełniane zerami)
 Konwersja i dekodowanie prowadzone są zawsze
 Obowiązkiem nadawcy i odbiorcy jest znać typy przekazywanych danych

Kodowanie i dekodowanie prowadzi się za pomocą funkcji XDR

Potok XDR – ciąg bajtów w którym dane reprezentowane są w postaci XDR. Są trzy
typy potoków: standardowe wejście wyjście, w pamięci, komunikaty.
Filtr XDR – procedura kodująca lub dekodująca określony typ danych (liczba cał-
kowita, rzeczywista, znak, tablica). Filtry XDR piszą i czytają dane z potoku.

Filtry XDR
 Filtry proste – umożliwiają konwersję typów prostych: char, int, long, float, do-

uble

 Filtry złożone - umożliwiają konwersję typów złożonych: string, opaque, bytes,

vector, array, union, reference, pointer

Język opisu interfejsu RPCGEN
RPCGEN jest językiem definiowania interfejsu i prekompilatorem. Na podstawie de-
finicji interfejsu RPCGEN tworzy następujące pliki w języku C:
1. Stopka klienta (ang. client stub)
2. Stopka serwera (ang. server stub)
3. Plik konwersji danych
4. Plik nagłówkowy

Aby utworzyć aplikację RPC należy:
1. Utworzyć w języku RPCGEN plik interfejsu opisujący funkcje które mają być za-
implementowane. Nadać numery programu, wersji i funkcji.
2. Skompilować plik interfejsu za pomocą programu rpcgen. W rezultacie utworzo- ny
zostanie plik nagłówkowy, plik filtrów XDR, stopka klienta i stopka serwera.
3. Napisać program klienta korzystając z definicji funkcji klienta podanych w pliku
nagłówkowym.
4. Utworzyć plik serwera korzystając z definicji funkcji serwera podanych w pliku
nagłówkowym. Dokonać implementacji tych funkcji.
5. Skompilować plik klienta, stopki klienta, filtrów XDR i połączyć w program klienta.
6. Skompilować plik serwera, stopki serwera, filtrów XDR i połączyć w program ser-
wera.

Przekazywanie argumentów:
1. Wywołanie odległej procedury dopuszcza tylko jeden argument wywołania i zwra- ca
jeden wynik.
2. Gdy występuje więcej elementów to należy umieścić je w strukturach.

background image

79

3. W programach klienta i serwera argumentem lub wynikiem jest wtedy wskaźnik
na strukturę.

Tworzenie aplikacji przy pomocy prekompilatora RPCGEN

Komunikacja między klientem a serwerem
Komunikacja pomiędzy klientem a serwerem odbywa się za pomocą protokołów TCP
lub UDP.
Adresowanie w RPC:
 Nazwa (adres IP) komputera na którym uruchomiony jest serwer
 Numer programu
 Numer wersji
 Numer procedury
Aby móc skorzystać z RPC należy uruchomić program łącznika portmap. Procedury
zdalne na danym komputerze są identyfikowane przez trzy liczby:
 numer programu,
 numer wersji programu
 numer procedury.
Numer programu i wersji – identyfikuje procesy serwerowe. Numery procedur iden-
tyfikują procedury w serwerze.
Portmapper RPC jest serwerem, który zamienia numery programu RPC na numery
portów protokołu TCP albo UDP. Musi być on uruchomiony, aby móc używać na tej
maszynie odwołań RPC do serwerów RPC. Kiedy serwer RPC jest startowany, poin-
formuje on portmapper na których portach nasłuchuje, i jakimi numerami progra-
mowymi RPC może służyć. Kiedy klient chce odwołać się przez RPC do danego nu-

background image

80

meru programu, najpierw skontaktuje się z portmapperem na maszynie serwerowej,
aby określić numer portu, do którego należy wysłać pakiety RPC. Komunikacja z
portmapperem odbywa się na ustalonym porcie 111. Aby uzyskać informację o nu-
merach procedur, portach można użyć narzędzia rpcinfo.

Schemat rozproszonej aplikacji RPC

Cechy systemu RPC:

Zalety:
 Prostota
 Duża wydajność
 Rozpowszechnienie standardu
Wady:
 Brak wsparcia serwerów RPC dla wielowątkowości
 Słabe techniki autoryzacji klienta
 Brak zabezpieczenia przed konfliktem numerów programów
 W interfejsach tylko funkcje jednoargumentowe
 Trudności w użyciu wskaźników do przekazywania parametrów
 Wsparcie tylko dla języka C


Wyszukiwarka

Podobne podstrony:
krym prob zagadnienia2006 2007
Opracowanie Zagadnień na egzamin Mikroprocki
opracowane zagadnienia 2011
monopolizacja gospodarki, Opracowane zagadnienia
Opracowanie zagadnień NIK, Bezpieczenstwo Narodowe rok I
temp krytyczna, TRANSPORT PWR, STUDIA, SEMESTR II, FIZYKA, fizyka-wyklad, zagadnienia opracowane, za
socjologia - opracowane zagadnienia(2), Uniwerek
Opracowane zagadnienia na koło z podstaw turystyki, Notatki na koła
opracowane zagadnienia ściąga nowa
chemia fizyczna wykłady, sprawozdania, opracowane zagadnienia do egzaminu Sprawozdanie ćw 7 zależ
Drobnoustroje chorobotwórcze opracowane zagadnienia
Egzamin opracowane zagadnienia 2
Opracowanie zagadnień na prawo handlowe
Podstawy biologicznego rozwoju człowieka opracowane zagadnienia z roku 14 2015
opracowane zagadnienia na egazamin
Opracowane Zagadnienia
Socjologia organizacji socjologia organizacji opracowanie zagadnien
ChMB kolokwium I opracowane zagadnienia by Owca

więcej podobnych podstron