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.
K předávání zpráv slouží 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 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 (nearpointer, 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.
Kernel sestává ze zdrojových souborů: MAKRA.H, PROC_PUB.H, PROC.H, PROC.C,MPX88.C a KERNEL88.C. Poslední dva jsou napsány v assembleru, PROC.C je stejně jako zbytek systému v C.
Jako služba OS bylo zvoleno přerušení INT 31h (např. v MS-DOS je toINT
21h)
Konstanty představující jednotlivé služby jádra OS (INT 31h):
#define SEND 1
#define RECEIVE 2
#define BOTH 3 /* send & receive */
#define TRAP 4
#define DIE_TRAP 8
Zprávy pro makra musí byt globálními proměnnými příslušných modulů.Viditelnost těchto zpráv bude omezena modifikátorem static. Makra se nebudou volat stylem receive(x,&m), ale receive(x,m)! Struktura se zprávou nesmí být lokální!
Veřejná část vyčleněná speciálně pro ostatní ze souboru PROC.H. Tento hlavičkový soubor je includován z hlavičkového souboru PROC.H.
Neveřejná část pro hlavičkový soubor PROC.C.
Vyšší úroveň obsluhy přerušení (převod na zprávy), mechanismus doručování zpráv, plánovač
voláno z nejnižších vrstev obsluhy přerušení (MPX88.S), pokusí se určené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áštní kód HARDWARE. V případě, že se předání zprávy nepodařilo (task byl zaneprázdněn), nastaví se příslušný bit v busy_map a zprávu se uschová do task_mess. Navíc, pokud šlo o zprávu pro clock task, zvyší se hodnota proměnné lost_ticks, aby později bylo možné opožděné přerušení vykompenzovat. Pokud se zpráva odeslala, zruší se 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á se, zda právě běžící proces je uživatelským procesem (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 rutinou s_call (MPX88.C). 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 prostoru, 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 zablokuje vysílající proces (volá se unready() a nastaví se flagSENDING 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ů.
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.
/* Vyber nejprioritnější frontu, ve které je někdo připraven k běhu */
int q;
for(q=TASK_Q; q<NQ; q++)
if (rdy_head[q] != NIL_PROC)
{
prev_ptr=proc_ptr;
cur_proc = rdy_head[q] - proc;
proc_ptr = rdy_head[q];
if (cur_proc >= LOW_USER) bill_ptr = proc_ptr; return;
}
cur_proc = IDLE;
bill_ptr = proc_ptr = proc_addr(IDLE);
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.
/* disable interrupts */
k_lock();
/* pokud je USER_Q prázdná, nedělej nic */
if (rdy_head[USER_Q] == NIL_PROC) return;
/* vezmi proces z cele USER_Q a presun jej na konec */
rdy_tail[USER_Q]->p_nextready = rdy_head[USER_Q];
rdy_tail[USER_Q] = rdy_head[USER_Q];
rdy_head[USER_Q] = rdy_head[USER_Q]->p_nextready;
rdy_tail[USER_Q]->p_nextready = NIL_PROC;
/*vyber proces s nejvyšší prioritou*/
pick_proc();
/* restore interrupts */
k_unlock();