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, a takáto alokácia môže trvať dlhý čas. Napríklad pre jeden gigabajt je potrebné alokovať 262 144 stránok pamäte s veľkosťou 4 kB. Aj keď je alokácia jednej stránky relatívne rýchla, pri takomto veľkom počte alokovaných stránok je dĺžka tejto operácie nezanedbateľná. Naviac, niektoré programy alokujú pamäť, ktorú celú nikdy nepoužijú – napr. riedke polia (polia, v ktorých má väčšina prvkov nulovú hodnotu) alebo alokujú pamäť veľmi skoro pred jej samotným použitím. Sofistikované jadrá 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ý jadro obslúži alokáciou fyzickej pamäte, jej vynulovaním a a namapovaním. V tomto cvičení sa budeme venovať implementácii tohto mechanizmu do xv6.
Než sa vrhnete do programovania, prečítajte si štvrtú kapitolu z knižky o xv6 a súbory, ktoré budete upravovať:
Začneme prepnutím repozitára do vetvy lazy
.
1 2 3 |
$ git fetch $ git switch lazy # alebo git checkout lazy $ make clean |
sbrk()
(ľahká)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ť (ale veľkosť procesu musíte aj tak zvýšiť!).
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:
1 2 3 4 5 6 |
init: starting sh $ echo hi usertrap(): unexpected scause 0x000000000000000f pid=3 sepc=0x0000000000001258 stval=0x0000000000004008 va=0x0000000000004000 pte=0x0000000000000000 panic: uvmunmap: not mapped |
Správa usertrap(): … pochádza z obsluhy užívateľských výnimiek 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.
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. Upravte zvyšok kódu jadra, aby ste mohli spustiť echo hi.
Pomôcky:
usertrap()
zavoláte r_scause()
a tá vám vráti 13 alebo 15.r_stval()
vráti register stval, ktorý obsahuje virtuálnu adresu, ktorá spôsobila výpadok stránky.uvmalloc()
vo vm.c
. Tento kód používa aj sbrk()
(cez growproc()
). V riešení zavoláte dve funkcie: kalloc()
a mappages()
.PGROUNDDOWN(va)
.uvmunmap()
bude panikáriť; modifikujte ju tak, aby nevyvolala panic, ak nejaké stránky nie sú namapované.kernel/kernel.asm
a nájdite v ňom problémovú inštrukciu na adrese sepc.spinlock.h
a za ním proc.h
.
Ak všetko dopadlo dobre, mali by ste dosiahnuť stav, v ktorom viete spustiť echo hi
. V shelli by mal nastať aspoň jeden alebo dva výpadky stránok (a po nich lenivá alokácia).
Užívateľský program lazytests dôsledne otestuje váš lenivý pamäťový alokátor. Opravte svoj kód tak, aby prešli všetky testy programov lazytests a usertests.
sbrk()
so zápornými argumentami.sbrk()
.fork()
korektne.sbrk()
v systémovom volaní read
alebo write
, ale pamäť pre danú oblasť ešte nebola alokovaná.kalloc()
zlyhá počas obsluhy výpadku stránky, ukončite aktuálny proces.Vaše riešenie je hotové, ak prejde testami lazytests a usertests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ lazytests lazytests starting running test lazy alloc test lazy alloc: OK running test lazy unmap... usertrap(): ... test lazy unmap: OK running test out of memory usertrap(): ... test out of memory: OK ALL TESTS PASSED $ usertests ... ALL TESTS PASSED $ |
Spustite príkaz make grade, aby ste overili správnosť svojho riešenia. Aby ste prešli posledným testom time, musíte vytvoriť nový súbor time.txt, v ktorom uvediete počet hodín, ktorý ste strávili nad zadaním ako celé číslo.
Týmto je cvičenie ukončené. Svoje zmeny môžete commitnúť do repozitára.
copyin
z tretieho cvičenia.