Toto cvičenie vás zoznámi s operačným systémom xv6 a jeho systémovými volaniami.
Aby ste mohli začať pracovať s xv6, musíte mať nainštalované všetky nástroje, vrátane operačného systému xv6. Ak používate nami poskytnutý virtuálny stroj, všetko je už pripravené a sťahovanie repozitára môžete preskočiť.
Stiahnite zdrojáky xv6:
1 2 3 4 |
$ git clone git://g.csail.mit.edu/xv6-labs-2022 Cloning into 'xv6-labs-2022'... ... $ cd xv6-labs-2022 |
Repozitár je nastavený tak, aby sa po naklonovaní automaticky prepol na vetvu util. Môžete si to overiť príkazom git status:
1 2 3 4 5 |
$ git status On branch util Your branch is up to date with 'origin/util'. nothing to commit, working tree clean |
Repozitár xv6-labs-2022 sa trochu líši od systému xv6-riscv popísaného v sprievodnej knihe kurzu. Repozitár, ktorý ste práve naklonovali obsahuje niekoľko súborov naviac. Ak ste zvedaví, pozrite si históriu repozitára príkazom:
1 |
$ git log |
Súbory, ktoré budeme potrebovať na tomto a ďalších cvičeniach budú distribuované pomocou Gitu. Na každom cvičení prepnete repozitár na inú vetvu, ktorá obsahuje verziu xv6 špeciálne pripravenú pre dané cvičenie. Napríklad na vetvu util sa prepnete príkazom git switch util. Viac informácií o programe git nájdete v používateľskom manuáli. Pozrite si tiež návod, kde je zhrnutá základná konfiguráciu gitu a činnosti, ktoré budete s gitom vykonávať. Git vám umožní zaznamenať zmeny, ktoré urobíte v kóde. Napríklad, keď dokončíte jednu úlohu z cvičenia, svoje zmeny uložíte (commitnete) takto:
1 2 3 4 5 |
$ git add user/sleep.c Makefile $ git commit -m 'feat: implement sleep' Created commit 60d2135: feat: implement sleep 2 files changed, 15 insertions(+), 0 deletions(-) $ |
Vysvetlenie: Príkazom git add sme pridali upravené/nové súbory, ktoré sa stanú súčasťou commitu. Príkazom git commit potom vyžiadame uskutočnenie commitu. Prepínačom -m môžete priamo v príkaze uviesť správu ku commitu. Bez prepínača je otvorený textový editor (nastavený v súbore ~/.gitconfig), v ktorom je možné správu upraviť. Pripomíname, že commity vo vašich repozitároch musia spĺňať náležitosti uvedené v špecifikácii Conventional Commits 1.0.0.
Svoje aktuálne zmeny môžete sledovať pomocou príkazu git diff. Týmto príkazom zobrazíte zmeny, ktoré ste urobili od posledného commitu a príkazom git diff origin/util zobrazíte všetky zmeny od stavu, v akom ste repozitár xv6-labs-2021 obdržali. origin/util uvedený v príkaze je názov vetvy, v ktorej sa nachádza pôvodný kód z MIT, ktorý ste si stiahli na začiatku cvičenia (origin štandardne označuje vzdialený repozitár, z ktorého sťahujete kód).
Riešenia úloh z tohto cvičenia ukladajte do vetvy util. Túto vetvu budeme kontrolovať v rámci priebežnej kontroly repozitárov.
Skompilujte a spustite xv6:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
$ make qemu riscv64-unknown-elf-gcc -c -o kernel/entry.o kernel/entry.S riscv64-unknown-elf-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_UTIL -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/start.o kernel/start.c ... riscv64-unknown-elf-ld -z max-page-size=4096 -N -e main -Ttext 0 -o user/_zombie user/zombie.o user/ulib.o user/usys.o user/printf.o user/umalloc.o riscv64-unknown-elf-objdump -S user/_zombie > user/zombie.asm riscv64-unknown-elf-objdump -t user/_zombie | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > user/zombie.sym mkfs/mkfs fs.img README user/xargstest.sh user/_cat user/_echo user/_forktest user/_grep user/_init user/_kill user/_ln user/_ls user/_mkdir user/_rm user/_sh user/_stressfs user/_usertests user/_grind user/_wc user/_zombie nmeta 46 (boot, super, log blocks 30 inode blocks 13, bitmap blocks 1) blocks 954 total 1000 balloc: first 591 blocks have been allocated balloc: write bitmap block at sector 45 qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 3 -nographic -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 xv6 kernel is booting hart 2 starting hart 1 starting init: starting sh $ |
Ak do príkazového riadka napíšete ls, mali by ste uvidieť podobný výstup:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
$ ls . 1 1 1024 .. 1 1 1024 README 2 2 2227 xargstest.sh 2 3 93 cat 2 4 32864 echo 2 5 31720 forktest 2 6 15856 grep 2 7 36240 init 2 8 32216 kill 2 9 31680 ln 2 10 31504 ls 2 11 34808 mkdir 2 12 31736 rm 2 13 31720 sh 2 14 54168 stressfs 2 15 32608 usertests 2 16 178800 grind 2 17 47528 wc 2 18 33816 zombie 2 19 31080 |
Tieto súbory vytvorí mkfs počas inicializácie súborového systému; väčšina z nich sú programy, ktoré môžete spustiť. Pred chvíľou ste jeden z nich spustili: ls.
xv6 nemá príkaz ps, ale ak stlačíte Ctrl-p, jadro vypíše informácie o každom procese. Ak to vyskúšate teraz, uvidíte dva riadky: jeden pre init a druhý pre sh.
Na vypnutie qemu stlačte postupne: spolu Ctrl-a a potom samostatne x.
Svoje riešenie môžete otestovať hodnotiacim programom zavolaním make grade. V jeho výstupe zistíte, ktoré úlohy ste vyriešeili správne. Plný počet bodov však neznamená, že váš kód je bezchybný.
Pomôcky:
atoi()
(viď user/ulib.c).
sleep()
.
sleep()
(hľadajte sys_sleep()
)sleep()
z užívateľského programumain()
by mala ako posledné volanie zavolať exit(0)
.
UPROGS
v súbore Makefile. Keď to máte hotové, príkazom make qemu skompilujete program a môžete ho spustiť z xv6 shellu.
Spustite program z xv6 shellu:
1 2 3 4 5 6 |
$ make qemu ... init: starting sh $ sleep 10 (chvíľu sa nič nebude diať) $ |
Ak sa váš program pozastavil, vaše riešenie je správne. Spustite make grade, či program sleep prejde všetkými testami.
make grade spúšťa všetky testy, aj tie, ktoré ešte nemáte vypracované. Ak chcete spustiť len jeden test, napíšte:
1 |
$ ./grade-lab-util sleep |
Týmto spustíte testy, ktoré obsahujú „sleep“. Druhý spôsob:
1 |
$ make GRADEFLAGS=sleep grade |
urobí to isté.
Po úspešnom dokončení úlohy nezabudnite vytvoriť commit!
Pomôcky:
pipe()
na vytvorenie rúr.
fork()
na vytvorenie detského procesu.
read()
na prečítanie dát z rúry a write()
na zápis do rúry.
getpid()
na zistenie ID volajúceho procesu.
UPROGS
v súbore Makefile.
Spustite program z xv6 shellu. Mali by ste vidieť takýto výstup:
1 2 3 4 5 6 7 |
$ make qemu ... init: starting sh $ pingpong 4: received ping 3: received pong $ |
Vaše riešenie je správne ak program vymení bajt medzi dvoma procesmi a vypíše vyššie uvedený výstup.
Po úspešnom dokončení úlohy nezabudnite vytvoriť commit!
Vaším cieľom je použiť pipe()
a fork()
na zostavenie „potrubia“. Prvý proces vloží čísla od 2 po 35 do potrubia. Pre každé prvočíslo sa vytvorí jeden proces, ktorý bude prijímať dáta od ľavého suseda cez rúru a zapisovať pravému susedovi cez inú rúru. Keďže xv6 má obmedzený počet deskriptorov a procesov, prvý proces musí prestať pri čísle 35.
Pomôcky:
read()
vráti nulu keď je zapisovacia strana rúry zatvorená.
int
) ako používať formátované ASCII znaky.
UPROGS
v súbore Makefile.
Vaše riešenie je správne, ak implementuje prvočíselné sito založené na rúrach a má takýto výstup:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ make qemu ... init: starting sh $ primes prime 2 prime 3 prime 5 prime 7 prime 11 prime 13 prime 17 prime 19 prime 23 prime 29 prime 31 $ |
Po úspešnom dokončení úlohy nezabudnite vytvoriť commit!
Pomôcky:
.
a ..
.
strcmp()
.
UPROGS
v súbore Makefile.
Vaše riešenie je správne, ak vyprodukuje takýto výstup (za predpokladu, že súborový systém obsahuje súbory b, a/b a a/aa/b):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$ make qemu ... init: starting sh $ echo > b $ mkdir a $ echo > a/b $ mkdir a/aa $ echo > a/aa/b $ find . b ./b ./a/b ./a/aa/b $ |
Po úspešnom dokončení úlohy nezabudnite vytvoriť commit!
Teto príklad ilustruje správanie programu xarg:
1 2 3 |
$ echo hello too | xargs echo bye bye hello too $ |
Samotný príkaz je echo bye a ďalšie argumenty sú hello too. Úplný príkaz, ktorý bol spustený je echo bye hello too a jeho výstup je bye hello too.
Pomôcky:
fork()
a exec()
na vyvolanie príkazu na každý riadok vstupu. V rodičovi použite wait
, aby počkal kým dieťa vykoná príkaz.
'\n'
).
kernel/param.h
je deklarovaná konštanta MAXARG
, ktorá vám môže byť užitočná na deklaráciu poľa argv
.
xargs, find, a grep sa dajú výhdne použiť dokopy. Príkaz
1 |
$ find . b | xargs grep hello |
spustí „grep hello“ na každom súbore s názvom b v priečnikoch pod aktuálnym (.
) priečinkom.
Na otestovanie správnej funkcionality xargs spustite shell skript xargstest.sh. Vaše riešenie je správne, ak má takýto výstup:
1 2 3 4 5 6 7 8 |
$ make qemu ... init: starting sh $ sh < xargstest.sh $ $ $ $ $ $ hello hello hello $ $ |
Ak takýto výstup nevidíte, budete musieť opraviť chyby vo vašom programe find. Výstup obsahuje veľa dolárov ($), pretože shell xv6 nerozozná, že spracúva príkaz zo súboru namiesto z konzoly a vypíše $ pre každý príkaz v súbore.
Po úspešnom dokončení úlohy nezabudnite vytvoriť commit!
Týmto je cvičenie hotové. Skontrolujte, či vaše riešenie prejde všetkými testami make grade. 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 nezabudnite aj túto zmenu commitnúť do repozitára.
Napíšte program uptime, ktorý vypíše ako dlho je počítač zapnutý v tikoch použitím systémového volania uptime()
. (ľahká)
Naprogramujte podporu regulárnych výrazov pre program find. V súbore grep.c nájdete zjednodušenú implementáciu regulárnych výrazov, ktorú môžete použiť. (ľahká)
(
a )
(stredná)alebo hocičo iné, čo by ste chceli, aby váš shell robil. (Ak sa cítite dobrodružne, môžete upraviť aj jadro systému o funkcionality, ktoré potrebujete; samotný xv6 toho veľa nepodporuje.)