Pomôcky k cvičeniam

Náročnosť úloh na cvičení

Pri každej úlohe je vyznačená jej zložitosť:

  • Ľahká: vypracovanie trvá menej ako hodinu. Tieto úlohy sú zahrievacie na vnorenie sa do problematiky.

  • Stredná: 1–2 hodiny.

  • Ťažká: viac ako 2 hodiny. Tieto úlohy väčšinou nevyžadujú veľa kódu, ale napísať ho správne je náročné.

Dané časy sú iba odhad. Niektoré voliteľné úlohy sme neriešili a ich náročnosť iba tipujeme. Ak riešenie úlohy trvá dlhšie, ozvite sa počas cvičenia.

Vo všeobecnosti platí, že úlohy z cvičení nevyžadujú veľa riadkov kódu (v priemere desiatky až niekoľko stoviek riadkov). Samotný kód je ale koncepčne komplikovaný a veľmi záleží na detailoch. Pred riešením cvičení nezabudnite preštudovať odporúčané materiály, zdrojové kódy, s ktorými budete pracovať a dokumentáciu (manuály RICV-V a iné sú na stránke MIT). Programovať začnite až keď dobre chápete úlohu a jej riešenie. Samotné riešenie implementujte malými krokmi (v zadaní je často problém rozložený na niekoľko podproblémov). Implementáciu po každom kroku otestujte.

Varovanie: najefektívnejšie je rozložiť si riešenie cvičení na niekoľko dní. Určité chyby môžu byť veľmi zarážajúce a vyžadujú veľa rozmýšľania a dôsledné ladenie.

Ladiace tipy

Niekoľko tipov, ktoré môžete využiť počas ladenia vašich riešení:

  • Dobre si naštudujte jazyk C a smerníky. Kniha „The C programming language (second edition)“ od Kernighana a Ritchieho je stručným popisom céčka. Užitočné cvičenia na smerníky nájdete tu. Ak nemáte hlboké znalosti C, určite si toto cvičenie vypracujte. Kolega Marián Šebeňa pre vás tiež pripravil opakovanie jazyka C vo forme video tutoriálu. Ak nerozumiete smerníkom alebo céčku, zažijete počas cvičení ukrutné utrpenie, keďže sa ich budete musieť naučiť tým ťažším spôsobom. A verte nám, nechcete zažiť, čo je to ten ťažší spôsob.

    Zapamätajte si tieto použitia smerníkov:

    • Ak int *p = (int*)100, potom (int)p + 1 and (int)(p + 1) sú rozdielne čísla. Prvé je 101 a druhé 104. Keď pripočítate celé číslo k smerníku (ako v druhom prípade), číslo je implicitne vynásobené veľkosťou objektu, na ktorý smerník ukazuje.
    • p[i] je z definície to isté ako *(p+i), znamená to i-ty objekt od objektu s adresou p. Vďaka predchádzajúcemu pravidlu tento zápis funguje aj vtedy, keď je veľkosť objektu väčšia ako jeden bajt.
    • &p[i] je to isté ako (p+i). Jedná sa o adresu i-teho objektu v pamäti od objektu s adresou p.

    Väčšina programov písaná v céčku nepotrebuje pretypovávať smerníky na celé čísla a opačne. Operačné systémy to ale často robia. Ak uvidíte operáciu sčítania s pamäťovou adresou ako operandom, zamyslite sa, či sa jedná o celočíselnú alebo smerníkovú aritmetiku.

  • Ak vami riešená úloha už čiastočne funguje, zaznamenajte si váš postup do repozitára. Ak sa niečo pokazí, viete sa vrátiť späť a pokračovať od pomalšími krokmi miesta, kde váš kód fungoval. Viac o gite nájdete v užívateľskej príručke.

  • Ak vám zlyhá test, zistite dôvod zlyhania. Použite ladenie výpismi, aby ste pochopili, čo sa deje.

  • Ak vaše ladiace výpisy produkujú veľa výstupu a chcete v nich vyhľadávať, môžete spustiť make qemu v programe script (prečítajte si manuál man script). Program script zaznamená všetok výstup do súboru, v ktorom potom môžete vyhľadávať. Nezabudnite script ukončiť.

  • Vo veľa prípadoch vám budú stačiť ladiace výpisy, ale niekedy budete musieť krokovať kód assembly alebo čítať premenné na zásobníku. Aby ste mohli použiť ladiaci nástroj gdb, najprv spustite make qemu-gdb v jednom okne a potom gdb-multiarch (alebo riscv64-linux-gnu-gdb) v druhom. Nastavte breakpoint, zadajte príkaz c (continue) a xv6 bude bežať, až kým nenarazí na vami určený breakpoint. (Pozrite si prezentáciu Používanie GDB (EN) od našich kolegov z MIT pre ďalšie tipy.) Ak spustíte a vidíte takéte varovanie: warning: File "…/.gdbinit" auto-loading has been declined, otvorte súbor ~/.gdbinit a pridajte do neho jeden z riadkov, ktoré sú vypísané vo varovaní.

  • Ak chcete vidieť kód assembly, ktorý generuje kompilátor pre jadro alebo chcete nájsť inštrukciu na nejakej adrese jadra, pozrite súbor kernel.asm, ktorý je vytvorený vždy po kompilácii jadra. (Súbor Makefile obsahuje direktívy, ktoré generujú .asm súbory pre jadro a všetky užívateľské programy.)

  • Ak jadro spanikári, vypíše chybovú hlášku s hodnotou počítadla inštrukcií (inštrukčný smerník). Túto hodnotu môžete vyhľadať v súbore kernel.asm. Tým zistíte v akej funkcii program padol. Alternatívne môžete spustiť addr2line -e kernel/kernel hodnota-pc (pozrite manuál man addr2line pre viac informácií). Ak chcete vidieť backtrace, reštartujte systém cez gdb: spustite make qemu-gdb v jednom okne a gdb-multiarch (alebo riscv64-linux-gnu-gdb) v druhom, nastavte breakpoint vo funkcii panic (b panic) a zadajte príkaz c (continue). Keď jadro narazí na breakpoint, zadajte bt na vypísanie backtrace.

  • Ak jadro zamrzne (napríklad kvôli deadlocku) alebo ak nemôže ďalej pokračovať (napríklad ak nastal výpadok stránky počas vykonávania inštrukcie v jadre), môžete použiť gdb na zistenie miesta, kde jadro zamrzlo. Spustite make qemu-gdb v jednom okne a gdb (alebo riscv64-linux-gnu-gdb) v druhom a zadajte príkaz c (continue). Keď jadro zamrzne, stlačte Ctrl-C v okne qemu-gdb a napíšte bt na výpis backtrace.

  • qemumonitor, ktorým môžete zisťovať stav emulovaného stroja. Otvoríte ho stlačením skratky Ctrl-a c (c je skratka pre console). Užitočný príkaz monitora je info mem, ktorým môžete vypísať tabuľku stránok. Predtým asi budete musieť použiť príkaz cpu, ktorým vyberiete jadro, na ktoré sa má príkaz info mem pozrieť. Alternatívne môžete spustiť qemu ako make CPUS=1 qemu, aby bolo použité len jedno jadro.