PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO. Mariusz RUDNICKI: pok. 753 tel.:

Podobne dokumenty
Mariusz Rudnicki PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ.4

Mariusz Rudnicki PROGRAMOWANIE WSPÓŁBIEŻNE I SYSTEMY CZASU RZECZYWISTEGO CZ.4

Mariusz Rudnicki PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ.5

Mariusz Rudnicki PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ.2

w odróżnieniu od procesów współdzielą przestrzeń adresową mogą komunikować się za pomocą zmiennych globalnych

PROGRAMOWANIE SYSTEMÓW WBUDOWANYCH INTER-PROCESS COMMUNICATION

Prezentacja systemu RTLinux

Mariusz Rudnicki PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ.3

Programowanie równoległe i rozproszone. Monitory i zmienne warunku. Krzysztof Banaś Programowanie równoległe i rozproszone 1

5. Model komunikujących się procesów, komunikaty

Mariusz Rudnicki PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ.1

Mariusz Rudnicki PROGRAMOWANIE WSPÓŁBIEŻNE I SYSTEMY CZASU RZECZYWISTEGO CZ.3

4. Procesy pojęcia podstawowe

PROGRAMOWANIE SYSTEMÓW WBUDOWANYCH

Temat zajęć: Tworzenie i obsługa wątków.

Mechanizmy pracy równoległej. Jarosław Kuchta

Mariusz Rudnicki PROGRAMOWANIE WSPÓŁBIEŻNE I SYSTEMY CZASU RZECZYWISTEGO CZ.2

SYSTEMY OPERACYJNE WYKLAD 6 - wątki

QNX Neutrino (v 6.3)

1. Uruchom poniższy program tworzący pojedynczy wątek:

Systemy operacyjne III

Od uczestników szkolenia wymagana jest umiejętność programowania w języku C oraz podstawowa znajomość obsługi systemu Linux.

4. Procesy pojęcia podstawowe

Jądro systemu operacyjnego

Systemy operacyjne III

7. Szeregowanie procesów w systemie QNX6 Neutrino

Komunikacja za pomocą potoków. Tomasz Borzyszkowski

Działanie systemu operacyjnego

Wykład 3: Implementacja programów wbudowanych

Projektowanie oprogramowania systemów PROCESY I ZARZĄDZANIE PROCESAMI

Działanie systemu operacyjnego

Programowanie równoległe i rozproszone. W1. Wielowątkowość. Krzysztof Banaś Programowanie równoległe i rozproszone 1

Struktury systemów operacyjnych

4. Procesy pojęcia podstawowe

SYSTEMY OPERACYJNE: STRUKTURY I FUNKCJE (opracowano na podstawie skryptu PP: Królikowski Z., Sajkowski M. 1992: Użytkowanie systemu operacyjnego UNIX)

Wykład 3. Procesy i wątki. Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Wielozadaniowość w systemie Microsoft Windows

Aplikacja Sieciowa wątki po stronie klienta

Sieciowe Systemy Operacyjne

SYSTEMY CZASU RZECZYWISTEGO - VxWorks

Działanie systemu operacyjnego

Procesy, wątki i zasoby

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO

1. Etapy rozwoju systemów komputerowych

Systemy Czasu Rzeczywistego (SCR)

IPC: Kolejki komunikatów

Kolejki FIFO (łącza nazwane)

Działanie systemu operacyjnego

SYSTEMY OPERACYJNE WYKLAD 6 - procesy

Systemy operacyjne Procesy i wątki

Zarządzanie procesami i wątkami

System operacyjny MACH

Jadro monolityczne vs. mikrojadro. Mikrojadro. Olga Kowalczuk. 9 grudnia 2008

dr inż. Jarosław Forenc

Podstawy informatyki. System operacyjny. dr inż. Adam Klimowicz

Procesy pojęcia podstawowe. 1.1 Jak kod źródłowy przekształca się w proces

IdyllaOS. Prosty, alternatywny system operacyjny. Autor: Grzegorz Gliński. Kontakt:

Wielowątkowość mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2011

Stan procesu. gotowy - czeka na przydział procesora, zakończony - zakończył działanie.

Od uczestników szkolenia wymagana jest umiejętność programowania w języku C oraz podstawowa znajomość obsługi systemu Windows.

projektowanie systemu

Procesy i wątki. Blok kontrolny procesu. Proces. Proces - elementy. Stan procesu. Blok kontrolny procesu

1.1 Definicja procesu

Wątek - definicja. Wykorzystanie kilku rdzeni procesora jednocześnie Zrównoleglenie obliczeń Jednoczesna obsługa ekranu i procesu obliczeniowego

1. Procesy i współbieżność

1. Tworzenie nowego projektu.

Synchronizacja procesów i wątków

Dodatek B. Zasady komunikacji z otoczeniem w typowych systemach komputerowych

Systemy operacyjne III

Wprowadzenie do systemu Minix

Tryby komunikacji między procesami w standardzie Message Passing Interface. Piotr Stasiak Krzysztof Materla

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

Procesy, zasoby i wątki

Technika mikroprocesorowa. Systemy operacyjne czasu rzeczywistego

Procesy, zasoby i wątki

Procesy, zasoby i wątki

Problemy współbieżności

Poniższe funkcje opisane są w 2 i 3 części pomocy systemowej.

Wieloprogramowanie. Systemy operacyjne / Procesy i wątki str.4/32. Proces w systemie operacyjnym. Tworzenie i kończenie procesów

RPC. Zdalne wywoływanie procedur (ang. Remote Procedure Calls )

projekt akademicki w Institute for Mining and Technology of New Mexico. Autor Victor Yodaiken FSMLabs komercyjna odmiana RTLinuxPro

Przetwarzanie wielowątkowe przetwarzanie współbieżne. Krzysztof Banaś Obliczenia równoległe 1

System komputerowy. System komputerowy

Programowanie równoległe i rozproszone. Praca zbiorowa pod redakcją Andrzeja Karbowskiego i Ewy Niewiadomskiej-Szynkiewicz

7. Szeregowanie procesów w systemie QNX6 Neutrino

Implementacje zgodne z tym standardem są nazywane wątkami POSIX lub Pthreads.

Budowa systemów komputerowych

Struktura i funkcjonowanie komputera pamięć komputerowa, hierarchia pamięci pamięć podręczna. System operacyjny. Zarządzanie procesami

Szeregowanie zadań w Linux Kernel 2.6. Daniel Górski Przemysław Jakubowski

Współbieżność w środowisku Java

LEKCJA TEMAT: Zasada działania komputera.

Przeplot. Synchronizacja procesów. Cel i metody synchronizacji procesów. Wątki współbieżne

Łącza nienazwane(potoki) Łącza nienazwane mogą być używane tylko pomiędzy procesami ze sobą powiązanymi.

Procesy i wątki. Krzysztof Banaś Obliczenia równoległe 1

Klient-Serwer Komunikacja przy pomocy gniazd

Wykład 6. Planowanie (szeregowanie) procesów (ang. process scheduling) Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Wielowątkowy serwer TCP

Mechanizmy z grupy IPC

Informatyka, systemy, sieci komputerowe

Jedrzej Ułasiewicz Komputerowe systemy sterowania 1

Transkrypt:

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO Mariusz RUDNICKI: mariusz.rudnicki@eti.pg.gda.pl pok. 753 tel.: 347 26 39

Zagadnienia Pojęcia podstawowe SCR Architektura QNX Neutrino Procesy, wątki i synchronizacja Komunikacja międzyprocesowa Porównanie metod IPC Wprowadzenie do programowania sprzętu Czas 2

Wprowadzenie QNX Neutrino RTOS: System czasu rzeczywistego, który charakteryzują: duża stabilność i elastyczność; twarde wymagania czasowe; zmodularyzowane, rozbudowane wielozadaniowe aplikacje; stosunkowo niewielkie wymagania sprzętowe; kompatybilność z szeroką gamą sprzętu. 3

Wprowadzenie Zakres omawianych zagadnień: Architektura QNX Neutrino RTOS i korzyści z niej płynące. Technologia pracy współbieżnej. System jako zbiór wielu współpracujących ze sobą procesów. Komunikacja międzyprocesowa (IPC). Konfiguracja platformy docelowej. 4

Architektura tradycyjna W tradycyjnej strukturze systemu czasu rzeczywistego: Interfejs użytkownika Szeregowanie Alokator pamięci Stos sieciowy Aplikacja Aplikacja Sterownik RS Sterownik dysku System plików wszystkie moduły współdzielą tą samą przestrzeń pamięci, tworząc jeden duży program. 5

Architektura monolityczna W monolitycznym systemie operacyjnym: Aplikacja Interfejs użytkownika Aplikacja Szeregowanie Manager pamięci Stos sieciowy Sterownik RS Sterownik dysku System plików Monolityczne jądro Jądro zawiera oprócz własnej funkcjonalności, również wszystkie sterowniki, co powoduje złożoność procesu wytwarzania i debugowania oprogramowania. Aplikacje są procesami umieszczonymi w chronionej pamięci powodując, że jądro jest chronione przed wpływem aplikacji użytkownika i aplikacje są chronione na wzajem. 6

Architektura mikrojądra W systemie operacyjnym QNX Neutrino: Microkernel Process Manager devb-eide ksh Application process Application process Network Stack User Interface Software Bus phfontfa devc-ser8250 System operacyjny zawiera mikrojądro i zbiór współpracujących procesów. Procesy są odseparowane od jądra, więc w przypadku awarii oprogramowania, nie ma ona wpływu na jądro. 7

Architektura - IPC Procesy komunikują się z innymi: Microkernel Process Manager devb-eide ksh Application process Application process Network Stack User Interface Software Bus phfontfa devc-ser8250 Procesy systemowe i użytkownika komunikują się ze sobą wykorzystując IPC. Oprogramowanie systemowe i aplikacyjne tworzą jeden spójny system. Istnieje duża różnorodność typów IPC. 8

Architektura Przykłady procesów: Sterowniki dysków; Stos sieciowy; Sterowniki znakowe; Elementy interfejsu graficznego; Sterowniki magistral; Demony systemowe. 9

Zalety i wady: korzyści: Architektura elastyczność i niezawodność; łatwość konfiguracji i rekonfiguracji; łatwość debugowania; łatwość wytwarzania oprogramowania; skalowalność systemu, koszty: więcej przełączeń kontekstu; więcej kopiowanych danych. 10

Architektura - Procesy Proces: program załadowany do pamięci; identyfikowany przez id procesu, zwykle nazywany jako pid; 2 wątki wspólne zasoby procesu: pamięć, włączając kod i dane otwarte pliki deskryptory plików identyfikatory kanał id użytkownika, id grupy timery. mutex pamięć Zasoby należące do jednego procesu są chronione przed innymi procesami. 11

Wątek: Architektura - Wątki wątek jest pojedynczym strumieniem wykonania wątek posiada pewne atrybuty: priorytet, algorytm kolejkowania, zestaw rejestrów, maska CPU dla SMP, maska sygnałów, i inne. 3 threads 12

Architektura - Procesy i wątki Wątki są uruchamiane w procesach: proces musi posiadać przynajmniej jeden wątek; wątki w procesie współdzielą wszystkie zasoby procesu. 2 wątki mutexy pamięć deskryptory plików kanał 1 wątek deskryptory plików połączenie pamięć 13

Architektura - Procesy i wątki cd. Procesy i wątki: procesy są komponentami składowymi systemu widoczne dla każdego; komunikują się z każdym, wątki są szczegółową implementacją procesu: ukryte wewnątrz procesu. 14

Funkcje jądra: Architektura - Jądro jest spoiwem, łączącym cały system; programy mają do czynienia z jądrem za pomocą specjalnych procedur bibliotecznych, nazywanych wywołaniami jądra ang. kernel calls, które wykonują kod umieszczony w jądrze; większość podsystemów, włączając aplikacje użytkownika, komunikują się nawzajem używając mechanizmu przekazywania wiadomości, dostarczanego przez jądro za pomocą wywołań jądra. 15

Architektura - Jądro Jądro jest rdzeniem systemu: Wywołania jądra: często będziemy korzystać z wywołań jądra, np. w trakcie komunikacji IPC lub podczas obsługi przerwań; to znaczy, że będzie wykonywany kod w jądrze podczas wywołania; co się stanie jeżeli wystąpi krytyczne zdarzenie? Sieć Stos sieciowy Przerwania sprzętowe jądro Proces A Proces B 16

Priorytet Jądro - wywłaszczanie Wywołania jądra są wywłaszczające Driver s ISR ISR returns to kernel with priority 27 pulse so kernel makes Driver s thread READY and switches to it Hardware interrupt handlers 27.. Driver s Pulse & Driver s thread 10 Receiver 9 Sender.. MsgSend() 0 idle Wewnętrzna wstępna część kodu jądra Wątki Driver blocks MsgReceive() 17

Operacje jądra: Jądro - wywłaszczanie wywołanie jądra powrót Wejście długa operacja jądra która może być przerwana i kontynuowana np. przekazywanie komunikatu Zablokowany krótka operacja jeżeli wywłaszczona, następuje restart Wyjście zapamiętanie stanu przywrócenie stanu Przerwania wyłączone Pełne wywłaszczania Przerwania włączone Brak wywłaszczania przerwania włączone Pełne wywłaszczania Przerwania włączone Przerwania wyłączone 18

Jądro - wywłaszczanie Jakie są zalety i wady wywłaszczania? Więcej ustępstw: korzyści: zmniejszone opóźnienie; szybsza odpowiedź na nowe zdarzenia; krótsze opóźnienie przerwania, koszty: wydajność wymaga więcej czasu by wznowić przerwane wywołanie jądra; zabiera więcej czasu i zasobów na zachowanie aktualnego stanu i wznowienie wywłaszczonego procesu przekazywania wiadomości. 19

Jądro - usługi Usługi jądra: Network media Synchronizacja Stos sieciowy Szeregowanie Wątki Timery IPC Przekierowanie przerwań Proces B Proces A Przerwania sprzętowe 20

Jądro - IPC Formy IPC dostarczane przez jądro: Komunikaty - wymiana informacji między procesami. Pulsy - dostarczenie zawiadomienia do procesu. Sygnały - przerwanie procesu i wykonanie innej części kodu lub zakończenie. 21

Jądro - IPC Komunikaty QNX Neutrino: SEND Klient Serwer REPLY 22

Jądro - IPC Pulsy QNX Neutrino - używane do powiadamiania o zdarzeniu: coś się zdarzyło. PULSE Proces 1 Proces 2 23

Jądro - IPC Sygnały POSIX - przerwanie innego procesu. SYGNAŁ Proces 1 Proces 2 24

Jądro - wątki Funkcje związane z wątkami: utworzenie / usunięcie wątku; oczekiwanie na zakończenie wątku; zmiana atrybutów wątku. 25

Jądro - synchronizacja Metody synchronizacji wątków: mutex condvar semaphore rwlock join spinlock sleepon barrier wzajemne wykluczenie wątków oczekiwanie na zmienną oczekiwanie na licznik synchronizacja wątków piszących i czytających synchronizacja do zakończenia wątku oczekiwanie na alokację pamięci podobnie do condvars, z dynamiczną alokacją oczekiwanie na określoną liczbę wątków 26

Jądro - czas Koncepcja czasu w QNX Neutrino : Przerwanie zegarowe. P r o g r a m o b sł u gi z e g a r a Time of Day Timers Apps Apps 27

Jądro - przerwania Obsługa przerwania: DriverA Źródło przerwania PIC handler() { return event; } jądro main() { } Wszystkie przerwania sprzętowe są wektorowane dla jądra. Możliwości procesu w zakresie obsługi: rejestracja funkcji, wywoływanej przez jądro w momencie wystąpienia przerwania; zgłoszenie powiadomienia o wystąpieniu przerwania. Driver B main() { } 28

Jądro - Podsumowanie Jądro - można sobie wyobrazić jako bibliotekę, inaczej mówiąc, zbiór funkcji, w których brak pętli przetwarzających, brak pętli while(1), uruchamianych w sytuacjach: wywołania jądra; wystąpienia przerwania; wyjątek procesora np. niedozwolony rozkaz, błędny adres. 29

Manager Procesów Zadania Managera Procesów: grupowanie wątków wewnątrz procesów; ochrona pamięci, zarządzanie przestrzenią adresową włączając pamięć wspólną dla IPC; zarządzanie przestrzenią nazw; powołanie i zakończenie procesu: spawn / exec / fork ładowanie programów wykonawczych ELF (Executable and Linkable Format opracowany przez Unix System Laboratories); bezczynny wątek, zużywający wolny czas procesora. 30

Manager Procesów Komunikacja z Managerem Procesów: Microkernel procnto Process Manager devb-eide ksh Application process Application process Network Stack User Interface Software Bus phfontfa devc-ser8250 Procesy komunikują się z managerem procesów za pomocą IPC 31

Menedżer Procesów Zarządzanie pamięcią Używamy wirtualnego modelu adresowania: każdy proces wykonuje się we własnej chronionej wirtualnej przestrzeni adresowej; wskaźniki z którymi mamy do czynienia zawierają adres wirtualny, nie fizyczny fizycznie dzielą tą samą przestrzeń adresową. Wątki managera procesów Proces systemowy (procnto) Proces użytkownika #1 Proces użytkownika #2 Proces użytkownika #3 Mikrojądro Adresy wirtualne x86: 3.5G 4G 0 3.5G 0 3.5G 0 3.5G PPC: 0 1G 1G 4G 1G 4G 1G 4G MIPS: 2G 4G 0 2G 0 2G 0 2G SH4: 2G 4G 0 2G 0 2G 0 2G ARM: 2G 4G Programowanie 0 32M Systemów Czasu 0 32M 0 32M 32

Zarządzanie pamięcią Pamięć dzielona Mapowanie wirtualnych adresów do adresów fizycznych: Wirtualna przestrzeń adresowa procesu A np. 0x4000A000 (x86) Dane, Stos, Kod Przestrzeń mapowana Przestrzeń adresowa pamięci fizycznej Współdzielona RAM RAM... np. 0xFC80000 Dane, Stos, Kod Przestrzeń mapowana Wirtualna przestrzeń adresowa procesu B np. 0x40006000 (x86) Wskaźniki których używamy zawierają adres wirtualny, nie fizyczny 33

Manager procesów zarządzanie przestrzenią nazw W momencie uruchamiania QNX Neutrino, cała przestrzeń nazw należy do procnto: / /proc Manager procesów Wszystkie zapytania o pliki i urządzenia są przechwytywane przez procnto. 34

Szeregowanie Algorytm szeregowania (ang. scheduler - planista) to algorytm rozwiązujący jedno z najważniejszych zagadnień programowania systemów czasu rzeczywistego - jak rozdzielić czas procesora i dostęp do innych zasobów tj. pamięć, zasoby sprzętowe pomiędzy zadania, które w praktyce zwykle o te zasoby konkurują. 35

Szeregowanie Implementacja jest umieszczana zwykle w jądrze systemu, ale też może być jednym ze zwykłych zadań, spełniającym usługę dla jądra. Algorytm szeregowania musi rozpatrywać wiele czynników: priorytet zadania; stan zadania (gotowość do wykonania); przeciwdziałać zagłodzeniu procesu. 36

Szeregowanie Szeregowanie a wywłaszczanie Wymuszone oddawania kontroli - wielozadaniowość z wywłaszczaniem Dobrowolne oddawanie kontroli - wielozadaniowość oparta na współpracy (rzadziej stosowana źle zaprojektowany proces może zdestabilizować system) 37

Szeregowanie QNX Neutrino Wątki posiadają dwa podstawowe stany: zablokowany: oczekuje na zdarzenie; istnieje wiele stanów blokowania w zależności od tego na co proces oczekuje np.: blokada REPLY proces oczekuje na odpowiedź IPC; blokada MUTEX proces oczekuje na mutex; blokada RECEIVE proces oczekuje na komunikat, gotowy: zdolny do użycia CPU; dwa podstawowe stany gotowości: RUNNING - proces aktualnie używa CPU; READY proces oczekuje podczas gdy inny proces jest uruchomiony. 38

Szeregowanie Priorytety QNX Neutrino Wszystkie wątki posiadają priorytety: priorytety są w zakresie od 0 (niski) do 255 (wysoki) mechanizm priorytetów tylko dla wątków w stanie gotowy jądro zawsze wybiera wątek o najwyższym priorytecie i będący w stanie READY (pełne wywłaszczanie) stan wątku zmienia się na RUNNING zablokowane wątki nawet nie są rozważane większość wątków spędza najwięcej swojego czasu w stanie zablokowanym 39

Priorytet Szeregowanie Priorytety QNX Neutrino Priorytety: Priorytety wysoki (program obsługi przerwania sprzętowego) niski 255.. 21.. 11 10 9.. 1 0 devb-eide (jeden z wielu wątków devb-eide) ksh procnto (wątek jałowy) 40

Szeregowanie - Algorytmy Używane najczęściej: FIFO; ROUND-ROBIN; SPORADYCZNY; Mniej powszechne: FCFS (first come, first serve) podobny do FIFO, bardzo słaba interaktywność systemu pojedynczy długi proces blokuje cały system, brak priorytetów nie ma możliwości wywłaszczenia; SJF (shortest job first) pierwsze najkrótsze zadanie, wada problem głodzenia długich procesów; 41

Szeregowanie - Algorytmy Deterministyczne stosowane w automatyce zadanie musi wykonać się przed upływem określonego czasu: RMS - Rate Monotonic Scheduling Algorithm uwzględnia częstość wykonywania zadania; wybierane jest zadanie gotowe do wykonania i o najwyższym priorytecie; jeżeli jest ich kilka to wybierane jest pierwsze znalezione (RTLinux); brak podziału czasowego zadanie zostaje wywłaszczone przez proces o wyższym priorytecie. EDF - Earliest Deadline First pobierany jest z kolejki proces o najwyższym priorytecie najbliższy do swojego deadline u. 42

Priorytet Szeregowanie QNX Neutrino Kolejka READY najwyższy 255 kolejka READY Running 10 Wątek A Wątek B Wątek C 6 Wątek D Wątek F Wątek Z Blocked najniższy 2 1 0 Wątek E 43

Priorytet Szeregowanie Round-Robin Algorytm kolejkowania: Karuzelowy najwyższy kolejka READY Priorytet A B C A B C A 4 4 millisekundowy kwant czasu Czas 4 Wątek A Round- Robin Wątek B Round- Robin Wątek C Round- Robin najniższy 44

Priorytet Szeregowanie - Sporadyczne Algorytm kolejkowania : Sporadyczny najwyższy budżet Wątek wyczerpał budżet i obniża priorytet budżet odnowiony przywrócenie priorytetu Wątek może się wykonywać lub nie okres odnowienia najniższy CZAS 45

Szeregowanie Przerwania wywłaszczające Priorytet Szeregowanie przerwań (wywłaszczające): najwyższy Interrupt Priority Przerwanie B C B Thread Priority Przerwanie A D A najniższy CZAS 46

Szeregowanie Przerwania niewywłaszczające Priorytet Szeregowanie przerwań (niewywłaszczające): najwyższy Interrupt Priority B C Thread Priority Przerwanie A Przerwanie D A najniższy CZAS 47

Technologia partycjonowania Projektant systemu: tworzy partycje dla mechanizmu priorytetowania; decyduje, które procesy/wątki zostaną umieszczone w danej partycji: proces/wątek potomny domyślnie zostaje umieszczony w partycji rodzica; określa minimalne % zużycie procesora dla każdej partycji. Cały system Partycja A: 20% Partycja systemowa: 50% Partycja B: 30% 48

Technologia partycjonowania Priorytetowanie jest Adaptacyjne : jeżeli czas CPU nie jest wykorzystywany przez partycję, może być wykorzystany przez inną; jeżeli obciążenie systemu jest < 100%: priorytetowanie działa bez partycjonowania adaptacyjnego; czas CPU jest wykorzystywany przez wątek o najwyższym priorytecie, wątki ściśle wymagające czasu rzeczywistego mogą być zaprojektowane jako critical threads : np. wątek obsługi przerwania; krytyczne wątki mogą zapożyczać czas jeżeli ich partycja wykorzystuje całkowicie swój budżet; Technologia partycjonowania jest dostępna jako TDK (Technology Development Kit). 49

Technologia SMP SMP ang. Symmetrical Multi Processor: oznacza to, że używana jest płyta zawierająca więcej niż jeden procesor nie potrzeba pisać specyficznego dla tej konfiguracji kodu źródłowego wymagane jest odpowiednie jądro: np. procnto-smp, procnto-smp-instr, procnto-600- smp w systemie SMP, wątki o różnych priorytetach lub kilka wątków FIFO o tym samym priorytecie mogą wykonywać się w tym samym czasie 50

Manager zasobów Co to jest manager zasobów? program rozszerzający system operacyjny przez: tworzenie i zarządzanie nazwą w przestrzeni nazw; dostarczenie interfejsu POSIX dla klientów (np. open(), read(), write(),...); może być skojarzony ze sprzętem (jak port szeregowy lub napęd dyskowy); lub może być po prostu encją oprogramowania (jak mqueue manager kolejek POSIX). 51

Lokalizacja managera zasobów Interakcje: fd = open("/dev/ser1", ); 1 CLIENT library 3 Process Manager 2 4 RESMGR 1 Biblioteka klienta (open()) wysyła komunikat zapytanie. 2 Manager Procesów odpowiada, kto jest odpowiedzialny. 3 Biblioteka klienta ustanawia połączenie do odpowiedniego managera zasobów i wysyła odpowiedni komunikat otwarcia. 4 Manager zasobów odpowiada ze statusem (pass/fail). 52

Manager zasobów Dalsza komunikacja jest przekazywaniem komunikatów bezpośrednio do managera zasobów: SEND Client: write() Resource Manager REPLY 53

Manager zasobów Inne uwagi: taka konfiguracja pozwala na wiele rozwiązań: debugowanie sterowników za pomocą wysokopoziomowego debugera; dystrybucja sterowników poprzez sieć QNX; dostęp do sterowników za pomocą sieciowych systemów plików NFS lub CIFS; wprowadza elastyczność dostępu do usług systemu, dostępne są biblioteki dostarczające wiele użytecznego kodu w celu minimalizacji niezbędnej pracy programisty. 54

Architektura bibliotek Wiele standardowych funkcji bibliotecznych jest zbudowanych na bazie wywołań jądra: zwykle jest to cienka warstwa, która zmienia format argumentów, np.: funkcja POSIX timer_settime() wywołuje funkcję jądra Timer_Settime() następuje zmiana reprezentacji z formatu POSIX sekundy i nanosekundy na 64-bitową reprezentację jądra, zalecane jest używanie wywołań standardowych kod jest bardziej przenoszalny; wykorzystywane są wywołania, bardziej czytelne przez innych projektantów. 55

Architektura bibliotek Wiele funkcji dostarcza dodatkową wyższą warstwę: np. funkcje stdio dostarczają buforowania dla funkcji niższej warstwy jak read() i write(): jeżeli mamy potrzebę przeczytania jednego bajta jednorazowo, fread() byłaby właściwym wyborem, następnie wykonujemy wysłanie komunikatu co 1000 wywołań tej funkcji; jeżeli potrzebny jest odczyt jednocześnie 64k, chociaż, lepiej jest podzielić na 64 1K wywołań read()s, lepiej użyć funkcję read(). 56

Obiekty współdzielone Obiekty współdzielone: biblioteki ładowane i linkowane w czasie uruchamiania; jedna kopia wykorzystywana (współdzielona) przez wszystkie programy wykorzystujące bibliotekę; czasami nazywane DLL. Proces 1 Proces 2 Proces 3 obiekty współdzielone/ DLL 57

Procesy - przypomnienie Co to jest proces? program załadowany do pamięci; identyfikowany przez id procesu, zwykle nazywany jako pid; posiada zasoby: pamięć, kod i dane; otwarte pliki; identyfikatory - user id, group id; timery. 2 wątki Zasoby posiadane przez jeden proces są chronione przed dostępem innych procesów. deskryptory plików kanał mutex pamięć 58

Wątki - przypomnienie Co to jest wątek? wątek jest pojedynczym strumieniem wykonania wątek posiada pewne atrybuty: Priorytet; algorytm kolejkowania; zestaw rejestrów; maska CPU dla SMP; maska sygnałów. 3 wątki 59

Procesy Przykład Linia montażowa wiertarka (duże wiertło) wiertarka (małe wiertło) prasa taśmociąg silnik taśmociągu produkowane elementy 60

Procesy Procesy monitorują i kontrolują urządzenia: wiertarka wiertarka prasa silnik 61

Procesy nieprzezroczystość procesu Nieprzeźroczystość procesu: jeden proces nie powinien mieć informacji o wątkach innego procesu: wątki są szczegółową implementacją procesu w którym są uruchomione, dlaczego? projektowanie zorientowane obiektowo proces jest obiektem; elastyczność - w skład procesu może wchodzić jeden lub wiele wątków, wątki mogą być tworzone i usuwane dynamicznie w zależności od potrzeb. 62

Procesy - Dlaczego używać wątków? Przykłady procesów wielowątkowych: wątek o wysokim priorytecie, przeznaczony do obsługi komunikacji ze sprzętem; inne wątki do komunikacji z klientami; Jeżeli jeden lub więcej wątków jest zajętych obsługą wcześniejszych zgłoszeń, nadal istnieją wątki do obsługi nowych zgłoszeń. zajęte wątki komunikat odbiera wolny wątek 63

Procesy - Virtual Address Space Wirtualna przestrzeń adresowa procesu: Biblioteki współdzielone Każdy wątek ma swój stos. Wszystko pozostałe jest współdzielone Pamięć dzielona Stos Dane Kod programu Stos wąktu 1 Stos wątku 2 64

Wątki - synchronizacja dziedziczenie Wewnątrz procesu wątki współdzielą: Timery, Kanały, Połączenia, Dostęp do pamięci, Deskryptory plików, Funkcje obsługi sygnałów. 2 threads file descriptors channel timer memory 65

Wątki - synchronizacja - problemy Wątki dostarczają nowych rozwiązań ale także nowych problemów: Wspólne obszary pamięci: wielokrotne zapisy mogą zamazać oczekiwaną wartość; wątek czytający dane nie wie kiedy dane są stabilne lub jeszcze nie, Podobne problemy występują z innymi współdzielonymi zasobami... 66

Wątki - synchronizacja - problemy Nasz problem to: SYNCHRONIZACJA W następnej części przedstawione zostaną narzędzia rozwiązujące ten problem: mutexy, zmienne warunkowe, semafory, operacje atomowe. 67

Wątki - synchronizacja Inne narzędzia synchronizacji: rwlocks: pozwala na wielokrotne czytanie bez możliwości zapisu, lub tylko jeden zapis bez możliwości czytania jednokrotna inicjalizacja: Pierwsze wywołanie pthread_once z danym argumentem once_control powoduje wykonanie kodu bezargumentowej procedury init_routine i zmienia wartość zmiennej once_control żeby zaznaczyć, ze inicjalizacja była wykonana. Kolejne wywołania pthread_once z tym samym argumentem once_control będą pustymi wywołaniami; dane własne wątku: Zmienne przechowywane są powielane dla każdego wątku i każdy może modyfikować swoją kopię zmiennej bez wpływu na inne wątki. 68

Wątki - synchronizacja - volatile Zmienne które są współdzielone: powinny być zadeklarowane jako volatile volatile unsigned flags;... atomic_clr (&flags, A_FLAG); volatile jest słowem kluczowym ANSI C informującym kompilator żeby nie optymalizował tej zmiennej Przykład: Kompilator może optymalizować kod umieszczając wartości w rejestrze i odnosząc się do rejestru zamiast sięgania do pamięci. Tymczasem inny wątek uzyskiwałby dostęp do pamięci! 69

Synchronizacja -Wzajemne wykluczanie Wzajemne wykluczanie projektuje się tak by zsynchronizować wątki by każdy przetwarzał dane i wykonywał sekcję krytyczną w taki sposób aby nie pokrywało się to z wykonaniem sekcji krytycznej innych wątków. Do realizacji tego zagadnienia, należy do funkcji każdego wątku dodać dodatkowe instrukcje poprzedzające i następujące po sekcji krytycznej. 70

Wymagania czasowe Projektując wzajemne wykluczanie musimy uwzględnić kilka ważnych aspektów: Żaden wątek nie może wykonywać swej sekcji krytycznej nieskończenie długo, nie może się w niej zapętlić lub zakończyć w wyniku jakiegoś błędu; Ważna zasada w sekcji krytycznej wątek przebywa jak najkrócej i nie ma możliwości zakończenia się błędem. 71

Blokada W pewnych sytuacjach wątek będzie wstrzymywany w oczekiwaniu na sygnał od innego wątku; Blokada występuje wtedy gdy zbiór procesów jest wstrzymany w oczekiwaniu na zdarzenie, które może być spowodowane tylko przez jakiś inny proces z tego zbioru. 72

Zagłodzenie Jeśli sygnał synchronizujący może być odebrany tylko przez jeden wątek czekający to trzeba któryś wybrać Oznacza to, że wątek o niskim priorytecie może się zagłodzić gdyż ciągle będą wybierane wątki o wyższym priorytecie Mechanizm eliminacji zagłodzenia wątków może być realizowany np. poprzez dynamiczne podwyższanie priorytetów (Windows). 73

Mechanizmy synchronizacji w WinAPI Zdarzenia Mutexy Semafory Sekcje krytyczne Zegary oczekujące 74

Zegary oczekujące - WinAPI Zegary umożliwiają głównie regularne wywoływanie wątków. Zegar oczekujący przechodzi w stan sygnalizowany po upływie określonego czasu lub w określonych odstępach czasu z automatycznym powrotem do stanu niesygnalizowanego. Zegar oczekujący tworzymy funkcją: CreateWaitableTimer(), której argumenty to: wskaźnik na strukturę SA, zmienna BOOL mówiąca o tym czy zegar ustawiamy ręcznie (TRUE) czy automatycznie (FALSE), nazwa zegara. Funkcja zwraca uchwyt do zegara. 75

POSIX thread pthread jest to najpopularniejsza biblioteka służąca do implementacji wątków wchodząca w skład standardu POSIX Umożliwia implementację zarówno w systemach UNIX, Linux, a także w Windows. Interfejs jest zaprojektowany obiektowo pthread umożliwia: Tworzenie wątków Synchroniczne kończenie wątków Lokalne dane wątku Obsługę mutexów Funkcje oczekujące Ustalanie priorytetów Ograniczenia czasowe na zajście niektórych zdarzeń 76

POSIX thread Tworzenie wątku: int pthread_create( pthread_t *id, const pthread_attr_t *attr, void* (fun*)(void*), void* arg) id unikalny identyfikator wątku; attr - wskaźnik na atrybuty wątku, określające szczegóły dotyczące wątku; można podać NULL, wówczas zostaną użyte domyślne wartości; fun adres funkcji wykonywania wątku; przyjmuje argument typu void* i zwraca wartość tego samego typu; arg argument przekazywany do funkcji fun. 77

Thread Na platformie.net wykorzystuje się klasę Thread. W C# dołączamy using System.Threading. Tworzymy własną klasę i publiczną metodę obsługi wątku, którą podajemy w argumencie konstruktora klasy Thread jako argument funkcji ThreadStart Np. Thread watek = new Thread(ThreadStart(moja_metoda)); 78

Synchronizacja - (Mutual Exclusion) Wzajemne wykluczenie oznacza, że tylko jeden wątek: ma dostęp do krytycznej sekcji kodu w danym momencie; ma dostęp do szczególnego fragmentu danych w danej chwili. 79

Synchronizacja - wzajemne wykluczenie POSIX dostarcza następujących wywołań: administracyjnych pthread_mutex_init (pthread_mutex_t *, pthread_mutexattr_t *); pthread_mutex_destroy (pthread_mutex_t *); użytkowych pthread_mutex_lock (pthread_mutex_t *); pthread_mutex_trylock (pthread_mutex_t *); pthread_mutex_unlock (pthread_mutex_t *); 80

Synchronizacja - wzajemne wykluczenie Prosty przykład: pthread_mutex_t mymutex; init () { domyślne... atrybuty // utworzenie mutexu pthread_mutex_init (&mymutex, NULL);... } thread_func () {... // otrzymaj mutex, czekaj w razie potrzeby pthread_mutex_lock (&mymutex); // operowanie na krytycznych danych // koniec krytycznej sekcji, zwolnij mutex pthread_mutex_unlock (&mymutex);... } cleanup () { pthread_mutex_destroy (&mymutex); } 81

Synchronizacja - wzajemne wykluczenie Rozważmy funkcję malloc(): freelist memoryarea 1 memoryarea 2 memoryarea 3 memoryarea 4 NULL Pewna liczba wątków żądających pamięci od malloc(). Wewnętrznie, malloc() przechowuje listę wolnych bloków pamięci które są dostępne dla alokacji. Wszystkie wątki w procesie używają tej samej listy. 82

Synchronizacja - wzajemne wykluczenie W uproszczeniu źródło malloc() wygląda następująco: void * malloc (int nbytes) { while (freelist && freelist -> size!= nbytes) { freelist = freelist -> next; } if (freelist) { // mark block as used, and return block address to caller return (freelist -> memory_block); } } 83

Synchronizacja - wzajemne wykluczenie Teraz rozważmy pewną liczbę wątków, które używają malloc(): thread1 () { char *data; } data = malloc (64); thread2 () { char *other_data; } other_data = malloc (64); 84

Synchronizacja - wzajemne wykluczenie Coś poszło źle! freelist memoryarea 1 memoryarea 3 memoryarea 4 NULL Hello wor4fe5 85

Synchronizacja - wzajemne wykluczenie Problem polega na tym, że wiele wątków może wchodzić sobie w drogę! Rozwiązanie problemu - wyłączny dostęp do struktury danych! Użyjemy MUTEXa aby rozwiązać problem. 86

Synchronizacja - wzajemne wykluczenie Poprawmy funkcję malloc: pthread_mutex_t malloc_mutex; void * malloc (int nbytes) { pthread_mutex_lock (&malloc_mutex); while (freelist && freelist -> size!= nbytes) { freelist = freelist -> next; } if (freelist) { // mark block as used, and return block block = freelist -> memory_block; pthread_mutex_unlock (&malloc_mutex); return (block); } pthread_mutex_unlock (&malloc_mutex); } } sekcja krytyczna 87

Synchronizacja -inicjalizacja Mutexu Aby zainicjować mutex należy wykonać następujący fragment kodu: pthread_mutex_init (&malloc_mutex, NULL); Jeżeli zakończy się powodzeniem gwarantuje, że wszystkie odpowiednie zasoby zostały zaalokowane dla mutexu. 88

Synchronizacja -inicjalizacja Mutexu Prosta metoda inicjalizacji mutexu: // statyczna inicjalizacja Mutexu pthread_mutex_t malloc_mutex = PTHREAD_MUTEX_INITIALIZER; void * malloc (int nbytes) {... // MUTEX będzie zainicjowany po pierwszym //użyciu pthread_mutex_lock (&malloc_mutex);... Oznacza: nie używany i będzie zainicjowany przez jądro po pierwszym użyciu. 89

Współdzielenie mutexów pomiędzy procesami Domyślnie mutexy nie mogą być współdzielone między procesami aby współdzielić mutex, ustawiamy dla niego flagę PTHREAD_PROCESS_SHARED mutex powinien być w pamięci dzielonej np.: pthread_mutexattr_t mutex_attr; pthread_mutex_t *mutex; pthread_mutexattr_init( &mutex_attr ); pthread_mutexattr_setpshared( &mutex_attr, PTHREAD_PROCESS_SHARED); mutex = (pthread_mutex_t *)shmem_ptr; pthread_mutex_init( mutex, &mutex_attr ); 90

Mutexy czas użycia blokady mutex Podstawowa zasada utrzymuj mutexy zablokowane na krótkie okresy czasu. Pamiętaj! podczas gdy jeden wątek zablokuje mutex, inne wątki czekają na zwolnienia dostępu do zasobów. 91

Synchronizacja - zmienne warunkowe Rozważmy prosty przypadek, gdzie: musimy zablokować proces, oczekując aż inny wątek zmieni pewną zmienną: int state; thread_1 () { while (1) { // wait until state changes, // then, perform some work } } zmienne warunkowe spełniają rolę mechanizmu oczekiwania na taką zmianę 92

Synchronizacja - zmienne warunkowe Przykład: dostawca danych 2 wątek obsługi sprzętu 1 3 1. wątek dostarczający dane dostaje pewną ich ilość, np. od procesu klienta, i dodaje je do kolejki 2. powiadamia wątek obsługi sprzętu o nowych danych 3. wątek obsługi sprzętu budzi się, pobiera dane z kolejki i transferuje do sprzętu Żeby to wykonać potrzebujemy dwóch rzeczy: mutex aby zapewnić pojedynczy dostęp do kolejki danych w tym samym czasie mechanizm dla wątku dostarczającego dane by poinformował wątek obsługi sprzętu o nowych danych i obudził go 93

Synchronizacja - zmienne warunkowe Przyjrzyjmy się bliżej funkcji wait : pthread_cond_wait (&condvar, &mutex); Zwalnia rygiel z mutexu podanego jako drugi parametr (dlatego przed wywołaniem tej funkcji wątek powinien zaryglować tego mutexa), następnie usypia aktualny wątek. Podczas sukcesu ponownie ustawia rygiel na podanym mutexie i zwraca 0. 94

Synchronizacja - zmienne warunkowe Dlaczego wykonujemy to sprawdzenie? while (1) { 2 pthread_mutex_lock (&mutex); if (data_ready == 0)... // wyłączny dostęp pthread_cond_wait (&cond, &mutex); // oczekujemy data_ready = 0; pthread_mutex_unlock (&mutex); 1 } // ponownie ustawiamy flagę jeżeli sygnalizujemy zmienną warunkową i żaden wątek nie czeka na sygnał, jest on tracony; sygnał powinien być wysłany pomiędzy 1 i 2 ; proces sygnalizujący ustawia też flagę (data_ready = 1) 95

Synchronizacja - zmienne warunkowe Sygnalizacja vs broadcast: Wątki 1, 2 i 3 (wszystkie o takim samym priorytecie) czekają na zmianę używając pthread_cond_wait(), Wątek 4 dokonuje zmiany i sygnalizuje za pomocą pthread_cond_signal(), Najdłużej czekający wątek (powiedzmy 2 ) jest informowany o zmianie i próbuje zablokować mutex ( automatycznie przez pthread_cond_wait()), Wątek 2 sprawdza warunek, przeprowadza działania lub wraca do uśpienia 96

Synchronizacja - zmienne warunkowe Co się dzieje z wątkami 1 i 3? nigdy nie zauważą zmiany! Jeżeli zmienimy przykład: wykorzystamy pthread_cond_broadcast() zamiast pthread_cond_signal(), wtedy wszystkie trzy wątki otrzymają powiadomienie wszystkie wątki zmianią stan na READY, jednak tylko jeden z nich może zablokować mutex jednocześnie - pozostałe kolejno. 97

Synchronizacja - zmienne warunkowe Co wybrać? wybieramy sygnał, jeżeli: mamy jeden oczekujący wątek; potrzebny jest tylko jeden wątek do przetwarzania i nie musimy informować pozostałych; używamy broadcast, jeżeli mamy wiele wątków i: wszystkie muszą przeprowadzić działania po dokonanej zmianie lub; nie wszystkie potrzebują dostępu do zmiany, ale nie wiemy, który z nich obudzić. 98

Synchronizacja - zmienne warunkowe Połączenie nie musi być jeden do jednego Muvw Mxyz mutex u v w x y z w x y z condvar CVwx CVyz 99

Synchronizacja - zmienne warunkowe Domyślnie, zmienne warunkowe nie mogą być współdzielone z innymi procesami aby współdzielić zmienne warunkowe, ustawiamy odpowiednią flagę PTHREAD_PROCESS_SHARED zmienna warunkowa powinna być w pamięci współdzielonej np.: pthread_condattr_t cond_attr; pthread_cond_t *cond; pthread_condattr_init( &cond_attr ); pthread_condattr_setpshared( &cond_attr, PTHREAD_PROCESS_SHARED); cond = (pthread_cond_t *)shmem_ptr; pthread_cond_init(cond, &cond_attr ); 100

Synchronizacja - zmienne warunkowe Przykład producenta/konsumenta: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; volatile int state = 0; volatile int product = 0; void *consume (void *arg) { } while (1) { pthread_mutex_lock (&mutex); while (state == 0) { pthread_cond_wait (&cond, &mutex); } printf ( Consumed %d\n, product); state = 0; pthread_cond_signal (&cond); pthread_mutex_unlock (&mutex); do_consumer_work (); } return (0); 101

Synchronizacja - zmienne warunkowe void *produce (void *arg) { while (1) { pthread_mutex_lock (&mutex); while (state == 1) { pthread_cond_wait (&cond, &mutex); } printf ( Produced %d\n, product++); state = 1; pthread_cond_signal (&cond); pthread_mutex_unlock (&mutex); do_producer_work (); } return (0); } int main () { pthread_create (NULL, NULL, &produce, NULL); consume (NULL); return (EXIT_SUCCESS); } 102

Synchronizacja - semafory Wykorzystanie semaforów dla sterowania dostępem administracja: unnamed semaphores sem_init (sem_t *semaphore, int pshared, unsigned int val); sem_destroy (sem_t *semaphore); sem_t *sem_open (char *name, int oflag, [int sharing, unsigned int val]); sem_close (sem_t *semaphore); named semaphores sem_unlink (char *name); użycie: sem_post (sem_t *semaphore); sem_trywait (sem_t *semaphore); sem_wait (sem_t *semaphore); sem_getvalue (sem_t *semaphore, int *value); 103

Synchronizacja - semafory Nienazwane oraz nazwane semafory: z nienazwanymi, wywołania sem_post() i sem_wait() powodują bezpośrednie wywołanie jądra związane z semaforem z nazwanymi, wywołania sem_post() i sem_wait() wysyłają komunikaty do mqueue a stąd generowane są wywołania jądra semafory nienazwane są szybsze niż nazwane semafory nazwane wykorzystywane są do komunikacji pomiędzy procesami 104

Synchronizacja operacje atomowe Dla krótkich operacji, takich jak zwiększanie zmiennej: atomic_add atomic_add_value atomic_clr atomic_clr_value atomic_set atomic_set_value atomic_sub atomic_sub_value atomic_toggle atomic_toggle_value operacja += wartość atomic_add(), zwraca wynik operacja &= ~ wartość atomic_clr(), zwraca wynik operacja = wartość atomic_set(), zwraca wynik operacja -= wartość atomic_sub(), zwraca wynik operacja ^= wartość atomic_toggle(), zwraca wynik Funkcje te: mają gwarancję wykonania bez wywłaszczenia; mogą być użyte między dwoma wątkami (nawet na SMP); mogą być użyte między wątkiem a ISR. 105

Rodzima komunikacja międzyprocesowa QNX 106

IPC - Wprowadzenie IPC: Komunikacja międzyprocesowa Dwa procesy wymieniają: dane kontrolę zawiadomienie o wystąpieniu zdarzenia IPC Wątek Wątek Proces A Proces B 107

IPC - Wprowadzenie QNX Neutrino wspiera różnorodne mechanizmy IPC: Rodzima komunikacja QNX (API jest unikalne dla QNX) zawiera: Komunikaty QNX Neutrino Pulsy QNX Neutrino POSIX/Unix (dobrze znane, przenośne API) zawiera: sygnały pamięć dzielona potoki (wymagany proces pipe) kolejki komunikatów POSIX (wymagany proces mqueue lub mq ) gniazda TCP/IP (wymagany proces io-net) 108

Rodzima komunikacja QNX Rodzima IPC QNX: 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 109

Połączenia i kanały: IPC komunikacja serwer odbiera w kanale, klient dołącza się do kanału klient wysyła przez kanał serwera po dołączeniu do niego połączenie kanał klient serwer 110

IPC komunikacja 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 kanał połączenie klient A serwer A połącznie kanał połączenie klient B serwer B 111

Pseudo kod serwera : utwórz kanał ( ChannelCreate() ) czekaj na komunikat ( MsgReceive() ) przetwarzaj odpowiedz ( MsgReply() ) przejdź do oczekiwania Pseudo-kod klienta: IPC klient - serwer dołącz do kanału ( ConnectAttach() ) wyślij komunikat ( MsgSend() ) wykorzystaj odpowiedź serwera 112

IPC komunikacja Serwer tworzy kanał wykorzystując: chid = ChannelCreate (flagi); kanał (prawdopodobnie wielowątkowy) klient serwer kanał jest powiązany z procesem dowolny wątek w procesie może odbierać komunikaty poprzez kanał, jeżeli tego potrzebuje 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 113

IPC komunikacja 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 114

IPC komunikacja 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 115

Klient - Przekazywanie komunikatów 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*() 116

Serwer - Przekazywanie komunikatów 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 117

Serwer - Przekazywanie komunikatów 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 118

Serwer - Przekazywanie komunikatów 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 119

Przekazywanie komunikatów dane 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,...) 120

Przekazywanie komunikatów dane 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 121

System komunikacji typy struktur 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. 122

System komunikacji typy komunikatów 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; które są w zakresie: 0 to _IO_MAX (511); używanie wartości większych niż _IO_MAX jest zawsze bezpieczne. 123

System komunikacji kod serwera Po stronie serwera fragment uzależniony od typu komunikatu, np.: while(1) { rcvid = MsgReceive( chid, &msg, sizeof(msg), NULL ); switch( msg.type ) { case MSG_TYPE_1: handle_msg_type_1(rcvid, &msg); break; case MSG_TYPE_2: } } 124

Komunikacja Synchroniczna i Asynchroniczna PULSY 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? 125

Komunikacja asynchroniczna 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. 126

Pulsy: 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 127

PULSY - wysyłanie 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; wysyłanie pulse do innego procesu wymaga aby nadawca miał taki sam userid jak odbiorca lub był użytkownikiem root. 128

PULSY - odbiór Pulsy są odbierana tak samo jak komunikaty, za pomocą wywołania MsgReceive*(): serwer określa odebranie pulse 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. 129

PULSY odbiór przykład 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 msg.pulse w celu odczytania danych } else { // to jest regularny komunikat } } 130

PULSY struktura pulsu 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; }; 131

PULSY odbiór pulsu przykład 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 msg.pulse switch (msg.pulse.code) { case MY_PULSE_CODE1: // wykonaj odpowiednie czynności... break; case MY_PULSE_CODE2: // wykonaj odpowiednie czynności... break; 132

W jaki sposób klient odnajduje serwer? 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? 133

W jaki sposób klient odnajduje serwer? 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 134

W jaki sposób klient odnajduje serwer? Manager zasobów 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 135

Szukanie serwera name_attach()/name_open() 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 ); Po stronie klienta: coid = name_open( myname, 0 );... MsgSend( coid, &msg, sizeof(msg), NULL, 0 );... name_close( coid ); 136

Flagi kanału 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ć. 137

Flagi ChannelCreate Flagi ChannelCreate() ustawiane przez name_attach(): _NTO_CHF_DISCONNECT: żądanie zgłoszenia powiadomienia w sytuacji zakończenia klienta; pulse będzie miało pole code: _PULSE_CODE_DISCONNECT; _NTO_CHF_COID_DISCONNECT: żądanie zgłoszenia powiadomienia w sytuacji zakończenia serwera; pulse będzie miało pole code _PULSE_CODE_COIDDEATH; _NTO_CHF_UNBLOCK: żądanie zgłoszenia jeżeli klient zablokowany na REPLY, wymaga odblokowania; pulse będzie miało pole code _PULSE_CODE_UNBLOCK; 138

Komunikaty CONNECT z name_open() 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(msg.pulse.code) { //Jaki typ pulsu case _PULSE_CODE_DISCONNECT: //odłączenie klienta break; case _PULSE_CODE_UNBLOCK: //klient żąda odblokowania break; case //inne 139

Komunikaty CONNECT z name_open() Jeżeli komunikaty przekazywane są przez sieć, name_open() wysyła komunikat CONNECT do serwera: po odebraniu name_open(), serwer musi odpowiedzieć MsgReply() ze statusem, np. if(msg.type == _IO_CONNECT) { } MsgReply(rcvid, EOK, NULL, 0); status no data 140

Informacje o klientach W niektórych sytuacjach serwer może potrzebować uzyskać informacje o każdym z dołączonych klientów: np. status klienta, żądania obsłużone/bieżące ten typ informacji musi być dostępny tak długo jak długo klient jest dołączony do serwera potrzeba usunąć kiedy klient rozłącza się jak zobaczymy później, jest to ważne z punktu widzenia dostarczania zdarzeń 141

Flagi ChannelCreate - odłączenie 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ć klient połączenie połączenie kanał kiedy dwa połączenia są odłączone, pulse będzie wysłane serwer pole code będzie miało wartość _PULSE_CODE_DISCONNECT 142

Flagi ChannelCreate - odłączenie 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 pulse oznaczające rozłączenie z klientem, musimy: usunąć informacje związane z klientem; wykonać ConnectDetach(pulse.scoid) aby zwolnić scoid. połączenie połączenie klient A połączenie klient B kanał serwer 2 scoid - jeden na klienta 143

Name_attach()/name_open() - przykład 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 (msg.pulse.code) { case _PULSE_CODE_DISCONNECT: //code to clean up per-client info ConnectDetach(msg.pulse.scoid); /* zwalniamy scoid */ break; } 144

Komunikaty wieloczęściowe - IOV IOV - Input/Output Vectors: tablica wskaźników do buforów zastosowanie: zapobiega kopiowaniu danych w przypadku wieloczęściowych komunikatów, komunikaty zmiennej długości: serwer nie zna rozmiaru komunikatu wysyłanego przez klienta. 145

Komunikaty wieloczęściowe - IOV Zamiast podawania adresu jednego bufora używając MsgSend()... MsgSend (int coid, void *smsg, int sbytes, void *rmsg, int rbytes); jeden bufor jeden bufor... podajemy do jądra tablicę wskaźników do buforów, używając MsgSendv(): MsgSendv (int coid, iov_t *siov, int sparts, iov_t *riov, int rparts); tablica wskaźników do kilku buforów tablica wskaźników do kilku buforów 146

Komunikaty wieloczęściowe - IOV Spojrzenie na strukturę iov_t typedef struct { void *iov_base; size_t iov_len; } iov_t; Bardziej użyteczna jako tablica: iov_t iovs [3]; liczba elementów tablicy >= projektowana liczba elementów komunikatów 147

Komunikaty wieloczęściowe - IOV Użycie IOV: każdy element IOV zawiera wskaźnik i długość 0 1 2 część1 część2 część3 SETIOV (&iovs [0], &part1, sizeof (part1)); SETIOV (&iovs [1], &part2, sizeof (part2)); SETIOV (&iovs [2], &part3, sizeof (part3)); część1 część2 część3 Podczas wysyłania wszystkie części są ułożone jako jedna sekwencja bajtów. IOV jest zastosowane w funkcjach komunikacyjnych, zwierających na końcu nazwy v : (MsgReadv/MsgReceivev/MsgReplyv/MsgSendsv/MsgSendv/MsgSendvs/MsgWritev) 148

Komunikaty wieloczęściowe - IOV Przykład wieloczęściowej komunikacji: chcemy wysłać 3-częściowy komunikat; część1 (750 KB) część2(500 KB) część3(1000 KB) moglibyśmy zaalokować (malloc()) wystarczającą ilość pamięci na duży kompletny komunikat (750 + 500 +1000 KB = 2.25 MB); następnie 3-krotnie memcpy() żeby to wszystko umieścić w jednym komunikacie. część1 część2 część3 149

Komunikaty wieloczęściowe - IOV Przykład wieloczęściowej komunikacji: przygotowujemy 3-częściową tablicę IOV, ze wskaźnikami do danych i używamy MsgSendv(), co jest bardziej efektywne zamiast malloc() i memcpy() 0 1 2 część1 część2 część3 MsgSendv() Serwer 150