4. cvičenie – lenivý alokátor ?

Lenivá alokácia stránok v xv6

Programátori OS dokážu robiť všelijaké triky s pamäťovým hardvérom. Príkladom je lenivá alokácia na halde pre užívateľské programy. Aplikácie na xv6 si môžu vypýtať pamäť na halde od operačného systému pomocou systémového volania sbrk(). V jadre, ktoré vyvíjate, sbrk() alokuje fyzickú pamäť požadovanej veľkosti a namapuje ju do virtuálneho adresného priestoru procesu. Existujú však programy, ktoré takýmto spôsobom alokujú veľké množstvo pamäte, ale nikdy ho celé nepoužijú – napr. riedke polia (polia, v ktorých má väčšina prvkov nulovú hodnotu). Sofistikované jadra pre tento prípad alokujú pamäť lenivo. To znamená, že sbrk() nealokuje fyzickú pamäť – iba si zapamätá adresný rozsah, na ktorom proces očakáva alokovanú pamäť na halde. Keď proces prvýkrát pristúpi k stránke v tomto rozsahu, CPU vyvolá výpadok stránky, ktorý kernel obslúži alokáciou fyzickej pamäte, jej vynulovaním a a namapovaním. V tomto cvičení sa budeme venovať implementácii tohoto mechanizmu do xv6.

Než sa vrhnete do programovania, prečítajte si tretiu kapitolu „Page Tables“ z knižky o xv6 (ak ste tak predtým nevykonali).

Začneme prepnutím repozitára do vetvy lazy.

Výpis tabuľky stránok

Pri vývoji softvéru sa určite oplatí napísať ladiaci kód a preto vašou prvou úlohou je implementovať funkciu, ktorá vypíše obsah tabuľky stránok. Implementujte funkciu void vmprint(pagetable_t) v súbore kernel/vm.c. Vďaka nej sa vám bude systém ľahšie ladiť a lepšie pochopíte stránkovanie na architektúre RISC-V. Korektnú implementáciu môžete overiť tak, že ju zavoláte v súbore exec.c. Tým vypíšete tabuľku stránok prvého procesu; výstup by mal vyzerať takto:

Prvý riadok obsahuje adresu argumentu funkcie vmprint. Riadky PTE obsahujú index PTE v danej tabuľke stránok, adresu pte a fyzickú adresu PTE. Výstup tiež signalizuje úroveň slovníka stránok: položky najvyššej úrovne sú označené .., o úroveň nižšie .. .. atď. Nevypisujte záznamy, ktoré nie sú namapované. V príklade vyššie sú v najvyššom slovníku stránok namapované záznamy 0 a 255. Záznam 0 má namapovaný iba index 0 a najnižšia úroveň pre tento index má namapované záznamy 0, 1 a 2.

Zopár pomôcok:

  • Pozrite si makrá na konci súboru kernel/riscv.h a použite ich.
  • Inšpirujte sa funkciou freewalk.
  • Definujte prototyp pre vmprint v kernel/defs.h, aby ste ju mohli zavolať z exec.c.

Odstránenie alokácie v sbrk()

Vašou prvou úlohou je odstrániť alokáciu v systémovom volaní sbrk(n). Jeho implementáciu sys_sbrk() nájdete v súbore sysproc.c. Systémové volanie sbrk(n) zväčší pamäť procesu o n bajtov a vráti smerník na začiatok alokovaného regiónu (pôvodnú veľkosť, ak pamäť počítame od nuly). Vaša nová implementácia sbrk(n) iba navýši počítadlo veľkosti procesu (myproc()->sz) o n a vráti pôvodnú veľkosť. Novú pamäť nebude alokovať — volanie growproc() musíte odstrániť.

Teraz si skúste tipnúť, čo sa stane, ak po tejto modifikácii naštartujeme systém. Čo sa pokazí?

Ukončite stávky prosím!

Naštartujte xv6, do shellu napíšte echo hi. Mali by ste uvidieť niečo takéto:

Správa „usertrap(): …“ pochádza z užívateľskej trap obsluhy v trap.c. Ďalej vidíme, že takýto druh výnimky obsluha nepozná (unexpected scause). Uistite sa, že rozumiete prečo došlo k výpadku stránky. Hodnota „stval=0x0..04008“ nám napríklad hovorí, že virtuálna adresa, ktorá spôsobila výnimku je 0x4008.

Lenivá alokácia

Upravte kód v trap.c, aby obslúžil výpadok stránky z užívateľského priestoru namapovaním novo-alokovanej stránky fyzickej pamäte na adresu výpadku. Po obsluhe zabezpečte návrat do užívateľského priestoru, aby mohol proces pokračovať vo vykonávaní. Obsluhu by ste mali napísať pred volaním printf, ktoré spôsobilo vyššie uvedený výpis. Riešenie je hotové, ak prejdete testami usertests.

Odporúčame vám začať opravením funkcie usertrap() v trap.c tak, aby ste mohli spustiť echo hi v shelli. Akonáhle sa vám to podarí, objavíte ďalšie problémy, ktoré budete musieť vyriešiť, aby usertests fungoval korektne. Nasledujú užitočné pomôcky.

  • Výnimka je výpadok stránky vtedy, ak z funkcie usertrap() zavoláte r_scause() a ten vám vráti 13 alebo 15.
  • Ako zistiť virtuálnu adresu, kde nastal výpadok? Pozrite sa do argumentov volania printf() v usertrap(), ktoré sme vyššie popisovali.
  • Požičajte si (rozumej ukradnite) kód z funkcie uvmalloc() vo vm.c. Tento kód používa aj sbrk() (cez growproc()). V riešení zavoláte dve funkcie: kalloc() a mappages().

  • Nezabudnite zarovnať virtuálnu adresu nadol na najbližšiu hranicu stránok pomocou makra PGROUNDDOWN(va).
  • uvmunmap() bude panikáriť; modifikujte ju tak, aby nevyvolala panic, ak nejaké stránky nie sú namapované.
  • Ak kernel zlyhá, otvorte disassemblovaný kód jadra zo súboru kernel/kernel.asm a nájdite v ňom problémovú inštrukciu sepc.
  • Nezabudnite využiť funkciu na vypísanie tabuliek stránok, ktorú ste si napísali na začiatku cvičenia.
  • Ak vám kompilátor vypíše chybu „incomplete type proc“, includnite proc.h (a spinlock.h).

Ak všetko dopadlo dobre, mali by ste dosiahnuť stav, v ktorom viete spustiť echo hi. V shelli by mal nastať aspoň jeden výpadok stránky (a po ňom lenivá alokácia), možno dva.

Usertests

Keď vám zbehne základ, opravte svoj kód tak, aby prešli všetky testy programu usertests:

  • Obslúžte volania sbrk() so zápornými argumentami.
  • Ukončite proces, ktorý pristúpil k virtuálnej adrese vyššej ako tej, ktorú mal alokovanú pomocou sbrk().
  • Obslúžte volanie fork() korektne.
  • Ošetrite prípad, v ktorom proces použije validnú adresu z rozsahu od sbrk() v systémovom volaní read alebo write, ale pamäť pre danú oblasť ešte nebola alokovaná.
  • Ošetrite stav, v ktorom systém nemá voľnú pamäť: ak kalloc() zlyhá počas obsluhy výpadku stránky, ukončite aktuálny proces.
  • Ošetrite výpadky na neplatnej stránke pod zásobníkom.

Vaše riešenie je hotové, ak prejde testami lazytests a usertests:

Spustite príkaz make grade, aby ste overili správnosť svojho riešenia.

Týmto je cvičenie ukončené. Svoje zmeny môžete commitnúť do repozitára.