Virtuálna pamäť umožňuje implementovať určitý spôsob presmerovania prístupu k dátam. Ak jadro označí PTE ako neplatný alebo iba na čítanie, prístup k danej stránke spôsobí výpadok stránky a jadro môže rozhodnúť, čo tento prístup znamená.
V počítačových systémoch existuje porekadlo, ktoré hovorí, že akýkoľvek systémový problém je možné vyriešiť presmerovaním. V tomto cvičení sa pozrieme na ďalší príklad – copy-on write fork.
Predtým, ako začnete programovať, prečítajte si sekciu 4.6 xv6 knižky a pozrite si príslušné súbory odkazované z knižky.
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.
Pred začiatkom práce prepnite repozitár do vetvy cow:
1 2 3 |
$ git fetch $ git switch cow $ make clean |
Systémové volanie fork()
v xv6 kopíruje celý adresný priestor rodiča do priestoru potomka. Ak je adresný priestor rodiča veľký, kopírovanie môže trvať dlho (relatívne k rýchlosti procesora). Naviac sa týmto tiež zbytočne plytvá pamäťou – niektoré stránky nemodifikuje ani rodič, ani potomok. To znamená, že by mohli byť spoločné pre oba procesy. Najlepším príkladom neefektívnosti takéhoto kopírovania je volanie exec()
v detskom procese, ktoré obvykle nasleduje hneď po forku. exec()
uvoľní všetky ťažko nakopírované stránky zvyčajne bez toho, aby nejakú použil. Kopírovať stránku by sme v ideálnom prípade mali až vtedy, ak ju zdieľajú rodič aj dieťa a aspoň jeden chce do nej zapisovať.
Cieľom vašej implementácie copy-on-write (COW) forku je odložiť alokáciu a kopírovanie fyzickej pamäte pre detský proces, až kým kópie nie sú skutočne potrebné (t. j. buď rodič a/alebo dieťa sa pokúsi o zápis do spoločnej stránky).
COW fork()
vytvorí iba tabuľku stránok pre detský proces. Záznamy v tabuľke (PTE) budú ukazovať na fyzické stránky (údaje v RAM) rodičovského procesu. Zároveň záznamy v tabuľke stránok oboch procesov budú označené iba na čítanie. Ak sa jeden z procesov pokúsi zapísať niečo do COW stránky, CPU vynúti výpadok stránky. Obsluha výpadkov stránky v jadre tento výpadok zachytí, alokuje stránku fyzickej pamäte pre proces, ktorý spôsobil výpadok, skopíruje obsah pôvodnej (spoločnej) stránky do novej stránky a upraví súvisiace záznamy v tabuľke stránok, aby ukazovali na novú stránku s príznakom PTE_W, ktorý umožní zapisovanie. Po návrate z obsluhy výpadku bude môcť užívateľský proces pokračovať v zápise do vlastnej kópie stránky.
Uvoľňovanie fyzických stránok procesov bude pri COW forku o niečo zložitejšie. Jedna fyzická stránka môže byť namapovaná vo viacerých procesoch. Uvoľnená by mala byť až po zániku poslednej referencie, teda keď ju odmapuje posledný proces, ktorý ju používa. V jednoduchom jadre, ako xv6, nie je zložité udržať informáciu o procesoch, v ktorých je namapovaná nejaká stránka. V produkčných jadrách to už môže byť náročnejšia úloha. Prečítajte si článok o tom, ako to robia v Linuxe: Patching until the COWs come home.
Na otestovanie vašej implementácie môžete použiť program cowtest (jeho zdrojový súbor nájdete v user/cowtest.c
). cowtest obsahuje niekoľko testov, na neupravenom kóde xv6 zlyhá hneď prvý test. Takto to vyzerá:
1 2 3 |
$ cowtest simple: fork() failed $ |
Test simple alokuje viac ako polovicu dostupnej fyzickej pamäte a potom zavolá fork()
. Fork zlyhá, pretože nie je miesto na skopírovanie celého adresného priestoru rodiča.
Implementácia je korektná, ak úspešne prebehnú programy cowtest a usertests -q. Takto by to malo vyzerať:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ cowtest simple: ok simple: ok three: ok three: ok three: ok file: ok forkfork: ok ALL COW TESTS PASSED $ usertests -q ... ALL TESTS PASSED $ |
Postupovať by ste mali takto:
uvmcopy()
tak, aby namapovala fyzické stránky rodiča do dieťaťa namiesto alokácie nových stránok. Aj v rodičovi, aj v potomkovi vynulujte príznak PTE_W
stránkam, ktoré ho majú nastavený.usertrap()
tak, aby rozoznala výpadky stránok. Ak pri zapisovaní nastane výpadok stránky na COW stránke, ktorá bola pôvodne zapisovateľná, alokujte novú stránku pomocou kalloc()
, skopírujte obsah starej stránky do novej stránky a namapujte novú stránku do adresného priestoru procesu, ktorý výpadok spôsobil s nastaveným príznakom PTE_W
. Stránky, ktoré boli pôvodne mapované len na čítanie (nemali nastavený príznak PTE_W
, napríklad stránky zo segmentu text) by mali byť zdieľané medzi rodičom a potomkom len na čítanie. Ak sa rodič alebo potomok pokúsi zapisovať do takejto stránky, musí byť ukončený.kalloc.c
a pouvažujte, či by sa tam nehodilo implementovať počítanie referencií.copyout()
tak, aby používal vyššie popísanú schému pri výpadku stránky COW.Pomôcky:
cowtest
netestuje. Nezabudnite preto skontrolovať výsledok oboch testov.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.
Prvý riešiteľ každej úlohy môže dostať bonusové body po kontrole riešenia. Informujte sa u prednášajúceho alebo cvičiaceho. Voliteľné úlohy môžete riešiť až po vyriešení všetkých úloh základnej časti cvičenia! Riešenia prijímame iba do konca deviateho týždňa.