V tomto cvičení preskúmate tabuľky stránok a upravíte ich za účelom zjednodušenia funkcií, ktoré kopírujú dáta z užívateľského priestoru do priestoru jadra.
Než začnete programovať, prečítajte si kapitolu 3 xv6 knižky a pozrite si príslušné súbory:
Najprv sa prepnite do vetvy pgtbl:
1 2 3 |
$ git fetch $ git switch pgtbl # alebo git checkout pgtbl $ make clean |
Vašou prvou úohou je napísať funkciu, ktorá vypíše obsah tabuľky stránok. Pomôže vám to pri pochopení, ako fungujú tabuľky stránok v RISC-V a funkciu môžete v budúcnosti použiť pri ladení systému.
vmprint()
. Bude mať jeden argument typu pagetable_t
a vypíše tabuľku stránok vo formáte, ktorý je uvedený nižšie. Funkciu môžete otestovať napríkad tak, že ju zavoláte v súbore exec.c tesne pred return argc
. Aby bola tabuľka vypísaná len pre prvý proces, skontrolujte číso procesu: if(p->pid==1) vmprint(p->pagetable)
. Plný počet bodov za túto úlohu dostanete, ak prejdete testom pte printout.
Keď spustíte xv6, mali by ste vidieť podobný výstup. Ak ste funkciu zavolali vyššie popísaným spôsobom, jedná sa o stav tabuľky prvého procesu (init) tesne pred jeho spustením v systémovom volaní exec
.
1 2 3 4 5 6 7 8 9 10 |
page table 0x0000000087f6e000 ..0: pte 0x0000000021fda801 pa 0x0000000087f6a000 .. ..0: pte 0x0000000021fda401 pa 0x0000000087f69000 .. .. ..0: pte 0x0000000021fdac1f pa 0x0000000087f6b000 .. .. ..1: pte 0x0000000021fda00f pa 0x0000000087f68000 .. .. ..2: pte 0x0000000021fd9c1f pa 0x0000000087f67000 ..255: pte 0x0000000021fdb401 pa 0x0000000087f6d000 .. ..511: pte 0x0000000021fdb001 pa 0x0000000087f6c000 .. .. ..510: pte 0x0000000021fdd807 pa 0x0000000087f76000 .. .. ..511: pte 0x0000000020001c0b pa 0x0000000080007000 |
V prvom riadku je argument funkcie vmprint
. Potom nasledujú riadky pre každý PTE, vrátane vnorených záznamov. Každý riadok PTE je odsadený znakmi ..
podľa toho, ako hboko v strome sa nachádza. Každý riadok obsahuje index záznamu, bity pte
a fyzickú adresu vytiahnutú z PTE. Nevypisujte neplatné záznamy. V príklade vyššie má tabuľka najvyššej úrovne mapovania pre záznamy 0 a 255. O úroveň nižšie, záznam 0 má namapovaný len index 0. Na najnižšej úrovni má tento index namapované záznamy 0, 1 a 2.
Fyzické adresy na vašom počítači môžu byť odlišné od tých v ukážke výstupu. Počet záznamov a virtuálne adresy by mali byť rovnaké.
Pomôcky:
vmprint()
môžete definovať v kernel/vm.c.
freewalk()
.
vmprint()
dajte do kernel/defs.h, aby ste ju mohi zavoať z exec.c.
vmprint()
a jeho súvis s obrázkom 3-4 z xv6 knižky. Čo obsahuje stránka 0? Čo je v stránke 2? Keď proces beží v užívateľskom móde, môže prečítať/zapísať pamäť mapovanú stránkou 1?
Xv6 má jednu tabuľku stránok jadra, ktorá sa používa počas vykonávania kódu jadra. Tabuľka stránok jadra priamo mapuje virtuálne adresy na identické fyzické adresy. Xv6 má tiež samostatnú tabuľku stránok pre adresný priestor každého používateľského procesu. Tieto tabuľky obsahujú iba mapovania užívateľskej pamäte pre konkrétny proces od adresy nula. Keďže tabuľka stránok jadra neobsahuje tieto mapovania, užívateľské adresy sú v jadre neplatné. To znamená, že ak chce jadro použiť užívateľský smerník zo systémového volania (napr. smerník na buffer v systémovom volaní write()
), musí najprv preložiť smerník na fyzickú adresu. Cieľom tejto a ďalšej sekcie je umožniť jadru priamo dereferencovať užívateľské smerníky.
Vašou prvou úlohou je modifikovať jadro tak, aby každý proces používal vlastnú kópiu tabuľky stránok jadra počas vykonávania kódu v jadre. Upravte struct proc
, aby uchovala tabuľku stránok jadra pre každý proces a modifikujte plánovač, aby prepol tabuľku stránok jadra pri prepínaní procesov. Zatiaľ stačí, aby bola každá tabuľka stránok jadra pre každý proces identická s globálnou tabuľkou stránok jadra. Úlohu ste zvládli, ak zbehnú testy usertests.
Prečítajte si kapitolu z knižky xv6 a súbory uvedené na začiatku tohoto cvičenia. Ľahšie sa vám bude pracovať s virtuálnou pamäťou, keď jej budete rozumieť. Chyby v tabuľkách stránok môžu spôsobovať výnimky kvôli chýbajúcim mapovaniam, čítanie a zápis nezamýšľaných fyzických stránok a spustenie inštrukcií z nesprávnych stránok pamäte.
Pomôcky:
struct proc
, kde bude uložená tabuľka stránok jadra.
kvminit()
, ktorá vytvorí novú tabuľku stránok namiesto modifikovania kernel_pagetable
. Túto funkciu budete volať z allocproc()
(odporúčame všetky funkcie, ktoré pracujú s premennou kernel_pagetable
dôkladne preskúmať a zvážiť, či ich netreba vzhľadom na zadanie modifikovať, aby sa v nich používala tabuľka stránok dodaná ako argument volania – viď napríklad funkciu kvmmap()
)procinit()
, ktorá je zavolaná počas bootovanie mapuje zásobníky jadra pre všetky procesy do tabuľky stránok jadra. Môžete to urobiť podobne, alebo namapovať iba zásobník jadra konkrétneho procesu.scheduler()
, aby načítala tabuľku stránok jadra pre proces do registra satp (viď kvminithart()
pre inšpiráciu). Nezabudnite zavolať sfence_vma()
po zavolaní w_satp().kernel_pagetable
.freeproc()
. V závislosti od toho, ako ste vyriešili problém so zásobníkom jadra, môže byť nutné v tej istej funkcii uvoľniť aj pamäť zásobníka pre jadro.Funkcia jadra copyin()
číta pamäť, na ktorú ukazujú užívateľské smerníky. Robí to preložením virtuálnej adresy na fyzickú adresu, ktorú jadro vie priamo dereferencovať. Preklad sa vykonáva softvérovým kráčaním po tabuľke stránok procesu. Vašou úlohou je pridať užívateľské mapovania do každej tabuľky stránok jadra pre každý proces (ktoré ste vytvorili v predchádzajúcej sekcii), ktoré by umožnili funkcii copyin()
(a príbuznej funkcii pre kopírovanie reťazcov copyinstr
) priamo dereferencovať užívateľské smerníky.
copyin()
v súbore kernel/vm.c volaním funkcie copyin_new()
(definovanej v kernel/vmcopyin.c); to isté spravte pre copyinstr()
a copyinstr_new()
. Pridajte mapovanie užívateľských adries do každej tabuľky jadra pre každý proces tak, aby copyin_new()
a copyinstr_new()
fungovali. Úlohu ste splnili, ak prejdete testami usertests a taktiež všetkými zvyšnými testami make grade.
Táto schéma spolieha na to, že užívateľský adresný priestor sa neprekrýva s rozsahom virtuálnych adries, ktoré používa jadro pre svoje vlastné inštrukcie a dáta. Xv6 pre užívateľské priestory používa virtuálne adresy, ktoré začínajú na nule, zatiaľ čo pamäť jadra začína na vyšších adresách. To znamená, že veľkosť užívateľského procesu musí byť menšia ako najmenšia virtuálna adresa jadra. Po naštartovaní jadra je táto adresa 0xC000000, čo je zároveň adresa registrov PLIC, viď kvminit()
v kernel/vm.c, kernel/memlayout.h, a obrázok 3.3 z knižky. Ak sa pozriete pozornejšie, skutočná najnižšia adresa, ktorú jadro využíva, je 0x2000000, adresa registrov CLINT. Xv6 ale používa tieto registre iba počas bootovacieho procesu, takže je bezpečné ich premapovať. Musíte upraviť xv6, aby užívateľské procesy neboli väčšie ako adresa PLIC.
Pomôcky:
copyin()
volaním copyin_new()
tak, aby to fungovalo, než sa pustíte do copyinstr().p->pagetable
), doplňte zodpovedajúcu modifikáciu tabuliek virtuálnej pamäte pre jadro toho istého procesu (na prístup k virtuálnej pamäti jadra použite vami vytvorenú položku v štruktúre proc
z predošlej úlohy). Zamerajte sa na systémové volania fork()
, exec()
a sbrk()
.userinit()
.PTE_U
nemôže jadro pristúpiť.)Linux používa podobnú techniku, ako ste práve implementovali. Ešte pred pár rokmi sa štandardne používala rovnaká tabuľka stránok v užívateľskom priestore aj priestore jadra, kde boli spolu mapované užívateľské adresy a adresy jadra. Výhoda takéhoto riešenia bolo, že jadro nemuselo prepínať tabuľky stránok pri prechode medzi užívateľským priestorom a priestorom jadra. Po objavení zraniteľností Meltdown a Spectre musel byť tento spôsob nahradený.
Vysvetlite, prečo je potrebný druhý test srcva + len < srcva
vo funkcii copyin_new()
: uveďte konkrétne príklady hodnôt, pre ktoré prvý test (srcva+len >= p->z
) zlyhá, ale druhý prejde.
Týmto je cvičenie hotové. Skontrolujte, či vaše riešenie prejde všetkými testami make grade. 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. Na záver nezabudnite svoje zmeny commitnúť do repozitára.
make grade môže zlyhať aj v prípade, že testy osamote zbehnú v poriadku. Je to spôsobené časovým limitom testov. Môžete ho zvýšiť v príslušnom súbore grade-lab-pgtbl (parametre timeout
). Časový limit zmeňte iba v takom prípade, že viete, že samotný test zbehne v poriadku a nič iné v súbore neupravujte.
Pre úspešné testovanie je potrebné použiť aspoň 3 jadrá vo VirtualBoxe.