Jádro plní dvě základní funkce: implementuje mechanismus doručování zpráv a řídí plánování procesů. Mimo to obsahuje nejnižší úroveň obslužných rutin přerušení a zajišťuje převod hardwarového přerušení na zaslání zprávy příslušnému tasku. Mechanismus předávání zpráv i plánovač již byly popsány ve dřívějších sekcích, proto se zde uvedeme pouze poznámky sloužící jako vodítko pro implementaci.
Doposud jsme se v souvislosti s předáváním zpráv odkazovali na primitiva SEND, RECEIVE a SENDREC. Jejich skutečná implementace je realizována pomocí maker, jež jsou využívána ostatními vrstvami systému, včetně systémových knihoven. Makro se rozvine na vyvolání softwarového přerušení, které je nasměrováno na dále popisovanou rutinu jádra s_call. Parametry volání se předávají v registrech. Jde zejména o informaci o typu primitiva (SEND/RECEIVE/SENDREC), identifikace procesu, kterému se má zpráva předat, resp. od kterého se má zpráva převzít a pointer na buffer se zprávou (near pointer, v rámci adresového prostoru právě běžícího procesu). Pro určení odesílatele zprávy a přepočet virtuální adresy bufferu se zprávou potřebujeme určit proces, který primitivu zavolal. Tímto procesem však může být jedině právě běžící proces, jehož identifikaci v podobě pozice v tabulce procesů najdeme v proměnné cur_proc.
Přesný formát predávaných parametru je třeba stanovit a zdokumentovat.
Kernel sestává ze tří zdrojových souborů: PROC.C, MPX88.S a KERNEL88.S (viz pseudokódy). Poslední dva jsou napsány v assembleru, PROC.C je stejně jako zbytek systému v C.
Kernel obhospodaruje následující proměnné:
int cur_proc - index právě běžícího procesu v tabulce proc[] struct struct_proc *proc_ptr; /* &proc[cur_proc]) */ - pointer na prvek proc[] patřící právě běžícího procesu v tabulce struct struct_proc *bill_ptr; - pointer na (uživatelský) proces, kterému se bude účtovat tik hodin struct struct_proc *rdy_head[NQ]; - pointer na pole začátků front běhuschopných procesů struct struct_proc *rdy_tail[NQ]; - pointer na pole konců front běhuschopných procesů unsigned busy_map; - bitmapa: každý bit odpovídá jednomu tasku. Jednička na příslušném bitu indikuje, že se obslužná rutina přerušení svázaného s daným taskem pokoušela předat tasku zprávu, ale task byl zaneprázdněn. Proto se musí funkce interrupt() při dalším přerušení pokusit zprávu doručit znovu. Zpráva je mezitím uložena na odpovídající pozici pole task_mess[NR_TASKS]. message task_mess[NR_TASKS]; Pole pro uložení zpráv o přerušení, které dosud nemohly být doručeny příslušným taskům z důvodu jejich zaneprázdněnosti. proc_ptr int_message - buffer pro zprávu, která se vytváří v době přerušení a odesílá se příslušnému tasku int sig_procs - počet procesů, jimž má být doručen signál
Jednotlivé moduly obsahují zejména dále popsané funkce
Nejnižší úroveň přepínání procesů a obsluhy přerušení. Obsahuje zejména tyto rutiny
Slouží k uložení a obnovení registrů do/z tabulky procesů a to do/z pozice, na kterou ukazuje cur_proc. Jelikož se mezi voláním save a restart může hodnota cur_proc změnit (pokud byla zavolán plánovač), lze takto provádět přepínání mezi procesy. Plánovač zajistí pouze nastavení cur_proc, samotné uschování původních obsahů registrů a jejich nové naplnění provedou save a restart. Rutina save navíc nastavuje zásobník na zásobník kernelu (k_stack[]). Pokud v okamžiku, kdy je vyvoláno restart, není žádný proces schopen běhu (cur_proc=IDLE), provede se odskok na zvláštní smyčku, ve které se povolí přerušení a instrukcí wait se čeká na další přerušení, které způsobí, že se některý task stane schopným běhu. restart končí iret => znovupovolení přerušení, na stacku musí být nové CS:IP:FLAGS.
obsluha SW přerušení pro předávání zpráv. V ní se uschovají registry (save), z předaných hodnot (caller=cur_proc, destination, typ požadavku: SEND/RECEIVE/SENDREC, ptr na buffer se zprávou)se zformulují parametry (vytvoří se zásobník obsahující tyto parametry) pro C-funkci sys_call() a tato funkce se zavolá. Ve funkci může mj. dojít k přeplánování, což se projeví změnou hodnoty proměnné cur_proc. Po návratu se volá restart, která nastaví registry podle právě prováděného procesu (promměnná cur_proc).
save, zformulování zprávy pro příslušný task v int_message, volání C-funkce interrupt(), restart
save, volání C-funkce keyboard() [HW závislá část TTY, tam přečtení scan code, převod na ASCII a pokus o zaslání zprávy HW nezávislé části ovladače TTY], restart
save, volání C-funkce rs232(portNo) [HW závislá část TTY, tam přečtení znaku z portu, pokus o zaslání zprávy HW nezávislé části ovladače TTY], restart
Nevyužitá přerušení jsou nasměrovány na tuto obslužnou rutinu. Volá se save, provede se výpis chybového hlášení prostřednictvím BIOSu, restart
Implementuje skupinu pomocných nízkoúrovňových funkcí, které mohou být volány tasky (připomeňme, že jádro je s tasky slinkováno). Jde např. o funkce podporující kopírování bloků (lineární) paměti, povolování přerušení, vstup/výstup z portů atd., viz pseudokód. Jelikož těchto rutin využívají tasky, je nezbytné zdokumentovat jejich rozhraní a funkci a poskytnout odpovídající C-headery, které umožní volání rutin z C-kódu stejně jako by se volaly C-funkce. Parametry funkcí se tedy předávají přes zásobník ve volací konvenci podporované jazykem C.
Vyšší úroven obsluhy prerušení (prevod na zprávy), mechanismus dorucování zpráv, plánovac
voláno z nejnižších vrstev obsluhy přerušení (MPX88.S), pokusí se urcenému tasku zaslat zprávu a tím dosáhnout jeho naplánování. Zpráva se zde ovšem nezasílá běžným primitivem SEND, které může způsobit zablokování odesílatele do doby, kdy bude adresát schopen převzít zprávu, ale zvláštní funkcí mini_send(), která indikuje úspěch odeslání jako návratový kód. Jako odesílatel zprávy se namísto pozice odesílatele v proc[] uvádí zvl. kód HARDWARE. V případě, že se předání zprávy nepodařilo (task byl zaneprázdněn), nastav příslušný bit v busy_map a zprávu uschovej do task_mess. Navíc, pokud šlo o zprávu pro clock task, zvyš hodnotu proměnné lost_ticks, aby později bylo možné opožděné přerušení zkompenzovat. Pokud se zpráva odeslala, zruš příslušný bit v busy_map, byl-li nastaven.
Ať již se zaslání zprávy podařilo nebo ne, projde se ve smyčce busy_map a vždy se pokusíme odeslat zprávu těm taskům, kterým měla a doposud nemohla být doručena. V případě úspěšného doručení se samozřejmě nuluje příslušný bit busy_map. Protože v mini_send() mohlo dojít k naplánování tasku s vyšší prioritou, než má právě běžící proces, zkoumáme, zda právě běžící proces je uživatelským (nebo dokonce idle). Pokud tomu tak je a ve frontě čeká nějaký task schopný běhu (rdy_head[TASK_Q]!=NULL), provede se přeplánování voláním funkce pick_proc(). Ta může změnit hodnotu cur_proc a po návratu z interrupt() rutina restart provede přechod k jinému procesu, než běžel doposud.řeší předávání zpráv a je přímo volána již popsanou rutinou s_call. Součástí těla funkce je test, zda odesílatel a příjemce zprávy leží ve stejné, popř. přilehlé vrstvě (jinak se komunikace neumožní). V případě zasílání zprávy uživatelským procesem (resp. jeho knihovanami) se umožní pouze volání primitiva SEND_REC. Pro zaslání zprávy se zavolá funkce mini_send(caller,dest,mess_ptr), pro převzetí pak mini_rec(caller,src,mess_ptr).
pokus o zaslání zprávy od zdrojového procesu k cílovému procesu. Pokud je cílový proces zablokován čekáním na zprávu (od tohoto zdroje), zpráva se nakopíruje do jeho adresového prostor cílový proces se odblokuje (zruší se flag RECEIVING v tab. procesů a volá se ready()). Pokud cílový proces nečeká na převzetí zprávy od daného zdroje a nejedná se o "zprávu od hardware", zdrojový proces se přidá do fronty čekajících na odeslání zprávy a zablokuj vysílající proces (volá se unready() a nastaví se flag SENDING v tab. procesů). Jednalo-li se o "zprávu od hardware" (tj. pokus o zaslání zprávy některému tasku z obslužné rutiny přerušení, kdy je odesílatel namísto pozicí v tabulce procesů identifikován konstantou HARDWARE), žádné zablokování a vkládání do fronty se neděje, ale pouze se vrací chybový kód.
Dále se ve funkci provádí několik pomocných testů pro udržení konzistence systému v případě chybných parametrů, viz pseudokód.pokus procesu o převzetí zprávy od zdrojového procesu. Pokud je zpráva již ve frontě, odblokuje se její odesílatel (zruší se flag SENDING a volá se ready()) a zpráva se předá. Pokud žádná zpráva ve frontě nečeká, volající proces se zablokuje (volání unready(), nastavení flagu RECEIVING). V závěru funkce je navíc vhodné místo pro obsluhu signálů, pokud byly nějaké vygenerovány. Testuje se, zda kernel nemá zaregistrovány nevyřízené signály a zda volání receive nevyvolal memory manager (kterému zpracování signálů přísluší). Pokud tomu tak je a není pro MM právě žádná zpráva, volá se zvláštní funkce inform() (součást system tasku), která zajistí předání zprávy žádosti o obsloužení signálu (KSIG) memory manageru.
Hlavní rutina plánovače. Nastavuje cur_proc (a proc_ptr) na proces s nejvyšší prioritou, který má dále běžet. Pokud žádný proces není schopen běhu, nastaví se cur_proc=IDLE a proc_ptr na jinak nevyužitý slot tabulky procesů, aby bylo kam uschovat registry při dalším přerušení. Proměnná bill_ptr se stále udržuje ukazující na proces, kterému má být účtován následující tik hodin.
připojí proces, který byl prohlášen za schopný běhu, na konec příslušné fronty
odstraní proces, který nadále není schopen běhu z příslušné fronty procesů schopných běhu
rutina vyvolávána periodicky clock taskem při vypršení časového kvanta. Jestliže je ve frontě připravených uživatelských procesů více než jeden proces, tento proces se z čela fronty přesune na její konec. Tím se dostává ke slovu další uživatelský proces. Následně se volá pick_proc() pro naplánování procesu s nejvyšší prioritou.