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 na cvičení/Discorde.
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 RISC-V a iné sú na stránke predmetu v časti literatúra). 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.
Niekoľko tipov, ktoré môžete využiť počas ladenia:
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 – mali by ste rozumieť, prečo tento program dáva takéto výstupy.
Zapamätajte si tieto použitia smerníkov:
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 pretypovať 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ť pomalšími krokmi od miesta, kde váš kód fungoval. Viac o gite nájdete v použí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 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/kernel.asm, ktorý je vytvorený vždy po kompilácii jadra. (Súbor Makefile obsahuje príkazy, ktoré generujú súbory .asm pre jadro a všetky používateľské programy.)
Ak jadro vyvolá neočakávanú chybu (napr. pristúpi k neplatnej pamäťovej adrese), vypíše chybovú hlášku s hodnotou počítadla inštrukcií (inštrukčný smerník sepc). Túto hodnotu môžete vyhľadať v súbore kernel.asm. Tým zistíte, v ktorej 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 (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), môžete použiť gdb na zistenie miesta, kde jadro zamrzlo. Spustite make qemu-gdb v jednom okne a gdb-multiarch (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.