J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 1
Przesyłanie komunikatów
5. Przesyłanie komunikatów
Sk
ładowe systemu rozproszonego są rozłączne logicznie i
fizycznie. Aby wspó
łdziałać musza się komunikować.
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
komunikató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.
5.1
Wysłanie komunikatu
Komunikat do procesu P wysy
ła się wykonując funkcję Send. Prototyp funcji
Send podany zosta
ł ponizej.
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
Funkcja zwraca: 0 – sukces, -1 - b
łąd
Dzia
łanie funkcji Send jest następujące:
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.
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 2
Przesyłanie komunikatów
Proces P1
Proces P2
Send(...)
Receive(...)
RECEIVE_BLOCKED
Reply(...)
REPLY_BLOCKED
Komunikat
Odpowied
ź
READY
READY
Blokada
Odblokowanie
Odblokowanie
Blokada
Wymiana komunikatów pomiędzy procesami P1 i P2. Funkcja Receive
poprzedza funkcję Send.
Proces P1
Proces P2
Send(...)
Receive(...)
Reply(...)
REPLY_BLOCKED
Komunikat
Odpowied
ź
READY
READY
Blokada
Odblokowanie
SEND_BLOCKED
Wymiana komunikatów pomiędzy procesami P1 i P2. Funkcja Send
poprzedza funkcję Receive.
5.2
Odbiór komunikatu
Do odbioru komunikatów przez proces P s
łuży funkcja 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
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 3
Przesyłanie komunikatów
Funkcja zwraca: > 0 – PID procesu który nada
ł komunikat, - 1 - błąd
Gdy zachodzi potrzeba odbioru komunikatu nale
ży wykonać funkcję Receive.
Zwykle jako parametr pid 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 komunikatów.
Dzia
łanie funkcji jest następujące:
1. Gdy w kolejce Q s
ą nieodebrane komunikaty pierwszy z nich usuwany jest
z kolejki Q i umieszczany w buforze
msg. Funkcja zwraca PID procesu
który wys
łał ten komunikat. System powoduje odblokowanie procesu
wysy
łającego komunikat. Proces 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łodzenia
procesów.
P1
PN
P
K1
Kn
K2
Procesy wysylaj
ące
komunikaty
Proces odbieraj
ący
komunikaty
Kolejka Q
nieodebranych
komunikatow
P2
Proces P nie odebra
ł komunikatów wysłanych przez procesy P1, P2, …PN.
Komunikaty oczekuj
ą w kolejce Q.
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 4
Przesyłanie komunikatów
5.3
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
5.4
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
Funkcja zwraca:
> 0 – PID procesu który nada
ł komunikat
- 1 - b
łąd
Na poni
ższym rysunku za pomocą sieci Petriego przedstawiono wymianę
komunikatów pomi
ędzy procesami P1 i P1.
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 5
Przesyłanie komunikatów
Proces P1 wysylaj
ący
komunikat
Proces P2 odbieraj
ący
komunikat
Ready
Send
blocked
Reply
blocked
Ready
Receive
blocked
Ready
Receive
Reply
Send
Sie
ć Petriego przedstawiająca wymianę komunikatów pomiędzy procesami
P1 i P2
5.5
Nazwy
Proces P1 mo
że wysłać komunikat do P2 o ile zna jego PID. Bez
dodatkowych mechanizmó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 nazwy 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.
5.5.1 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.
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 6
Przesyłanie komunikatów
5.5.2 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
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 sieci. Gdy
nid # 0 proces będzie lokalizowany tylko na węźle o
numerze nid.
Gdy lokalizowany proces le
ży na innym węźle automatycznie tworzone jest
po
łączenie wirtualne (ang. Virtual Circuit) pomiędzy węzłami.
Je
żeli chcemy aby działała lokalizacja sieciowa, na przynajmniej jednym z
w
ęzłów sieci musi być uruchomiony proces nameloc. Zaleca się aby liczba
w
ęzłów z uruchomionymi procesami nameloc nie przekraczała 3.
5.5.3 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.
5.6
Przykład komunikacji
Przyk
ład procesu klienta P1 i serwera P2 wymieniające komunikaty. Procesy
mog
ą być uruchomione na tym samym lub oddzielnych węzłach połączonych
sieci
ą.
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 7
Przesyłanie komunikatów
Klient
P1
Proces wysylaj
ący
komunikaty
Proces odbieraj
ący
komunikaty
Serwer
P2
Komunikat
Odpowiedź
Proces klienta i serwera wymieniaj
ą komunikaty
// Proces klienta P1 – wysyła komunikaty
#include <sys/kernel.h>
#include <sys/name.h>
typedef struct {
int typ;
char text[80];
} msg_type;
main(void){
int pid,i;
msg_type msg;
// Lokalizacja serwera -----------
pid = qnx_name_locate(0,”serwer1”,sizeof(msg_type),
NULL);
if(pid == -1) { perror("LOKALIZACJA"); exit(1);}
msg.typ = 0;
for(i=0;i<10;i++) {
sprintf(msg.text,"Komunikat - %d\n",i);
// Wysłanie komunikatu --------
res = Send(pid,&msg,&msg,sizeof(msg),
sizeof(msg));
printf("Wysłano: %s \n",msg.text);
sleep(1);
};
exit(0);
}
Proces klienta wysy
łający komunikaty
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 8
Przesyłanie komunikatów
// Proces serwera P2 – odbiera komunikaty i wysyła
odpowiedzi
#include <sys/kernel.h>
#include <sys/name.h>
typedef struct {
int typ;
char text[80];
} msg_type;
main(void){
int pid,my_name;
msg_type msg;
// Rejestracja nazwy ---------
my_name = qnx_name_attach(0,”/serwer1);
if(my_name == -1) { perror("ATTACH"); exit(1);}
do { // Pętla główna --------
// Odbiór komunikatu ------------
pid = Receive(0,&msg,sizeof(msg));
printf("Komunikat od %d text: %s\n ",pid,msg.text);
msg.msg_type = 1;
// Wysłanie odpowiedzi ----------
Reply(pid,&msg,sizeof(msg));
} while(1);
qnx_name_detach(0,my_name);
}
Proces serwera odbieraj
ący komunikaty
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 9
Przesyłanie komunikatów
5.7
Przekazywanie komunikatów przez sieć – połączenia wirtualne
System QNX jest od podstaw zbudowany jako rozproszony system sieciowy.
Z
punktu
widzenia
programisty
komunikacja
pomi
ędzy procesami
zlokalizowanymi na jednym w
ęźle nie różni się od komunikacji przez sieć.
5.7.1 Budowa połączenia wirtualnego
P1
P2
VC1
VC2
Bufor 1
Bufor 2
Net
Driver
NIC
MEDIUM
Net
Driver
NIC
WEZEŁ 1
WEZEŁ 2
Poziom aplikacji
Poziom systemu
Mikro-
jądro
Mikro-
jądro
Po
łączenie wirtualne pomiędzy procesami P1 i P2
Po
łączenie wirtualne jest tworzone podczas lokalizacji procesu serwera.
Tworzone s
ą procesy wirtualne VC1 i VC2 które są pośrednikami w transmisji
komunikatów.
Proces VC1 reprezentuje proces P2 na w
ęźle 1 a proces VC2 reprezentuje
proces P1 na w
ęźle 2.
Ka
żdy z procesów wirtualnych dysponuje buforem na komunikaty.
Ka
żde połączenie wirtualne musi utrzymywać następujące informacje:
1. PID procesu lokalnego i zdalnego.
2. VID procesu lokalnego i zdalnego
3. NID (numery w
ęzłów) lokalnego i zdalnego
Przyk
ład - demonstracja testowania połączenia wirtualnego.
Problem: z w
ęzła 3 próbujemy wylistować znaki przychodzące na port
szeregowy w
ęzła 1
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 10
Przesyłanie komunikatów
Na w
ęźle 3 wykonujemy polecenie:
$ cat //1/dev/ser1
Na innej konsoli w
ęzła 3 piszemy:
$ sin vcs
LOCAL
REMOTE
VID PID LK STATE PROG NID VID PID PROG
15275 15273 1 REPLY //3/bin/cat 1
26518 16 //1/bin/Dev32
Na w
ęźle 1 wykonujemy polecenie:
$ sin vcs
Na konsoli pojawia si
ę informacja o połączeniach wirtualnych widzianych z
w
ęzła 3.
LOCAL
REMOTE
VID PID LK STATE PROG NID VID PID PROG
26518 16 1 REPLY //1/bin/Dev32 3 15275 15273 //1/bin/cat
Po
łączenie wirtualne posiada dwa końce i identyfikowane jest przez dwa
parametry – NID i VID.
cat
15273
Dev32
16
3 / 15275
1 / 26518
Po
łączenie wirtualne pomiędzy procesami cat na węźle 3 i Dev32 na węźle 1
5.7.2 Kontrola połączenia wirtualnego
System operacyjny samoczynnie kontroluje sprawno
ść połączenia
wirtualnego. Po
łączenie wirtualne stanie się nieoperatywne gdy:
1. Zdalny komputer zostanie wy
łączony lub uszkodzony.
2. Po
łączenie sieciowe do zdalnego komputera ulegnie uszkodzeniu.
3. Zdalny proces si
ę zakończy.
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 11
Przesyłanie komunikatów
5.8
Depozyty – powiadomienia asynchroniczne
W aplikacjach wspó
łbieżnych potrzebny jest często nieblokują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
ą rodzajem komunikatów o ustalonej treści (zwykle zerowej) nie
wymagaj
ącym potwierdzenia.
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).
Tworzenie depozytu:
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
wykonuj
ącego funkcję).
Funkcja zwraca:
> 0 - identyfikator depozytu
-1 - b
łąd
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.
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 12
Przesyłanie komunikatów
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.
Proces P1
Proces P2
proxy = qnx_proxy_attach(0,0,0,-1)
pid = receive(0,msg,size)
RECEIVE_BLOCKED
Trigger(proxy)
Odblokowanie
U
życie depozytu do odblokowania procesu
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 13
Przesyłanie komunikatów
#include <sys/kernel.h>
main() {
int i,sec = 0;
pid_t proxy;
proxy = qnx_proxy_attach(0,0,0,-1);
if(proxy == -1) { perror("Proxy"); exit(0);}
if(fork() == 0) { //Proces wysylajacy proxy ----
for(i=0;i<20;i++) {
Trigger(proxy); // Wyslanie proxy
sleep(1);
};
exit(0);
}
// Proces odbierajacy -------------------------
do {
pid = Receive(0,&msg,sizeof(msg));
// Odbior proxy lub komunikatu
if(pid == proxy) { // Proxy
printf("Sekund %d\n",sec++);
if(sec > 10) break;
} else { // Komunikat
printf("Komunikat \n");
....
}
} while(1);
qnx_proxy_dettach(0,0,0,-1);
}
Przyk
ład zastosowania depozytu
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 14
Przesyłanie komunikatów
Przykład - 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. Podstawowe
schematy obs
ługi klientów:
- Send driven
- Reply driven
Klienci
Serwer
P1
K1
KN
P2
P3
P4
Procesy oczekujace w
stanie REPLY_BLOCKED
P5
P6
P7
Procesy oczekujace w
stanie SEND_BLOCKED
Metody kolejkowania nie obs
łużonych klientów.
Kroki implementacji systemu klient – serwer :
1. Zaplanowanie formatu komunikatów.
2. Implementacja procesu klienta.
3. Implementacja procesu serwera.
Planowanie formatu komunikatów
1. Dla ka
żdego zlecenia należy przygotować odpowiednią
struktur
ę danych a zleceniom przypisać unikalne numery.
2. Struktury nale
ży zgrupować w unię.
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 15
Przesyłanie komunikatów
#define SERWER_NAME „/mój_serwer”
#define MSG1 1
// Identyfikator zlecenia 1
typedef struct {
// Struktura zlecenia 1
int typ_zlec; // Typ zlecenia
int par1;
// Parametr 1
……
} st1_t;
#define MSG1 2
// Identyfikator zlecenia 2
typedef struct {
// Struktura zlecenia 2
int typ_zlec; // Typ zlecenia
char *nazwa;
// Parametr 1
……
} st2_t;
typedef union {
// Struktura komunikatu (unia)
int typ_zlec; // Typ zlecenia
st1_t st1; // Typ zlecenia 1
st2_t st2; // Typ zlecenia 2
……
} msgu_t;
Definicje wspólnych struktur danych procesów klienta i serwera
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 16
Przesyłanie komunikatów
// Proces klienta – wysyła komunikaty
#include „common.h”
main(void){
int pid;
st1_t msg;
// Lokalizacja serwera -----------
pid = qnx_name_locate(0,SERWER_NAME,
sizeof(msg),NULL);
msg.typ_zlec = MSG1;
do {
// Przygotowanie pol komunikatu
msg.par1 = ….
// Wysłanie komunikatu --------
res =Send(pid,&msg,&msg,sizeof(msg),
sizeof(msg));
if(res < 0) { // Obsluga błędu }
// Wykorzystanie odpowiedzi ---
x = msg.par2;
…
}
}
Szkic procesu klienta
PDF created with pdfFactory trial version
J
ędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 17
Przesyłanie komunikatów
// Proces serwera – wysyła komunikaty
#include „common.h”
main(void){
int pid;
msgu_t msg;
st1_t msg1;
st1_2 msg2;
// Rejestracja serwera -----------
pid = qnx_name_attach(0,SERWER_NAME);
do { // Odbiór komunikatu ----------
pid = Receive(0,&msg,sizeof(msg));
if(pid < 0) { // Obsluga błędu }
// obsluga zleceń ---
switch(msg.typ_zlec) {
case MSG1: // Obsługa zlecenia typu 1
---
…
Reply(pid,&msg1,sizeof(msg1);
break;
case MSG2: // Obsługa zlecenia typu 2
---
…
Reply(pid,&msg2,sizeof(msg2);
break;
}
…
} while(aktywny);
}
Szkic procesu serwera
PDF created with pdfFactory trial version