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 a prejdite do vetvy util:
1 2 3 4 5 |
$ git clone git://g.csail.mit.edu/xv6-labs-2021 Cloning into 'xv6-labs-2021'... ... $ cd xv6-labs-2021 $ git switch util |
Repozitár xv6-labs-2021 sa o niečo odlišuje od verzie xv6 popísanej v sprievodnej knihe kurzu; je tam niekoľko súborov naviac. Ak ste zvedaví, pozrite si históriu repozitára:
1 |
$ git log |
Súbory, ktoré budeme potrebovať na tomto a ďalších cvičeniach budú distribuované pomocou Gitu. Pred chvíľou ste vytvorili novú vetvu (git branch util) na uloženie riešení tohto cvičenia. Viac informácií o gite nájdete v používateľskom manuáli. 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.
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 22 |
$ ls . 1 1 1024 .. 1 1 1024 README 2 2 2059 xargstest.sh 2 3 93 cat 2 4 24256 echo 2 5 23080 forktest 2 6 13272 grep 2 7 27560 init 2 8 23816 kill 2 9 23024 ln 2 10 22880 ls 2 11 26448 mkdir 2 12 23176 rm 2 13 23160 sh 2 14 41976 stressfs 2 15 24016 usertests 2 16 148456 grind 2 17 38144 wc 2 18 25344 zombie 2 19 22408 console 3 20 0 |
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: Ctrl-a 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()
volá exit()
na ukončenie programu.
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 a/b):
1 2 3 4 5 6 7 8 9 10 |
$ make qemu ... init: starting sh $ echo > b $ mkdir a $ echo > a/b $ find . b ./b ./a/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íkazu 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 (ľahká, obsadená)
uptime()
.
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.)