world: :%-20s: :hello, world : :%20.10s: : hello, wor: :%-20.10s: :hello, wor : :%.10s: :hello, wor: PREDOSTEREZHENIE: printf ispol'zuet svoj pervyj argument dlya opredeleniya chisla posleduyushchih argumentov i ih tipov. Esli kolichestvo argumentov okazhetsya nedostatochnym ili oni budut imet' nesootvetstvuyushchie tipy, to vozniknet putanica i rezul'taty budut nevernymi. 13.4. Formatnyj vvod - funkciya scanf Osushchestvlyayushchaya vvod funkciya scanf yavlyaetsya analogom printf i pozvolyaet provodit' v obratnom napravlenii mnogie iz teh zhe samyh preobrazovanij. Funkciya scanf(control, arg1, arg2, ...) chitaet simvoly iz standartnogo vvoda, interpretiruet ih v sootvetstvii s formatom, ukazannom v argumente control, i pomeshchaet rezul'taty v ostal'nye argumenty. Upravlyayushchaya stroka opisyvaetsya nizhe; drugie argumenty, kazhdyj iz kotoryh dolzhen byt' ukazatelem, opredelyayut, kuda sleduet pomestit' sootvetstvuyushchim obrazom preobrazovannyj vvod. Upravlyayushchaya stroka obychno soderzhit specifikacii preob- razovaniya, kotorye ispol'zuyutsya dlya neposredstvennoj interp- retacii vhodnyh posledovatel'nostej. Upravlyayushchaya stroka -55- mozhet soderzhat': - probely, tabulyacii ili simvoly novoj stroki ("simvoly pustyh promezhutkov"), kotorye ignoriruyutsya; - obychnye simvoly (ne %), kotorye predpolagayutsya sovpa- dayushchimi so sleduyushchimi otlichnymi ot "simvolov pustyh promezhutkov" simvolami vhodnogo potoka; - specifikacii preobrazovaniya, sostoyashchie iz simvola %, neobyazatel'nogo simvola podavleniya prisvaivaniya *, neobyazatel'nogo chisla, zadayushchego maksimal'nuyu shirinu polya i simvola preobrazovaniya. Specifikaciya preobrazovaniya upravlyaet preobrazovaniem sleduyushchego polya vvoda. Obychno rezul'tat pomeshchaetsya v pere- mennuyu, kotoraya ukazyvaetsya sootvetstvuyushchim argumentom. Esli, odnako , s pomoshch'yu simvola * ukazano podavlenie pris- vaivaniya, to eto pole vvoda prosto propuskaetsya i nikakogo prisvaivaniya ne proizvoditsya. Pole vvoda opredelyaetsya kak stroka simvolov, kotorye otlichny ot "simvolov prostyh prome- zhutkov"; ono prodolzhaetsya libo do sleduyushchego simvola pustogo promezhutka, libo poka ne budet ischerpana shirina polya, esli ona ukazana. Otsyuda sleduet, chto pri poiske nuzhnogo ej vvoda, funkciya scanf budet peresekat' granicy strok, pos- kol'ku simvol novoj stroki yavlyaetsya odnim iz simvolov pustyh promezhutkov. Imeetsya vozmozhnost' zadaniya bolee slozhnogo algoritma vydeleniya polej vvoda, kotoraya opisana v rukovodstve prog- rammista ("scanf(3)"). Simvol preobrazovaniya opredelyaet interpretaciyu polya vvoda; poskol'ku v Si argumenty peredayutsya po znacheniyu, argumenty scanf dolzhny byt' ukazatelyami. Dopuskayutsya sleduyu- shchie simvoly preobrazovaniya: d - Na vvode ozhidaetsya desyatichnoe celoe; sootvetstvuyushchij argument dolzhen byt' ukazatelem na celoe. o - Na vvode ozhidaetsya vos'merichnoe celoe (s lidiruyushchim nulem ili bez nego); sootvetstvuyushchij argument dolzhen byt' ukazatelem na celoe. x - Na vvode ozhidaetsya shestnadcatirichnoe celoe (s lidiru- yushchimi 0h ili bez nih); sootvetstvuyushchij argument dol- zhen byt' ukazatelem na celoe. h - Na vvode ozhidaetsya celoe tipa short; sootvetstvuyushchij argument dolzhen byt' ukazatelem na celoe tipa short. c - Ozhidaetsya otdel'nyj simvol; sootvetstvuyushchij argument dolzhen byt' ukazatelem na simvoly; sleduyushchij vvodimyj -56- simvol pomeshchaetsya v ukazannoe mesto. Obychnyj propusk simvolov pustyh promezhutkov v etom sluchae podavlya- etsya; dlya chteniya sleduyushchego simvola, kotoryj ne yavlya- etsya simvolom pustogo promezhutka, pol'zujtes' speci- fikaciej preobrazovaniya %1s. s - Ozhidaetsya simvol'naya stroka; sootvetstvuyushchij argument dolzhen byt' ukazatelem simvolov, kotoryj ukazyvaet na massiv simvolov, dostatochno bol'shoj dlya prinyatiya stroki i dobavlyaemogo v konce simvola \0. f - Ozhidaetsya chislo s veshchestvennoj tochkoj; sootvetstvuyu- shchij argument dolzhen byt' ukazatelem na peremennuyu tipa float. e - Simvol preobrazovaniya e yavlyaetsya sinonimom dlya f. Format vvoda peremennoj tipa float vklyuchaet neobyaza- tel'nyj znak, stroku cifr, vozmozhno soderzhashchuyu desya- tichnuyu tochku i neobyazatel'noe pole eksponenty, sosto- yashchee iz bukvy e, za kotoroj sleduet celoe, vozmozhno imeyushchee znak. Pered simvolami preobrazovaniya d, o i x mozhet stoyat' bukva l, kotoraya oznachaet, chto v spiske argumentov dolzhen nahodit'sya ukazatel' na peremennuyu tipa long, a ne tipa int. Analogichno, bukva l mozhet stoyat' pered simvolami preobrazo- vaniya e ili f, govorya o tom, chto v spiske argumentov dolzhen nahodit'sya ukazatel' na peremennuyu tipa double, a ne tipa float. Naprimer, obrashchenie int 1; float h; char name[50]; scanf("%d %f %s", &i, &h, name); so strokoj na vvode 25 54.32e-1 thompson privodit k prisvaivaniyu i znacheniya 25, h - znacheniya 5.432 i name - stroki "thompson", nadlezhashchim obrazom zakonchennoj simvolom \0. |ti tri polya vvoda mozhno razdelit' proizvol'- nym chislom probelov, tabulyacij i simvolov novoj stroki, skol'ko vy pozhelaete. Obrashchenie int i; float h; char name[50]; scanf("%2d %f %*d %2s", &i, &h, name); s vvodom -57- 56789 0123 45a72 prisvoit i znachenie 56, h - 789.0, propustit 0123 i pomestit v name stroku "45". Pri sleduyushchem obrashchenii k lyuboj proce- dure vvoda rassmotrenie nachnetsya s bukvy a. V etih dvuh pri- merah name yavlyaetsya ukazatelem i, sledovatel'no, pered nim ne nuzhno pomeshchat' znak &. V kachestve drugogo primera privedem programmu dlya sum- mirovaniya chisel, vvodimyh s terminala: #include <stdio.h> main() /* Primitivnyj kal'kulyator */ { double sum, v; sum =0; while (scanf("%lf", &v) !=EOF) printf("\t%.2fFI\n", sum += v); } Vypolnenie funkcii scanf zakanchivaetsya libo togda, kogda ona ischerpyvaet svoyu upravlyayushchuyu stroku, libo kogda nekotoryj element vvoda ne sootvetstvuet ocherednoj specifikacii preob- razovaniya. V kachestve svoego znacheniya ona vozvrashchaet chislo pravil'no raspoznannyh elementov vvoda. |to chislo mozhet byt' ispol'zovano dlya opredeleniya kolichestva najdennyh elementov vvoda. Pri vyhode na konec fajla vozvrashchaetsya EOF; podcherk- nem, chto eto znachenie otlichno ot 0, oznachayushchego, chto sleduyu- shchij vvodimyj simvol ne udovletvoryaet pervoj specifikacii v upravlyayushchej stroke. Pri sleduyushchem obrashchenii k scanf poisk vozobnovlyaetsya neposredstvenno za poslednim vvedennym simvo- lom. PREDOSTEREZHENIE: argumenty funkcii scanf dolzhny byt' ukazatelyami. Nesomnenno, naibolee rasprostranennaya oshibka sostoit v napisanii scanf("%d", n); vmesto scanf("%d", &n); 13.5. Formatnoe preobrazovanie v pamyati Ot funkcii scanf i printf proishodyat funkcii sscanf i sprintf, kotorye osushchestvlyayut analogichnye preobrazovaniya, no operiruyut so strokoj, a ne s fajlom. Obrashcheniya k etim funk- ciyam imeyut vid: -58- sprintf(string, control, arg1, arg2, ...) sscanf(string, control, arg1, arg2, ...) Kak i ran'she , funkciya sprintf preobrazuet svoi argumenty arg1, arg2 i t.d. v sootvetstvii s formatom, ukazannym v control, no pomeshchaet rezul'taty v string, a ne v standartnyj vyvod. Konechno, stroka string dolzhna byt' dostatochno velika, chtoby prinyat' rezul'tat. Naprimer, esli name - eto simvol'- nyj massiv, a n - celoe, to sprintf(name, "temp%d", n); sozdaet v name stroku vida "tempnnn", gde nnn - znachenie n. Funkciya sscanf vypolnyaet obratnye preobrazovaniya - ona prosmatrivaet stroku string v sootvetstvii s formatom v argumente control i pomeshchaet rezul'tiruyushchie znacheniya v argu- menty arg1, arg2 i t.d. |ti argumenty dolzhny byt' ukazate- lyami. V rezul'tate obrashcheniya sscanf(name, "temp%d", &n); peremennaya n poluchaet znachenie stroki cifr, sleduyushchih za temp v name. 13.6. Dostup k fajlam Opisannye v nachale dannogo razdela programmy chitayut iz standartnogo vvoda i pishut v standartnyj vyvod, kotorye pre- dostavlyayutsya programme operacionnoj sistemoj. Dlya programm, kotorye sami dolzhny organizovyvat' svyaz' s fajlami, v biblioteke vvoda/vyvoda dejstvuyut sleduyushchie pravila. Prezhde chem schityvat' iz nekotorogo fajla ili zapisy- vat' v nego, etot fajl dolzhen byt' otkryt s pomoshch'yu funkcii open iz standartnoj biblioteki. Funkciya fopen beret vneshnee imya (podobnoe h.c ili "temp002") i vozvrashchaet vnutrennee imya, kotoroe dolzhno ispol'zovat'sya pri posleduyushchih chteniyah iz fajla ili zapisyah v nego. |to vnutrennee imya, nazyvaemoe "ukazatelem fajla", fak- ticheski yavlyaetsya ukazatelem struktury, kotoraya soderzhit informaciyu o fajle, takuyu kak mesto razmeshcheniya bufera, teku- shchaya poziciya simvola v bufere, proishodit li chtenie iz fajla ili zapis' v nego i tomu podobnoe. Pol'zovateli ne obyazany znat' eti detali, potomu chto sredi opredelenij, poluchaemyh iz fajla stdio.h, soderzhitsya opredelenie etoj struktury. Edinstvennoe neobhodimoe dlya ukazatelya fajla opisanie demonstriruetsya primerom: FILE *fp; -59- Zdes' govoritsya, chto fp yavlyaetsya ukazatelem na FILE. Obratite vnimanie, chto file yavlyaetsya imenem tipa, podobnym int, a ne yarlykom struktury; eto realizovano cherez "#define". Obrashchenie k funkcii fopen v programme imeet vid: fp=fopen(name,mode); Pervym argumentom funkcii fopen yavlyaetsya imya fajla, kotoroe zadaetsya v vide simvol'noj sroki "name". Vtoroj argument mode (rezhim) takzhe yavlyaetsya simvol'noj strokoj, kotoraya uka- zyvaet, kak etot fajl budet ispol'zovat'sya. Dopustimymi rezhimami yavlyayutsya: chtenie (r), zapis' (w) i dobavlenie (a), vozmozhen eshche simvol + sprava (naprimer, r+), kotoryj ozna- chaet, chto vozmozhno i chtenie, i zapis' v fajl. Esli vy otkroete fajl, kotoryj eshche ne sushchestvuet, dlya zapisi ili dobavleniya, to takoj fajl budet sozdan (esli eto vozmozhno). Otkrytie sushchestvuyushchego fajla na zapis' privodit k otbrasyvaniyu ego starogo soderzhimogo. Popytka chteniya nesu- shchestvuyushchego fajla yavlyaetsya oshchibkoj. Oshibki mogut byt' obus- lovleny i drugimi prichinami (naprimer, popytka chteniya iz fajla, ne imeya na to razresheniya). Pri nalichii kakoj-libo oshibki funkciya vozvrashchaet nulevoe znachenie ukazatelya NULL (kotoroe dlya udobstva takzhe opredelyaetsya v fajle stdio.h). Drugoj neobhodimoj veshch'yu yavlyaetsya sposob chteniya ili zapisi, esli fajl uzhe otkryt. Zdes' imeetsya neskol'ko voz- mozhnostej, iz kotoryh getc i putc yavlyayutsya prostejshimi. Funkciya getc schityvaet iz fajla sleduyushchij simvol; ej neobho- dim ukazatel' fajla, chtoby znat', iz kakogo fajla chitat'. Obrashchenie: c=getc(fp) pomeshchaet v c sleduyushchij simvol iz fajla, ukazannogo posredst- vom fp, i EOF, esli dostignut konec fajla. Funkciya putc: putc(c,fp) pomeshchaet simvol c v fajl fp i vozvrashchaet c. Podobno funkciyam getchar i putchar, getc i putc mogut byt' makrosami, a ne funkciyami. Pri zapuske programmy avtomaticheski otkryvayutsya tri fajla, kotorye snabzheny opredelennymi ukazatelyami fajlov. |timi fajlami yavlyayutsya standartnyj vvod, standartnyj vyvod i standartnyj vyvod oshibok; sootvetstvuyushchie ukazateli fajlov nazyvayutsya stdin, stdout i stderr. Obychno vse eti ukazateli svyazany s terminalom, no stdin i stdout mogut byt' perenap- ravleny na fajly ili v mezhprocessnyj kanal. Funkcii getchar i putchar mogut byt' opredeleny v ter- minah getc, putc, stdin i stdout sleduyushchim obrazom: #define getchar() getc(stdin) #define putchar(c) putc(c, stdout) -60- Pri rabote s fajlami dlya formatnogo vvoda i vyvoda mozhno ispol'zovat' funkcii fscanf i fprintf. Oni identichny funk- ciyam scanf i printf, za isklyucheniem togo, chto pervym argu- mentom yavlyaetsya ukazatel' fajla, opredelyayushchij tot fajl, kotoryj budet chitat'sya ili kuda budet vestis' zapis'; uprav- lyayushchaya stroka budet vtorym argumentom. Ukazateli fajlov stdin i stdout zaranee opredeleny v biblioteke vvoda-vyvoda kak standartnyj vvod i standartnyj vyvod; oni mogut byt' ispol'zovany v lyubom meste, gde mozhno ispol'zovat' ob容kt tipa FILE *. Oni, odnako, yavlyayutsya kons- tantami, a ne peremennymi, tak chto ih nel'zya izmenyat'. Funkciya fclose yavlyaetsya obratnoj po otnosheniyu k fopen; ona razryvaet svyaz' mezhdu ukazatelem fajla i vneshnim imenem, ustanovlennuyu funkciej fopen, i vysvobozhdaet ukazatel' fajla dlya drugogo fajla. V operacionnoj sisteme imeyutsya ograniche- niya na chislo odnovremenno otkrytyh fajlov, kotorymi mozhet rasporyazhat'sya programma. Funkciya fclose zakryvaet fajl, a takzhe vyzyvaet vydachu informacii iz bufera, v kotorom putc sobiraet vyvod (pri normal'nom zavershenii programmy funkciya fclose vyzyvaetsya avtomaticheski dlya kazhdogo otkrytogo fajla). 13.7. Obrabotka oshibok - stderr i exit Pri pechati diagnosticheskih soobshchenij zhelatel'no, chtoby oni postupali na terminal, dazhe esli standartnyj vyvod pos- tupaet v nekotoryj fajl ili v mezhprocessnyj kanal. CHtoby luchshe obrabatyvat' takuyu situaciyu, k programme tochno takim zhe obrazom, kak stdin i stdout, avtomaticheski prisoedinyaetsya vtoroj vyhodnoj fajl, nazyvaemyj stderr. Esli eto voobshche vozmozhno, vyvod, zapisannyj v fajle stderr, poyav- lyaetsya na terminale pol'zovatelya, dazhe esli standartnyj vyvod napravlyaetsya v drugoe mesto (na samom dele imeetsya vozmozhnost' napravit' takie soobshcheniya v fajl, no etogo ne proishodit pri prostom perenapravlenii standartnogo vyvoda). Programma mozhet takzhe ispol'zovat' funkciyu exit iz standartnoj biblioteki, obrashchenie k kotoroj vyzyvaet zaver- shenie vypolneniya programmy. Argument funkcii exit dostupen programme, vyzvavshej programmu pol'zovatelya v kachestve pod- zadachi, tak chto ona mozhet proverit' uspeshnoe ili neudachnoe zavershenie dannoj programmy. Po soglasheniyu, velichina 0 v kachestve vozvrashchaemogo znacheniya svidetel'stvuet o tom, chto vse v poryadke, a razlichnye nenulevye znacheniya yavlyayutsya priz- nakami nenormal'nyh situacij. Funkciya exit vyzyvaet funkciyu fclose dlya kazhdogo otkry- togo vyhodnogo fajla, s tem chtoby vyvesti vsyu pomeshchennuyu v bufery vyhodnuyu informaciyu, a zatem vyzyvaet funkciyu _exit. Funkciya _exit privodit k nemedlennomu zaversheniyu bez ochistki -61- kakih-libo buferov; konechno, pri zhelanii k etoj funkcii mozhno obratit'sya neposredstvenno. 13.8. Vvod i vyvod strok Standartnaya biblioteka soderzhit funkciyu fgets. V rezul'tate obrashcheniya fgets(line, maxline, fp) sleduyushchaya stroka vvoda (vklyuchaya simvol novoj stroki) schity- vaetsya iz fajla fp v simvol'nyj massiv line; samoe bol'shee maxline-1 simvol budet prochitan. Rezul'tiruyushchaya stroka zakanchivaetsya simvolom \0. Obychno funkciya fgets vozvrashchaet line; v konce fajla ona vozvrashchaet NULL. Prednaznachennaya dlya vyvoda funkciya fputs zapisyvaet stroku (kotoraya ne obyazana soderzhat' simvol novoj stroki) v fajl: fputs(line, fp) Funkcii gets i puts yavlyayutsya uproshchennymi variantami fgets i fputs, kotorye rabotayut s fajlami standartnogo vvoda i vyvoda i ne proveryayut dlinu stroki; gets ne zapisy- vaet simvol novoj stroki v pamyat', a puts dopisyvaet etot simvol v fajl v konce stroki: gets(line) puts(line) 13.9. Funkciya ungetc Standartnaya biblioteka soderzhit funkciyu, vozvrashchayushchuyu poslednij schitannyj simvol. V rezul'tate obrashcheniya ungetc(c,fp) simvol c vozvrashchaetsya v fajl fp. Pozvolyaetsya vozvrashchat' v kazhdyj fajl tol'ko odin simvol. 13.10. Raznye standartnye funkcii Standartnaya biblioteka predostavlyaet mnozhestvo raznoob- raznyh funkcij, nekotorye iz kotoryh okazyvayutsya osobenno poleznymi. 13.10.1. Upravlenie pamyat'yu Funkciya calloc sluzhit dlya zaprosov pamyati. V rezul'- tate obrashcheniya -62- calloc(n, sizeof(object)) vozvrashchaetsya libo ukazatel' prostranstva, dostatochnogo dlya razmeshcheniya n ob容ktov ukazannogo razmera, libo NULL, esli zapros ne mozhet byt' udovletvoren. Otvodimaya pamyat' inicia- liziruetsya nulevymi znacheniyami. Funkciya malloc delaet to zhe samoe, no pamyat' zadaetsya v bajtah: malloc(size) Ukazatel' obladaet nuzhnym dlya rassmatrivaemyh ob容ktov vyravnivaniem, no emu sleduet pripisyvat' sootvetstvuyushchij tip, kak v sleduyushchem primere char *calloc(); int *ip; ip=(int*) calloc(n,sizeof(int)); Funkciya free(p) osvobozhdaet prostranstvo, na kotoroe ukazyvaet p, prichem ukazatel' p pervonachal'no dolzhen byt' poluchen v rezul'tate obrashcheniya k calloc. Zdes' net nikakih ogranichenij na poryadok osvobozhdeniya prostranstva, no osvo- bozhdenie chego libo, ne poluchennogo s pomoshch'yu calloc ili mal- loc, privodit k tyazhelym oshibkam. 13.10.2. Standartnye funkcii yazyka Si V standartnuyu biblioteku funkcij na yazyke Si vhodit, pomimo opisannyh, mnozhestvo samyh raznyh funkcij. Podrobnoe opisanie ih privedeno v rukovodstve programmistu po OS DEMOS, chast' 4 (bibliotechnye funkcii), i v operativnoj doku- mentacii (man(3)). Nizhe v skobkah privedeny nazvaniya razde- lov operativnoj dokumentacii, v kotoryh imeyutsya sootvetstvu- yushchie opisaniya: - operacii so strokami (string); - preobrazovanie dannyh bez sscanf i sprintf" (atoi, itoa, atof, ftoa); - matematicheskie funkcii (sin, exp, ...); - proverka i preobrazovanie simvolov (ctype); - i mnogoe drugoe.  * 14. VZAIMODEJSTVIE S OPERACIONNOJ SISTEMOJ -63- 14.1. Podgotovka programm na Si v OS DEMOS V operacionnoj sisteme DEMOS programmy mogut sostoyat' iz odnogo ili neskol'kih modulej, napisannyh na yazykah Si, Fortran-77, Assembler. Dlya translyacii i sborki programm na yazyke Si sluzhit komanda cc. V prostejshem sluchae translyaciya osushchestvlyaetsya po komande: cc fajl1.c fajl2.c ... gde fajl1.c, fajl2.c, ... - imena fajlov, soderzhashchih prog- rammy na yazyke Si (imena takih fajlov dolzhny okanchivat'sya na suffiks .c). Komanda osushchestvlyaet translyaciyu perechislennyh programm i ih ob容dinenie redaktorom svyazej. Esli translya- ciya proshla bez oshibok, sozdaetsya ispolnyaemyj fajl a.out, kotoryj mozhno zapustit' na schet, vvedya komandu: a.out (to est' nabrav prosto imya etogo fajla). Translyaciyu chasto provodyat v dva etapa: snachala transliruyut otdel'nye prog- rammy, poluchaya ob容ktnye moduli, a zatem ob容dinyayut ih vmeste (v predydushchem primere eto bylo sdelano avtomaticheski komandoj cc). Razdel'naya translyaciya vyglyadit primerno tak: cc fajl1.c fajl2.c ... cc fajlN.c fajlN1.c ... cc fajl1.o fajl2.o ... fajlN.o ... V bolee slozhnom sluchae programma mozhet sostoyat' iz modulej na raznyh yazykah, rezul'tat translyacii mozhet byt' zapisan v fajl, otlichnyj ot a.out, mozhno ottranslirovat' programmu dlya otladki s pomoshch'yu otladchika cdeb, i t.p. Podrobnoe opisanie vyzova kompilyatora imeetsya v rukovodstve programmista (cc(1), ld(1)). V obshchem sluchae programmy na Si zapuskayutsya interpretatorami shell ili cshell komandoj: imya_fajla argumenty naznachenie_vvoda_vyvoda gde lyubaya chast', krome imeni fajla, mozhet otsutstvovat'. Lyubaya programma na Si v OS DEMOS dolzhna soderzhat' golovnuyu funkciyu s imenem main. Rabota programmy nachinaetsya s etoj funkcii, prichem informaciya o argumentah komandy pere- daetsya cherez ee formal'nye parametry. 14.2. Dostup k argumentam komandy Operacionnaya sistema DEMOS pozvolyaet peredavat' argu- menty komandy nachinayushchej vypolnyat'sya programme. Kogda funk- ciya main vyzyvaetsya sistemoj, ona vyzyvaetsya s dvumya argu- mentami. Pervyj argument (tipa int, uslovno nazyvaemyj argc) ukazyvaet chislo argumentov v komandnoj stroke, s kotorymi -64- proishodit obrashchenie k programme; vtoroj argument (argv) yavlyaetsya ukazatelem na massiv simvol'nyh strok, soderzhashchih eti argumenty, po odnomu v stroke. Samuyu prostuyu illyustraciyu etoj vozmozhnosti i neobhodi- myh pri etom opisanij daet programma echo, kotoraya prosto pechataet v odnu stroku argumenty komandnoj stroki, razdelyaya ih probelami. , Esli dana komanda echo hello, world to v rezul'tate poluchim: hello, world Po soglasheniyu argv[0] yavlyaetsya imenem, po kotoromu vyzyvaetsya programma, tak chto argc po men'shej mere raven 1. V privedennom vyshe primere argc raven 3, a argv[0], argv[1] i argv[2] ravny sootvetstvenno echo, hello, i world. Pervym fakticheskim agumentom yavlyaetsya argv[1], a poslednim - argv[argc-1]. Esli argc raven 1, to za imenem programmy ne sleduet nikakoj komandnoj stroki argumentov. Vse eto poka- zano v echo: main(argc, argv) int argc; char *argv[]; { int i; for (i = 1; i < argc; i++) printf("%s%c", argv[i], (i<argc-1) ? ' ' : '\n'); } Poskol'ku argv yavlyaetsya ukazatelem na massiv ukazatelej, to sushchestvuet neskol'ko sposobov napisaniya etoj programmy, ispol'zuyushchih rabotu s ukazatelem, a ne s indeksaciej mas- siva. Sleduyushchij primer demonstriruet drugoj variant: main(argc, argv) int argc; char **argv; { while (--argc > 0) printf("%s%c",*++argv, (argc > 1) ? ' ' : '\n'); } Krome stroki argumentov, programma poluchaet ot sistemy nabor peremennyh, opisyvayushchih sredu, v kotoroj ona vypolnya- etsya. Kazhdaya peremennaya sostoit iz imeni i znacheniya -65- (tekstovoj stroki). Naprimer, peremennaya TERM peredaet tip terminala, s kotorogo programma zapushchena. Dlya zaprosa znache- niya peremennoj po imeni ispol'zuetsya funkciya getenv: char *getenv(); par = getenv("imya_peremennoj") Funkciya vozvrashchaet ukazatel' na stroku - znachenie peremen- noj, libo NULL, esli imya ne najdeno v opisanii sredy.  * 15. INTERFEJS SISTEMY DEMOS Vse bez isklyucheniya vozmozhnosti operacionnoj sistemy DEMOS dostupny iz programm na yazyke Si. Material etoj glavy otnositsya k interfejsu mezhdu Si-programmami i operacionnoj sistemoj DEMOS. Material delitsya na sleduyushchie chasti: vvod/vyvod, sistema fajlov, processy, signaly. Predpolaga- etsya znanie osnovnyh koncepcij OS DEMOS, a takzhe ponyatij "fajl", "process", "signal". Podrobnoe opisanie sistemnyh vyzovov i sootvetstvuyushchih im funkcij iz standartnoj biblio- teke imeetsya v rukovodstve programmista po OS DEMOS i v ope- rativnoj dokumentacii (chasti 2 i 3). Naprimer, esli v opi- sanii govoritsya o funkcii popen(3), to podrobnoe opisanie sleduet iskat' v rukovodstve programmista, chast' 4, ili v operativnoj dokumentacii, chast' 3; spravku o funkcii mozhno poluchit' na terminal, nabrav man 3 popen. 15.1. Vvod/vyvod V opisanii biblioteki vvoda/vyvoda byl opisan univer- sal'nyj interfejs, kotoryj odinakov dlya vsego mnogoobraziya operacionnyh sistem. Na kazhdoj konkretnoj operacionnoj sis- teme funkcii standartnoj biblioteki dolzhny byt' napisany v terminah vvoda-vyvoda, dostupnyh na dannoj mashine. V sleduyu- shchih razdelah opisan nabor funkcij vvoda/vyvoda nizhnego urovnya, podderzhivaemyh yadrom operacionnoj sistemy DEMOS. 15.1.1. Deskriptory fajlov V operacionnoj sisteme DEMOS ves' vvod i vyvod osu- shchestvlyaetsya posredstvom chteniya fajlov ili ih zapisi, potomu chto vse periferijnye ustrojstva, vklyuchaya terminal pol'zova- telya, yavlyayutsya fajlami opredelennoj fajlovoj sistemy. |to oznachaet, chto odin odnorodnyj interfejs upravlyaet vsemi svya- zyami mezhdu programmoj i periferijnymi ustrojstvami. V naibolee obshchem sluchae pered chteniem iz fajla ili zapis'yu v fajl neobhodimo soobshchit' sisteme o namerenii sde- lat' eto; etot process nazyvaetsya "otkrytiem" fajla. Sistema vyyasnyaet, imeet li programma pravo postupat' takim obrazom (sushchestvuet li etot fajl? imeetsya li razreshenie na obrashche- nie k nemu?), i esli vse v poryadke, vozvrashchaet v programmu nebol'shoe polozhitel'noe celoe chislo, nazyvaemoe deskriptorom -66- fajla. Vsyakij raz, kogda etot fajl ispol'zuetsya dlya vvoda ili vyvoda, dlya identifikacii fajla upotreblyaetsya deskriptor fajla, a ne ego imya (zdes' sushchestvuet primernaya analogiya s ispol'zovaniem read (5,...) i write (6,...) v Fortrane). Vsya informaciya ob otkrytom fajle soderzhitsya v sisteme; programma pol'zovatelya obrashchaetsya k fajlu tol'ko cherez deskriptor fajla. Dlya udobstva vypolneniya obychnyh operacij vvoda i vyvoda s pomoshch'yu terminala pol'zovatelya sushchestvuyut special'nye sog- lasheniya. Kogda interpretator komand (shell) progonyaet prog- rammu, on otkryvaet tri fajla, nazyvaemye standartnym vvo- dom, standartnym vyvodom i standartnym vyvodom oshibok, koto- rye imeyut sootvetstvenno chisla 0, 1 i 2 v kachestve deskrip- torov etih fajlov. V normal'nom sostoyanii vse oni svyazany s terminalom, tak chto esli programma chitaet s deskriptorom fajla 0 i pishet s deskriptorami fajlov 1 i 2, to ona mozhet osushchestvlyat' vvod i vyvod s pomoshch'yu terminala, ne zabotyas' ob otkrytii sootvetstvuyushchih fajlov. Pol'zovatel' programmy mozhet perenapravlyat' vvod i vyvod na fajly, ispol'zuya v interpretatore komand simvoly < i >: prog < infile > outfile V etom sluchae interpretator komand izmenit opredelenie deskriptorov fajlov 0 i 1 s terminala na ukazannye fajly. Obychno deskriptor fajla 2 ostaetsya svyazannym s terminalom, tak chto soobshcheniya ob oshibkah mogut postupat' tuda. Podobnye zamechaniya spravedlivy i togda, kogda vvod i vyvod svyazan s mezhprocessnym kanalom. Sleduet otmetit', chto v etom sluchae svyaz' programmy s fajlami izmenyaetsya interpretatorom shell (ili cshell), a ne programmoj. Sama programma, poka ona ispol'zuet fajl 0 dlya vvoda i fajly 1 i 2 dlya vyvoda, ne znaet ni otkuda prihodit ee vvod, ni kuda postupaet ee vydacha. 15.1.2. Nizkourovnevyj vvod/vyvod. Samyj nizkij uroven' vvoda/vyvoda v sisteme DEMOS ne predusmatrivaet ni kakoj-libo buferizacii, ni kakogo-libo drugogo servisa; on po sushchestvu yavlyaetsya neposredstvennym obrashcheniem k operacionnoj sisteme. Ves' vvod i vyvod osu- shchestvlyaetsya dvumya funkciyami: read i write. Pervym argumen- tom obeih funkcij yavlyaetsya deskriptor fajla. Vtorym argumen- tom yavlyaetsya bufer v vashej programme, otkuda ili kuda dolzhny postupat' dannye. Tretij argument - eto chislo podlezhashchih peresylke bajtov. Obrashcheniya k etim funkciyam imeyut vid: n_read=read(fd,buf,n); n_written=write(fd,buf,n); -67- Pri kazhdom obrashchenii vozvrashchaetsya schetchik bajtov, ukazyvayu- shchij fakticheskoe chislo peredannyh bajtov. Pri chtenii vozvra- shchennoe chislo bajtov mozhet okazat'sya men'she, chem zaproshennoe chislo. Vozvrashchennoe nulevoe chislo bajtov oznachaet konec fajla, a "-1" ukazyvaet na nalichie kakoj-libo oshibki. Pri zapisi vozvrashchennoe znachenie ravno chislu fakticheski zapisan- nyh bajtov; nesovpadenie etogo chisla s chislom bajtov, koto- roe predpolagalos' zapisat', obychno svidetel'stvuet ob oshibke. Kolichestvo bajtov, podlezhashchih chteniyu ili zapisi, mozhet byt' sovershenno proizvol'nym. Dvumya samymi rasprostranennymi velichinami yavlyayutsya "1", chto oznachaet peredachu odnogo sim- vola za obrashchenie (t.e. bez ispol'zovaniya bufera), i "512", chto sootvetstvuet fizicheskomu razmeru bloka na mnogih peri- ferijnyh ustrojstvah. |tot poslednij razmer budet naibolee effektivnym, no dazhe vvod ili vyvod po odnomu simvolu za obrashchenie ne budet slishkom dorogim. Primer. Kopirovanie vvoda na vyvod. V sisteme DEMOS eta programma budet kopirovat' chto ugodno kuda ugodno, potomu chto vvod i vyvod mogut byt' perenapravleny na lyuboj fajl ili ustrojstvo. #define BUFSIZE 512 main() /*copy input to output*/ { char buf[BUFSIZE]; int n; while((n=read(0,buf,BUFSIZE))>0) write(1,buf,n); } Esli razmer fajla ne budet kraten BUFSIZE, to pri ocherednom obrashchenii k read budet vozvrashcheno men'shee chislo bajtov, kotorye zatem zapisyvayutsya s pomoshch'yu write; pri sleduyushchem posle etogo obrashchenii k read budet vozvrashchen nul'. 15.1.3. Otkrytie, sozdanie, zakrytie i udalenie Vo vseh sluchayah, esli tol'ko ne ispol'zuyutsya opredelen- nye po umolchaniyu standartnye fajly vvoda, vyvoda i oshibok, vy dolzhny yavno otkryvat' fajly, chtoby zatem chitat' iz nih ili pisat' v nih. Dlya etoj celi sushchestvuyut dve funkcii: open i creat. Funkciya open ves'ma shodna s funkciej fopen, rassmot- rennoj vyshe, za isklyucheniem togo, chto vmesto vozvrashcheniya ukazatelya fajla ona vozvrashchaet deskriptor fajla, kotoryj yavlyaetsya prosto celym tipa int. int fd; fd=open(name,rwmode); -68- Kak i v sluchae fopen, argument name yavlyaetsya simvol'noj strokoj, sootvetstvuyushchej vneshnemu imeni fajla. Odnako argu- ment, opredelyayushchij rezhim dostupa, otlichen: rwmode ravno: 0 - dlya chteniya, 1 - dlya zapisi, 2 - dlya chteniya i zapisi. Esli proishodit kakaya-to oshibka, funkciya open vozvrashchaet "-1"; v protivnom sluchae ona vozvrashchaet neotricatel'nyj deskriptor fajla. Popytka otkryt' fajl, kotoryj ne sushchestvuet, yavlyaetsya oshibkoj. Funkciya creat predostavlyaet vozmozhnost' sozdaniya novyh fajlov ili perezapisi staryh. V rezul'tate obrashcheniya: fd=creat(name,pmode); vozvrashchaet deskriptor fajla, esli okazalos' vozmozhnym soz- dat' fajl s imenem name, i "-1" v protivnom sluchae. Sozda- nie fajla, kotoryj uzhe sushchestvuet, ne yavlyaetsya oshibkoj: creat usechet ego do nulevoj dliny. Esli fajl ranee ne sushchestvoval, to creat sozdaet ego s opredelennym rezhimom zashchity, specificiruemym argumentom pmode. V sisteme fajlov OS DEMOS s fajlom svyazyvayutsya devyat' bitov zashchity informacii, kotorye upravlyayut razresheniem na chtenie, zapis' i vypolnenie dlya vladel'ca fajla, dlya gruppy vladel'cev i dlya vseh ostal'nyh pol'zovatelej. Takim obra- zom, trehznachnoe vos'merichnoe chislo naibolee udobno dlya zapisi rezhima zashchity. Naprimer, chislo 0755 svidetel'stvuet o razreshenii na chtenie, zapis' i vypolnenie dlya vladel'ca i o razreshenii na chtenie i vypolnenie dlya gruppy i vseh ostal'- nyh. Sushchestvuet ogranichenie (obychno 15 - 25) na kolichestvo fajlov, kotorye programma mozhet imet' otkrytymi odnovre- menno. V sootvetstvii s etim lyubaya programma, sobirayushchayasya rabotat' so mnogimi fajlami, dolzhna byt' podgotovlena k pov- tornomu ispol'zovaniyu deskriptorov fajlov. Procedura close preryvaet svyaz' mezhdu deskriptorom fajla i otkrytym fajlom i osvobozhdaet deskriptor fajla dlya ispol'zovaniya s drugim faj- lom. Zavershenie vypolneniya programmy cherez exit ili v rezul'tate vozvrata iz golovnoj funkcii privodit k zakrytiyu vseh otkrytyh fajlov. Funkciya udaleniya unlink(filename) udalyaet iz sistemy fajl s imenem filename (Tochnee, udalyaet imya filename, fajl udalyaetsya, esli na nego ne ostaetsya ssylok pod drugimi ime- nam). 15.1.4. Proizvol'nyj dostup - lseek Obychno pri rabote s fajlami vvod i vyvod osushchestvlyaetsya posledovatel'no: pri kazhdom obrashchenii k funkciyam read i write chtenie ili zapis' nachinayutsya s pozicii, neposredst- venno sleduyushchej za predydushchej obrabotannoj. No pri -69- neobhodimosti fajl mozhet chitat'sya ili zapisyvat'sya v lyubom proizvol'nom poryadke. Obrashchenie k sisteme s pomoshch'yu funkcii lseek pozvolyaet peredvigat'sya po fajlu, ne proizvodya fakti- cheskogo chteniya ili zapisi. V rezul'tate obrashcheniya lseek(fd,offset,origin); tekushchaya poziciya v fajle s deskriptorom fd peredvigaetsya na poziciyu offset (smeshchenie), kotoraya otschityvaetsya ot mesta, ukazyvaemogo argumentom origin (nachalo otscheta). Posleduyushchee chtenie ili zapis' budut teper' nachinat'sya s etoj pozicii. Argument offset imeet tip long; fd i origin imeyut tip int. Argument origin mozhet prinimat' znacheniya 0, 1 ili 2, ukazy- vaya na to, chto velichina offset dolzhna otschityvat'sya soot- vetstvenno ot nachala fajla, ot tekushchej pozicii ili ot konca fajla. Naprimer, chtoby dopolnit' fajl, sleduet pered zapis'yu najti ego konec: lseek(fd,0l,2); chtoby vernut'sya k nachalu, mozhno napisat': lseek(fd,0l,0); Obratite vnimanie na argument 0l; ego mozhno bylo by zapisat' i v vide (long) 0. Funkciya lseek pozvolyaet obrashchat'sya s fajlami primerno tak zhe, kak s bol'shimi massivami, tol'ko cenoj bolee medlen- nogo dostupa. Primer. Funkciya, schityvayushchaya lyuboe kolichestvo bajtov, nachi- naya s proizvol'nogo mesta v fajle. /*chitat' n bajtov s pozicii pos v buf */ get(fd,pos,buf,n) int fd, n; long pos; char *buf; { lseek(fd,pos,0); /*get to pos */ return(read(fd,buf,n)); } 15.2. Upravlenie processami V operacionnoj sisteme DEMOS chasto trebuetsya vyzvat' iz programmy i vypolnit' v vide otdel'nogo processa druguyu programmu. Sleduyushchij razdel opisyvaet prostejshij sposob sdelat' eto, a dalee budut rassmotreny bazovye sredstva upravleniya processami, imeyushchiesya v OS DEMOS. -70- 15.2.1. Funkciya system Prostejshij sposob vyzvat' druguyu programmu - ispol'zo- vat' standartnuyu funkciyu system: system("komandnaya stroka") Funkciya imeet odin parametr - stroku, kotoruyu ona analizi- ruet i vypolnyaet tochno tak zhe, kak vypolnyayutsya komandy, vvo- dimye interpretatorom shell s terminala. Funkciya vypolnyaet komandu i vozvrashchaet celoe chislo - kod otveta vypolnennoj komandy (0, esli vse konchilos' normal'no). V komandnoj stroke vosprinimayutsya lyubye simvoly upravleniya vvodom/vyvodom >, <, i t.p. Sleduet uchest', chto, esli v programme vyvod buferizu- etsya, to pered vyzovom funkcii system neobhodimo vytolknut' bufera, naprimer vyzvav funkciyu fflush. 15.2.2. Vyzov programmy na nizkom urovne - execl Vyzov programmy v OS DEMOS osushchestvlyaetsya s pomoshch'yu neskol'kih elementarnyh funkcij, odna iz kotoryh - funkciya execl - osushchestvlyaet vyzov novoj programmy vmesto uzhe vypol- nyayushchejsya, bez vozvrata v vyzyvayushchuyu programmu. Obrashchenie v nej imeet vid: execl(komanda,arg0,arg1,...,argN,NULL); gde "komanda" - stroka simvolov, tochno imenuyushchaya fajl vyzy- vaemoj komandy. Naprimer, dlya vyzova komandy pr neobhodimo ukazat' imya /bin/pr. Ostal'nye argumenty takzhe predstavlyayut soboj stroki simvolov i prosto peredayutsya komande v kachestve argumentov, pri etom arg0 obychno predstavlyaet soboj prosto sokrashchennoe imya komandy, a ostal'nye argumenty - parametry dannoj komandy. Vyzov execl v sluchae normal'nogo zapuska novoj prog- rammy zamenyaet eyu tekushchuyu programmu, upravlenie iz funkcii execl vozvrashchaetsya tol'ko v sluchae oshibki (naprimer, ne naj- dena komanda s ukazannym imenem). V biblioteke imeetsya celyj nabor funkcij, osushchestvlyayushchih to zhe samoe i otlichayu- shchihsya tol'ko predstavleniem parametrov (execl(2), execv(2), execvp(2), ...) i tem, chto nekotorye funkcii osushchestvlyayut poisk komandy v standartnom nabore spravochnikov. 15.2.3. Porozhdenie novogo processa - fork Dlya togo, chtoby zapustit' parallel'no novuyu programmu, neobhodimo prezhde vsego umet' zapuskat' parallel'nyj pro- cess. Dlya etogo v OS DEMOS sluzhit funkciya "fork" (razvet- vit'sya): -71- proc_id = fork() Programma razdelyaetsya na dve identichnye kopii, kotorye pro- dolzhayut vypolnyat'sya kak dva nezavisimyh processa. Odna iz programm - process "syn" - poluchaet ot funkcii fork kod otveta 0, drugaya - "roditel'" - poluchaet nomer, pod kotorym zapushchen process "syn". V prostejshem sluchae dlya zapuska parallel'noj programmy vyzov fork kombiniruetsya s execl sle- duyushchim obrazom: if( fork() == 0) { /* |to process - syn */ ... nastrojka fajlov ... execl(... ); /*Syuda my popadaem pri oshibke v execl */ perror("Ne mogu zapustit' process"); exit(1); } ... prodolzhenie osnovnoj programmy ... Zdes' programma posle vyzova fork analiziruet, v kakom pro- cesse ("roditel'" ili "syn") ona vypolnyaetsya i, v zavisi- mosti ot etogo, vedet sebya po raznomu. Esli osnovnaya prog- ramma dolzhna zhdat' okonchaniya "syna", to ona dolzhna vyzvat' funkciyu wait: int status; ... if( fork() == 0) { ... execl(...); exit(1); } wait(&status)); Funkciya wait vozvrashchaet identifikator processa - "syna", i zasylaet v peremennuyu status kod zaversheniya etogo processa. Kod zaversheniya sostoit iz dvuh chastej - mladshie 8 bitov for- miruyutsya sistemoj i oboznachayut prichinu okonchaniya processa; v sluchae normal'nogo okonchaniya po funkcii exit" oni soderzhat 0. Starshie 8 bitov v sluchae, esli programma okonchilas' v rezul'tate vyzova exit, berutsya iz argumenta vyzova funkcii exit; obychno peredaetsya 0 pri normal'nom zavershenii i chislo, otlichnoe ot nulya, v sluchae kakih libo oshibok. Ni fork, ni execl ne zatragivayut otkrytyh fajlov, posle fork ranee otkrytye fajly nachinayut ispol'zovat'sya oboimi processami sovmestno, to est' ispol'zuyut