Mariusz Rudnicki mariuszrudnicki@etipggdapl PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ5
Komunikacja IPC Omawiane zagadnienia Czym jest komunikacja międzyprocesowa? W jakim celu stosuje się komunikację międzyprocesową? Metody IPC w RT OS: QNX Neutrino, RT Linux, Windows CE Niebezpieczeństwa związane z mechanizmami komunikacji IPC 2/89
Komunikacja 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 3/89
Komunikacja IPC Komunikacja IPC ang Interprocess Communication zapewnia mechanizmy wymiany danych pomiędzy procesami Procesy mogą wymieniać: dane; kontrolę; powiadomienie o wystąpieniu zdarzenia 4/89
QNX Neutrino wspiera różnorodne mechanizmy komunikacji IPC: rodzima komunikacja QNX Neutrino (API unikalne dla QNX): komunikaty QNX Neutrino; pulsy QNX Neutrino; 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) 5/89
Komunikaty QNX Neutrino: oparta na modelu klient serwer; komunikacja dwukierunkowa Proces klienta Proces serwera Wątek Wątek 1 MsgSend() MsgReceive() 2 process msg 1 klient wysyła zapytanie/dane do serwera MsgReply() 3 2 serwer odbiera i przetwarza 3 server odpowiada klientowi, klient odbiera odpowiedź i kontynuuje 6/89
Komunikaty QNX Neutrino: architektura zdalnego wywoływania kernel calls; w pełni synchroniczna: w czasie bezczynności, serwer jest zablokowany w oczekiwaniu na komunikat klient jest zablokowany do czasu odpowiedzi serwera 7/89
Komunikaty QNX Neutrino: CZAS 1 Proces klienta Wątek MsgSend()entry Proces serwera Wątek MsgReceive() 2 process msg 3 5 MsgSend()exit MsgReply() 4 Zablokowany 8/89
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() MsgReply() Stan READY RECEIVE Blocked READY 9/89
PRZYPADEK 2: Send przed Receive zajęty serwer Klient Serwer Stan READY SEND Blocked REPLY Blocked READY Wątek MsgSend() MsgReceive() kopiowanie danych wysłanych CZAS Wątek MsgReply() kopiowanie danych zwrotnych Stan READY Blocked 10/89
Komunikaty QNX Neutrino: przyjrzyjmy się wątkowi klienta: 11/89
Komunikaty QNX Neutrino: przyjrzyjmy się stanom wątku serwera: 12/89
Komunikaty QNX Neutrino: 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 13/89
Komunikaty QNX Neutrino: 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 połączenie połączenie kanał klient A serwer A połącznie połączenie kanał klient B serwer B 14/89
Komunikaty QNX Neutrino: 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 15/89
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 16/89
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 17/89
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 18/89
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*(); 19/89
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 20/89
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 21/89
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 22/89
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) ); 23/89
Przykład klienta: #include <sys/neutrinoh> #include <sys/netmgrh> int main(void) { int coid; mymsg_t outgoing_msg; //Identyfikator połączenia 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) ); 24/89
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,) 25/89
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 26/89
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 27/89
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 28/89
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: } } 29/89
Rodzima komunikacja QNX Neutrino jest z natury synchroniczna: wysłanie MsgSend*() przez klienta powoduje jego zablokowanie; serwer musi wywołać MsgReply*() w celu odblokowania klienta Co jeżeli nie chcemy aby klient był zablokowany? 30/89
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 31/89
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 32/89
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 33/89
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 34/89
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 } } 35/89
Po odebraniu, struktura pulsu wygląda następująco: struct _pulse { signed char union sigval int }; code; value; scoid; 8-bit kod Pole value jest unią: union sigval { int sival_int; void* sival_ptr; }; 36/89
Serwer będzie chciał określić powód wysłania tego 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 MY_PULSE_CODE1: // wykonaj odpowiednie czynności break; case MY_PULSE_CODE2: // wykonaj odpowiednie czynności break; 37/89
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 jest w tym, skąd wziąć pozostałe parametry nd, pid i chid? 38/89
W jaki sposób klient odnajduje serwer? w naszych ćwiczeniach serwer drukuje pid/chid i klient korzysta z nich jako argumenty linii poleceń to nie jest dobre rozwiązanie 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 39/89
Jeżeli serwer jest managerem zasobów: manager zasobów 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, ); // odbiór danych LUB MsgSend( fd, ); // wysłanie/odbiór danych 40/89
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 ); 41/89
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 ); 42/89
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ć 43/89
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; 44/89
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 45/89
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łane serwer pole code będzie miało wartość _PULSE_CODE_DISCONNECT 46/89
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 47/89
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; } 48/89
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 49/89
Ilustracja flag odblokowania: Wątek klienta Sygnał, timeout, or cancel Pulse Wątek serwera ChannelCreate( _NTO_CHF_UNBLOCK) 3a 3b MsgSend() 1 MsgReceive() 6 5 4 2 MsgReply() 50/89
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 51/89
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() Zablokow any na SEND Zablokowany na REPLY CZAS MsgReceive() MsgReply() READY 52/89
Jeżeli wątki wywołują MsgSend() w kolejności: id wątku priorytet 1 2 3 4 10 15 10 20 Komunikaty będą odebrane w kolejności:?,?,?,? 53/89
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ć 54/89
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 2 1 5 3 4 6 Komunikat od wątku 1 dociera do serwera przed pulsem, który był wysłany wcześniej 55/89
Priorytety Komunikacja IPC QNX Neutrino Problem inwersji priorytetów: wysokie nikie 255 22 13 10 0 t2 t1 Uwaga: to nie ma miejsca w rzeczywistości server MsgReceive() 56/89
Priorytety Komunikacja IPC QNX Neutrino Rozwiązanie: wysoki niski 255 22 13 11 10 0 t1 t3 t2 4 server 5 server jeżeli wielu klientów wysyła SEND, serwer odziedziczy priorytet po kliencie o najwyższym priorytecie 57/89
Projektowanie systemu komunikacji Unikanie zakleszczeń Co się stanie jeżeli serwer potrzebuje wysłać SEND 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 58/89
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 SEND 2 3 REPLY 59/89
Projektowanie systemu komunikacji Unikanie zakleszczeń: Klient może mieć kanał: SEND klient kanał kanał serwer SEND 60/89
Projektowanie systemu komunikacji Unikanie zakleszczeń: Jeżeli mamy tylko dwa kanały: SEND Serwer SEND Klient rozpoznanie potencjalnego zakleszczenia jest proste, ale w złożonym systemie: P4 P1 P2 P3 jest trudniejsze P5 P6 P7 61/89
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 62/89
Nie blokujące pulsy wykorzystujemy do wysyłania powiadomień w górę: P1 P2 P4 P3 P5 P6 P7 blokujący send P8 nie blokujący puls 63/89
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() 64/89
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() 65/89
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 66/89
Komunikacja IPC 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? 67/89
Komunikacja IPC RT Linux Jądro Linuxa może być wydziedziczone przez zadania krytyczne, stąd nie mogą one wywoływać systemowych funkcji jądra Linuxa Rozwiązanie problemu? 68/89
Komunikacja IPC RT Linux RT FIFO: bufory o stałym rozmiarze; od strony procesów RT widziane są podobnie jak nazwane pipeline y; operacje wykonywane na RT-FIFO są nieblokujące z punktu widzenia zadań krytycznych; dostępne są w formie ładowalnego modułu RT Core; 69/89
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); 70/89
Komunikacja IPC 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 Linuxa: RT-FIFO zależy od wersji RT 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 mogą być tworzone z poziomu wątków Linuxa 71/89
Komunikacja IPC 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 72/89
Komunikacja IPC 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; 73/89
Komunikacja IPC 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 74/89
Komunikacja 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, 75/89
Komunikacja 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) 76/89
Komunikacja 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), 77/89
Komunikacja 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 78/89
Komunikacja Windows Embedded Przekazywanie komunikatów: komunikaty niekolejkowane komunikaty kolejkowane 79/89
Komunikacja Windows Embedded Komunikaty niekolejkowane: Natychmiastowa reakcja okna, np: WM_ACTIVATE WM_SETFOCUS WM_SETCURSOR Wysyłane przez funkcje: SendMessage SendMessageTimeout SendNotifyMessage BroadcastSystemMessage 80/89
Komunikacja 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 81/89
Komunikacja 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) 82/89
Komunikacja 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 83/89
Komunikacja 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 84/89
Komunikacja 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 85/89
Komunikacja 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 86/89
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 87/89
Komunikacja 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 88/89
Komunikacja 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); 89/89