Systémové volania mmap a munmap umožňujú UNIX programom získať väčšiu kontrolu nad ich adresným priestorom. Môžu byť použité na zdieľanie pamäte medzi procesmi, mapovanie súborov do adresného priestoru procesu a rôzne schémy na obsluhu výpadkov stránok z užívateľského prostredia. Na tomto cvičení budete implementovať tieto systémové volania na mapovanie súborov do pamäte.
Ak chcete pracovať s počítačmi v učebni, postupujte podľa sekcie Stiahnutie repozitára na cvičení na stránke Synchronizácia repozitára.
Stiahnite si kód pre cvičenie do repozitára a prepnite sa na vetvu mmap:
1 2 3 |
$ git fetch $ git switch mmap $ make clean |
Riešenia úloh z tohto cvičenia ukladajte do vetvy mmap.
Manuálová stránka Linuxu pre mmap (man 2 mmap) uvádza túto deklaráciu funkcie mmap
:
1 2 |
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset); |
Z manuálovej stránky môžete vidieť, že mmap
môže byť volaná mnohými spôsobmi. Na tomto cvičení nám ale bude stačiť funkcionalita pre mapovanie súboru do pamäte. Môžete predpokladať, že argument addr
bude vždy nulový, čiže adresu, na ktorú bude namapovaný súbor určí jadro. Funkcia mmap
vráti túto adresu, alebo číslo 0xffffffffffffffff
, ak sa súbor nepodarilo namapovať. Argument len
určuje počet bajtov, ktoré sa majú namapovať; nemusí byť rovnaký ako veľkosť súboru. Argument prot
určuje, či má byť pamäť mapovaná na čítanie, zápis a/alebo vykonanie. Môžete predpokladať, že bude nadobúdať hodnoty PROT_READ
, PROT_WRITE
, alebo kombináciu oboch. Argument flags
bude buď MAP_SHARED
, čo znamená, že úpravy v namapovanej pamäti majú byť zapísané späť do súboru, alebo MAP_PRIVATE
, kedy úpravy nebudú do súboru spätne zapísané. Podporu iných bitov vo flags
nie je potrebné implementovať. Argument fd je otvorený súborový deskriptor súboru, ktorý sa má namapovať. Môžete predpokladať, že offset
je nulový (určuje začiatočný bod v súbore, od ktorého sa bude súbor mapovať).
Tabuľku stránok napĺňajte na požiadanie obslúžením výpadkov stránok. Samotné volanie mmap()
by nemalo alokovať fyzickú pamäť, ani čítať obsah súboru. Tieto úkony je potrebné urobiť až v obsluhe výpadkov stránok z funkcie usertrap()
(alebo inej funkcie zavolanej odtiaľ), podobne ako pri riešení úlohy COW. Napĺňať tabuľku na požiadanie má výhodu v tom, že zavolanie mmap()
na veľkom súbore prebehne rýchlo a taktiež je možné namapovať súbor väčší ako dostupná fyzická pamäť.
Je v poriadku, ak procesy, ktoré zdieľajú rovnaký MAP_SHARED
súbor nezdieľajú rovnaké fyzické stránky.
Manuálová stránka (otvoríte ju spustením príkazu man 2 munmap) uvádza deklaráciu funkcie munmap
:
1 |
int munmap(void *addr, size_t len); |
Systémové volanie munmap(addr, length)
by malo odstrániť mapovania vytvorené volaním mmap
v požadovanom adresnom rozsahu, ak existujú. Ak proces modifikoval pamäť a táto bola namapovaná ako MAP_SHARED
, modifikácie by mali byť najprv zapísané do súboru. Volanie munmap
môže tiež iba čiastočne pokryť pôvodne mapovaný región. Môžete ale predpokladať, že sa odmapuje buď čiastočne región od začiatku alebo konca súboru, prípadne celý súbor (nikdy sa ale neodmapuje región v strede súboru). Pri ukončení procesu je potrebné zapísať úpravy v oblastiach MAP_SHARED
do príslušných súborov,ako keby proces na konci zavolal munmap()
.
mmap
a munmap
, aby ste prešli testom mmaptest. Ak mmaptest nepoužíva nejakú funkcionalitu systémového volania mmap
, nemusíte túto funkcionalitu implementovať. Testy usertests -q by tiež mali prechádzať.
Keď budete hotoví, mali by ste vidieť podobný výstup:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
$ mmaptest test basic mmap test basic mmap: OK test mmap private test mmap private: OK test mmap read-only test mmap read-only: OK test mmap read/write test mmap read/write: OK test mmap dirty test mmap dirty: OK test not-mapped unmap test not-mapped unmap: OK test lazy access test lazy access: OK test mmap two files test mmap two files: OK test fork test fork: OK test munmap prevents access usertrap(): unexpected scause 0xd pid=7 sepc=0x924 stval=0xc0001000 usertrap(): unexpected scause 0xd pid=8 sepc=0x9ac stval=0xc0000000 test munmap prevents access: OK test writes to read-only mapped memory usertrap(): unexpected scause 0xf pid=9 sepc=0xaf4 stval=0xc0000000 test writes to read-only mapped memory: OK mmaptest: all tests succeeded $ usertests -q usertests starting ... ALL TESTS PASSED $ |
Pomôcky:
_mmaptest
do UPROGS
a implementujte systémové volania mmap
a munmap
, aby ste mohli skompilovať user/mmaptest.c. Na začiatok stačí, aby ste z implementovaných systémových volaní vrátili chybové kódy. V súbore kernel/fcntl.h nájdete implementácie spomenutých makier, napríklad PROT_READ
. Spustite mmaptest, ktorý by mal zlyhať pri prvom volaní mmap
.mmap
pre ktorý proces. Definujte štruktúru VMA (virtual memory area), ktorá bude obsahovať pomocné údaje k mapovaniu. Zapísať by ste mali adresu, dĺžku, povolenia, súbor, atď. pre rozsah virtuálnej pamäte vytvorený volaním mmap
. Keďže xv6 nemá v jadre alokátor pamäte s premenlivou veľkosťou, môžete deklarovať pole pevnej dĺžky, do ktorého budete ukladať položky VMA podľa potreby. Dĺžka 16 by mala byť postačujúca.mmap
: nájdite nevyužitý región vo virtuálnej pamäti procesu, do ktorého namapujete súbor a pridajte záznam VMA do tabuľky mapovaných regiónov daného procesu. Záznam VMA by mal obsahovať smerník na štruktúru struct file
súboru, ktorý je mapovaný. Nezabudnite navýšiť počítadlo referencií v tejto štruktúre, aby nezanikla po uzavretí súboru (viď filedup
). Spustite mmaptest: teraz by malo byť prvé volanie mmap
úspešné, ale prvý prístup k mapovanej pamäti spôsobí výpadok stránky, čo vyústi ukončením procesu mmaptest.readi
. Táto funkcia má na vstupe offset, od ktorého načíta súbor (nezabudnite zamknúť/odomknúť príslušný inode, ktorý čítate). Taktiež nezabudnite nastaviť správne povolenia počas mapovania stránky. Ak veľkosť súboru nie je zaokrúhlená na hranicu stránky, koniec poslednej namapovanej stránky za obsahom súboru vyplňte nulami. Po spustení testu mmaptest by ste sa mali dostať až k prvému volaniu munmap
, ktoré zlyhá.munmap
: nájdite VMA pre príslušný adresný rozsah a odmapujte požadované stránky (inšpirujte sa funkciou uvmunmap
). Ak munmap
odmapoval všetky stránky súboru, mal by znížiť počítadlo referencií príslušnej štruktúry file
. Ak stránka, ktorú odmapoval bola upravená a súbor bol mapovaný s príznakom MAP_SHARED, zapíšte stránku späť do súboru. Inšpiráciu k tomuto hľadajte vo funkcii filewrite
.MAP_SHARED
stránky, ktoré boli naozaj upravené. Bit dirty (D
) v PTE na RISC-V architektúre signalizuje, že do stránky prebehol zápis. Toto ale mmaptest
netestuje, tak sa zaobídete aj bez tejto úpravy. Teraz by mal prejsť mmap_test.exit
, aby odmapoval regióny procesu s namapovanými súbormi (tak isto, ako to robí munmap
). Po spustení mmaptest by mali prejsť všetky testy až po test mmap two files, ale fork_test už neprejde.fork
, aby detský proces mal namapované presne tie isté regióny ako jeho rodič. Nezabudnite tiež zvýšiť počítadlo referencií štruktúry file
pre každý záznam VMA. Pri obsluhe výpadku stránky v detskom procese je v poriadku, ak alokujete novú fyzickú stránku namiesto toho, aby ste ju zdieľali s rodičom. Bolo by to zaujímavé, ale vyžadovalo by to viac práce. Spustite mmaptest, mali by prejsť všetky testy.Na záver spustite usertests -q, aby ste overili, či funguje aj zvyšok systému.
Spustite príkaz make grade, aby ste overili správnosť svojho riešenia. Po úspešnom dokončení úlohy nezabudnite vytvoriť commit! 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 aj túto zmenu commitnite do repozitára. Týmto je cvičenie ukončené.
Prvý riešiteľ úlohy môže dostať bonusové body po kontrole riešenia. Informujte sa u prednášajúceho alebo cvičiaceho. Voliteľnú úlohu môžete riešiť až po vyriešení všetkých úloh základnej časti cvičenia! Riešenia prijímame iba do konca dvanásteho týždňa semestra.
BSIZE
na 4096). Namapované bloky musíte pripnúť do kešky, aby z nej nemohli byť vyhodené. Musíte teda upraviť počítadlo referencií.exec
, aby používal VMA pre rôzne sekcie spustiteľných súborov, aby načítanie inštrukcií pri behu programu prebiehalo na požiadanie. Takto budú programy štartovať rýchlejšie, pretože exec
nebude musieť načítať dáta zo súborového systému.