1. Jądro monolityczne a mikrojądro

Jądro jest centralną częścią systemu operacyjnego, odpowiedzialną za zarządzanie zasobami maszyny.

1.1. Koncepcja mikrojądra

Z założenia mikrojądro nie udostępnia usług, a jedynie mechanizmy umożliwiające zrealizowanie takich usług. Jądro zawiera jedynie niezbędne elementy:

  • komunikację międzyprocesową
  • zarządzanie procesami
  • obsługę przerwań

Pozostałe zadania takie jak obsługa urządzeń i sieci czy systemów plików realizowane są w trybie użytkownika przez serwery.

microkernel.png

Rys. 1. By Wooptoo – Own work, Public Domain, https://commons.wikimedia.org/w/index.php?curid=4265836

Zaleta mikrojądra: większa niezawodność

  • około 15 tys. wierszy kodu (Minix) w porównaniu z 15 mln. wierszy kodu (Linux) – statystycznie mniejsze prawdopodobieństwo wystąpienia błędu w jądrze (ewentualne błędy przeniesione do przestrzeni użytkownika)
  • strukturalna budowa – sterowniki i serwery nie mogą wykonywać instrukcji uprzywilejowanych, nie mają dostępu do struktur jądra ani do pamięci innych serwerów
  • kod sterowników (zewnętrzny, niezaufany) jest izolowany
  • statyczne struktury danych w jądrze

Wada: mniejsza wydajność

2. Architektura systemu

The_MINIX_3_Microkernel_Architecture.png

Rys. 2. By Faisal Akeel at the English language Wikipedia, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=11479076

Zadania (ang. tasks) mikrojądra:

  • Kernel – obsługa przerwań, szeregowanie procesów, zmiana stanów procesów, komunikacja międzyprocesowa
  • Clock – sterownik zegara
  • System – obsługa podstawowych funkcji systemowych – funkcji jądra
  • Idle – proces, który wykonuje się, kiedy nie ma żadnych innych procesów gotowych do wykonania
  • Asyncm – obsługa komunikacji asynchronicznej

3. Serwery w systemie Minix

Podstawowe serwery wchodzące w skład jądra:

  • pm (process manager) – zarządza procesami, obsługuje funkcje takie jak: fork(), exec(), exit(), kill()
  • sched (scheduler server) – bierze udział w szeregowaniu procesów (więcej na ten temat na następnych zajęciach)
  • vfs (virtual file system) – obsługuje funkcje związane z systemem plików, na przykład: chdir(), open(), read(), select(), write() (więcej o systemach plików na zajęciach 9 i 10)
  • vm (virtual memory manager) – zarządza pamięcią wirtualną (o zarządzaniu pamięcią na zajęciach 11)

Charakterystyczne dla systemu Minix:

  • rs (reincarnation server) – uruchamia serwery i sterowniki, które nie są uruchamiane w tym samym czasie co jądro, ale w kolejnej fazie uruchamiania systemu, nadzoruje ich działanie i w przypadków błędów uruchamia je ponownie; jest to możliwe dzięki temu, że proces rs jest rodzicem uruchamianych przez siebie procesów serwerów i sterowników
  • is (information server) – pomocniczy, dostarcza informacji na temat działania sterowników i serwerów
  • ds (data store server) – pomocniczy, informuje procesy o zmianie konfiguracji po wznowieniu działania serwera, spełnia rolę małej bazy danych

Zadanie G1

Sprawdź, jakie procesy są potomkami serwera rs.

4. Komunikacja międzyprocesowa

Procesy w trybie użytkownika mają dostęp tylko do własnej przestrzeni adresowej. Nie mogą bezpośrednio odczytywać ani zapisywać danych znajdujących się pamięci innych procesów. Do wymiany danych muszą skorzystać z funkcji dostarczanych przez jądro systemu. Tradycyjnymi metodami komunikacji w systemach operacyjnych są łącza, gniazda, pamięć dzielona oraz sygnały. Minix umożliwia komunikację na dwóch poziomach – niskim (wymiana komunikatów i kontrolowane udostępnianie przestrzeni adresowej) oraz wysokim (sygnały oraz pamięć dzielona i semafory – analogiczne do pakietu IPC w systemach uniksowych).

4.1. Wymiana wiadomości

Jądro Miniksa umożliwia przekazywanie komunikatów ustalonego rozmiaru (64-bajtowych).

4.1.1. Komunikat

Każdy komunikat jest typu message i składa się z:

  • 4-bajtowego identyfikatora nadawcy (tak zwany endpoint)
  • 4-bajtowego typu wiadomości
  • 56-bajtowej treści wiadomości

Wszystkie możliwe formaty komunikatów zadeklarowane są w /usr/src/minix/include/minix/ipc.h

4.1.2. Identyfikator

Identyfikator (endpoint) jednoznacznie określa proces. Składa się z indeksu procesu w tablicy procesów oraz numeru pokolenia. Numer pokolenia zabezpiecza przed przypadkowym odczytaniem danych wysłanych do procesu, który już zakończył działanie, przez nowy proces, któremu został przydzielony ten sam indeks.

Istnieją trzy identyfikatory specjalne: ANY, NONE i SELF, których znaczenia łatwo się domyśleć.

Definicję identyfikatora i szczegółowy jego opis znajdziemy w /usr/src/minix/include/minix/endpoint.h

4.1.3. Wysyłanie i odbieranie wiadomości

Podstawowe operacje służące do komunikacji są synchroniczne:

  • ipc_send(endpoint_t dest, message *m) – blokujące wysłanie: nadawca czeka aż odbiorca odbierze wiadomość
  • ipc_receive(endpoint_t src, message *m, int *status) – blokujące odebranie wiadomości
  • ipc_sendrec(endpoint_t src_dest, message *m) – wysłanie wiadomości, po którym nadawca czeka na odpowiedź od odbiorcy

Po wysłaniu wiadomości jądro kopiuje wiadomość z przestrzeni nadawcy do przestrzeni odbiorcy. Ściśle zdefiniowane zasady określają, do jakich procesów dany proces może wysłać wiadomość. Podyktowane jest to względami bezpieczeństwa i ma także zabezpieczać przed możliwością zakleszczenia.

Pozostałe funkcje nie są synchroniczne:

  • ipc_sendnb(endpoint_t dest, message *m) – nieblokujące wysłanie
  • ipc_notify(endpoint_t dest) – nieblokujące powiadomienie bez przekazywania wiadomości
  • ipc_senda(asynmsg_t *table, size_t count) – asynchroniczne wysłanie

4.2. Granty – niskopoziomowa pamięć dzielona

Przekazywanie większej ilości danych przy użyciu 64-bajtowych komunikatów nie jest efektywne. Dlatego w systemie Minix opracowano mechanizm kontrolowanego udostępniania fragmentów pamięci innym procesom: /usr/src/minix/include/minix/safecopies.h.

Więcej na ten temat dowiesz się z wiki.minix3.org/developersguide:memorygrants.

5. Schemat działania serwera

5.1. Serwer pm

Prześledźmy działanie serwera na przykładzie serwera pm. Zauważmy najpierw, że znane nam z poprzednich zajęć _syscall() polega głównie na wywołaniu ipc_sendrec(): /usr/src/minix/lib/libc/sys/syscall.c.

Natomiast podstawowym działaniem serwera jest oczekiwanie na przychodzące wiadomości: /usr/src/minix/servers/pm/main.c (wiersz 62). Funkcja sef_receive_status(ANY, &m_in, &ipc_status) zdefiniowana w /usr/src/minix/lib/libsys/sef.c wywołuje ipc_receive().

Pole m_type (wiersz 79) przechowuje numer wywołania systemowego. Jeśli jest on poprawny, zostaje wywołana funkcja odpowiadająca temu numerowi zgodnie z zawartością tablicy call_vec[] zdefiniowanej w /usr/src/minix/servers/pm/table.c. Na końcu (wiersz 107) serwer odpowiada procesowi, który zgłosił wywołanie systemowe, wykonując reply(), które wywołuje ipc_sendnb() (wiersz 268).

Zadanie G2

Zastanów się, jak serwer pm powinien obsłużyć funkcję wait(), którą wywołuje proces macierzysty w oczekiwaniu na zakończenie potomka.

  1. Podaj ciąg wywołań odpowiednich funkcji, począwszy od funkcji bibliotecznej.
  2. Jaki numer ma odpowiadające wait() wywołanie systemowe?
  3. W jaki sposób zrealizowane jest wstrzymywanie procesu macierzystego, aż do momentu zakończenia jednego z potomków?
  4. W jaki sposób proces macierzysty zostaje powiadomiony o zakończeniu potomka?

5.2. Semafory i pamięć dzielona, czyli serwer ipc

W Miniksie dostępne są także wysokopoziomowe mechanizmy komunikacji takie jak semafory i pamięć dzielona. Odpowiednie wywołania systemowe obsługuje serwer ipc.

Zadanie G3

Na podstawie analizy kodu serwera ipc określ, jakie operacje semaforowe są dostępne. Sprawdź, jak zrealizowane jest wstrzymywanie procesu na semaforze.

6. Do poczytania i posłuchania