PROGRAMOWANIE SYSTEMÓW WBUDOWANYCH INTER-PROCESS COMMUNICATION Mariusz Rudnicki mariuszrudnicki@etipgedupl Programowanie Systemów Wbudowanych 1/91
KOMUNIKACJA MIĘDZYPROCESOWA IPC Omawiane zagadnienia Czym jest komunikacja międzyprocesowa? W jakim celu stosuje się komunikację międzyprocesową? Metody IPC w RT OS:, RT Linux, Windows CE Niebezpieczeństwa związane z mechanizmami komunikacji IPC Programowanie Systemów Wbudowanych 2/91
KOMUNIKACJA MIĘDZYPROCESOWA IPC W większości systemów RT procesy uruchamiane są w chronionej przestrzeni adresowej Powoduje to brak dostępu do zasobów danego procesu od strony innych procesów W związku z tym musiały powstać mechanizmy, które pozwolą na wymianę zasobów pomiędzy procesami Programowanie Systemów Wbudowanych 3/91
KOMUNIKACJA MIĘDZYPROCESOWA IPC Komunikacja IPC ang Inter-Process Communication zapewnia mechanizmy wymiany danych pomiędzy procesami Procesy mogą wymieniać: dane; kontrolę; powiadomienie o wystąpieniu zdarzenia Programowanie Systemów Wbudowanych 4/91
wspiera różnorodne mechanizmy komunikacji IPC: rodzima komunikacja (API unikalne dla QNX): komunikaty ; pulsy ; mechanizmy zgodne z POSIX/UNIX (API przenośne): sygnały; pamięć dzielona; potoki (wymagany proces pipe); kolejki komunikatów POSIX (wymagany proces mqueue lub mq); gniazda TCP/IP (wymagany proces io-net) Programowanie Systemów Wbudowanych 5/91
Komunikaty : oparta na modelu klient serwer; komunikacja dwukierunkowa Proces klienta Proces serwera Wątek Wątek 1 MsgSend() MsgReceive() 2 1 2 3 klient wysyła zapytanie/dane do serwera serwer odbiera i przetwarza MsgReply() server odpowiada klientowi, klient odbiera odpowiedź i kontynuuje process msg 3 Programowanie Systemów Wbudowanych 6/91
Komunikaty : architektura zdalnego wywoływania funkcji jądra ang kernel calls; w pełni synchroniczna: w czasie bezczynności, serwer jest zablokowany w oczekiwaniu na komunikat klient jest zablokowany do czasu odpowiedzi serwera Programowanie Systemów Wbudowanych 7/91
Komunikaty : CZAS 1 Proces klienta Wątek MsgSend()entry Proces serwera Wątek MsgReceive() 2 process msg 3 5 MsgSend()exit MsgReply() 4 Zablokowany Programowanie Systemów Wbudowanych 8/91
PRZYPADEK 1: Send po Receive zablokowany serwer Klient Serwer Stan READY REPLY Blocked READY Blocked Wątek MsgSend() CZAS kopiowanie danych wysłanych kopiowanie danych zwrotnych Wątek MsgReceive() Programowanie Systemów Wbudowanych 9/91 MsgReply() Stan READY RECEIVE Blocked READY
PRZYPADEK 2: Send przed Receive zajęty serwer Klient Serwer Stan READY SEND Blocked REPLY Blocked READY Blocked Wątek MsgSend() MsgReceive() kopiowanie danych wysłanych CZAS Wątek MsgReply() kopiowanie danych zwrotnych Stan READY Programowanie Systemów Wbudowanych 10/91
Komunikaty : popatrzmy na wątek klienta i jego stany: Programowanie Systemów Wbudowanych 11/91
Komunikaty : spójrzmy na wątek serwera i jego stany: Programowanie Systemów Wbudowanych 12/91
Komunikaty : połączenia i kanały: serwer odbiera w kanale; klient dołącza się do kanału; klient wysyła dane przez kanał serwera po podłączeniu się do niego połączenie kanał klient serwer Programowanie Systemów Wbudowanych 13/91
Komunikaty : wielokrotne połączenia i kanały: klient może mieć połączenia z wieloma serwerami; serwer używa jednego kanału do odbierania wiadomości od wielu klientów klient A połączenie połączenie kanał serwer A połącznie kanał połączenie klient B serwer B Programowanie Systemów Wbudowanych 14/91
Komunikaty : Pseudo kod serwera: utwórz kanał ( ChannelCreate() ) czekaj na komunikat ( MsgReceive() ) przetwarzaj odpowiedz ( MsgReply() ) przejdź do oczekiwania Pseudo kod klienta dołącz do kanału ( ConnectAttach() ) wyślij komunikat ( MsgSend() ) wykorzystaj odpowiedź serwera Programowanie Systemów Wbudowanych 15/91
Serwer tworzy kanał wykorzystując: chid = ChannelCreate (flagi); kanał (prawdopodobnie wielowątkowy) serwer kanał powiązany jest z procesem; dowolny wątek w procesie może odbierać komunikaty poprzez kanał; kiedy komunikat nadchodzi, jądro po prostu wybiera wątek nasłuchujący MsgReceive(); flagi są ustawiane bitowo, niektóre z flag będą omówione w dalszej części Programowanie Systemów Wbudowanych 16/91
Klient dołącza się do kanału serwera: coid = ConnectAttach(nd, pid, chid, _NTO_SIDE_CHANNEL, flags); połączenie kanał klient serwer nd, pid, chid unikalnie identyfikuje kanał serwera; nd jest deskryptorem węzła: opisuje komputer na którym jest uruchomiony serwer; pid to id procesu serwera; chid id kanału; jako 4-ty argument zawsze podajemy NTO_SIDE_CHANNEL Programowanie Systemów Wbudowanych 17/91
ID połączenia (coids) może być dwóch typów: coids deskryptory plików połączenia kanałów fd = open (filename, O_RDONLY); coid = ConnectAttach (nd, pid, chid, _NTO_SIDE_CHANNEL, flags); kiedy wywołujemy ConnectAttach() nie chcemy, żeby coid był w przedziale deskryptorów plików; umieszczenie _NTO_SIDE_CHANNEL zabezpiecza przed tym Programowanie Systemów Wbudowanych 18/91
Wywołanie MsgSend() (po stronie klienta): status = MsgSend (coid, smsg, sbytes, rmsg, rbytes); coid identyfikator połączenia smsg dane do wysłania sbytes liczba bajtów do wysłania z bufora smsg rmsg bufor odbiorczy na przyjęcie komunikatu zwrotnego rbytes rozmiar bufora rmsg status jest wartością przekazywaną przez parametr status podczas odpowiedzi MsgReply*(); Programowanie Systemów Wbudowanych 19/91
Wywołanie MsgReceive() (po stronie serwera): rcvid = MsgReceive (chid, rmsg, rbytes, info); chid identyfikator kanału; rmsg bufor na odbierane dane; rbytes liczba bajtów możliwych do odebrania w buforze rmsg; info pozwala na uzyskanie dodatkowych informacji; rcvid pozwala na użycie MsgReply*() do klienta Programowanie Systemów Wbudowanych 20/91
Wywołanie MsgReply() (po stronie serwera): MsgReply (rcvid, status, msg, bytes); rcvid identyfikator nadawcy otrzymany po wywołaniu MsgReceive*(); status jest wartością przekazywaną do MsgSend*(); msg zwracany bufor; bytes liczba bajtów przekazywanych w buforze Programowanie Systemów Wbudowanych 21/91
Wywołanie MsgError() (po stronie serwera): spowoduje wyjście z MsgSend*() z wartością -1 i ustawienie errno MsgError (rcvid, error); rcvid identyfikator klienta zwrócony przez wywołanie MsgReceive*(); error wartość błędu Programowanie Systemów Wbudowanych 22/91
Przykład serwera: #include <sys/neutrinoh> int main(void) { int chid, rcvid; mymsg_t msg; chid = ChannelCreate(0); while(1) { rcvid = MsgReceive(chid, &msg, sizeof(msg), NULL); przetwórz informacje z komunikatu klienta MsgReply(rcvid, EOK, &reply_msg, sizeof(reply_msg) ); } } Programowanie Systemów Wbudowanych 23/91
Przykład klienta: #include <sys/neutrinoh> #include <sys/netmgrh> int main(void) { int coid; //Identyfikator połączenia mymsg_t outgoing_msg; int server_pid, server_chid, incoming_msg; coid = ConnectAttach(ND_LOCAL_NODE, server_pid, server_chid, _NTO_SIDE_CHANNEL, 0); MsgSend(coid, &outgoing_msg, sizeof(outgoing_msg), &incoming_msg, sizeof(incoming_msg) ); } Programowanie Systemów Wbudowanych 24/91
Treść komunikatu jest zawsze kopiowana Jądro nie przekazuje wskaźników Wątek klient Wątek serwer MsgSend(coid, msg,,replybuf,) rcvid = MsgReceive(chid, msg,) Bufory komunikatów MsgReply(rcvid, sts, replybuf,) Programowanie Systemów Wbudowanych 25/91
Serwer może odpowiedzieć bez danych: w celu odblokowanie klienta, w przypadku gdy chcemy jedynie przekazać potwierdzenie odbioru do MsgSend(); klient zna status żądania Klient Serwer Thread Thread MsgSend(coid, msg,,replybuf,) rcvid = MsgReceive(chid, msg,) MsgReply(rcvid, EOK, NULL, 0) Bufory komunikatów status no data Programowanie Systemów Wbudowanych 26/91
W jaki sposób zaprojektować interfejs komunikacyjny? zdefiniować typy komunikatów i struktur w pliku nagłówkowym: klient i serwer dołącza wspólny plik nagłówkowy; rozpoczynać wszystkie komunikaty typem wiadomości; utworzyć strukturę opisującą każdy typ komunikatu: jeżeli komunikaty są powiązane lub używają wspólnych struktur, rozważyć możliwość użycia typu i podtypu komunikatu; zdefiniować struktury dla odpowiedzi; unikać nakładania się typów komunikatów dla różnych serwerów Programowanie Systemów Wbudowanych 27/91
unikać nakładania się typów komunikatów z zakresu komunikatów systemowych QNX: w/w typy komunikatów generowane są przez funkcje biblioteczne QNX, np read(); wszystkie komunikaty QNX rozpoczynają się od: uint16_t type; systemowe komunikaty są w zakresie: 0 to _IO_MAX (511); używanie wartości większych niż _IO_MAX jest zawsze bezpieczne Programowanie Systemów Wbudowanych 28/91
Po stronie serwera fragment uzależniony od typu komunikatu, np: while(1) { } rcvid = MsgReceive( chid, &msg, sizeof(msg), NULL ); switch( msgtype ) { } case MSG_TYPE_1: handle_msg_type_1(rcvid, &msg); break; case MSG_TYPE_2: Programowanie Systemów Wbudowanych 29/91
Rodzima komunikacja jest z natury synchroniczna: wysłanie MsgSend*() przez klienta powoduje jego zablokowanie; serwer musi wywołać MsgReply*() w celu odblokowania klienta Jak postępować, jeżeli nie chcemy aby klient był zablokowany? Programowanie Systemów Wbudowanych 30/91
Kilka możliwości: jeżeli potrzebujemy przesłać dane: wątek przeznaczony na serwer: serwer odbiera komunikaty i natychmiast odpowiada minimalizując okres blokowania wątku klienta; wykorzystujemy komunikację asynchroniczną QNX; jeżeli nie potrzebujemy przesyłać danych lub jeśli potrzebujemy jedynie przesłać powiadomienie o dostępnych danych wykorzystujemy: sygnały; pulsy Programowanie Systemów Wbudowanych 31/91
Pulsy: nie blokujące dla nadawcy; ustalony rozmiar: 32 bitowa wartość, 8 bitowy kod (-128 to 127), ujemne wartości zarezerwowane dla systemu; jednokierunkowe (nie wymagają odpowiedzi); szybkie i niedrogie PULS Programowanie Systemów Wbudowanych 32/91
Pulsy są wysyłane za pomocą wywołania: MsgSendPulse (coid, priority, code, value); 8-bits 32-bits code zwykle używany do określenia typu pulsu: zakres od _PULSE_CODE_MINAVAIL do _PULSE_CODE_MAXAVAIL; priority priorytet wysyłanego komunikatu; wysłanie pulsu do innego procesu wymaga aby nadawca miał taki sam userid jak odbiorca lub był użytkownikiem root Programowanie Systemów Wbudowanych 33/91
Pulsy są odbierana tak samo jak komunikaty, za pomocą wywołania MsgReceive*(): serwer określa odebranie pulsu na podstawie wartości zwracanej przez MsgReceive(); jeżeli wartość zwracana przez MsgReceive() jest >0 - odebrano komunikat: ta wartość będzie niezbędna dla MsgReply(); jeżeli wartość zwracana przez MsgReceive() == 0 odebrano pulse: nie musimy odpowiadać MsgReply() dla pulsów; dane z pulsu będą zapisane do bufora odbiorczego Programowanie Systemów Wbudowanych 34/91
Przykład: typedef union { struct _pulse } mymessage_t; pulse; // inne typy odbieranych komunikatów mymessage_t msg; while (1) { rcvid = MsgReceive (chid, &msg, sizeof(msg), NULL); if (rcvid == 0) { // to jest pulse, spójrzmy w msgpulse w celu odczytania danych } else { // to jest regularny komunikat } } Programowanie Systemów Wbudowanych 35/91
Po odebraniu, struktura pulsu wygląda następująco: struct _pulse { signed char code; 8-bit kod union sigval value; }; int scoid; Pole value jest unią: union sigval { int sival_int; void* sival_ptr; }; Programowanie Systemów Wbudowanych 36/91
Serwer będzie chciał określić powód wysłania danego pulse poprzez sprawdzenie pola code: rcvid = MsgReceive (chid, &msg, sizeof(msg), NULL); if (rcvid == 0) { // to jest pulse, spójrzmy w msgpulse switch (msgpulsecode) { case case MY_PULSE_CODE1: // wykonaj odpowiednie czynności break; MY_PULSE_CODE2: // wykonaj odpowiednie czynności break; Programowanie Systemów Wbudowanych 37/91
W jaki sposób klient odnajduje serwer? klient potrzebuje identyfikatora połączenia (COID) z serwerem; np MsgSend(coid, &msg, ); jak widzieliśmy, aby otrzymać coid, klient wykonuje ConnectAttach(); np coid = ConnectAttach(nd, pid, chid, ); Problem w tym, skąd wziąć pozostałe parametry nd, pid i chid? Programowanie Systemów Wbudowanych 38/91
W jaki sposób klient odnajduje serwer? Na zajęciach laboratoryjnych serwer drukuje pid/chid i klient korzysta z nich jako argumenty linii poleceń to nie jest dobre rozwiązanie Dlaczego? istnieją dwie metody, w zależności od tego jaką serwer pełni rolę: managera zasobów; pętla MsgReceive(); obie metody korzystają z przestrzeni nazw: serwer rejestruje swoją nazwę w przestrzeni nazw; klient i serwer muszą znać tą nazwę; klient wykonuje open na tej nazwie i pobiera coid Programowanie Systemów Wbudowanych 39/91
Jeżeli serwer jest managerem zasobów: manager zasobu rejestruje swoją nazwę w przestrzeni nazw: resmgr_attach(, /dev/sound, ); klient wykonuje: fd = open( /dev/sound, ); LUB w przypadku sieciowym: fd = open("/net/nodename/dev/sound", ); wówczas może wykorzystać deskryptor fd write( fd, ); // wysłanie danych read( fd, ); lub // odbiór danych MsgSend( fd, ); // wysłanie/odbiór danych Programowanie Systemów Wbudowanych 40/91
Jeżeli serwer działa w pętli MsgReceive() używamy name_attach() i name_open(): Po stronie serwera: name_attach_t *attach; attach = name_attach( NULL, myname, 0 ); rcvid = MsgReceive( attach->chid, &msg, sizeof(msg), NULL ); name_detach( attach, 0 ); Programowanie Systemów Wbudowanych 41/91
Jeżeli serwer działa w pętli MsgReceive() używamy name_attach() i name_open(): Po stronie klienta: coid = name_open( myname, 0 ); MsgSend( coid, &msg, sizeof(msg), NULL, 0 ); name_close( coid ); Programowanie Systemów Wbudowanych 42/91
Wywołanie name_attach() tworzy kanał: wewnętrznie wywołuje ChannelCreate(); ustawia pewne flagi dla kanału; ustawienie odpowiednich flag powoduje wysyłanie pulsów przez jądro jako powiadomienia o różnych zdarzeniach; musimy być świadomi tego, że będziemy otrzymywać pulsy, które musimy odpowiednio obsłużyć Programowanie Systemów Wbudowanych 43/91
Flagi ChannelCreate() ustawiane przez name_attach(): _NTO_CHF_DISCONNECT: żądanie zgłoszenia powiadomienia w sytuacji zakończenia klienta; pulse będzie miał pole code: _PULSE_CODE_DISCONNECT; _NTO_CHF_COID_DISCONNECT: żądanie zgłoszenia powiadomienia w sytuacji zakończenia serwera; pulse będzie miał pole code _PULSE_CODE_COIDDEATH; _NTO_CHF_UNBLOCK: żądanie zgłoszenia jeżeli klient zablokowany na REPLY, wymaga odblokowania; pulse będzie miał pole code _PULSE_CODE_UNBLOCK; Programowanie Systemów Wbudowanych 44/91
Przykład serwera odbierającego pulsy od jądra: rcvid = MsgReceive(attach->chid, &msg, sizeof(msg), NULL); if(rcvid == 0) { //sprawdzenie czy przyszedł puls switch(msgpulsecode) { //Jaki typ pulsu case _PULSE_CODE_DISCONNECT: //odłączenie klienta break; case _PULSE_CODE_UNBLOCK: //klient żąda odblokowania break; case //inne Programowanie Systemów Wbudowanych 45/91
Flaga odłączenia _NTO_CHF_DISCONNECT: ustawiana kiedy kanał jest tworzony wymaga dostarczenia przez jądro odpowiedniego pulse do serwera w sytuacji: wszystkie połączenia od klienta są rozłączone, włączając: zakończenie procesu wywołanie ConnectDetach() dla wszystkich połączeń utrata połączenia sieciowego w sytuacji, kiedy komunikacja odbywała się przez sieć połączenie połączenie kanał klient kiedy dwa połączenia są odłączone, puls będzie wysłany serwer pole code będzie miało wartość _PULSE_CODE_DISCONNECT Programowanie Systemów Wbudowanych 46/91
Parametr scoid - Server Connection ID; identyfikuje proces klienta: nie można użyć pid jak identyfikatora klienta, ponieważ pid może być taki sam dla dwóch procesów uruchomionych na różnych węzłach sieci nowy scoid jest automatycznie tworzony kiedy dołącza się nowy klient; jeżeli flaga _NTO_CHF_DISCONNECT została ustawiona podczas tworzenia kanału, scoid powinien być zwolniony ręcznie; po odebraniu pulsu oznaczającego rozłączenie z klientem, musimy: usunąć informacje związane z klientem; wykonać ConnectDetach(pulsescoid) aby zwolnić scoid klient A połączenie klient B połączenie połączenie kanał serwer 2 scoid - jeden na klienta Programowanie Systemów Wbudowanych 47/91
Przykład czyszczenia po odłączeniu klienta: attach = name_attach(null, my_name, 0); while (1) { } rcvid = MsgReceive(attach->chid, &msg, sizeof(msg), NULL); if (rcvid == 0) { /* Pulse received */ switch (msgpulsecode) { case _PULSE_CODE_DISCONNECT: //code to clean up per-client info ConnectDetach(msgpulsescoid); /* zwalniamy scoid */ break; Programowanie Systemów Wbudowanych 48/91
Problemy związane z odblokowaniem klienta: Wątek klienta Wątek serwera Stan READY SEND Blocked REPLY Blocked READY Wątek 1 Wątek 2 MsgSend() 1 2 CZAS Signal, timeout, or cancel send data transmitted Signal, timeout, or cancel MsgReceive() MsgReceive() Stan READY RECEIVE Blocked Programowanie Systemów Wbudowanych 49/91
Ilustracja flag odblokowania: Wątek klienta Sygnał, timeout, or cancel 3a MsgSend() 1 Pulse 3b Wątek serwera ChannelCreate( _NTO_CHF_UNBLOCK) MsgReceive() 6 5 4 2 MsgReply() Programowanie Systemów Wbudowanych 50/91
Flaga _NTO_CHF_UNBLOCK: informuje jądro o konieczności wysłania pulsu do serwera kiedy zablokowany na REPLY wątek klienta otrzymuje: sygnał, zakończenie wątku, kernel timeout; pole code tegu pulsu będzie: _PULSE_CODE_UNBLOCK ; pole valuesival_int będzie zawierać rcvid odpowiadający wartości zwracanej przez MsgReceive*(); pozwala to serwerowi na oczyszczenie wszelkich zasobów zaalokowanych dla klienta, ponieważ klient nie jest już zainteresowany w oczekiwaniu na ich wynik; serwer MUSI wywołać MsgReply*() lub MsgError() do klienta, w przeciwnym razie klient zostanie zablokowany na zawsze Programowanie Systemów Wbudowanych 51/91
Jeżeli serwer wywołuje MsgReceive() oraz istnieje kilku klientów zablokowanych na SEND: komunikat SEND od procesu o najwyższym priorytecie zostanie odebrany; jeżeli wielu klientów posiada jednakowy priorytet, obsłużony będzie ten który najdłużej czekał; takie same reguły jak przy szeregowaniu PRZYPADEK 2: Send przed Receive serwer zajęty MsgSend() Zabloko wany na SEND Zablokowany na REPLY CZAS MsgReceive() MsgReply() READY Programowanie Systemów Wbudowanych 52/91
Jeżeli wątki wywołują MsgSend() w kolejności: id wątku 1 2 3 4 priorytet 10 15 10 20 Komunikaty będą odebrane w kolejności:?,?,?,? Programowanie Systemów Wbudowanych 53/91
Jeżeli proces odbiera komunikaty i pulsy: kolejność odbioru ciągle opiera się na priorytetach; wykorzystywany jest priorytet pulsu; pulsy i komunikaty mogą się mieszać Programowanie Systemów Wbudowanych 54/91
Jeżeli wątki wysyłają pulsy i komunikaty następująco: C Z A S Thread Thread id priority Action 1 10 Send pulse p1 with pulse priority 15 2 16 Send message 1 10 Send pulse p2 with pulse priority 9 3 11 Send message 1 10 Send message 4 17 Send pulse p3 with pulse priority 6 Komunikat od wątku 1 dociera do serwera przed pulsem p2, który był wysłany wcześniej Programowanie Systemów Wbudowanych 55/91
Priorytety Problem inwersji priorytetów: wysokie 255 22 13 10 t2 t1 server MsgReceive() nikie 0 Uwaga: to nie ma miejsca w rzeczywistości Programowanie Systemów Wbudowanych 56/91
Priorytety Rozwiązanie: wysoki niski 255 22 13 11 10 0 t1 t3 t2 4 server server jeżeli wielu klientów wysyła SEND, serwer odziedziczy priorytet po kliencie o najwyższym priorytecie _NTO_CHF_FIXED_PRIORITY wyłączanie dziedziczenia priorytetów Programowanie Systemów Wbudowanych 57/91 5
Komunikacja IPC Projektowanie systemu komunikacji Unikanie zakleszczeń Co się stanie jeżeli serwer potrzebuje wysłać komunikat do klienta? SEND klient serwer można umieścić kanał u klienta ale, jeżeli dwa procesy wykonują SEND jeden do drugiego, będą zablokowani, czekając nawzajem na odpowiedź: tzw DEADLOCK SEND Programowanie Systemów Wbudowanych 58/91
Projektowanie systemu komunikacji Unikanie zakleszczeń: możemy użyć klienta, wykonującego wszystkie blokujące operacje wysłania; serwer wykorzystuje nieblokujące pulsy; kiedy klient otrzymuje puls, wysyła do serwera komunikat z pytaniem o dane; SEND REPLY Serwer 1 pulse Klient 3 SEND REPLY Programowanie Systemów Wbudowanych 59/91 2
Projektowanie systemu komunikacji Unikanie zakleszczeń: Klient może mieć kanał: SEND klient kanał kanał serwer SEND Programowanie Systemów Wbudowanych 60/91
Projektowanie systemu komunikacji Unikanie zakleszczeń: Jeżeli mamy tylko dwa kanały: SEND Serwer Klient SEND rozpoznanie potencjalnego zakleszczenia jest proste, ale w złożonym systemie: P4 P1 P2 P3 jest trudniejsze P5 P6 P7 Programowanie Systemów Wbudowanych 61/91
Komunikacja IPC Rozwiązanie: Hierarchia wysyłania: P1 P2 P4 P3 P5 P6 P7 P8 projektujemy graf stanów gdzie polecenie wysłania zawsze schodzą w dół, nigdy nie powodując zakleszczenia; jeżeli dwa procesy na tym samym poziomie muszą się komunikować, po prostu tworzymy kolejny poziom Programowanie Systemów Wbudowanych 62/91
Nie blokujące pulsy wykorzystujemy do wysyłania powiadomień w górę: P1 P2 P4 P3 P5 P6 P7 blokujący send nie blokujący puls P8 Programowanie Systemów Wbudowanych 63/91
Asynchroniczne oraz synchroniczne przekazywanie komunikatów: komunikacja asynchroniczna: klient nie jest zablokowany w oczekiwaniu na Reply ; klienci mogą kolejkować wiele wiadomości; serwer może być zablokowany w oczekiwaniu na komunikaty lub może opróżnić kolejkę; Proces klienta Proces serwera Thread Wątek msg msg msg 1 2 asyncmsg_put() Thread Wątek asyncmsg_get() Programowanie Systemów Wbudowanych 64/91
Połączenia asynchroniczne i kanały: połączenia asynchroniczne i kanał nie są takie same jak połączenia i kanały synchroniczne, chociaż koncepcja jest podobna; klient/serwer często będzie mieć połączenie/kanał synchroniczny oraz dodatkowo asynchroniczne duplikaty klient serwer połączenie async kanał async asyncmsg_connect_attach() asyncmsg_channel_create() Programowanie Systemów Wbudowanych 65/91
Klient może zarejestrować wywołanie zwrotne (callback): callback jest wywoływany za każdym razem gdy komunikat zostanie dostarczony do serwera; wywołanie zwrotne rejestrowane jest podczas nawiązywania połączenia Programowanie Systemów Wbudowanych 66/91
RT Linux Istnienie procesów standardowego Linuxa oraz RT Linuxa stawia nowe wyzwania przed projektantami systemów w kwestii komunikacji zadań RT z zadaniami non-rt Co stanowi głównym zagrożeniem? Programowanie Systemów Wbudowanych 67/91
RT Linux Jądro Linuxa może być wywłaszczane przez zadania krytyczne, stąd nie mogą one wywoływać systemowych funkcji jądra Linuxa Rozwiązanie problemu? Programowanie Systemów Wbudowanych 68/91
Data Software level RT Linux RT-Kernel RT-FIFO Linux Process RT Processes X-Window Linux Kernel Display Disk Devices RTLINUX/RTFIFO and Tcl/Tk - Nishant Upadhyaya, Maung Wynn Aung Han - CIS642: Seminar in Real-time Systems, University of Pennsylvania Embedded System Programming 69/38
RT Linux RT FIFO: bufory o stałym rozmiarze; od strony procesów RT widziane są podobnie jak UNIX owe pipe y; operacje wykonywane na RT-FIFO są nieblokujące z punktu widzenia zadań krytycznych; dostępne są w formie ładowalnego modułu RT Core; Programowanie Systemów Wbudowanych 70/91
Komunikacja IPC RT Linux umieszczone są w przestrzeni RT Core; zadania krytyczne mogą wykonywać następujące operacje na RT-FIFO: tworzenie, usuwanie, odczytywanie, zapisywanie, operacje zapisu i odczytu są niepodzielne i nieblokujące od strony procesów RT (rozwiązuje to inwersję priorytetów); Programowanie Systemów Wbudowanych 71/91
RT Linux Linux RT FIFO: procesy Linuxa widzą RT-FIFO jako urządzenia znakowe; procesy Linuxa pracują na RT-FIFO jak na zwykłych plikach, pod warunkiem, że proces RT utworzył wcześniej odpowiadające mu RT-FIFO; sposób obsługi RT-FIFO zależy od wersji RT Linuxa: początkowo były to urządzenia nazwane /dev/rtf0 do rtf64 Pliki te musiały być utworzenie ręcznie z poziomu OS Linuxa; obecnie mogą mieć dowolną nazwę i nie muszą być tworzone z poziomu wątków Linuxa Programowanie Systemów Wbudowanych 72/91
RT Linux Linux RT FIFO: Pamięć dla RT-FIFO musi być alokowana z poziomu Linuxa np w module inicjującym OS, a nie bezpośrednio w procesie RT; Ilość RT-FIFO jest określona na poziomie kompilacji kodu jądra Czy potrzebne są mechanizmy synchronizacji zapisu i odczytu do RT- FIFO od strony zadań RT i procesów zwykłego Linuxa? Programowanie Systemów Wbudowanych 73/91
RT Linux RT-FIFO RT Task non-rt Task RT Task non-rt Task RT Kernel Space Std Linux Space RTLINUX/RTFIFO and Tcl/Tk - Nishant Upadhyaya, Maung Wynn Aung Han - CIS642: Seminar in Real-time Systems, University of Pennsylvania Programowanie Systemów Wbudowanych 74/91
RT Linux Linux pamięć współdzielona: mbuff driver alokuje nazwany obszar pamięci w wirtualnej pamięci jądra poprzez wykorzystanie funkcji vmalloc(); mbuff przegląda strona po stronie pamięć wirtualną i rezerwuje dla nich strony pamięci fizycznej; umieszczenie pamięci wirtualnej w pamięci fizycznej i odpowiednie jej stronicowanie zapobiega występowaniu wyjątku błędu strony; inaczej mówiąc VMM ang Virtual Machine Monitor nie jest wywoływany w momencie gdy proces jądra lub proces RT odwołuje się do zaalokowanej pamięci; Programowanie Systemów Wbudowanych 75/91
RT Linux Linux pamięć współdzielona: ponieważ RT-Core blokuje jądro zwykłego Linuxa w czasie wykonywania zadań RT jakiekolwiek odwołanie do VMM blokuje system; co więcej każde odwołanie jądra Linuxa do strony pamięci wirtualnej umieszczonej w fizycznej pamięci RAM powoduje błąd systemu; od strony zwykłego Linuxa funkcjonalność mbuff dostępna jest poprzez urządzenie /dev/mbuff Programowanie Systemów Wbudowanych 76/91
Windows Embedded W Windows każde okno ma swoją procedurę sterującą: LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { } gdzie: hwnd jest uchwytem identyfikatorem okna, message jest komunikatem kierowanym do okna, wparam jest parametrem krótkim komunikatu, lparam jest parametrem długim komunikatu, Programowanie Systemów Wbudowanych 77/91
Windows Embedded Komunikat jest częścią kodu do wykonania dla procedury okna Typy komunikatów: komunikaty systemowe (generowane przez system w reakcji na zdarzenia pochodzące od urządzeń); komunikaty aplikacji (zdefiniowane na etapie projektowania aplikacji i wysyłane od okna do okna) Programowanie Systemów Wbudowanych 78/91
Windows Embedded Komunikaty systemowe z predefiniowanym przedrostkiem: WM_ - duża grupa komunikatów ogólnych, CCM_ - komunikaty uogólnione różnych kontrolek, EM_, EN_ - komunikaty pola edycji, CDM_ - komunikaty dialogów (np otwarcia pliku), Programowanie Systemów Wbudowanych 79/91
Windows Embedded Komunikaty ogólne: komunikaty okien powiadomienia o zdarzeniach okien powiadomienia o zdarzeniach klawiatury komunikaty klawiatury powiadomienia o skrótach klawiaturowych komunikaty skrótów klawiaturowych powiadomienia o zdarzeniach myszy Programowanie Systemów Wbudowanych 80/91
Windows Embedded Przekazywanie komunikatów: komunikaty niekolejkowane komunikaty kolejkowane Programowanie Systemów Wbudowanych 81/91
Windows Embedded Komunikaty niekolejkowane: Natychmiastowa reakcja okna, np: WM_ACTIVATE WM_SETFOCUS WM_SETCURSOR Wysyłane przez funkcje: SendMessage ( ); SendMessageTimeout ( ); SendNotifyMessage ( ); BroadcastSystemMessage ( ) Programowanie Systemów Wbudowanych 82/91
Windows Embedded Funkcja SendMessage LRESULT WINAPI SendMessage( in HWND hwnd, in UINT Msg, in WPARAM wparam, in LPARAM lparam ); Wysyła komunikat do okna i czeka na jego obsłużenie Nie powraca, dopóki okno docelowe nie zareaguje na komunikat Programowanie Systemów Wbudowanych 83/91
Windows Embedded Funkcja SendMessageTimeout LRESULT WINAPI SendMessageTimeout( in HWND hwnd, in UINT Msg, in WPARAM wparam, in LPARAM lparam, in UINT fuflags, in UINT utimeout, out_opt PDWORD_PTR lpdwresult ); Wysyła komunikat do okna i czeka na jego obsłużenie, ale tylko określony czas Umożliwia przesłanie komunikatu do wszystkich okien najwyższego poziomu (głównych okien programów) Programowanie Systemów Wbudowanych 84/91
Windows Embedded Funkcja SendNotifyMessage LRESULT WINAPI SendNotifyMessage( in HWND hwnd, in UINT Msg, in WPARAM wparam, in LPARAM lparam ); Wysyła komunikat do okna i: jeśli okno należy do tego samego wątku, to czeka na jego obsłużenie; jeśli nie, to przekazuje komunikat do procedury okna i wraca od razu Programowanie Systemów Wbudowanych 85/91
Windows Embedded Funkcja BroadcastSystemMessage long WINAPI BroadcastSystemMessage( in DWORD dwflags, inout_opt LPDWORD lpdwrecipients, in UINT uimessage, in WPARAM wparam, in LPARAM lparam ); Wysyła komunikat do określonych odbiorców: aplikacji instalowanych sterowników urządzeń systemowych sterowników urządzeń sterowników sieciowych Programowanie Systemów Wbudowanych 86/91
Windows Embedded Komunikaty kolejkowane komunikaty od klawiatury komunikaty od myszy inne, które nie muszą być obsłużone natychmiast umieszczane w kolejce komunikatów Programowanie Systemów Wbudowanych 87/91
Windows Embedded Kolejki komunikatów Pojedyncza kolejka systemowa Kolejki własne wątków GUI Inicjalnie każdy wątek jest tworzony bez kolejki komunikatów Dla wątku, który wywołuje funkcje GUI, przy pierwszym wywołaniu tworzona jest kolejka komunikatów Funkcje nie należące do GUI nie tworzą kolejki komunikatów Programowanie Systemów Wbudowanych 88/91
Komunikacja Windows Embedded Umieszczanie komunikatów w kolejce: Funkcje: PostMessage PostThreadMessage Komunikaty są umieszczane na końcu kolejki Komunikaty opuszczają kolejkę i są przekazywane do procedury okna w kolejności ich umieszczania w kolejce z wyjątkiem: WM_PAINT WM_TIMER WM_QUIT które są zatrzymywane w kolejce do czasu, aż nie ma w niej innych komunikatów i wtedy są przekazywane do procedury okna Programowanie Systemów Wbudowanych 89/91
Windows Embedded Funkcja PostMessage: Wysyła komunikat do kolejki wątku skojarzonego z oknem i powraca natychmiast Funkcja PostThreadMessage: Wysyła komunikat do kolejki określonego wątku i powraca natychmiast Programowanie Systemów Wbudowanych 90/91
Windows Embedded Pętla obsługi komunikatów Aplikacja pobiera w pętli komunikaty z kolejki, tłumaczy je i kieruje do właściwej procedury okna MSG msg; BOOL bret; while((bret = GetMessage( &msg, NULL, 0, 0 ))!= 0){ } if (bret == -1){ } // handle the error and possibly exit else{ } TranslateMessage(&msg); DispatchMessage(&msg); Programowanie Systemów Wbudowanych 91/91