Warning
This page is located in archive.

NOVA – Systémová volání a správa paměti

Na tomto cvičení se seznámíte s jádrem miniaturního OS NOVA a implementujete do něj systémové volání brk. NOVA je mikrohypervizor původně vyvíjený na Drážďanské univerzitě, později ve firmě Intel a nyní firmami GENODE labs a Cyberus Technology. Na cvičeních však nebudete pracovat s kompletní verzí jádra NOVA, ale se zjednodušenou verzí pro výuku, která má pouze 2 tisíce řádek kódu.

Domácí příprava

Pro toto cvičení budete potřebovat následující:

Zadání úlohy

Implementujte systémové volání brk s prototypem:

void *brk(void *address)

Toto systémové volání nastaví konec datového segmentu v adresním prostoru procesu (tzv. program break nebo jen break) na adresu danou parametrem address. Tím se zvětší nebo zmenší množství alokované paměti, které může program využívat ke svému běhu. Break je první adresa za koncem namapovaného datového segmentu.

Vaše řešení by mělo splňovat následující požadavky:

  • Po úspěšném návratu ze systémového volání je break nastaven na hodnotu address. To znamená, že uživatelský program může používat paměť od adresy 0x1000 do o jedna menší než address. Přístup na stránky začínající na adrese vyšší či rovné address nebude programu dovolen.
  • Break nesmí jít nastavit na nižší hodnotu, než je jeho hodnota při spuštění programu. Tím by se program připravil o část svého kódu, dat nebo zásobníku.
  • Break nesmí jít nastavit na vyšší hodnotu než 0xC0000000. Tím by aplikace mohla přepsat jádro, které je namapováno od této adresy výš.
  • Při úspěšném dokončení je vrácena původní hodnota break před vykonáním systémového volání. Při chybě je vrácena hodnota 0.
  • Pokud je address rovno NULL (0), nejedná se o chybu a hodnota break se nemění. Toto volání slouží pouze ke zjištění aktuální hodnoty break.
  • Při žádném volání brk nesmí dojít k “pádu” systému.
  • ABI systémového volání bude následující. Vstup: AL=3, ESI=address. Výstup: EAX=návratová hodnota.
  • Nově alokovaná paměť bude inicializována na nulu.
  • Při snižování hodnoty break bude nepřístupná paměť dealokována (a odmapována), aby mohla být opět alokována později.
  • Při chybě alokace paměti v průběhu systémového volání nebude adresní prostor uživatelské aplikace změněn a částečně alokovaná paměť bude dealokována.
  • Při kompilaci nevypisuje kompilátor žádná varování.

Odevzdává se archiv se souborem ec_syscall.cc obsahující vaši implementaci, ideálně vytvořený pomocí

make hw10
.

Nápověda

  • Operační systém NOVA s testovací aplikací user/hello.c můžete nabootovat buď na fyzickém počítači pomocí zavaděče podporujícího specifikaci multiboot (např. GRUB 2), ale pravděpodobně bude efektivnější ho pouštět jako virtuální stroj například v emulátoru Qemu. Pro to stačí spustit příkaz
    make run
    .
  • Při vývoji operačního systému nelze používat debugger tak, jak jste zvyklí při vývoji aplikací. Ladit váš kód můžete buď přidáváním příkazů printf() na potřebná místa v kódu. Pokud vám to nestačí můžete použít parametr -gdb emulátoru Qemu, ale to je trochu komplikovanější.
  • Při zvětšování hodnoty program break musíte v jádře alokovat paměť a namapovat ji do adresního prostoru uživatelské aplikace modifikováním stránkovacích tabulek. Inspirací vám může být funkce Ec::root_invoke(), která připravuje paměť pro spouštěný program. Funkce čte hlavičky z binárky aplikace, které si můžete zobrazit příkazem readelf –program-headers hello.
  • Při snižování hodnoty program break naopak musíte mapování zrušit a paměť dealokovat.
  • Při startu uživatelského programu (např. hello) je jeho paměťová mapa následující (trochu zjednodušeno):
    • 0x00001000 – 0x00001fff – zásobník (4 kB), počáteční hodnota registru ESP je 0x2000.
    • 0x00002000 – 0xXXXXX000-1 – data programu (viz .data ve výstupu příkazu readelf –sections hello)
    • 0xXXXXX000 – 0xYYYYY000-1 – kód programu (viz .text ve výstupu příkazu readelf –sections hello)
    • 0xYYYYY000 – program break
  • Ke kódu jádra NOVA není žádná dokumentace, ale části, které budete potřebovat, jsou tak jednoduché, že byste měli být schopni jim porozumět na základě čtení kódu (a komentářů). Pokud však i přes veškerou vaší snahu něčemu nerozumíte, ptejte se na fóru.

Příklad

  • Předpokládejme, že break_start je na začátku Vašeho procesu nastaven na 0x5000.
  • Při volání funkce break(0x9000) dojde k alokování stránek 0x5000-0x5FFF, 0x6000-0x6FFF, 0x7000-0x7FFF a 0x8000-0x8FFF.
  • Při dalším volání break(0x7555) dojde k uvolnění stránky 0x8000-0x8FFF.
  • Při dalším volání break(0x7666) se neprovádí žádná alokace, ale měla by se vynulovat paměť 0x7555-0x7665.
  • Při dalším volání break(0xA000) se alokuje nová stránka pro 0x8000-0x8FFF a 0x9000-0x9FFF, a vynuluje se paměť 0x7666-0x7FFF
  • Poznámka, předpokládáme alokaci s vyplněním 0 v nově alokované stránce

Materiály

courses/b4b35osy/cviceni/cviceni10_brk.txt · Last modified: 2018/12/05 13:58 by sojkam1