V tomto cvičení sa naučíte, ako UNIXovské shelly používajú základné systémové volania.
Vašou úlohou je napísať jednoduchý shell pre xv6. Mal by byť schopný spustiť príkaz s argumentami, zvládať presmerovať vstup/výstup a podporovať aspoň jednu rúru v príkaze. Po jeho dokončení by ste v ňom mali bez problémov spustiť nasledovné príkazy (mal by mať podobné správanie ako shell xv6 sh):
1 2 3 4 5 6 |
echo hello there echo something > file.txt ls | grep READ grep lion < data.txt | wc > count echo echo hello | nsh find . b | xargs grep hello |
Zdrojové súbory shellu umiestnite do user/nsh.c a príslušne modifikujte Makefile, aby ho bolo možné skompilovať. Pri písaní a testovaní shellu modifikujte len user/nsh.c. Váš shell musí byť od iných zdrojových súborov nezávislý. Váš shell by mal používať @
(zavináč) ako znak vstupu príkazu, aby ste si ho nepomýlili so skutočným shellom. Práca v xv6 s vaším shelom môže vyzerať nasledovne:
1 2 3 4 5 |
xv6 kernel is booting $ nsh @ grep Ken < README xv6 is a re-implementation of Dennis Ritchie's and Ken Thompson's Unix @ |
Na tomto cvičení nesmiete používať dynamickú alokáciu pamäte pomocou malloc(). Namiesto nej používajte lokálne (na zásobníku) a globálne premenné. Je v poriadku, ak stanovíte limity na maximálnu dĺžku príkazu, maximálny počet argumentov, maximálnu dĺžku jedného argumentu a podobne.
Pripravili sme pre vás (teda nie my, ale ujovia z MIT) testovací program testsh. Jeho zdroják nájdete v user/testsh.c. Keď si budete myslieť, že máte shell hotový, môžete si otestovať práve pomocou tohto programu. Cvičenie ste nedokončili, až kým ste neprešli všetkými testami. Ukážka:
1 2 3 4 5 6 7 8 9 10 11 |
$ testsh nsh simple echo: PASS simple grep: PASS two commands: PASS output redirection: PASS input redirection: PASS both redirections: PASS simple pipe: PASS pipe and redirects: PASS lots of commands: PASS passed all tests |
V tomto a ďalších cvičeniach budeme postupne rozširovať xv6, ale aby sme v zdrojákoch mali poriadok, pre každé cvičenie vytvoríme samostatnú vetvu. Ak ste pracovali na utilitách v predchádzajúcom cvičení, teraz je vhodný čas uložiť si prácu do repozitára. Použite na to príkazy:
1 |
$ git commit -am 'riesenia prveho cvicenia' |
Teraz sa môžete prepnúť do vetvy sh:
1 2 |
$ git fetch $ git checkout sh |
Nie je potrebné, aby ste do tejto vetvy mergovali riešenie predchádzajúceho cvičenia (utility). Ak by ste odtiaľ potrebovali nejaký kód, môžete to urobiť, ale ináč sú skoro všetky cvičenia navzájom nezávislé.
Pomocou príkazu git checkout nazov-vetvy
môžete prepínať medzi jednotlivými vetvami. Pred prepnutím ale musíte commitnúť vaše zmeny. Ak tak neurobíte, tak git vám aj tak vynadá (ako ináč, keď ho napísal Linus Torvalds).
Neimplementujte funkcie, ktoré nie sú kontrolované v testoch. Váš shell nemusí podporovať zátvorky a úvodzovky (ich implementáciu vám ale nezakazujeme).
Knižka o céčku od Kernighana a Ritchieho je plná užitočných kúskov kódu – môžete ich voľne používať. Možno sa vám bude hodiť funkcia gettoken() z parsera v sekcii 5.12.
Nsh je oveľa jednoduchší ako pôvodný shell xv6, takže pre vaše programátorské pohodlie bude najlepšie, ak ho začnete písať od začiatku. Nič vám ale nebráni nakuknúť do zdrojákov xv6 shellu (user/sh.c) pre inšpiráciu.
V xv6 je pripravená malá knižnica funkcií jazyka C v user/ulib.c. Môžete ju používať, okrem funkcie malloc(), ako sme už spomenuli vyššie.
Nezabudnite zatvoriť nepotrebné súborové deskriptory. Je to potrebné z dvoch dôvodov:
Nezabudnite na kontrolu návratových hodnôt systémových volaní!
testsh presmeruje štandardný výstup vášho shellu. To znamená, že na obrazovke neuvidíte jeho výstup. Chybové a ladiace hlášky vypisujte na štandardný chybový výstup (súborový deskriptor 2) prostredníctvom funkcie fprintf(2,...).
Ak sa testsh sťažuje na váš shell, dovoľujeme vám modifikovať testsh tak, aby vypísal príkaz, ktorý sa snaží spustiť vo vašom shelli. Váš shell môžete tiež upraviť, aby vypísal argumenty, ktoré dostane od testovacieho skriptu na spustenie. To vám môže uľahčiť hľadanie chýb.
Ďalej vám dovoľujeme upraviť funkciu one() v testsh.c tak, aby vypísala výstup z vášho shellu a výstup, ktorý očakávala. Po porovnaní môžete zistiť príčinu chyby.