Mikrojądro - Microkernel

Struktura odpowiednio monolitycznych i mikrojądrowych systemów operacyjnych

W informatyce , o mikrojądro (często skracane jako ľ-kernel ) jest prawie minimalna ilość oprogramowania , które może dostarczyć mechanizmów potrzebnych do wdrożenia systemu operacyjnego (OS). Mechanizmy te obejmują zarządzanie przestrzenią adresową niskiego poziomu, zarządzanie wątkami i komunikację między procesami (IPC).

Jeśli sprzęt zapewnia wiele pierścieni lub trybów procesora , mikrojądro może być jedynym oprogramowaniem działającym na najbardziej uprzywilejowanym poziomie, który jest ogólnie określany jako tryb nadzorcy lub jądra . Tradycyjne funkcje systemu operacyjnego, takie jak sterowniki urządzeń , stosy protokołów i systemy plików , są zazwyczaj usuwane z samego mikrojądra i zamiast tego są uruchamiane w przestrzeni użytkownika .

Pod względem rozmiaru kodu źródłowego mikrojądra są często mniejsze niż jądra monolityczne . Na przykład mikrojądro MINIX 3 ma tylko około 12 000 linii kodu.

Historia

Microkernels wywodzi swoje korzenie z duńskiego pioniera komputerowego Pera Brincha Hansena i jego pracy w duńskiej firmie komputerowej Regnecentralen, gdzie kierował pracami nad rozwojem oprogramowania dla komputera RC 4000. W 1967 roku Regnecentralen instalowało prototyp RC 4000 w polskiej fabryce nawozów w Puławach . Komputer wykorzystywał niewielki system operacyjny czasu rzeczywistego dostosowany do potrzeb zakładu. Brinch Hansen i jego zespół zaniepokoili się brakiem ogólności i możliwości ponownego wykorzystania systemu RC 4000. Obawiali się, że każda instalacja będzie wymagała innego systemu operacyjnego, więc zaczęli badać nowatorskie i bardziej ogólne sposoby tworzenia oprogramowania dla RC 4000. W 1969 roku ich wysiłki zaowocowały ukończeniem systemu wieloprogramowego RC 4000 . Jej jądro zapewniało komunikację międzyprocesową opartą na przekazywaniu wiadomości dla maksymalnie 23 nieuprzywilejowanych procesów, z których 8 naraz było chronionych przed sobą. Ponadto zaimplementował planowanie przedziałów czasowych programów wykonywanych równolegle, inicjację i kontrolę wykonywania programu na żądanie innych uruchomionych programów oraz inicjację przesyłania danych do lub z urządzeń peryferyjnych. Poza tymi podstawowymi mechanizmami nie miał wbudowanej strategii realizacji programu i alokacji zasobów. Strategia ta miała być realizowana przez hierarchię uruchomionych programów, w której procesy nadrzędne miały pełną kontrolę nad procesami potomnymi i działały jako ich systemy operacyjne.

W ślad za pracą Brincha Hansena mikrojądra są opracowywane od lat 70. XX wieku. Sam termin mikrojądra pojawił się po raz pierwszy nie później niż w 1981 roku. Mikrojądra miały być odpowiedzią na zmiany w świecie komputerów i na kilka wyzwań, które przystosowują istniejące „ monojądra ” do tych nowych systemów. Cały czas opracowywano nowe sterowniki urządzeń, stosy protokołów, systemy plików i inne systemy niskiego poziomu. Kod ten zwykle znajdował się w jądrze monolitycznym, a zatem wymagał znacznej pracy i starannego zarządzania kodem. Mikrojądra zostały opracowane z myślą, że wszystkie te usługi zostaną zaimplementowane jako programy w przestrzeni użytkownika, jak każdy inny, umożliwiając pracę nad nimi monolitycznie oraz uruchamianie i zatrzymywanie jak każdy inny program. Umożliwiłoby to nie tylko łatwiejszą pracę nad tymi usługami, ale także oddzielenie kodu jądra, aby umożliwić jego precyzyjne dostrojenie bez martwienia się o niezamierzone skutki uboczne. Co więcej, umożliwiłoby to „zbudowanie” zupełnie nowych systemów operacyjnych na wspólnym rdzeniu, pomagając w badaniach nad systemami operacyjnymi.

Mikrojądra były bardzo gorącym tematem w latach 80-tych, kiedy wprowadzano pierwsze użyteczne sieci lokalne. Jądro AmigaOS Exec było wczesnym przykładem, wprowadzonym w 1986 roku i używanym w komputerach PC ze względnym komercyjnym sukcesem. Brak ochrony pamięci, uważany pod innymi względami za wadę, umożliwił temu jądru bardzo wysoką wydajność przekazywania komunikatów, ponieważ nie musiało kopiować danych podczas wymiany komunikatów między programami w przestrzeni użytkownika.

Te same mechanizmy, które umożliwiły dystrybucję jądra w przestrzeni użytkownika, pozwoliły również na dystrybucję systemu przez łącza sieciowe. Pierwsze mikrojądra, w szczególności Mach stworzony przez Richarda Rashida , okazały się rozczarowujące, ale nieodłączne zalety okazały się tak duże, że był to główny kierunek badań w późnych latach 90-tych. Jednak w tym czasie szybkość komputerów znacznie wzrosła w stosunku do systemów sieciowych, a wady wydajności przytłaczały zalety w zakresie rozwoju.

Podejmowano wiele prób dostosowania istniejących systemów w celu uzyskania lepszej wydajności, ale obciążenie zawsze było znaczne i większość tych wysiłków wymagała przeniesienia programów z przestrzeni użytkownika z powrotem do jądra. Do roku 2000 większość dużych Mach wysiłki jądra skończyła, choć Apple MacOS , wydany w 2001 roku, nadal wykorzystuje hybrydowy jądra o nazwie xnu , który łączy w sobie mocno zmodyfikowany (hybrydowy) OSF / 1 „s Mach kernel ( OSFMK 7.3 jądra) z kod z BSD UNIX, a to jądro jest również używane w iOS , tvOS i watchOS . Windows NT , począwszy od NT 3.1 i kontynuując Windows 10, używa hybrydowego projektu jądra. Począwszy od 2012 roku, GNU Hurd oparty na Mach jest również funkcjonalny i zawarty w testowych wersjach Arch Linux i Debian .

Chociaż główne prace nad mikrojądrami w dużej mierze zakończyły się, eksperymentatorzy kontynuowali rozwój. Od tego czasu wykazano, że wiele problemów z wydajnością wcześniejszych projektów nie było podstawowym ograniczeniem koncepcji, ale wynikało z chęci projektanta do wykorzystania systemów jednozadaniowych do wdrożenia jak największej liczby tych usług. Zastosowanie bardziej pragmatycznego podejścia do problemu, w tym kodu asemblera i poleganie na procesorze w celu wymuszania koncepcji normalnie obsługiwanych w oprogramowaniu, doprowadziło do nowej serii mikrojąder o radykalnie zwiększonej wydajności.

Mikrojądra są blisko spokrewnione z egzokernelami . Mają też wiele wspólnego z hipernadzorcami , ale te ostatnie nie rości sobie prawa do minimalizmu i specjalizują się w obsłudze maszyn wirtualnych ; mikrojądro L4 często znajduje zastosowanie w pojemności hypervisor.

Wstęp

Jądra wczesnych systemów operacyjnych były raczej małe, częściowo z powodu ograniczonej pamięci komputera. Wraz ze wzrostem możliwości komputerów rosła również liczba urządzeń kontrolowanych przez jądro. We wczesnej historii Unixa jądra były generalnie małe, mimo że zawierały różne sterowniki urządzeń i implementacje systemów plików . Kiedy przestrzenie adresowe wzrosły z 16 do 32 bitów, projekt jądra nie był już ograniczany przez architekturę sprzętową, a jądra zaczęły się rozrastać.

Berkeley Software Distribution (BSD) z Unix rozpoczął erę większymi jądrami. Oprócz obsługi podstawowego systemu składającego się z procesora, dysków i drukarek, BSD dodało kompletny system sieciowy TCP/IP oraz szereg „wirtualnych” urządzeń, które pozwalały istniejącym programom na „niewidoczną” pracę w sieci. Ten wzrost trwał przez wiele lat, w wyniku czego powstały jądra z milionami linii kodu źródłowego . W wyniku tego wzrostu ziarna były podatne na błędy i stawały się coraz trudniejsze w utrzymaniu.

Mikrojądro miało rozwiązać ten wzrost jąder i wynikające z tego trudności. W teorii konstrukcja mikrojądra pozwala na łatwiejsze zarządzanie kodem dzięki podziałowi na usługi w przestrzeni użytkownika . Pozwala to również na zwiększenie bezpieczeństwa i stabilności wynikające z mniejszej ilości kodu działającego w trybie jądra . Na przykład, jeśli usługa sieciowa ulegnie awarii z powodu przepełnienia bufora , tylko pamięć usługi sieciowej zostanie uszkodzona, pozostawiając resztę systemu nadal działającą.

Komunikacja między procesami

Komunikacja międzyprocesowa (IPC) to dowolny mechanizm, który pozwala oddzielnym procesom komunikować się ze sobą, zwykle poprzez wysyłanie wiadomości . Pamięć współdzielona to, ściśle określony, również mechanizm komunikacji międzyprocesowej, ale skrót IPC zwykle odnosi się tylko do przekazywania wiadomości i to ostatnie jest szczególnie istotne w przypadku mikrojądra. IPC pozwala na budowanie systemu operacyjnego z kilku mniejszych programów zwanych serwerami, które są używane przez inne programy w systemie, wywoływane przez IPC. Większość lub całość obsługi sprzętu peryferyjnego jest obsługiwana w ten sposób, z serwerami sterowników urządzeń, stosami protokołów sieciowych , systemami plików, grafiką itp.

IPC może być synchroniczny lub asynchroniczny. Asynchroniczne IPC jest analogiczne do komunikacji sieciowej: nadawca wysyła wiadomość i kontynuuje wykonywanie. Odbiorca sprawdza ( sonduje ) dostępność wiadomości lub jest o niej powiadamiany za pośrednictwem mechanizmu powiadomień. Asynchroniczne IPC wymaga, aby jądro obsługiwało bufory i kolejki wiadomości oraz radziło sobie z przepełnieniem bufora; wymaga również podwójnego kopiowania wiadomości (nadawcy do jądra i jądra do odbiorcy). W synchronicznym IPC pierwsza strona (nadawca lub odbiorca) blokuje się, dopóki druga strona nie będzie gotowa do wykonania IPC. Nie wymaga buforowania ani wielu kopii, ale niejawne spotkanie może utrudnić programowanie. Większość programistów preferuje wysyłanie asynchroniczne i odbieranie synchroniczne.

Mikrojądra pierwszej generacji zazwyczaj obsługiwały zarówno synchroniczne, jak i asynchroniczne IPC i cierpiały na słabą wydajność IPC. Jochen Liedtke założył, że głównym powodem tak niskiej wydajności jest zaprojektowanie i wdrożenie mechanizmów IPC. W swoim mikrojądrze L4 był pionierem metod, które obniżyły koszty IPC o rząd wielkości . Obejmują one wywołanie systemowe IPC, które obsługuje operacje wysyłania i odbierania, czyniąc wszystkie IPC synchronicznymi i przekazując jak najwięcej danych w rejestrach. Ponadto Liedtke wprowadził koncepcję bezpośredniego przełączania procesów , w której podczas wykonywania IPC (niepełne) przełączanie kontekstu jest wykonywane od nadawcy bezpośrednio do odbiorcy. Jeżeli, tak jak w L4, część lub całość komunikatu jest przekazywana w rejestrach, przenosi to część komunikatu w rejestrze bez żadnego kopiowania. Ponadto unika się narzutu związanego z wywoływaniem programu planującego; jest to szczególnie korzystne w powszechnym przypadku, gdy IPC jest używany w trybie zdalnego wywołania procedury (RPC) przez klienta wywołującego serwer. Inna optymalizacja, zwana planowaniem z opóźnieniem , pozwala uniknąć przechodzenia kolejek planowania podczas IPC, pozostawiając wątki blokujące podczas IPC w kolejce gotowości. Po wywołaniu harmonogramu przenosi takie wątki do odpowiedniej kolejki oczekującej. Ponieważ w wielu przypadkach wątek zostaje odblokowany przed następnym wywołaniem programu planującego, takie podejście oszczędza znaczną ilość pracy. Podobne podejścia zostały od tego czasu przyjęte przez QNX i MINIX 3 .

W serii eksperymentów Chen Berszad porównaniu pamięć cykli na instrukcję (MCPI) monolitycznego Ultrix z tych mikrojądra Mach połączeniu z 4.3BSD Unix serwer działa w przestrzeni użytkownika . Ich wyniki wyjaśniły gorszą wydajność Macha wyższą wartością MCPI i wykazały, że samo IPC nie jest odpowiedzialne za większość ogólnych kosztów systemu, co sugeruje, że optymalizacje skoncentrowane wyłącznie na IPC będą miały ograniczony wpływ. Liedtke później dopracował wyniki Chena i Bershada, zauważając, że większość różnic między Ultrix i Mach MCPI była spowodowana brakami pojemności pamięci podręcznej i doszedł do wniosku, że drastyczne zmniejszenie zestawu roboczego pamięci podręcznej mikrojądra rozwiąże problem.

W systemie klient-serwer większość komunikacji jest zasadniczo synchroniczna, nawet jeśli używa się asynchronicznych operacji podstawowych, ponieważ typową operacją jest to, że klient wywołuje serwer, a następnie czeka na odpowiedź. Ponieważ pozwala to również na bardziej wydajną implementację, większość mikrojąder ogólnie podążała za przykładem L4 i zapewniała tylko synchroniczny prymityw IPC. Asynchroniczne IPC można zaimplementować na górze przy użyciu wątków pomocniczych. Jednak doświadczenie pokazało, że użyteczność synchronicznego IPC jest wątpliwa: synchroniczny IPC wymusza wielowątkowy projekt w innych prostych systemach, z wynikającą z tego złożonością synchronizacji. Co więcej, wywołanie serwera podobne do RPC sekwencjonuje klienta i serwer, czego należy unikać, jeśli działają na oddzielnych rdzeniach. Wersje L4 wdrożone w produktach komercyjnych uznały zatem za konieczne dodanie mechanizmu powiadamiania asynchronicznego w celu lepszej obsługi komunikacji asynchronicznej. Ten mechanizm podobny do sygnału nie przenosi danych i dlatego nie wymaga buforowania przez jądro. Mając dwie formy IPC, naruszyli jednak zasadę minimalizmu. Inne wersje L4 całkowicie przeszły na asynchroniczny IPC.

Ponieważ synchroniczne IPC blokuje pierwszą stronę, dopóki druga nie będzie gotowa, nieograniczone korzystanie może łatwo doprowadzić do zakleszczenia. Co więcej, klient może łatwo przeprowadzić atak typu „odmowa usługi” na serwer, wysyłając żądanie i nigdy nie próbując otrzymać odpowiedzi. Dlatego synchroniczny IPC musi zapewniać środki zapobiegające blokowaniu na czas nieokreślony. Wiele mikrojąder zapewnia limity czasu na połączenia IPC, które ograniczają czas blokowania. W praktyce wybór rozsądnych wartości limitów czasu jest trudny, a systemy prawie nieuchronnie wykorzystują nieskończone limity czasu dla klientów i zerowe limity czasu dla serwerów. W konsekwencji trendem jest nie zapewnianie arbitralnych limitów czasu, a jedynie flaga, która wskazuje, że IPC powinien natychmiast zawieść, jeśli partner nie jest gotowy. Takie podejście skutecznie zapewnia wybór dwóch wartości limitu czasu: zero i nieskończoność. Ostatnie wersje L4 i MINIX poszły tą ścieżką (starsze wersje L4 wykorzystywały limity czasu). QNX unika problemu, wymagając od klienta określenia bufora odpowiedzi jako części wywołania wysyłania wiadomości. Gdy serwer odpowiada, jądro kopiuje dane do bufora klienta, bez konieczności oczekiwania na jawną odpowiedź klienta.

Serwery

Serwery mikrojądra są zasadniczo programami demonów , jak wszystkie inne, z wyjątkiem tego, że jądro przyznaje niektórym z nich uprawnienia do interakcji z częściami pamięci fizycznej, które w przeciwnym razie są niedostępne dla większości programów. Pozwala to niektórym serwerom, w szczególności sterownikom urządzeń, na bezpośrednią interakcję ze sprzętem.

Podstawowy zestaw serwerów dla mikrojądra ogólnego przeznaczenia obejmuje serwery systemu plików, serwery sterowników urządzeń, serwery sieciowe, serwery wyświetlania i serwery urządzeń interfejsu użytkownika. Ten zestaw serwerów (zaczerpnięty z QNX ) dostarcza mniej więcej zestaw usług oferowanych przez monolityczne jądro Unixa . Niezbędne serwery są uruchamiane podczas uruchamiania systemu i zapewniają usługi, takie jak dostęp do plików, sieci i urządzeń, zwykłym aplikacjom. Gdy takie serwery działają w środowisku aplikacji użytkownika, tworzenie serwerów jest podobne do tworzenia zwykłych aplikacji, a nie do procesu budowania i uruchamiania potrzebnego do rozwoju jądra.

Ponadto wiele „awarii” można naprawić, po prostu zatrzymując i ponownie uruchamiając serwer . Jednak część stanu systemu jest tracona wraz z uszkodzonym serwerem, stąd takie podejście wymaga od aplikacji radzenia sobie z awarią. Dobrym przykładem jest serwer odpowiedzialny za połączenia TCP/IP : Jeśli ten serwer zostanie zrestartowany, aplikacje doświadczą „utracenia” połączenia, co jest normalnym zjawiskiem w systemie sieciowym. W przypadku innych usług awaria jest mniej oczekiwana i może wymagać zmian w kodzie aplikacji. W przypadku QNX możliwość ponownego uruchomienia jest oferowana jako zestaw narzędzi wysokiej dostępności QNX.

Sterowniki urządzeń

Sterowniki urządzeń często wykonują bezpośredni dostęp do pamięci (DMA), a zatem mogą zapisywać w dowolnych lokalizacjach pamięci fizycznej, w tym w różnych strukturach danych jądra. Takim kierowcom należy zatem ufać. Powszechnym błędem jest to, że oznacza to, że muszą być częścią jądra. W rzeczywistości sterownik nie jest z natury mniej lub bardziej godny zaufania, ponieważ jest częścią jądra.

Chociaż uruchomienie sterownika urządzenia w przestrzeni użytkownika niekoniecznie zmniejsza szkody, jakie może spowodować źle działający sterownik, w praktyce jest to korzystne dla stabilności systemu w obecności wadliwych (a nie złośliwych) sterowników: naruszenia dostępu do pamięci przez sam kod sterownika ( w przeciwieństwie do urządzenia) może nadal być przechwytywany przez sprzęt do zarządzania pamięcią. Co więcej, wiele urządzeń nie obsługuje DMA, a ich sterowniki mogą stać się nieufne, uruchamiając je w przestrzeni użytkownika. Ostatnio coraz więcej komputerów jest wyposażonych w moduły IOMMU , z których wiele można wykorzystać do ograniczenia dostępu urządzenia do pamięci fizycznej. Pozwala to również na utratę zaufania sterowników trybu użytkownika.

Sterowniki trybu użytkownika w rzeczywistości poprzedzają mikrojądra. Systemu Terminal Michigan (MTS), w 1967 roku, przy wsparciu sterowników przestrzeni użytkownika (w tym jego obsługę systemu plików), pierwszy system operacyjny, aby być zaprojektowane z tej możliwości. W przeszłości sterowniki stanowiły mniejszy problem, ponieważ liczba urządzeń i tak była niewielka i godna zaufania, więc posiadanie ich w jądrze uprościło projekt i pozwoliło uniknąć potencjalnych problemów z wydajnością. Doprowadziło to do tradycyjnego stylu sterowników w jądrze w systemach Unix, Linux i Windows NT. Wraz z rozprzestrzenianiem się różnego rodzaju urządzeń peryferyjnych ilość kodu sterownika wzrosła, aw nowoczesnych systemach operacyjnych dominuje rozmiar kodu jądra.

Niezbędne komponenty i minimalność

Ponieważ mikrojądro musi umożliwiać budowanie na wierzchu dowolnych usług systemu operacyjnego, musi zapewniać pewną podstawową funkcjonalność. Obejmuje to co najmniej:

Ten minimalny projekt został zapoczątkowany przez Brinch Hansen „s Nucleus i hypervisor z IBM VM . Zostało to sformalizowane w zasadzie minimalności Liedtke :

Koncepcja jest tolerowana wewnątrz mikrojądra tylko wtedy, gdy przeniesienie jej poza jądro, tj. umożliwienie konkurencyjnych implementacji, uniemożliwiłoby implementację wymaganej funkcjonalności systemu.

Wszystko inne można wykonać w programie trybu użytkownika, chociaż sterowniki urządzeń zaimplementowane jako programy użytkownika mogą w niektórych architekturach procesorów wymagać specjalnych uprawnień, aby uzyskać dostęp do sprzętu we/wy.

Z zasadą minimalności, równie ważne dla projektowania mikrojądra, jest rozdzielenie mechanizmu i polityki , co umożliwia budowanie dowolnych systemów na bazie minimalnego jądra. Żadna polityka wbudowana w jądro nie może zostać nadpisana na poziomie użytkownika i dlatego ogranicza ogólność mikrojądra. Politykę zaimplementowaną w serwerach na poziomie użytkownika można zmienić, wymieniając serwery (lub pozwalając aplikacji wybierać między konkurencyjnymi serwerami oferującymi podobne usługi).

Aby zwiększyć wydajność, większość mikrojąder zawiera programy planujące i zarządzające zegarami, co narusza zasadę minimalności i zasadę separacji polityki od mechanizmu.

Uruchomienie ( bootowanie ) systemu opartego na mikrojądrze wymaga sterowników urządzeń , które nie są częścią jądra. Zazwyczaj oznacza to, że są one spakowane z jądrem w obrazie rozruchowym, a jądro obsługuje protokół ładowania początkowego, który definiuje sposób lokalizacji i uruchamiania sterowników; jest to tradycyjna procedura ładowania początkowego mikrojądra L4 . Niektóre mikrojądra upraszczają to, umieszczając kilka kluczowych sterowników wewnątrz jądra (z naruszeniem zasady minimalności), przykładami są LynxOS i oryginalny Minix . Niektóre zawierają nawet system plików w jądrze, aby uprościć uruchamianie. System oparty na mikrojądrze może uruchamiać się za pomocą programu ładującego zgodnego z wieloma systemami rozruchowymi. Takie systemy zwykle ładują serwery połączone statycznie w celu wykonania początkowego ładowania początkowego lub zamontowania obrazu systemu operacyjnego w celu kontynuowania ładowania początkowego.

Kluczowym elementem mikrojądra jest dobry system IPC i projekt menedżera pamięci wirtualnej, który pozwala w bezpieczny sposób zaimplementować obsługę błędów stron i zamianę w serwerach trybu użytkownika. Ponieważ wszystkie usługi są wykonywane przez programy działające w trybie użytkownika, wydajne środki komunikacji między programami są niezbędne, o wiele bardziej niż w jądrach monolitycznych. Konstrukcja systemu IPC tworzy lub łamie mikrojądro. Aby był skuteczny, system IPC musi nie tylko charakteryzować się niskim narzutem, ale także dobrze współdziałać z planowaniem procesora.

Wydajność

W przypadku większości procesorów głównego nurtu uzyskanie usługi jest z natury droższe w systemie opartym na mikrojądrze niż w systemie monolitycznym. W systemie monolitycznym usługa jest uzyskiwana przez pojedyncze wywołanie systemowe, co wymaga dwóch przełączeń trybów (zmiany ringu procesora lub trybu CPU ). W systemie opartym na mikrojądrze usługa jest uzyskiwana poprzez wysłanie wiadomości IPC do serwera i uzyskanie wyniku w innej wiadomości IPC z serwera. Wymaga to przełączenia kontekstu, jeśli sterowniki są zaimplementowane jako procesy, lub wywołania funkcji, jeśli są zaimplementowane jako procedury. Ponadto przekazywanie rzeczywistych danych do serwera iz powrotem może spowodować dodatkowe obciążenie związane z kopiowaniem, podczas gdy w systemie monolitycznym jądro może uzyskać bezpośredni dostęp do danych w buforach klienta.

Wydajność jest zatem potencjalnym problemem w systemach z mikrojądrem. Doświadczenia z mikrojądrami pierwszej generacji, takimi jak Mach i ChorusOS, pokazały, że oparte na nich systemy działały bardzo słabo. Jednak Jochen Liedtke wykazał, że problemy z wydajnością Macha były wynikiem złego projektu i implementacji, w szczególności nadmiernego zużycia pamięci podręcznej Macha . Liedtke zademonstrował za pomocą własnego mikrojądra L4, że dzięki starannemu projektowi i wdrożeniu, a zwłaszcza przestrzeganiu zasady minimalności, koszty IPC można zmniejszyć o ponad rząd wielkości w porównaniu z Mach. Wydajność IPC L4 jest nadal niepokonana w wielu architekturach.

Chociaż wyniki te pokazują, że słaba wydajność systemów opartych na mikrojądrach pierwszej generacji nie jest reprezentatywna dla jąder drugiej generacji, takich jak L4, nie stanowi to dowodu na to, że systemy oparte na mikrojądrach mogą być budowane z dobrą wydajnością. Wykazano, że monolityczny serwer linuksowy przeniesiony do L4 wykazuje tylko kilka procent narzutu w porównaniu z natywnym systemem Linux. Jednak taki system jednoserwerowy wykazuje niewiele, jeśli w ogóle, korzyści, jakie mikrojądra mają zapewniać, dzieląc funkcjonalność systemu operacyjnego na oddzielne serwery.

Istnieje szereg komercyjnych systemów wieloserwerowych, w szczególności systemy czasu rzeczywistego QNX i Integrity . Nie opublikowano żadnego kompleksowego porównania wydajności w stosunku do systemów monolitycznych dla tych systemów wieloserwerowych. Co więcej, wydajność nie wydaje się być nadrzędnym problemem dla tych systemów komercyjnych, które zamiast tego kładą nacisk na niezawodnie szybkie czasy odpowiedzi obsługi przerwań (QNX) i prostotę ze względu na niezawodność. Próbą zbudowania wysokowydajnego wieloserwerowego systemu operacyjnego był projekt IBM Sawmill Linux. Jednak ten projekt nigdy nie został ukończony.

W międzyczasie wykazano, że sterowniki urządzeń na poziomie użytkownika mogą zbliżyć się do wydajności sterowników wbudowanych w jądro, nawet w przypadku tak wysokoprzepustowych urządzeń o dużych przerwach, jak Gigabit Ethernet. Wydaje się to sugerować, że możliwe są wysokowydajne systemy wieloserwerowe.

Bezpieczeństwo

Korzyści bezpieczeństwa mikrojądra były często omawiane. W kontekście bezpieczeństwa zasada minimalności mikrojądra jest, jak twierdzą niektórzy, bezpośrednią konsekwencją zasady najmniejszych uprawnień , zgodnie z którą cały kod powinien mieć tylko uprawnienia potrzebne do zapewnienia wymaganej funkcjonalności. Minimalność wymaga, aby zaufana baza obliczeniowa systemu (TCB) była minimalna. Ponieważ jądro (kod wykonywany w trybie uprzywilejowanym sprzętu) ma niesprawdzony dostęp do dowolnych danych i w ten sposób może naruszać ich integralność lub poufność, jądro jest zawsze częścią TCB. Minimalizacja tego jest naturalna w przypadku projektu opartego na bezpieczeństwie.

W związku z tym projekty mikrojądra zostały wykorzystane w systemach zaprojektowanych do zastosowań o wysokim poziomie bezpieczeństwa, w tym KeyKOS , EROS i systemy wojskowe. W rzeczywistości wspólne kryteria (CC) na najwyższym poziomie pewności ( Evaluation Assurance Level (EAL) 7) mają wyraźny wymóg, aby cel oceny był „prosty”, potwierdzenie praktycznej niemożności ustalenia prawdziwej wiarygodności złożonego systemu. Znowu termin „prosty” jest mylący i źle zdefiniowany. Przynajmniej Kryteria oceny zaufanych systemów komputerowych Departamentu Obrony wprowadziły nieco bardziej precyzyjne słownictwo w klasach B3/A1:

„TCB [wdroży] kompletne, koncepcyjnie proste mechanizmy ochrony o precyzyjnie określonej semantyce. Istotne inżynieria systemu powinna być ukierunkowana na zminimalizowanie złożoności TCB, a także wykluczenie z TCB tych modułów, które nie są krytyczne dla ochrony.”

—  Kryteria oceny zaufanych systemów komputerowych Departamentu Obrony

W 2018 r. w artykule przedstawionym na konferencji Asia-Pacific Systems Conference stwierdzono, że mikrojądra są wyraźnie bezpieczniejsze niż jądra monolityczne, badając wszystkie opublikowane w tamtym czasie krytyczne CVE dla jądra systemu Linux. Badanie wykazało, że 40% problemów w ogóle nie może wystąpić w formalnie zweryfikowanym mikrojądrze, a tylko 4% problemów pozostanie całkowicie nie złagodzonych w takim systemie.

Trzecia generacja

Nowsze prace nad mikrojądrami skupiają się na formalnych specyfikacjach API jądra oraz formalnych dowodach właściwości bezpieczeństwa API i poprawności implementacji. Pierwszym tego przykładem jest matematyczny dowód mechanizmów ograniczania w EROS, oparty na uproszczonym modelu EROS API. Niedawno (w 2007 r.) przeprowadzono obszerny zestaw sprawdzanych maszynowo dowodów właściwości modelu ochrony seL4 , wersji L4.

Doprowadziło to do powstania tak zwanych mikrojąder trzeciej generacji , charakteryzujących się interfejsem API zorientowanym na bezpieczeństwo z dostępem do zasobów kontrolowanym przez możliwości , wirtualizacją jako pierwszorzędnym problemem, nowatorskimi podejściami do zarządzania zasobami jądra oraz celem projektowym dotyczącym przydatności do analiza formalna , poza zwykłym celem wysokiej wydajności. Przykładami są Coyotos , seL4 , Nova, Redox i Fiasco.OC.

W przypadku seL4 uzyskano pełną formalną weryfikację implementacji, czyli matematyczny dowód, że implementacja jądra jest zgodna z jego formalną specyfikacją. Daje to gwarancję, że właściwości sprawdzone w interfejsie API rzeczywiście mają zastosowanie dla prawdziwego jądra, a stopień pewności wykracza nawet poza CC EAL7. Następnie pojawiły się dowody właściwości API służących do egzekwowania zabezpieczeń oraz dowód pokazujący, że wykonywalny kod binarny jest poprawną translacją implementacji C, usuwając kompilator z bazy TCB. Podsumowując, dowody te stanowią kompleksowy dowód właściwości zabezpieczeń jądra.

Przykłady

Oto kilka przykładów mikrojąder:

Nanokernel

Termin nanokernel lub pikokernel historycznie odnosił się do:

  • Jądro, w którym całkowita ilość kodu jądra, tj. kodu wykonywanego w trybie uprzywilejowanym sprzętu, jest bardzo mała. Termin pikokernel był czasami używany do dalszego podkreślenia małego rozmiaru. Termin nanokernel został ukuty przez Jonathana S. Shapiro w artykule The KeyKOS NanoKernel Architecture . Była to sardoniczna odpowiedź na Macha , który twierdził, że jest mikrojądrem, podczas gdy Shapiro uważał go za monolityczny, zasadniczo nieustrukturyzowany i wolniejszy niż systemy, które chciał zastąpić. Późniejsze ponowne użycie i odpowiedź na ten termin, w tym moneta pikokernel, sugeruje, że w dużej mierze ten punkt został pominięty. Zarówno nanokernel, jak i pikokernel mają następnie to samo znaczenie, które wyraża termin mikrojądro.
  • Warstwa wirtualizacji pod systemem operacyjnym, bardziej poprawnie nazywana hiperwizorem .
  • Sprzętu warstwę abstrakcji , która tworzy część najniższego poziomu jądra, czasem wykorzystywane w celu zapewnienia w czasie rzeczywistym funkcjonalności do normalnych systemów operacyjnych, jak Adeos .

Istnieje również co najmniej jeden przypadek, w którym termin nanojądro jest używany nie w odniesieniu do małego jądra, ale takiego, które obsługuje rozdzielczość zegara nanosekundowego .

Zobacz też

Bibliografia

Dalsza lektura