Documente Academic
Documente Profesional
Documente Cultură
QNX Neutrino suporta mai multe mecanisme de comunicatie intre procese (IPC): mesaje Neutrino native pulsuri semnale memorie partajata pipe-uri (buffere dinamice) cozi de mesaje POSIX
Transmitere de mesaje:
transmiterea mesajelor se poate face: direct intre procese sau fire de executie, prin specificarea destinatiei, respectiv a sursei indirect, prin intermediul cutiilor postale sau canalelor de comunicatie (e.g. sub QNX) cu ajutorul pipe-urilor in general, functia de transmitere de mesaje este implementata sub forma unei perechi de primitive de tipul:
send(destinatie, mesaj) receive(sursa, mesaj)
in procesul sender in procesul receiver
transmiterea de mesaje intre procese (fire de executie) implica un anumit nivel de sincronizare
un proces care receptioneaza date (receiver) poate receptiona un mesaj doar atunci cand mesajul a fost transmis de procesul transmitator (sender) dupa efectuarea primitivelor send, respectiv receive exista doua posibilitati de comportament al procesului, adica ...
procesul sender se blocheaza pana cand mesajul este receptionat la destinatie, sau procesul sender nu se blocheaza
daca mesajul fusese transmis anterior executarii primitivei receive in taskul de destinatie, el va fi receptionat de catre acest task si executia taskului continua in cazul in care mesajul nu a fost inca transmis in momentul executarii primitivei receive, taskul care a efectuat receive se blocheaza pana la sosirea mesajului, sau taskul receptor isi continua executia, abandonand incercarea de a receptiona mesajul
In acest mod, atat taskul sender, cat si taskul receiver, pot fi cu blocare (engl. blocking sender and/or receiver) sau fara blocare (engl. nonblocking sender and/or receiver). Exista trei situatii posibile:
blocking send, blocking receive. Atat taskul sender, cat si taskul receiver se blocheaza pana la transmiterea, respectiv receptionarea mesajului. Se realizeaza un rendez-vous, sau sincronizare puternica (engl. tight syncronization). non-blocking send, blocking receive. Taskul sender poate continua sa transmita mesaje, chiar daca receptorul se blocheaza pana la receptionarea mesajului asteptat. Este cea mai utila combinatie, care permite unui proces sa transmita unul sau mai multe mesaje la destinatii diferite in timp foarte scurt (e.g. un proces server care are rolul de a furniza servicii catre alte procese). Este implementarea cea mai uzuala pentru programarea concurenta non-blocking send, non-blocking receive.
adresarea: intotdeauna este necesar ca in primitiva send sa fie specificat procesul care trebuie sa receptioneze mesajul si in procesul receiver trebuie sa se specifice sursa mesajului care trebuie receptionat adresare directa
primitiva send include un identificator specific al procesului receiver pentru primitiva receive este necesar ca procesul sa specifice procesul sender mesajele nu vor fi trimise direct de la taskul sender la taskul receiver, ci sunt transmise la o structura de date partajata (e.g. cozi care pot tine temporar mesaje). astfel de cozi poarta denumirea de cutii postale in acest caz, comunicatia intre doua procese presupune ca procesul transmitator sa trimita un mesaj la cutia postala corespunzatoare, de unde procesul receptor va ridica mesajul
adresare indirecta
adresare indirecta rela\ia [ntre senders ]i receivers poate fi (a) one-to-one canal privat de comunicatie
(b) many-to-many
Proces P1 Proces Q1
CUTIE POSTALA
Proces Pn
Proces Qm
Proces P1
PORT
Proces Q1
Proces P1
PORT
Proces Q1
relatie sender receiver de tip many-to many / one-to-many asocierea proceselor la cutiile postale poate fi statica sau dinamica porturile sunt de cele mai multe ori asociate static unui anumit proces (dupa creare, portul este asociat permanent procesului) cand exista mai multe procese sender, asocierea unui sender la o cutie postala poate fi facuta in mod dinamic (se folosesc primitive de tip connect si disconnect) exemplu: canale de comunicatie in QNX
Cine detine o cutie postala? in cazul unui port, acesta este in mod normal creat si detinut de procesul receiver (atunci cand procesul este distrus, va fi distrus si portul); in cazul general al cutiilor postale, sistemul de operare poate oferi un servicu special de creare a cutiilor postale
cea simpla metoda este cea de tip FIFO nu este indicata daca unele mesaje sunt mai urgente decat altele se aloca o prioritate mesajelor, in functie de tipul mesajului sau de sender i se poate permite receiver-ului sa inspecteze coada de mesaje si sa selecteze mesajul pe care il va receptiona
se considera doua taskuri care trebuie sa-si coordoneze activitatile, astfel incat Task 1 sa-si poata continua executia doar dupa executarea activitatii 2 (dupa sosirea mesajului de la Task 2)
Task 1 activitate_1() receive(Task2, mesaj) activitate_3() Task 2 activitate_2() send(Task1, mesaj) activitate_4()
functioneaza corect daca folosim primitive de tipul non-blocking send / blocking receive starile initiale ale cutiilor postale sunt foarte importante pentru exemplul de mai sus, cutia postala trebuie de fie vida initial analogie cu primitivele P si V pe semafor binar, initial valSB = 0
Task 1 activitate_1() P(SB) activitate_3() Task 2 activitate_2() V(SB) activitate_4()
analogie intre semafoare binare si cutii postale: Cutie vida Cutie nevida Primitiva receive Primitiva send Semafor binar = 0 Semafor binar = 1 Primitiva P Primitiva V
deoarece cutia postala poate contine mai multe mesaje, analogia corecta este cu semafoarele generalizate, astfel: numarul de mesaje din cutia postala este echivalentul valorii semaforului generalizat operatia de extragere de mesaje din este echivalenta cu decrementarea valorii semaforului generalizat operatia de depunere de mesaje in cutie este echivalenta cu incrementarea valorii semaforului generalizat impreuna cu eventuala asteptare a taskului care face extragerea, deci practic efectele sunt similare cu cele ale primitivelor P si V pe un semafor generalizat rezulta modul in care se executa operatiile se sincronizare, excludere mutuala, etc.
solutia cu primitive de tip blocking receive si (non) blocking send asigura ca atunci cand mai multe procese executa operatia receive in mod concurent:
daca este un mesaj in cutie, el va fi transmis unui singur proces, iar celelalte procese se blocheaza atunci cand coada de mesaje este vida, toate procesele vor fi blocate; atunci cand este disponibil un mesaj, un singur proces va fi activat si va primi mesajul
problema producator consumator cu buffer finit se utilizeaza doua cutii postale, mayproduce si mayconsume
cap = capacitatea bufferului mayconsume mayconsume este organizata ca un buffer, datele din buffer sunt organizate ca o coada de mesaje mayproduce este umpluta initial cu un numar de mesaje nule egal cu capacitatea bufferului mayconsume numarul de mesaje din mayproduce descreste cu fiecare producere de date si creste cu fiecare consumare de date
Task initializare create_mailbox(mayproduce); create_mailbox(mayconsume); for(i=1; i < cap; i++) send(mayproduce, NULL); fork(producator, consumator);
Mesaje QNX:
www.qnx.com
pentru studiu individual, toata informatia referitoare la mesaje continuta in aceasta prezentare este disponibila free pe site-ul QNX, in sectiunea documentatie sincronizarea prin intermediul schimbului de mesaje reprezinta principala forma de IPC pusa se dispozitie de nucleul Neutrino schimbul de mesaje este implementat prin functiile specifice de tip MsgSend(),
MsgReceive() ]i MsgReply()
blocarea inerenta sincronizeaza executia firului care trimite mesajul, acesta se blocheaza pana la confirmarea receptiei, in timp ce firul de executie care primeste mesajul este programat pentru executie
Mesaje QNX:
MsgReply() sau MsgError() SENDblocked MsgSend()
MsgSend()
RECEIVEblocked MsgReceive()
READY
MsgReceive()
REPLYblocked
schimbul de mesaje asociat nucleului de TR Neutrino nu se realizeaza direct, ci prin intermediul canalelor si conexiunilor
Server Canal Conexiune Client Conexiune Server Canal
un fir de execute care doreste sa primeasca un mesaj creaza mai intai un canal de comunicatie un alt fir de executie care doreste sa ii trimita un mesaj trebuie sa creeze o conexiune, prin atasarea la canalul respectiv
Mesaje - QNX:
canalele si conexiunile sunt reprezentate in cadrul procesului printr-un identificator de tip small integer conexiunile clientilor sunt mapate direct in descriptori de fisier, acest lucru eliminand necesitatea determinarii destinatarului mesajului bazandu-ne pe descriptorul de fisier (de exemplu, prin read(fd))
Mesaje - QNX:
conexiunile sunt folosite de firele de executie client, prin atasarea lor la canalele puse la dispozitie de server
} ConnectDetach(cID);
Output Program:
Server: MsgReceive pe rcvID = 65539 Server: mesaj de la client este: Mesaj SEND de la client! Client: am primit mesajul: Mesaj REPLY de la server.
Server: MsgReceive pe rcvID = 65539 Server: mesaj de la client este: Mesaj SEND de la client! Client: am primit mesajul: Mesaj REPLY de la server.
Exemplu:
Enunt problema: Sa se scrie o aplicatie multitasking pentru monitorizarea accesului mai multor procese la 2 imprimante (de tip A sau B). Se presupune ca exista 3 tipuri de procese care pot utiliza imprimantele: procese care utilizeaza doar imprimanta A; procese care utilizeaza doar imprimanta B; Sa se scrie o aplicatie multitasking formata din 3 tipuri de taskuri, astfel incat: sa existe taskuri care pot accesa cele doua tipuri de imprimanta sunt procese de tip client care vor face cereri de alocare / dealocare resursa (imprimanta); sa existe un proces de tip server care monitoriza alocarea imprimantelor pentru procesele de tip client. Observatie: se va considera faptul ca un proces de tip client poate elibera imprimanta pe care o foloseste (dealocare resursa) la un anumit moment de timp.
Exemplu:
Cerinte:
sa se analizeze problema si sa se propuna o solutie pentru implementare sa se defineasca taskurile sa se organizeze aplicatia sub forma organigramelor sa se aleaga un mecanism de sincronizare / comunicare (sub QNX) pentru implementare sa se schiteze programul cu functii specifice sub QNX
asteapta mesaj eliberare resursa A_FREE (B_FREE) A_READY = 1 A_REQ (B_REQ) 1 A_READY 0 cerere resursa
m es aj
transmite A_FREE
A_READY = 0
transmite A_FREE
transmite A_BUSY
mesaj necunoscut
mesaj raspuns
A_FREE (B_FREE)
mutex_lock(&A_printer)
utilizare resursa
mutex_unlock(&A_printer)
transmite A_FREE
EXIT