WOJSKOWA AKADEMIA TECHNICZNA
IM. JAROSŁAWA DĄBROWSKIEGO W WARSZAWIE
WYDZIAŁ CYBERNETYKI
INSTYTUT TELEINFORMATYKI I AUTOMATYKI
TECHNIKA MIKROKOMPUTEROWA
REFERAT
Zarządzanie procesami w systemie QNX.
Wykonała:
Dagmara Furtak
gr. I5X1S0
SPIS TREŚCI
1. Wprowadzenie 3
2. Zarządzanie procesami 5
2.1. Procesy systemowe i aplikacyjne 5
2.1.1. Jądro i procesy systemowe 5
2.1.2. Sesja użytkownika 6
2.1.3. Graf stanów procesu. 6
2.1.4. Szeregowanie procesów. 7
2.2. Tworzenie i usuwanie procesów 7
2.2.1. Tworzenie procesów 8
2.2.2. Usuwanie procesów 8
2.3. Komunikacja i synchronizacja 9
2.3.1. Wiadomości 9
2.3.2. Depozyty (proxy) 10
2.3.3. Potoki i Kolejki
Wprowadzenie
Pierwsze systemy komputerowe umożliwiały wykonywanie tylko jednego programu w danej chwili. Program taki sprawował całkowity nadzór nad systemem, a do jego dyspozycji pozostawały wszystkie zasoby systemu. Współczesne systemy komputerowe pozwalają na umieszczenie w pamięci operacyjnej wielu programów i współbieżne ich wykonywanie. Te zmiany pociągnęły za sobą konieczność ostrzejszej kontroli i większego odseparowania od siebie poszczególnych programów. Doprowadziło to do powstania pojęcia procesu, przez które rozumie się program będący w trakcie wykonywania.
Wraz ze wzrostem ilości programów znajdujących się w pamięci operacyjnej i współbieżnym ich wykonywaniem pojawiło się więcej procesów żądających przydziału do procesora w tym samym momencie. W związku z tym w każdym systemie operacyjnym jednostką odpowiadającą za szeregowanie procesów, komunikację międzyprocesową oraz przydział pamięci jest jądro systemu. Innymi słowy jedną z głównych funkcji jądra systemu operacyjnego jest zarządzanie procesami.
Rozwój techniki komputerowej w ostatnich latach doprowadził do powstania kilku systemów operacyjnych. Wśród komputerów PC używanych w zastosowaniach domowych i biurowych najszerzej stosowanym systemami operacyjnymi są Linux i Widnows. W zastosowaniach przemysłowych oraz innych wymagających m.in. bezwzględnego reżimu czasowego, dominuje QNX.
Szczególną cechą systemu QNX (systemu czasu rzeczywistego), która odróżnia go od systemów ogólnego przeznaczenia, jest możliwość tworzenia aplikacji pracujących w czasie rzeczywistym. Aplikacje takie składają się zazwyczaj z wielu komunikujących się ze sobą procesów, a ich nadrzędnym celem jest reagowanie z zadaną prędkością na zdarzenia zachodzące w otoczeniu komputera. Ten rodzaj pracy jest charakterystyczny dla systemów sterujących instalacjami przemysłowymi, komunalnymi lub wojskowymi.
Kluczem do tworzenia programów czasu rzeczywistego jest możliwość podziału pracy między wiele współbieżnie wykonywanych procesów oraz istnienie niezawodnych narzędzi do komunikacji procesów, sygnalizacji stanów wyjątkowych i planowania wykonania procesów w funkcji czasu.
Podsumowując powyższy wywód :
System QNX jest systemem operacyjnym czasu rzeczywistego, którego budowa oparta jest na mikrojądrze, którego głównym zadaniem jest zarządzanie procesami domagającymi się przydziału do procesora w tej samej jednostce czasu. Dzięki odpowiednim narzędziom i mechanizmom takim jak kolejkowanie procesów, synchronizacja procesów oraz komunikacja międzyprocesowa, mikrojądro systemu QNX doskonale spełnia swoja główna funkcję. W kolejnych rozdziałach omówię wyżej wymienione mechanizmy.
Definicje - rozkład tematu na czynniki pierwsze
QNX - system operacyjny czasu rzeczywistego zaliczany do klasy Unix, tworzący w drzewie rozwoju Uniksa własną i niezależną gałąź.
System operacyjny (ang. skrót OS Operating System) - zorganizowany zbiór programów pośredniczących między sprzętem a użytkownikiem, dokonujących dynamicznego rozdziału dostępnych zasobów pomiędzy istniejące procesy w sposób zapewniający ich poprawne i efektywne wykorzystanie.
System operacyjny czasu rzeczywistego (ang. real-time operating system) - komputerowy system operacyjny, który został opracowany zgodnie ze specyficznymi wymaganiami związanymi z czasem wykonywanych przez niego operacji. Zdarzenia zewnętrzne (przerwania) nie są obsługiwane dosłownie w czasie rzeczywistym, ale system zapewnia rozpoczęcie ich obsługiwania w ciągu określonego czasu. Może być fragmentem większego systemu czasu rzeczywistego i zwykle przyjmuje w nim rolę elementu nadzorującego działanie całości.
Proces - obiekt, któremu przydziela się procesor, zawiera kod programu wykonywanego w trakcie istnienia takiego obiektu, posiada własny obszar pamięci operacyjnej oraz dysponuje zbiorem potrzebnych mu do zasobów.
Jądro systemu operacyjnego - najbardziej wewnętrzna warstwa systemu, odpowiedzialna za szeregowanie procesów, komunikację międzyprocesową oraz zarządzanie pamięcią.
Mikrojądro (ang. microkernel) - rodzaj jądra systemu operacyjnego, które zawiera tylko najbardziej niezbędne elementy, takie jak funkcje zarządzania wątkami, komunikacją międzyprocesową, oraz obsługą przerwań i wyjątków.
Zarządzanie procesami
Procesy systemowe i aplikacyjne
Jądro i procesy systemowe
System QNX składa się z jądra, pełniącego rolę egzekutora wielozadaniowego, i zespołu procesów (zadań) systemowych. Zakres funkcji jądra obejmuje tylko szeregowanie procesów, implementację komunikacji między procesami oraz przyjmowanie zgłoszeń przerwań. Pozostałe funkcje wypełniają procesy systemowe. Do najważniejszych procesów, występujących w każdej typowej konfiguracji systemu, należą:
Menedżer procesów (Process Manager), nadzorujący tworzenie i usuwanie procesów, rejestrację nazw oraz odmierzanie czasu.
Menedżer plików (Filesystem Manager), organizujący system plików w pamięciach dyskowych, taśmowych i na dyskach optycznych.
Menedżer urządzeń (Device Manager), formatujący dane przekazywane między procesami a urządzeniami zewnętrznymi.
Procesy sterujące urządzeniami (Device Driver), inicjujące i kończące działanie urządzeń i obsługujące zgłaszane przerwania.
Menedżer sieci (Network Manager), obsługujący łącza sieciowe i komunikację węzłów sieci.
Zarówno procesy aplikacyjne, jak systemowe, komunikują się wyłącznie za pomocą standardowych funkcji implementowanych przez jądro systemu. Procesy systemowe pełnią rolę serwerów obsługujących żądania innych procesów (architektura client-server). Odwołania procesów do serwerów mają postać wiadomości, przenoszących żądania wykonania określonych usług, np. utworzenia nowego procesu przez menedżera procesów lub odczytania pliku dyskowego przez menedżera plików. Dzięki możliwości odbycia spotkania przez procesy wykonywane w różnych węzłach sieci, wszystkie procesy mogą korzystać z zasobów zainstalowanych w innych węzłach.
Sesja użytkownika
Zgłoszenie użytkownika do systemu (login) otwiera nową sesję i powoduje automatyczne uruchomienie procesu interpretatora komend systemowych, o nazwie sh (shell). Podczas sesji użytkownik może tworzyć i wykonywać dalsze procesy - zarówno standardowe, uruchamiane odpowiednimi komendami systemowymi, jak i swoje własne. Proces interpretatora komend, uruchomiony wraz z rozpoczęciem sesji, jest procesem wiodącym sesji (session leader), a konsola, na której został uruchomiony, jest terminalem sesji. Zakończenie procesu wiodącego lub awaria terminala sesji powodują zawiadomienie i najczęściej zakończenie wszystkich procesów uruchomionych podczas trwania tej sesji.
Procesy wykonywane podczas sesji są powiązane hierarchiczną zależnością wynikającą z kolejności ich tworzenia: proces macierzysty tworzy procesy potomne. Relacja ta, nazywana relacją pokrewieństwa, jest przechodnia i tworzy strukturę drzewiastą, w której korzeniem jest na ogół proces wiodący sesji. W pewnych warunkach relacja pokrewieństwa może być zerwana podczas tworzenia procesu, co prowadzi do powstania odrębnego drzewa procesów.
Zakończenie procesu macierzystego nie powoduje automatycznego zakończenia procesów potomnych. Zachowanie relacji pokrewieństwa daje natomiast procesowi macierzystemu możliwość sprawdzania stanu procesów potomnych i oczekiwania na ich zakończenie. Działania te realizują dwie funkcje systemowe:
wait() - zawieszenie procesu macierzystego do chwili zakończenia
jednego z procesów potomnych.
waitpid(T) - zawieszenie procesu macierzystego do chwili zakończenia procesu potomnego o identyfikatorze T.
Graf stanów procesu.
Cykl życia procesu, od utworzenia do usunięcia z systemu, można przedstawić w postaci grafu stanów. W chwili utworzenia, proces otrzymuje potrzebne zasoby i jest od razu Gotowy do wykonania. Gdy system przełączy procesor na proces, przechodzi on do stanu Wykonywany. Jeżeli kontynuacja wykonania staje się niemożliwa, proces zostaje Zawieszony. Istnienie kilku stanów zawieszenia wynika to działania operacji komunikacyjnych. W chwili zakończenia, proces zwalnia wszystkie zasoby i zostaje usunięty z systemu.
Przed ostatecznym usunięciem, proces potomny powinien jednak poinformować proces macierzysty o swoim zakończeniu. Informacja ta jest przekazywana podczas wykonania przez proces macierzysty funkcji wait lub waitpid. Jeżeli proces potomny zakończy się przed wykonaniem jednej z tych funkcji przez proces macierzysty, to przechodzi w stan Martwy, w którym oczekuje na możliwość poinformowania o swoim zakończeniu. Opuszczenie tego stanu następuje w chwili poinformowania procesu macierzystego lub zakończeniu tego procesu.
Proces macierzysty może zrezygnować z informacji o zakończeniu procesu potomnego. W takim wypadku proces potomny jest usuwany natychmiast, bez oczekiwania w stanie Martwy.
Szeregowanie procesów.
Wchodzący w skład jądra egzekutor szereguje procesy Gotowe na 32 poziomach priorytetu, numerowanych od 0 (min) do 31 (max). Procesy o wyższym poziomie priorytetu zawsze wywłaszczają procesy o priorytecie niższym. Procesy znajdujące się na tym samym poziomie priorytetu mogą być szeregowane zgodnie z jednym z trzech różnych algorytmów.
Algorytm FIFO. Do wykonania wybiera się proces najdłużej oczekujący w stanie Gotowy. Wybrany proces wykonuje się aż do zakończenia lub zawieszenia.
Algorytm karuzelowy. Procesy wybiera się do wykonania kolejno, przy czym każde z nich otrzymuje ograniczony kwant czasu. Jeżeli wykonywany proces nie zakończy się lub nie zawiesi wcześniej, to w chwili wyczerpania kwantu system operacyjny wywłaszcza go i podejmuje wykonanie następnego procesu.
Algorytm adaptacyjny. Procesy wybiera się do wykonania kolejno, podobnie jak w algorytmie karuzelowym. Po wyczerpaniu kwantu czasu system operacyjny wywłaszcza wykonywany proces i obniża jego priorytet o 1. Jeżeli wykonanie procesu o obniżonym priorytecie nie zostanie podjęte w ciągu 2 sekund, jego priorytet podnosi się o 1 (ale nigdy powyżej oryginalnego priorytetu). Proces zawieszony odzyskuje natychmiast swój oryginalny priorytet.
W chwili uruchomienia nowy proces dziedziczy priorytet po procesie macierzystym. Standardowo, procesy uruchamiane przez interpretator shell otrzymują priorytet 10. Zarówno priorytet, jak i Algorytm szeregowania, stanowią parametry aktualnie wykonywanego procesu i mogą być dynamicznie zmieniane podczas pracy systemu za pomocą funkcji systemowych:
getprio(T) - odczytanie priorytetu procesu T.
setprio(T,p) - ustalenie priorytetu p dla procesu T.
sched_getscheduler(T) - odczytanie algorytmu szeregowania ustalonego dla procesu T
sched_setscheduler(T,s) - ustalenie priorytetu i algorytmu szeregowania dla procesu T.
Tworzenie i usuwanie procesów
Utworzenie procesu polega na załadowaniu programu tego procesu do pamięci operacyjnej, wstępnym wypełnieniu struktur systemowych opisujących stan procesu i określeniu numerycznego identyfikatora (process identifier - pid) jednoznacznie wskazującego proces w węźle sieci. Czynności te wykonuje systemowy menedżer procesów na żądanie procesu macierzystego. Ponieważ żądania są przenoszone przez wiadomości przekazywane w obrębie całej sieci zarządzanej przez system QNX, więc proces wykonywany w jednym węźle może utworzyć proces potomny w dowolnym innym węźle sieci. Bezpośrednio po utworzeniu nowy proces jest od razu gotowy do wykonania.
Zakończenie i usunięcie procesu może nastąpić na polecenie tego procesu lub w wyniku otrzymania sygnału od innego procesu. Ponieważ sygnały mogą być przekazywane w obrębie całej sieci zarządzanej przez system QNX, więc proces wykonywany w jednym węźle może zostać zakończony przez proces wykonywany w innym węźle sieci.
Tworzenie procesów
System QNX implementuje trzy różne funkcje, umożliwiające utworzenie nowego procesu. Poszczególne funkcje mają różny zakres działania oraz ustalają różny stosunek procesu macierzystego i procesu potomnego:
fork() - utworzenie dokładnej kopii procesu macierzystego, zawierającej zarówno ten sam kod programu, jak i obszar danych.
exec(F) - zastąpienie programu procesu macierzystego przez
nowy program, odczytany z pliku F.
spawn(F) - utworzenie nowego, odrębnego procesu, którego program zostanie odczytany z pliku F.
Parametr |
fork() |
exec() |
spawn() |
Identyfikator procesu Identyfikator sesji Grupa procesów |
nie tak tak |
tak tak tak |
nie (tak) (tak) |
Rzeczywisty UID, GID Skuteczny UID, GID |
tak tak |
tak (tak) |
tak (tak) |
Priorytet Algorytm szeregowania Liczniki czasu |
tak tak nie |
(tak) (tak) (nie) |
(tak) (tak) nie |
Katalog roboczy Zmienne środowiskowe |
tak tak |
(tak) (tak) |
(tak) (tak) |
Maska tworzenia plików Otwarte pliki Blokady plików |
tak tak nie |
tak (tak) tak |
tak (tak) nie |
Funkcje fork i exec, zdefiniowane w standardzie POSIX, działają tylko w obrębie jednego węzła sieci. Funkcja spawu, specyficzna dla systemu QNX, może utworzyć proces w dowolnym węźle sieci. W każdym wypadku, utworzony proces potomny dziedziczy po procesie macierzystym wiele parametrów systemowych. Zakres dziedziczenia, zależy od rodzaju funkcji, za pomocą której proces został utworzony. Parametry systemowe wyróżnione w tablicy symbolem nawiasów () mogą być dziedziczone lub nie, zależnie od wartości parametrów wpisanych do globalnej struktury systemowej:
qnx_spawn-option - określenie numeru węzła, w którym ma być utworzony proces, algorytmu szeregowania, priorytetu oraz sposobu dziedziczenia parametrów systemowych.
Wartości podane w nawiasach odpowiadają wartościom domyślnym tej struktury. Zmienne środowiskowe mogą być przekazane do procesu potomnego jako jeden z argumentów wywołania funkcji.
Usuwanie procesów
Program procesu jest przykładowo programem napisanym w języku C, zgodnie z normalnymi regułami tego języka. Wykonanie programu zaczyna się wobec tego od pierwszej instrukcji funkcji main(), a kończy po dojściu do klamry zamykającej tę funkcję (zakończenie normalne) lub otrzymaniu sygnału (zakończenie awaryjne). Dodatkowo, proces może w dowolnym momencie wywołać systemową funkcję zakończenia normalnego lub awaryjnego.
Normalne zakończenie procesu rozpoczyna się od wykonania zestawu funkcji, wskazanych przez kończony proces. Dalsze czynności są w każdym wypadku takie same:
Zamknięcie wszystkich otwartych plików i strumieni.
Usunięcie wszystkich połączeń, liczników czasu, nazw symbolicznych, funkcji obsługi przerwań i depozytów należących do zakończonego procesu oraz zwolnienie zajmowanej pamięci.
Powiadomienie procesu macierzystego, przez wysłanie do niego sygnału SIGCHLD i przekazanie informacji o zakończeniu.
Zakończenie procesu wiodącego sesji powoduje dodatkowo zwolnienie terminala sesji i wysłanie sygnału SIGHUP do procesów potomnych.
Podstawowe operacje związane z zakończeniem procesu realizują następujące funkcje systemowe:
atexit(H) - rejestracja funkcji H, wykonywanej podczas normalnego kończenia procesu.
exit() - normalne zakończenie procesu.
abort() - awaryjne zakończenie procesu.
Komunikacja i synchronizacja
Jądro systemu QNX implementuje trzy mechanizmy komunikacji procesów: synchroniczny przekaz wiadomości (message) podczas spotkania, sygnalizację zdarzeń (signal) i asynchroniczny przekaz depozytów (stałych wiadomości). Wszystkie mechanizmy mogą być wykorzystane do komunikacji procesów wykonywanych w różnych węzłach sieci. Dodatkowo, systemowy proces menedżera plików umożliwia buforowane przekazywanie danych między procesami poprzez potoki (pipe) i kolejki (FIFO).
Wiadomości
Podstawowym mechanizmem komunikacji procesów jest wymiana pary wiadomości.
realizowana według schematu spotkania. Podstawowe etapy wymiany wiadomości realizują trzy funkcje systemowe:
Send(T2,M,Mr) - wysłanie wiadomości M do procesu T2 i zawieszenie procesu nadającego do czasu otrzymania odpowiedzi Mr; proces nadający wchodzi w stan Zs.
Receive(Tl,M) - odebranie wiadomości M od procesu Tl; proces, który nadał wiadomość, jest przenoszony do stanu Zr.
Reply(Tl,Mr) - wznowienie procesu Tl i przekazanie mu odpowiedzi Mr.
Jeżeli funkcja Receive zostanie wywołana przed nadaniem wiadomości, to proces odbierający zostanie zawieszony w stanie Z. Wznowienie procesu nastąpi w chwili nadejścia wiadomości. Odrębna funkcja systemowa umożliwia odbiór wiadomości bez zawieszania procesu:
Creceive(T,M) - odebranie oczekującej wiadomości; jeżeli żadna wiadomość nie została nadana, funkcja zwraca kod błędu.
Proces nadający wiadomość musi zawsze znać tożsamość odbiorcy, którego identyfikator jest jednym z parametrów funkcji Send, Proces odbierający wiadomość nie musi znać tożsamości nadawcy. Zależnie od sposobu wywołania funkcji Receive, wiadomości będą odbierane od wskazanego nadawcy albo od każdego (dowolnego) procesu.
W razie jednoczesnego nadania wielu wiadomości skierowanych przez różnych nadawców do tego samego odbiorcy, wszystkie procesy nadające są zawieszane w stanie Zs. Przez domniemanie, kolejka procesów oczekujących (i ich wiadomości) jest obsługiwana zgodnie z algorytmem FIFO: odbiorca otrzymuje najpierw najstarszą wiadomość. Algorytm ten można jednak zmienić na priorytetowy.
Wszystkie wiadomości są przekazywane bezpośrednio między procesami, tzn. treść wiadomości jest kopiowana z przestrzeni adresowej-nadawcy do przestrzeni adresowej odbiorcy. Proces kopiowania może być realizowany fragmentami, bez potrzeby kumulowania treści całej wiadomości w jednej strukturze. Operacje te realizują dwie dalsze funkcje jądra systemu:
Readmsg() - pobranie fragmentu oczekującej wiadomości. Wykonanie tej funkcji nie zmienia stanu żadnego procesu.
Writemsg() - dopisanie fragmentu odpowiedzi. Wykonanie tej funkcji nie zmienia stanu żadnego procesu.
Podczas komunikacji procesów wykonywanych w różnych węzłach sieci, wiadomości są przenoszone poprzez sieć w sposób niewidoczny dla procesów. Nie są potrzebne żadne specjalne operacje związane z komunikacją procesów oddalonych.
Depozyty (proxy)
Szczególnym przypadkiem komunikacji procesów jest przekazywanie sygnałów pobudzających proces odbiorcy do działania, bez wstrzymywania biegu procesu nadawcy. W ten sposób mogą współpracować nie tylko dwa różne procesy, ale również proces (odbiorca) i funkcja obsługi przerwania lub sygnału (nadawca). Podstawowym narzędziem synchronizacji są w systemie QNX depozyty.
Depozyt zachowuje się jak odrębny proces, przypisany do procesu właściciela, i przechowujący jedną, ustaloną wiadomość. Dowolny proces albo funkcja obsługi przerwania łub sygnału może pobudzić depozyt, powodując wysłanie przechowywanej wiadomości do procesu właściciela. Wielokrotne pobudzenie depozytu powoduje wielokrotne wysłanie tej samej wiadomości. Wszystkie wysłane wiadomości są ustawiane w kolejkę, w której oczekują na odebranie. Właściciel depozytu może odebrać wysłaną wiadomość, wykonując w dowolnej chwili funkcję Receive, adresowaną do swojego depozytu. Wykonania tej funkcji powoduje przekazanie jednej wiadomości albo zawieszenie procesu, jeżeli żadna wiadomość nie została jeszcze wysłana. Warto zauważyć, że proces pobudzający nie jest nigdy zawieszany, natomiast proces właściciela może zostać zawieszony w oczekiwaniu na nadejście wiadomości.
Funkcję pobudzenia (Trigger) może wywołać funkcja obsługi przerwania lub sygnału. Podstawowym obszarem zastosowania tego mechanizmu jest dostarczanie procesom informacji o zdarzeniach zewnętrznych odbieranych przez funkcje obsługi przerwań.
Podstawowe operacje związane z wykorzystaniem mechanizmu depozytów realizuje funkcja odbioru wiadomości Receive i trzy dalsze funkcje systemowe:
qnx_proxy_attach(T,M) - utworzenie depozytu z wiadomością M stanowiącego własność procesu T. Funkcja zwraca identyfikator P utworzonego depozytu.
qnx_proxy_detach(P) - usunięcie depozytu P.
Trigger(P) - pobudzenie depozytu P i wysłanie wiadomości do procesu właściciela.
Wykorzystanie depozytów w aplikacjach rozproszonych, wykonywanych w różnych węzłach sieci, wymaga utworzenia depozytu w węźle wykonującym proces właściciela, a następnie utworzenie procesu reprezentującego depozyt w węźle wykonującym program pobudzający (proces lub funkcję obsługi przerwania). Proces właściciela może teraz odebrać wiadomość, wywołując funkcję Receive adresowaną do swojego depozytu. Program pobudzający, wykonywany w innym węźle sieci, wywołuje funkcję Trigger, skierowaną do procesu reprezentującego depozyt w swoim węźle. Operacje utworzenia i usunięcia procesu reprezentującego depozyt realizują dwie funkcje systemowe:
qnx_proxy_rm_attach(N,P) - utworzenie reprezentanta depozytu P w węźle N. funkcja zwraca identyfikator reprezentanta V.
qnx_proxy_rem_detach(N,V) - usunięcie reprezentanta depozytu V w węźle V.
Potoki i Kolejki
Funkcjonalnie, obydwa mechanizmy implementują niemal identyczne bufory komunikacyjne, umożliwiające magazynowanie i jednokierunkowe przekazywanie danych między różnymi procesami. W obydwu wypadkach dane są zapisywane i odczytywane
zgodnie z algorytmem FIFO, w postaci strumienia bajtów bez określonej struktury wewnętrznej. Operacja zapisania danych nie wstrzymuje nadawcy, natomiast operacja odczytu z pustego bufora może spowodować zawieszenie odbiorcy do czasu pojawienia się danych.
Mimo funkcjonalnego podobieństwa, obydwa mechanizmy różnią się istotnie na poziomie implementacji. Kolejki są realizowane jako specjalne pliki dyskowe, z buforowanymi operacjami zapisu i odczytu. Pliki te rezydują na dysku aż do usunięcia, a ich nazwy są normalnie zapisane w katalogu. Wszystkie operacje na kolejkach wykonuje systemowy menedżer plików.
Implementacja potoków może być dwojaka, zależnie od tego, czy będą one organizowane przez menedżer plików, czy specjalny menedżer potoków (Pipe). W pierwszym wypadku, potoki są realizowane jako tymczasowe pliki dyskowe bez nazw, z buforowanymi operacjami zapisu i odczytu. W drugim, dane są przekazywane wyłącznie poprzez bufory w pamięci, bez zapisywania ich na dysku.
Wadą implementacji opartej na wykorzystaniu plików jest długi czas wykonania operacji, związany z koniecznością dostępu do dysku. Implementacja oparta na buforach w pamięci operacyjnej jest znacznie szybsza, jednak również w tym wypadku wykonanie każdej operacji wymaga więcej czasu niż przekazanie wiadomości między współpracującymi procesami.
Przykładem zastosowania potoków jest przekazywanie danych między procesami uruchomionymi równolegle przez standardowe komendy systemowe, połączone operatorem |, np. Is | more. Kolejkę FIFO można utworzyć za pomocą komendy systemowej mkfifo. Procesy mogą tworzyć potoki i kolejki za pomocą funkcji systemowych:
pipe() - utworzenie potoku.
popen() - utworzenie procesu potomnego, z jednoczesnym otwarciem potoku między nim, a procesem macierzystym.
mkfifo() - utworzenie kolejki.
Wszystkie dalsze operacje na potokach i kolejkach (zapis, odczyt, usunięcie kolejki) wykonuje się za pomocą standardowych operacji na odpowiednich plikach. Usunięcie potoku następuje automatycznie po zakończeniu wszystkich korzystających z niego procesów. Podobnie jak pliki, potoki i kolejki mogą być używane przez procesy wykonywane w różnych węzłach sieci.
Sygnały
Mechanizm sygnałów stanowi podstawowy środek informowania procesów o błędach i nienormalnych stanach powstających podczas obliczenia. Funkcjonalnie, sygnały przypominają przerwania programowe generowane przez programy jądra systemu, procesy systemowe lub procesy aplikacyjne w chwili wystąpienia sytuacji nienormalnej. Wygenerowany sygnał jest dostarczany do procesu, dla którego jest przeznaczony.
Sposób reakcji procesu na dostarczony sygnał zależy od wybranego trybu obsługi. Po pierwsze, proces może zdefiniować własną funkcję (podprogram) obsługi, wykonywaną w chwili dostarczenia sygnału analogicznie do wykonania funkcji obsługi przerwania. Po drugie, proces może ignorować dostarczane do siebie sygnały. Jeżeli jednak żaden tryb obsługi nie zostanie określony, to dostarczenie sygnału spowoduje zakończenie wykonania procesu. Niezależnie od wybranego trybu obsługi proces może maskować wybrane sygnały, powodując czasowe wstrzymanie ich dostarczania.
Lista sygnałów, które mogą być przekazywane, jest stała i obejmuje sygnały wymienione w standardzie POSIX, trzy sygnały specyficzne dla implementacji systemu QNX oraz kilka dodatkowych sygnałów systemu UNIX przekazywanych, ale nie wytwarzanych przez programy systemu QNX. Znaczenie większości sygnałów jest ustalone. Dwa sygnały są pozostawione do dowolnego wykorzystania przez procesy aplikacyjne.
Sygnały wyróżnione w tablicach gwiazdką (*) oznaczają i katastrofalne błędy sprzętowe. Błędy te mogą być obsługiwane przez odpowiednie funkcje obsługi. Jeżeli jednak ten sam błąd wystąpi ponownie podczas wykonywania funkcji obsługi, to proces zostaje zawsze zakończony. Trzy sygnały: SIGKILL, SIGSTOP i SIGCONT nie mogą być ignorowane, obsługiwane ani maskowane przez procesy.
Podstawowe operacje związane z generacją i obsługą, sygnałów realizują następujące funkcje systemowe:
raise(S) - wygenerowanie sygnału S w obrębie procesu (np. dla poinformowania o błędzie podczas wykonania podprogramu);
kill(T,S) - wygenerowanie sygnału S skierowanego do procesu T.
sigaction(S,A) - określenie sposobu obsługi sygnału S: akcja domyślna, zignorowanie sygnału łub wykonanie funkcji obsługi A.
signal(S,A) - określenie sposobu obsługi sygnału S (uproszczona wersja poprzedniej funkcji).
sigsuspend(SS) - zawieszenie procesu do chwili dostarczenia do niego jednego z sygnałów ze zbioru SS.
pause() - zawieszenie procesu do chwili dostarczenia dowolnego: sygnału, który spowoduje wykonanie funkcji obsługi lub zakończenie procesu.
Sygnały standardowe (POSIX)
Nazwa |
Akcja domyślna |
Znaczenie |
SIGABRT |
Zakończenie procesu |
Nienormalne zakończenie procesu (np. przez wykonanie funkcji abort) |
SIGALRM |
Zakończenie procesu |
Przeterminowanie (np. funkcja alarm) |
SIGFPE * |
Zakończenie procesu |
Błąd operacji arytmetycznej (np. dzielenie przez zero lub nadmiar) |
SIGHUP |
Zakończenie procesu |
Zakończenie procesu wiodącego sesji (z reguły sh) lub brak komunikacji z terminalem sesji |
SIGILL * |
Zakończenie procesu |
Próba wykonania niepoprawnej instrukcji |
SIGINT |
Zakończenie procesu |
Przerwanie z klawiatury (klawisz Break) |
SIGKILL |
Zakończenie procesu |
Awaryjne zakończenie procesu |
SIGPIPE |
Zakończenie procesu |
Próba zapisu do potoku (pipę), który nie został nigdzie otwarty do odczytu |
SIGQUIT |
Zakończenie procesu |
Zakończenie procesu przez operatora |
SIGSEGV * |
Zakończenie procesu |
Naruszenie ochrony pamięci |
SIGTERM |
Zakończenie procesu |
Normalne zakończenie procesu |
SIGUSR1 |
Zakończenie procesu |
Do zdefiniowania przez użytkownika |
SIGUSR2 |
Zakończenie procesu |
Do zdefiniowania przez użytkownika |
SIGCHLD |
Ignorowanie sygnału |
Zakończenie procesu potomnego |
SIGCONT |
Wznowienie procesu |
Wznowienie procesu zawieszonego przez sygnał SIGSTOP |
SIGSTOP |
Zawieszenie procesu |
Zawieszenie procesu w oczekiwaniu na sygnał SIGCONT |
Sygnały dodatkowe systemu QNX
Nazwa |
Akcja domyślna |
Znaczenie |
SIGBUS * |
Zakończenie procesu |
Błąd parzystości pamięci |
SIGDEV |
Zakończenie procesu |
Wykrycie oczekiwanego zdarzenia przez menedżera urządzeń |
SIGPWR |
Zakończenie procesu |
Żądanie restartu systemu wywołane przez naciśnięcie klawiszy Ctrl-Alt-Shift-Del lub wykonanie komendy shutdown |
SIGTRAP |
Zakończenie procesu |
Nie obsługiwane przerwanie programowe |
Sygnały dodatkowe systemu UNIX
Nazwa |
Akcja domyślna |
Znaczenie |
SIGSYS |
Zakończenie procesu |
Błędne argumenty funkcji systemowej |
SIGWINCH |
Zakończenie procesu |
Zmiana okna |
SIGURG |
Zakończenie procesu |
Pilne zdarzenie na łączu TCP/IP |
SIGPOLL |
Zakończenie procesu |
Zdarzenie w urządzeniu wejścia-wyjścia |
SIGEMT |
Zakończenie procesu |
Próba wykonania instrukcji EMT |
Maskowanie Sygnałów
Maskowanie sygnału oznacza czasowe wstrzymanie jego dostarczania. To znaczy, system operacyjny pamięta fakt wygenerowania sygnału, natomiast nie podejmuje żadnych akcji związanych z jego obsługą. W tym stanie sygnał oczekuje na zmianę maski i możliwość dostarczenia (pending signal). System nie zlicza oczekujących sygnałów - w każdej chwili w każdym procesie może oczekiwać co najwyżej jeden sygnał każdego rodzaju, System QNX automatycznie maskuje dostarczony sygnał podczas wykonania jego funkcji obsługi.
Określenie maski sygnałów tzn. zdefiniowanie zbioru sygnałów, których dostarczenie do procesu ma zostać wstrzymane, wymaga wy konania kilku czynności. Po pierwsze, należy utworzyć i zainicjować zbiór sygnałów - pusty lub zawierający wszystkie sygnały. Dalej, należy dodać do zbioru lub usunąć z niego wybrane sygnały. Dopiero teraz można definiować zbiór jako maskę sygnałów. Czynności te wykonują następujące funkcje systemowe:
sigemptyset(SS) - zainicjowanie pustego zbioru sygnałów SS;
sigfillset(SS) - zainicjowanie zbioru sygnałów SS, zawierającego wszystkie sygnały;
sigaddset(SS,S) - dodanie sygnału S do zbioru SS;
sigdelset(SS,S) - usunięcie sygnału S ze zbioru SS;
sigismemher(SS,S) - sprawdzenie, czy sygnał S należy do zbioru sygnałów SS;
sigprocmask(SS) - ustawienie maski określonej przez zbiór sygnałów SS;
igpending(SS) - odczytanie maski do zbioru sygnałów SS.
Obsługa sygnałów
Funkcja obsługi sygnału jest zwykłą funkcją języka C, której nagłówek może mieć postać, np.: void handler(int signo, gdzie handler jest nazwą funkcji, a signo nazwą zmiennej, której system nadaje wartość równą numerowi odebranego sygnału. W treści funkcji obsługi mogą być wywoływane wyłącznie funkcje wielowejściowe (re-entrant). Lista funkcji systemowych dozwolonych do użycia wewnątrz funkcji obsługi sygnału jest podana w tabeli.
Program funkcji obsługi sygnału stanowi część programu procesu i może odwoływać się do tych samych zmiennych, zgodnie z ogólnymi ladami dostępności zmiennych w języku C. Ponieważ jednak wykonanie funkcji obsługi sygnału przebiega asynchronicznie w stosunku do wykonania procesu, przetwarzanie tych samych zmiennych może prowadzić do błędu. Aby temu zapobiec, wprowadzono specjalny typ całkowity, sig_atomic_t, na którym podstawowe operacje zwiększania i zmniejszania wartości, wykonują się w sposób niepodzielny.
Funkcja obsługi sygnału kończy się po dojściu do klamry zamykającej } lub po wywołaniu funkcji siglongjmp. W pierwszym wypadku wykonanie procesu kontynuuje się od miejsca, w którym zostało przerwane przez otrzymany sygnał. W drugim przenosi się do miejsca zaznaczonego wcześniej przez wywołanie funkcji sigsetjmp:
sigsetjmp() - zapamiętanie adresu powrotu i maski sygnałów;
siglongjmp() - powrót z odtworzeniem maski sygnałów.
Funkcje dozwolone wewnątrz funkcji obsługi sygnału
_exit() |
getegid() |
rmdir() |
tcflow() |
access() |
geteuid() |
setgid() |
tcflush() |
alarm () |
getgidf) |
setpgid() |
tcgetattr() |
cfgetispeedf) |
getgroups() |
setsid() |
tcgetpgrp() |
cfgetospeed() |
getpgrp() |
setuid() |
tcsendbreak() |
cfsetispeed() |
getpidf) |
sigaction() |
tcsetattr() |
cfsetospeed() |
getppid() |
sigaddset() |
tcsetgrp() |
chdir() |
getuid() |
sigdelset() |
time() |
chmod() |
kiii() |
sigemptyset() |
times() |
chown() |
link() |
signllset() |
umask() |
dose() |
lseek() |
sigismember() |
uname() |
creat() |
mkdir() |
signal() |
unlink() |
dup() |
mkfifo() |
sigpending() |
ustat() |
duP2() |
open() |
sigprocmask() |
utime() |
execle() |
pathconf() |
sigsuspend() |
wait() |
execve() |
pause() |
sleep() |
waitpid() |
fcntl() |
pipe() |
stat() |
write() |
fork() |
read() |
sysconf() |
|
fstat() |
rename() |
tcdrain() |
|
Zasięg sygnałów
Różne procesy wykonywane w systemie wielozadaniowym mogą należeć do różnych prac wykonywanych przez tego samego lub przez różnych użytkowników. Procesy tej samej pracy są ściśle związane, natomiast procesy należące do różnych prac mogą być całkowicie niezależne. Bezpieczeństwo wykonania prac wymaga ograniczenia swobody komunikacji procesów tak, aby procesy jednej pracy nie mogły zakłócać biegu procesów innej pracy.
Podstawową jednostką ochrony prac jest w systemie QNX grupa procesów. Otwarcie nowej sesji powoduje utworzenie nowej grupy procesów, zawierającej początkowo tylko proces wiodący sesji, który staje się również procesem wiodącym grupy. Identyfikator procesu wiodącego grupy stanowi jednocześnie identyfikator grupy procesów (pgid). Wszystkie procesy potomne, utworzone przez proces wiodący grupy, stają się członkami tej samej grupy i dziedziczą jej identyfikator.
Pojęcia sesji i grupy procesów nie są jednak tożsame, gdyż w tej samej sesji może istnieć wiele grup procesów. Wykonywany proces może utworzyć nową grupę podczas tworzenia procesu potomnego za pomocą funkcji spawu lub w innym momencie, wywołując funkcję utworzenia sesji lub zmiany grupy procesów:
setsid() - utworzenie nowej sesji;
setpgid() - utworzenie grupy procesów lub przeniesienie procesu do innej grupy;
getpgrp() - odczytanie identyfikatora grupy} do której należy proces wywołujący funkcję.
Komunikacja procesów poprzez wiadomości wymaga świadomego współdziałania nadawcy i odbiorcy, a odebranie wiadomości następuje zawsze w chwili wykonania odpowiedniej funkcji systemowej. Zupełnie inne zasady rządzą przekazywaniem sygnałów, które mogą się pojawiać w nieoczekiwanych momentach, powodując wykonanie funkcji obsługi lub zakończenie procesu. Nieskrępowany przepływ sygnałów między procesami mógłby doprowadzić do sytuacji, w której proces należący do jednej pracy zmieniałby niespodzianie przebieg procesu innej pracy. Aby temu zapobiec, możliwość przekazywania sygnałów jest ograniczona do procesów należących do tej samej grupy. Ograniczenie to nie dotyczy administratora systemu, którego procesy mogą przekazywać sygnały do dowolnych innych procesów.
Komunikacja w sieci
System QNX (jądro i menedżer sieci) implementuje warstwę 1 i 2 siedmiowarstwowego modelu ISO, nad którymi są nadbudowane podstawowe usługi warstwy aplikacji. Warstwa fizyczna sieci może być oparta na standardzie Arcnet lub Ethernet, przy czym podsieci obydwu rodzajów mogą pracować w obrębie tego samego systemu. Możliwe jest dublowanie łączy między węzłami, a menedżer sieci optymalizuje wybór drogi połączeń i zapewnia automatyczne przełączenie komunikacji w razie awarii odcinka łącza.
Ograniczenie usług sieciowych systemu operacyjnego do warstwy drugiej (łącza logicznego) sprawia, że komunikacja procesów i związana z nią możliwość współdzielenia zasobów funkcjonuje tylko w obrębie bezpośrednio połączonego segmentu sieci (węzły 1, 2, 3, 4 lub 1, 5). Współdziałanie procesów wykonywanych w węzłach nie mających bezpośredniego połączenia wymaga aktywnego pośrednictwa procesów w węzłach pośrednich. Przykładem może być wykorzystanie potoku obsługiwanego przez proces menedżera plików w węźle 1 do komunikacji procesów wykonywanych w węzłach 4 i 5.
Połączenia
Wszystkie usługi warstwy aplikacji (przekazywanie wiadomości, depozytów i sygnałów) są realizowane zgodnie z protokołem połączeniowym, gwarantującym poprawność przekazywania komunikatów. Współpraca dwóch procesów rozpoczyna się od otwarcia
między nimi połączenia (virtual circuit), znajdującego się pod stałym nadzorem systemu operacyjnego.
Połączenie, otwierane zazwyczaj przez nadawcę pierwszej wiadomości, składa się z dwóch procesów wirtualnych zlokalizowanych po jednym w każdym ze współpracujących komputerów. Proces wirtualny reprezentuje lokalnie odległy proces współpracujący. Funkcja otwierająca połączenie zwraca po zakończeniu identyfikator procesu wirtualnego, który może być używany jako parametr funkcji systemowych nadania wiadomości lub sygnału do odległego procesu, albo odbioru wiadomości od odległego procesu. Każde połączenie może przenosić komunikaty w obydwu kierunkach.
Zakończenie jednego ze współpracujących procesów przed zamknięciem połączenia powoduje zakończenie jego procesów wirtualnych we wszystkich komputerach sieci. W takiej sytuacji, wszystkie procesy współpracujące, oczekujące na przesłanie wiadomości, zostają natychmiast wznowione i powiadomione o zerwaniu połączenia. Odpowiednią wiadomość otrzymują też procesy systemowe (menedżer), które zwalniają wszystkie zasoby używane przez zakończony proces.
W praktyce, procesy aplikacyjne bardzo rzadko otwierają połączenia w sposób jawny. Typowy wstęp do współpracy procesów obejmuje rejestrację nazwy przez jeden z procesów i jej odszukanie przez proces współpracujący. Jeżeli obydwa procesy wykonują się w różnych węzłach sieci, to funkcja odnajdująca nazwę automatycznie otwiera połączenie i zwraca identyfikator procesu wirtualnego.
Połączenia przenoszą w sieci wiadomości i sygnały. Podobny, chociaż odrębny mechanizm służy do przenoszenia depozytów. Po utworzeniu depozytu P, każdy proces wykonywany w tym samym węźle może pobudzić depozyt, wywołując funkcję Trigger(P). Utworzenie w innym węźle oddalonego reprezentanta V depozytu P powoduje otwarcie między nimi połączenia, przenoszącego pobudzenia powodowane przez wywołania funkcji Trigger(V). Z punktu widzenia właściciela depozytu (proces T2), skutki wywołania funkcji Trigger(P) i Trigger(V) są takie same.
Nazwy procesów
Parametrem większości funkcji służących do komunikacji procesów jest numeryczny identyfikator procesu współpracującego. W wypadku procesów wykonywanych w różnych węzłach sieci, parametrem funkcji jest identyfikator procesu wirtualnego, związanego z otwartym połączeniem. Ponieważ identyfikatory są nadawane procesom przez system QNX w sposób niezależny od programisty i nie są z góry znane, pierwszym krokiem do nawiązania komunikacji musi być znalezienie identyfikatora procesu współpracującego.
Rozwiązaniem tego problemu są tekstowe nazwy, które mogą być dowolnie określane podczas opracowywania programów. Podczas wykonania, proces może zarejestrować swą nazwę w dowolnym węźle sieci, w katalogu prowadzonym przez menedżera procesów. Nawiązanie współpracy z innym procesem wymaga odszukania jego nazwy w rejestrze i odwzorowania jej na numeryczny identyfikator procesu. Podstawowe operacje związane z rejestracją i wyszukiwaniem nazw procesów wykonują trzy funkcje systemowe:
qnx_name_attach(N,M) - rejestracja nazwy M w węźle N;
qnx_name_detach(N,M) - wymazanie nazwy M z węzła N;
qnx_name_locate(N,M) - odszukanie nazwy M w węźle N.
Jeżeli znaleziony proces wykonuje się w tym samym węźle, to funkcja qnx_name_locate zwraca identyfikator tego właśnie procesu. Jeżeli znaleziony proces wykonuje się w innym węźle sieci, to funkcja otwiera połączenie między obydwoma procesami i zwraca identyfikator procesu wirtualnego, związanego z tym połączeniem. W obydwu wypadkach zwrócony identyfikator może być użyty jako parametr funkcji komunikacyjnej Send, Receive itd.
Nazwy symboliczne nadawane procesom dzielą się na dwie kategorie. Nazwy zaczynające się od dowolnego znaku, różnego od znaku ukośnika (/), są lokalne w węźle, w którym zostały zarejestrowane.
Nazwy zaczynające się od znaku ukośnika (/), np. /ia./monitor są nazwami globalnymi, których zakres obejmuje całą sieć. Ewidencję nazw globalnych prowadzi systemowy proces menedżera nazw (name-loc). Dla zabezpieczenia systemu przed utratą całej ewidencji nazw w razie awarii komputera wykonującego proces menedżera nazw, system dopuszcza możliwość uruchomienia do dziesięciu kopii tego procesu w różnych węzłach sieci. Spójność ewidencji prowadzonych przez wszystkie kopie jest zachowywana automatycznie.
Podczas rejestracji nazwy, menedżer procesów sprawdza zawartość katalogu nazw i uniemożliwia zarejestrowanie tej samej nazwy przez różne procesy. System nie kontroluje natomiast jednoznaczności nazw globalnych - różne procesy mogą zarejestrować tę samą nazwę. Sytuacja taka może prowadzić do niejednoznaczności i błędnego działania programów. Jako rozwiązanie problemu, proponuje się używanie nazw ścieżkowych z przedrostkami identyfikującymi autorów nazw.
Zakończenie procesu powoduje automatyczne usunięcie wszystkich nazw zarejestrowanych przez ten proces z katalogów wszystkich węzłów i z centralnej ewidencji nazw.
Podsumowanie
Proces jest to wykonywany program. W trakcie postępowania procesu zachodzą zmiany w jego stanie. W trakcie wykonywania procesu zachodzą zmiany w jego stanie. Stan procesu jest określony przez bieżące działanie procesu. Każdy proces może być w jednym z następujących stanów:
gotowy,
wykonywany,
martwy ,
zawieszony.
Proces, który nie jest wykonywany, zostaje umieszczony w oczekującej na coś kolejce. W systemie operacyjnym procesy mogą być szeregowane zgodnie z jednym z trzech różnych algorytmów:
Algorytm FIFO
Algorytm karuzelowy
Algorytm adaptacyjny
Z istnieniem procesu związanie jest jego tworzenie i usuwanie. W systemie QNX utworzenie procesu polega na załadowaniu programu tego procesu do pamięci operacyjnej, wstępnym wypełnieniu struktur systemowych opisujących stan procesu i określeniu numerycznego identyfikatora jednoznacznie wskazującego proces w węźle sieci. Czynności te wykonuje systemowy menedżer procesów na żądanie procesu macierzystego. Zakończenie i usunięcie procesu może nastąpić na polecenie tego procesu lub w wyniku otrzymania sygnału od innego procesu.
Procesy w systemie operacyjnym mogą być wykonywane niezależnie lub mogą ze sobą współpracować. Współpracujące ze sobą procesy musza mieć środki wzajemnej komunikacji. Jądro systemu QNX implementuje trzy mechanizmy komunikacji procesów:
Synchroniczny przekaz wiadomości podczas spotkania,
Sygnalizacje zdarzeń
Asynchroniczny przekaz wiadomości (stałych wiadomości).
Dodatkowo, systemowy proces menadżera plików umożliwia buforowane przekazywanie danych między procesami poprzez:
Potoki (Pipe),
Kolejki (FIFO).
W każdym systemie operacyjnym potrzebny jest także środek informowania procesów o błędach i nienormalnych stanach powstających podczas obliczenia. W systemie QNX role taka odgrywa mechanizm sygnałów. Funkcjonalnie, sygnały przypominają przerwania programowe generowane przez programy jądra systemu, procesy systemowe lub procesy aplikacyjne w chwili wystąpienia sytuacji nienormalnej. Wygenerowany sygnał jest dostarczony do procesu, dla którego jest przeznaczony. Sposób reakcji na dostarczony sygnał zależy od wybranego trybu obsługi:
Proces może zdefiniować własna funkcję (podprogram) obsługi przerwania,
Proces może ignorować dostarczane do siebie sygnały,
Proces może zakończyć swoje wykonywanie w wyniku braku określenia trybu obsługi przerwania,
Proces może maskować wybrane sygnały, powodując czasowe wstrzymywanie ich dostarczenia.
Bibliografia
QNX - System Operacyjny - Krzysztof Sacha
Podstawy systemów operacyjnych - Abraham Silberschatz, Peter B. Galvin
Internet - „wikipedia”
2