bauer péter – hatwágner f....

204
Bauer Péter – Hatwágner F. Miklós PROGRAMOZÁS II.

Upload: others

Post on 21-Nov-2019

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Bauer Péter – Hatwágner F. Miklós

PROGRAMOZÁS II.

Page 2: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Készült a HEFOP 3.3.1-P.-2004-09-0102/1.0 pályázat támogatásával.

Szerzők: Bauer Péter egyetemi adjunktus

Hatwágner F. Miklós tanszéki mérnök

Lektor: dr. Szörényi Miklós főiskolai docens

© Szerzők, 2006

Page 3: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. A dokumentum használata

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 3 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 3 ►

A dokumentum használata

Mozgás a dokumentumban A dokumentumban való mozgáshoz a Windows és az Adobe Reader meg-szokott elemeit és módszereit használhatjuk.

Minden lap tetején és alján egy navigációs sor található, itt a megfelelő hivatkozásra kattintva ugorhatunk a használati útmutatóra, a tartalomjegy-zékre, valamint a tárgymutatóra. A ◄ és a ► nyilakkal az előző és a követ-kező oldalra léphetünk át, míg a Vissza mező az utoljára megnézett oldalra visz vissza bennünket.

Pozícionálás a könyvjelzőablak segítségével A bal oldali könyvjelző ablakban tartalomjegyzékfa található, amelynek bejegyzéseire kattintva az adott fejezet/alfejezet első oldalára jutunk. Az aktuális pozíciónkat a tartalomjegyzékfában kiemelt bejegyzés mutatja.

A tartalomjegyzék és a tárgymutató használata

Ugrás megadott helyre a tartalomjegyzék segítségével Kattintsunk a tartalomjegyzék megfelelő pontjára, ezzel az adott fejezet első oldalára jutunk.

A tárgymutató használata, keresés a szövegben Keressük meg a tárgyszavak között a bejegyzést, majd kattintsunk a hozzá tartozó oldalszámok közül a megfelelőre. A további előfordulások megte-kintéséhez használjuk a Vissza mezőt.

A dokumentumban való kereséshez használjuk megszokott módon a Szerkesztés menü Keresés parancsát. Az Adobe Reader az adott pozíció-tól kezdve keres a szövegben.

Page 4: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Tartalomjegyzék

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 4 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 4 ►

Tartalomjegyzék 1. Bevezetés ........................................................................................ 6 1.1. Jelölések......................................................................................................... 6

2. Helyi információ............................................................................. 8 2.1. setlocale ......................................................................................................... 8 2.2. lconv struktúra............................................................................................13 2.3. localeconv ...................................................................................................15 2.4. Példa.............................................................................................................16 2.5. Ellenőrző kérdések: ...................................................................................17 2.6. Megoldandó feladatok:..............................................................................17

3. Többájtos és széles karakterek .................................................... 18 3.1. Egybájtos karakterek (Single Byte Characters) ...................................... 18 3.2. Többájtos karakterek (MultiByte Characters) ........................................ 18 3.3. Széles karakterek (Wide Characters) ....................................................... 20 3.4. Konverzió többájtos és széles karakterek közt...................................... 22 3.5. Széles karakterek osztályozása ................................................................. 31 3.6. Széles karakterláncok és memóriaterületek kezelése ............................ 37 3.7. Adatkonverzió ............................................................................................49 3.8. Széles (Unicode) folyam (stream) B/K szöveges és bináris

módban .......................................................................................................52 3.9. Ellenőrző kérdések: ...................................................................................57 3.10. Megoldandó feladatok:............................................................................58

4. Az idő kezelése ............................................................................. 60 4.1. Az idő karakterlánc....................................................................................61 4.2. Az idő struktúra (struct tm)......................................................................61 4.3. time ..............................................................................................................61 4.4. time_t típus .................................................................................................62 4.5. difftime ........................................................................................................62 4.6. ctime ............................................................................................................62 4.7. gmtime, localtime.......................................................................................63 4.8. mktime.........................................................................................................64 4.9. asctime.........................................................................................................65 4.10. strftime, wcsftime ....................................................................................66 4.11. clock...........................................................................................................69 4.12. Ellenőrző kérdések: .................................................................................70

Page 5: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Tartalomjegyzék

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 5 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 5 ►

4.13. Megoldandó feladatok:............................................................................71

5. Nem helyi vezérlésátadás és jel(zés)kezelés................................ 71 5.1. Nem helyi vezérlésátadás ..........................................................................71 5.2. Jel(zés)kezelés .............................................................................................73 5.3. Ellenőrző kérdések ....................................................................................76 5.4. Megoldandó feladatok...............................................................................77

6. Fájlok és könyvtárak kezelése ...................................................... 78 6.2. Fájlok megnyitása és bezárása .................................................................. 83 6.3. Olvasás és írás.............................................................................................89 6.4. Pozícionálás a fájlban ................................................................................94 6.5. Fájl attribútumok .......................................................................................98 6.6. Fájl zárak ...................................................................................................132 6.7. Könyvtárak ...............................................................................................138 6.8. Ellenőrző kérdések: .................................................................................148 6.9. Megoldandó feladatok:............................................................................149

7. Folyamatok.................................................................................. 151 7.1. Parancsok végrehajtása............................................................................151 7.2. Folyamatok létrehozásának módjai .......................................................153 7.3. Folyamatok azonosítása ..........................................................................154 7.4. Folyamatok létrehozása...........................................................................154 7.5. Fájlok futtatása .........................................................................................157 7.6. Folyamatok befejeződése........................................................................162 7.7. Állapotinformációk értelmezése ............................................................165 7.8. Ellenőrző kérdések ..................................................................................166 7.9. Megoldandó feladatok.............................................................................167

8. Folyamatközi kommunikáció .....................................................168 8.1. Osztott és leképezett memória ..............................................................168 8.2. Csövek .......................................................................................................169 8.3. Nevesített csövek .....................................................................................174 8.4. Foglalatok..................................................................................................177 8.5. Ellenőrző kérdések ..................................................................................198 8.6. Megoldandó feladatok.............................................................................198 Irodalomjegyzék..................................................................................... 199 Tárgymutató ........................................................................................... 200

Page 6: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Bevezetés

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 6 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 6 ►

1. Bevezetés

Ez a jegyzet a Széchenyi István Egyetem informatikai szakjai programozás II. tantárgyához készült. A programozás II. a programozás I. tantárgy szerves folytatása, melyek együtt két féléven keresztül kívánják megala-pozni a mérnök hallgatók programozási készségeinek kialakítását.

A programozás I. és II. tantárgyak célját és tananyagát Dr. Sziray Jó-zsef, Dr. Benyó Balázs és Heckenast Tamás javaslata rögzítette. Cél az ANSI/ISO szabványos C nyelv haladó szintű használatának elsajátítása, beleértve az operációs rendszerekhez közeli programozást is. Ez utóbbi ismeretek tanítása a Web–programozás irányába történő előkészítést is szolgálja.

A [2] lefedi a teljes programozás I. tárgyat és a programozás II. első fe-lét (7 hét). Ez a jegyzet a szemeszter maradék 8 hetének tananyagát taglal-ja, melynek van szabványos és operációs rendszerekhez közeli része. A szabványos részek:

• a helyi információ, • a többájtos és széles karakterek, • az idő és • a jel(zés)ek (signal) kezelése.

A POSIX szabványos rendszer közeli részek:

• az alacsony szintű fájl és könyvtár manipulációk, • a folyamatkezelés és • a folyamatközi kommunikáció (cső és foglalat).

1.1. Jelölések < > zárójel párba tettük, ami elhagyható, ha ez a szintaktikának nem része. | függőleges vonallal jelöltük a vagylagosságot.

Figyelem felkeltés. Valamely következtetés levonása az eddigiek-ből. Esetleg: merre találhatók további részletek a kérdéses témával kapcso-latban.

Lexikális ismeretek taglalása. Valamely folyamat pontosabb részle-tei. Egy fogalom precízebb definíciója.

Valamilyen aránylag könnyedén elkövethető, de nehezen lokalizál-ható hiba.

Page 7: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Bevezetés

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 7 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 7 ►

Forrásprogramok és képernyő tartalmak szövege. Valamilyen konkrétummal helyettesítendő szintaktikai egység. Kulcsszó vagy valamilyen azonosító.

A fogalom első előfordulásának jelölésére szolgál.

A megoldandó, vagy megoldott feladatokat így jelöltük.

Page 8: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Helyi információ

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 8 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 8 ►

2. Helyi információ

A LOCALE.H fejfájlt be kell kapcsolni az itt tárgyalt ANSI/ISO szabvá-nyos típusok, szimbolikus állandók és függvények használatához.

A helyi információ ilyenekből tevődik össze, mint az ország, a nyelv, a dátum formátuma, a pénznem, stb. Ezeket az összetevőket helyi kategó-riáknak nevezzük. A helyi kategóriák praktikusan a LOCALE.H fejfájl-ban definiált szimbolikus állandók, melyeket a lokalizációs rutin használ annak specifikálására, hogy a program helyi információjának mely részéről van szó.

A hely bizonyos vonatkozásait a könyvtári rutinok automatikusan ke-zelik. Például: karakterláncok összehasonlítását a kiválasztott helynek meg-felelően az strcoll, vagy az strxfrm függvények segítségével tehetjük meg.

Más helyi vonatkozások viszont kívül esnek a könyvtári rutinok hatás-körén. Ilyen például a (hiba)üzenetek nyelve.

Alapértelmezés a ”C” hely, mely minimális ANSI/ISO konform környezetet definiál a C fordításhoz, és az ANSI/ISO szabvány azt mond-ja ki, hogy minden program e hely beállításával indul. A ”C” hely részleteiről a fejezet további részeiben lesz még szó!

2.1. setlocale char*setlocale(int kategoria, const char *locale); Az aktuális program helyi információját vagy annak egy részét a locale

és a kategoria paraméterek értékének megfelelően a setlocale függvény állítja be, módosítja, vagy kérdezi le. A locale arra a helyre (ország és nyelv) hivatkozik, melynek bizonyos kategoriajára hatni kívánunk a függvénnyel. A lehetséges kategóriák, s hogy az egyes helyi kategóriák mire hatnak, a következő felsorolásban láthatók:

2.1.1. LC_ALL Minden hely specifikus viselkedésre (minden kategóriára) hat.

2.1.2. LC_COLLATE Olyan helyeken, ahol a karakterkészlet kódjainak szigorú numerikus sor-rendje eltér a lexikografikus karaktersorrendtől, a karakterláncokat a kö-vetkező függvényekkel kell összehasonlítani: kis/nagybetű érzékenyen a szabványos strcoll–lal, wcscoll–lal, kis/nagybetű érzékenység nélkül a

Page 9: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Helyi információ

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 9 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 9 ►

nem szabványos _stricoll–lal, _wcsicoll–lal és az első n karaktert ugyan-ilyen módon a nem szabványos _strnicoll–lal, _wcsnicoll–lal.

A karakterláncokat a helynek megfelelő összehasonlítható formába transzformáló szabványos strxfrm és wcsxfrm rutinok viselkedésére is hat ez a kategória. Előbb az strxfrm alkalmazandó az összehasonlítandó, eredeti karakterláncokra, s csak aztán hívható meg rájuk az strcmp.

A ”C” helyen a lexikografikus sorrend egyezik a karakterkódok nume-rikus sorrendjével, azaz az strcoll azonos az strcmp–vel, és nincs szükség az strxfrm–re.

Az itt említett rutinokat a következő nagy fejezetben tárgyaljuk!

2.1.3. LC_CTYPE Az isdigit, az isxdigit függvények kivételével a karakterosztályozó, vala-mint bizonyos többájtos és széles karakteres konverziós rutinok viselkedé-sét befolyásolja a kategória. Ideértendők például:

• az egész értéket tesztelő is… és isw… függvények, • a többájtos karakter(lánc) érvényességét vizsgáló és bájtban mért hosz-

szát megállapító rutinok, • a többájtos karaktert/karaktersorozatot széles karakter-

ré/karaktersorozattá alakító és a megfordított konverziót végző függ-vények,

• az adott karaktert kisbetűssé konvertáló tolower, towlower és nagybe-tűssé konvertáló toupper, towupper.

Az itt említett rutinokkal is a következő nagy fejezetben foglalkozunk!

2.1.4. LC_MONETARY Pénzügyi értékek formázását befolyásolja. Lásd a localeconv függvényt később!

2.1.5. LC_NUMERIC Nem pénzügyi értékek formázására hat. Lásd később a localeconv–ot!

A decimális pont karaktert, a számjegy csoportokat elválasztó karak-tert, a számrendszer alap felismerést stb. rögzíti. A sima vagy széles karak-terláncokat belső ábrázolás formákba konvertáló rutinokra (atof, atoi, atol, strtod, wcstod, strtol, wcstol, strtoul és wcstoul) van hatással.

Page 10: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Helyi információ

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 10 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 10 ►

A decimális pont karaktert, a számrendszer alap karaktert és felisme-rést stb. befolyásolja a formázott kimenetre (a printf család), valamint a formázott bemenetre (a scanf család).

2.1.6. LC_TIME A dátum és idő értékeket formázó strftime és wcsftime függvények vi-selkedésére hat. Lásd az „Az idő kezelése” fejezetben a részleteket!

Visszatérve a setlocale tárgyalásához, ha érvényes locale és kategoria pa-ramétert adnak meg, akkor a függvény a specifikált hellyel és kategóriával kapcsolatos, statikus élettartamú pufferben elhelyezett karakterláncra mu-tató mutatóval tér vissza. Érvénytelen paraméter esetén NULL mutatót szolgáltat a rutin, és a program aktuális helyi információ beállításai válto-zatlanok.

Ha a locale NULL mutató, akkor a setlocale a program helyi informá-ció kategoriajához kapcsolódó karakterláncra mutató mutatóval tér vissza, és a program aktuális helyi beállításai változatlanok. A NULL mutató setlocale–lal kapcsolatban egyébként a nemzetközi környezet lekérdezését „erőltető” direktíva.

setlocale(LC_ALL, NULL);

A ”C” locale minimális ANSI konform környezetet definiál a C fordí-táshoz a megadott kategoriara. A ”C” locale feltételezi, hogy minden char adattípus 1 bájtos, és hogy értéke mindig kisebb 256–nál. A programindí-tás hatására mindig

setlocale(LC_ALL, "C");

hív a cél környezet, mielőtt a main–t indítaná. Ha a locale mutatta karakterlánc üres, setlocale(LC_ALL, "");

akkor a hely az implementáció definiálta, eredeti környezet a kategoria pa-raméterrel előírt kategóriára. Az eredeti környezet tulajdonképpen az ope-rációs rendszertől nyert, alapértelmezés szerinti hely.

A locale mutathat a setlocale korábbi hívásából visszakapott karakter-láncra, és természetesen az implementáció definiálta más karakterláncokra.

A setlocale által visszaadott karakteres mutató csak a rákövetkező függvényhívásban használható a program helyi információ részeinek hely-reállítására, feltéve, hogy a program közben nem változtatta meg sem a mutató, sem a mutatott karakterlánc értékét. A későbbi setlocale hívás felülírja ezt a statikus helyfoglalású karakterláncot, s így a kérdéses hely karakterláncot későbbi felhasználáshoz feltétlenül el kell menteni. Például:

Page 11: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Helyi információ

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 11 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 11 ►

char * forras = setlocale(LC_ALL, ””), *ment = malloc(strlen(forras)+1); strcpy(ment, forras);

A nagy fejezet végén ismertetett HELYINFO.C példa lekérdezi és kijelzi, hogy milyen hely van érvényben a program indulásakor. Rendszer alapér-telmezett helyet állít, és ezt is kijelzi. Végül minimális ANSI konform C helyet rögzít, és demonstrálja ezt is.

2.1.7. Microsoft Visual C++ Windows–os környezetben A locale paraméter a hely nevét specifikáló karakterláncra mutat. A Micro-soft Visual C++ támogat minden, a „Language and Country/Region Strings” címszó alatt listázott helyet. Például a setlocale(LC_ALL, "English"); minden kategóriát beállít, és csak az ”English_USA.1252” karakterlánccal tér vissza. Ha nem minden kategóriát állíttatunk be, akkor a setlocale mindenegyes kategória aktuális beállításait (egymástól ; választja el őket!) jelző karakterláncot szolgáltat.

Például a következő függvényhívás sorozat // Minden kategória beállítása és "English_USA.1252" // visszaadása. setlocale(LC_ALL, "English"); // Csak az LC_MONETARY kategória beállítása és // "French_France.1252" szolgáltatása. setlocale(LC_MONETARY, "French"); setlocale(LC_ALL, NULL); azt eredményezi, hogy az LC_ALL kategóriára visszakapott karakterlánc: LC_COLLATE=English_USA.1252; LC_CTYPE=English_USA.1252; LC_MONETARY=French_France.1252; LC_NUMERIC=English_USA.1252; LC_TIME=English_USA.1252

A locale paraméter a következő formájú: "<lang<_country<.code_page>>>" | ".code_page" | "" | NULL

Végül is a Win32 NLS API által támogatott, minden nyelv (lang), min-den ország (_country) és minden kódlap (.code_page) rendelkezésre áll. Az operációs rendszer által nem támogatott nyelveket a setlocale sem fogadja. A három betűs nyelv kód ("hun") csak Windows NT–ben és en-nél későbbi verziókban érvényes, viszont bárhol használható a hosszú

Page 12: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Helyi információ

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 12 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 12 ►

nyelv karakterlánc ("hungarian"). Az ország kódokról ("hun" vagy "hungary") ugyanezek mondhatók el! A kódlapokkal később még bőveb-ben foglalkozunk! A következő példák mind az LC_ALL kategóriára vo-natkoznak. A

setlocale(LC_ALL, "<lang_country>");

a megadottra állítja a hely nyelvét, országát, és az operációs rendszertől nyert alapértelmezett kódlapot használtatja. A

setlocale(LC_ALL, "<lang_country.code_page>");

a paraméterként megadottakra állítja a nyelvet, az országot és a kódlapot. A nyelv, az ország és kódlap változatos kombinációit használhatjuk.

Például: setlocale(LC_ALL, "French_Canada.1252");

// Francia Kanadához ANSI alapértelmezett kódlapot állít. setlocale(LC_ALL, "French_Canada.ACP");

// Francia Kanadához OEM alapértelmezett kódlapot állít. setlocale(LC_ALL, "French_Canada.OCP");

A megadott nyelvre, s a nyelvre alapértelmezett országra állítja a helyet a setlocale(LC_ALL, "<lang>");

függvény és a kódlap az operációs rendszertől az országra kinyert rendszer alapértelmezett, ANSI kódlap lesz. A következő, két setlocale hívás funk-cionálisan ekvivalens:

setlocale(LC_ALL, "English"); setlocale(LC_ALL, "English_United States.1252");

A kódlapot a megadottra állítja a setlocale(LC_ALL, "<.code_page>");

és a kódlaphoz a gazda operációs rendszerben definiált, alapértelmezett országot, nyelvet használja.

A ".code_page" helyén mind az ".OCP", mind az ".ACP" karakterlánc használható a rendszer alapértelmezett OEM, ill. ANSI kódlap definiálásá-ra. A

setlocale(LC_ALL, ".OCP");

a helyi információt explicit módon az operációs rendszertől nyert, aktuális OEM kódlapra állítja. A

setlocale(LC_ALL, ".ACP");

az operációs rendszertől nyert, aktuális ANSI kódlapot állítja be.

Page 13: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Helyi információ

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 13 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 13 ►

Ha a kódlapot változtatni kívánjuk, akkor a kategorianak LC_ALL–nak vagy LC_CTYPE–nak kell lennie. Ha a gazda operációs rendszer alapértelmezett országa és nyelve “United States” és “English”, akkor a következő két setlocale hívás ekvivalens:

setlocale(LC_ALL, ".1252"); setlocale(LC_ALL, "English_United States.1252");

A #pragma setlocale a „Preprocessor Reference”–en belül a „Pragma Directives” címszónál található meg!

2.2. lconv struktúra A struct lconv tagjai leírják, hogyan kell formázni a numerikus és a

pénzügyi értékeket. A szabványos C könyvtár függvényei a struktúrából csak a decimal_point tagot használják, s a többi információt nem veszik igénybe.

Az összes char * típusú tag – legfeljebb üres ("") – karakterláncokra mutat. Az üres karakterlánc az elsődleges értelmén túl azt is jelenti, hogy az ilyen tagot nem támogatja az aktuális hely. Programindításkor az auto-matikusan beállított ”C” helyen az összes char * típusú tag üres karakter-lánc a decimal_point kivételével, ami viszont ”.”.

A struktúra char típusú tagjai nem negatív számok. Ezek közül a CHAR_MAX értékűeket nem támogatja az aktuális hely. A ”C” helyen az összes char típusú tag CHAR_MAX értékű.

A struct lconv tagjai után zárójelbe tettük az érintett helyi kategóriát, ill. a másik zárójelben az USA hely vonatkozó értékét közöltük. A struktú-ra tagjai a következők:

• char *decimal_point: (LC_NUMERIC) (”.”) Decimális pont karak-ter nem pénzügyi mennyiségekre.

• char *thousands_sep: (LC_NUMERIC) (”,”) A decimális ponttól balra képzett számjegy csoportokat elválasztó karakter nem pénzügyi mennyiségekre.

• char *grouping: (LC_NUMERIC) (”\3”) A számjegy csoportok mérete nem pénzügyi mennyiségekben. A karakterlánc egymást követő char típusú elemei írják le a decimális ponttól indulva és balra haladva a csoportokat. A karaktertömb elemei a következők lehetnek:

Page 14: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Helyi információ

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 14 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 14 ►

• CHAR_MAX: Ne hajtson végre semmilyen további csoportosí-tást. A CHAR_MAX elemérték befejezi a további csoportosítás, s ennél fogva a karakterláncot is.

• 0: A karakterlánc záró null karakter az előző elemet ismételteti kor-látlanul, azaz előírja, hogy az előző elemet kell továbbhasználni a maradék számjegyekre rendre.

• n: Az aktuális csoportot alkotó számjegyek száma. A következő elemet vizsgálja az aktuális előtti, következő számjegy csoport mé-retének meghatározásához.

A {3, 2, CHAR_MAX} tartalmú tömb előír egy hármas, majd egy kettes számjegy csoportot, s aztán jöhet, ami marad. Ez alapján a 987654321 kijelzése 9876,54,321. lenne. A ”\3” hármas számjegy cso-portot ismétel, azaz az előbbi érték kijelzése 987,654,321.

A ”\3” karakterlánc ugye {3, 0} tartalmú tömböt jelent! • char *int_curr_symbol: (LC_MONETARY) (”USD ”) Az aktuális

hely nemzetközi pénznem szimbóluma. Az első három karakter úgy specifikálja a pénznem szimbólumot, ahogyan azt az ISO 4217 Codes for the Representation of Currency and Funds szabvány meghatározza. A lezáró null karaktert közvetlenül megelőző, negyedik karakter választja el a szimbólumot a pénzügyi mennyiségtől.

• char *currency_symbol: (LC_MONETARY) (”$”) Pénznem szim-bólum az aktuális helyre.

• char *mon_decimal_point: (LC_MONETARY) (”.”) Decimális pont karakter pénzügyi mennyiségekre.

• char *mon_thousands_sep: (LC_MONETARY) (”,”) A decimális ponttól balra képzett számjegy csoportokat elválasztó karakter pénz-ügyi mennyiségekre.

• char *mon_grouping: (LC_MONETARY) (”\3”) A számjegy cso-portok mérete pénzügyi mennyiségekben. A karakterlánc egymást kö-vető elemei írják le a decimális ponttól indulva és balra haladva a cso-portokat. A kódolás egyezik a grouping tagnál leírtakkal.

• char *positive_sign: (LC_MONETARY) (”+”) Nem negatív pénz-ügyi mennyiségek előjelét jelző karakterlánc.

• char *negative_sign: (LC_MONETARY) (”–”) Negatív pénzügyi mennyiségek előjelét jelző karakterlánc.

Page 15: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Helyi információ

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 15 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 15 ►

• char int_frac_digits: (LC_MONETARY) (2) Számjegyek száma a decimális ponttól jobbra nemzetközi formázású pénzügyi mennyisé-gekben.

• char frac_digits: (LC_MONETARY) (2) Számjegyek száma a deci-mális ponttól jobbra formázott pénzügyi mennyiségekben.

• char p_cs_precedes: (LC_MONETARY) (1) 1, ha a pénznem szim-bólum megelőzi a nem negatív formázott pénzügyi mennyiséget, s 0, ha követi.

• char p_sep_by_space: (LC_MONETARY) (0) 1, ha a pénznem szimbólumot szóköz választja el a nem negatív formázott pénzügyi mennyiségtől, s 0, ha nincs szóközös elválasztás.

• char n_cs_precedes: (LC_MONETARY) (1) 1, ha a pénznem szim-bólum megelőzi a negatív formázott pénzügyi mennyiséget, s 0, ha kö-veti.

• char n_sep_by_space: (LC_MONETARY) (0) 1, ha a pénznem szimbólumot szóköz választja el a negatív formázott pénzügyi mennyi-ségtől, s 0, ha nincs szóközös elválasztás.

• char p_sign_posn: (LC_MONETARY) (4) A pozitív előjel pozíciója nem negatív formázott pénzügyi mennyiségekben.

• char n_sign_posn: (LC_MONETARY) (4) Az előjel pozíciója nega-tív formázott pénzügyi mennyiségekben. A p_sign_posn és az n_sign_posn értékére a következő szabályok

érvényesek: • 0: A zárójelek foglalják be a mennyiséget és a pénznem szimbó-

lumot. • 1: Az előjel megelőzi a mennyiséget és a pénznem szimbólumot. • 2: Az előjel követi a mennyiséget és a pénznem szimbólumot. • 3: Az előjel közvetlenül megelőzi a pénznem szimbólumot. • 4: Az előjel közvetlenül követi a pénznem szimbólumot.

2.3. localeconv Részletes információt szolgáltat az aktuális helyi numerikus és pénzügyi beállításokról a

struct lconv *localeconv(void);

függvény. Az információt egy statikus lconv struktúrában helyezi el, és visszaadja ennek címét. E struktúra tartalmát a következő localeconv,

Page 16: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Helyi információ

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 16 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 16 ►

vagy LC_ALL, LC_NUMERIC vagy LC_MONETARY kategóriás setlocale hívás felülírja.

A visszakapott struktúra tagjait ne módosítsuk közvetlenül!

2.4. Példa

A HELYINFO.C példa lekérdezi és kijelzi, hogy milyen hely van ér-vényben a program indulásakor. Rendszer alapértelmezett helyet állít, és ezt is kijelzi. Megjeleníti a tizedespont karaktert és a pénznem szimbólu-mot. Végül minimális ANSI konform C helyet rögzít, és demonstrálja ezt is. A példa minden beállított helyen megpróbálkozik a ”3,14” karakterlánc konverziójával, s az átalakítás eredményét meg is jelenteti.

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <locale.h> void main(void){ struct lconv *plconv; char puff[256]; printf("Helyi információ:\n\n"); /* A helyi infó lekérdezése: */ printf("A program indulásakor: %s\n", setlocale(LC_ALL, NULL)); printf("atof(\"3,14\") 4.2 pontossággal: %4.2f\n", atof("3,14")); /* Alapértelmezett hely beállítása: */ strcpy(puff, setlocale(LC_ALL, "")); printf("Rendszer alapértelmezett hely: %s\n", puff); printf("atof(\"3,14\") 4.2 pontossággal: %4.2f\n", atof("3,14")); /* Tizedespont és pénznem karakter: */ plconv = localeconv(); printf("A nem pénzügyi decimális pont: %s\n", plconv->decimal_point); printf("A pénznem szimbólum: %s\n", plconv->currency_symbol); /* A hely visszaállítása C környezetre: */ printf("Minimális ANSI konform C hely: %s\n", setlocale(LC_ALL, "C")); printf("atof(\"3,14\") 4.2 pontossággal: %4.2f\n", atof("3,14")); }

Page 17: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Helyi információ

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 17 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 17 ►

2.5. Ellenőrző kérdések:

• Melyik fejfájlt kell bekapcsolni a szabványos helyi információt beállító, lekérdező rutinok használatához?

• Mik a helyi kategóriák? • Mik a paraméterei és a visszaadott értéke a setlocale függvénynek? • Milyen viselkedést befolyásol az LC_ALL kategória? • Mire hat az LC_COLLATE kategória? • Mit befolyásol az LC_CTYPE kategória? • Mit állít be az LC_TIME kategória? • Hogyan kérdezhető le egy kategória beállítása? • Hogyan lehet ANSI konform C környezetet állítani, és hogyan lehet

alapértelmezés szerinti helyet választani? • Mire való az lconv struktúra, s hogyan lehet lekérdezni és elérni? • Milyen típusú tagjai vannak az lconv struktúrának, s mit használnak

ezek közül a szabványos könyvtár függvényei? • Mire való az LC_NUMERIC kategória, és az lconv struktúrának

milyen tagjaira hat? • Mire jó az LC_MONETARY kategória, és az lconv struktúrának

milyen tagjait befolyásolja? • Milyen lconv struktúra tagok, és milyen formában határozzák meg a

számjegy csoportok méretét? • Melyik lconv struktúra tagok határozzák meg az előjel pozícióját, és

hogyan?

2.6. Megoldandó feladatok:

Készítsen programot, mely megjelentet egy kétoszlopos táblázatot! A bal oldali oszlopban a kategóriák látszanak szimbolikus állandók formájában balra zártan, s a jobb oldali oszlop jobbra zártan a megfelelő számértéke-ket tartalmazza. A táblázat oszlop feliratai: „Kategória” és „Érték”. Készítsen void lconvki(struct lconv * mutato);

függvényt, mely megjelenteti alkalmas formában a mutato paraméterével elért lconv struktúra tagjait! Írjon kipróbáló programot is a függvényhez!

Page 18: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 18 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 18 ►

3. Többájtos és széles karakterek

Azon ANSI/ISO szabványos függvények, melyek nevébe w (wide – szé-les), wc (wide character – széles karakter), wcs (wide character string – széles karakterlánc) került, mindig párjuk széles karakteres változatai, és működésük ettől eltekintve egyezik párjukéval. Prototípusaik, a bennük használt típusok és szimbolikus állandók a szabványos WCHAR.H, ill. a karakterosztályozóké a WCTYPE.H, fejfájlokban találhatók.

A helyi információval, a helyi kategóriákkal és a setlocale függ-vénnyel az előző fejezet foglalkozik!

3.1. Egybájtos karakterek (Single Byte Characters) Az ASCII karakterkészlet a 0x00 – 0x7F tartományban definiál karaktere-ket. Vannak aztán Európában olyan karakterkészletek, melyek az ASCII 0x00 – 0x7F tartományát megtartva a 0x80 – 0xFF intervallumra is meg-határoznak karaktereket (kódlapokkal). A sok európai nyelvi bővítményt is hozzáértve az ASCII karakterkészlethez 8 bites, vagy egybájtos karak-terkészletet (Single-Byte – Character Set: SBCS) kapunk.

3.2. Többájtos karakterek (MultiByte Characters) Néhány nem európai karakterkészletre (például a japán Kanji–ra) is tekin-tettel, jóval több karakter van, mint amennyit az egybájtos karakterkódolá-si séma megenged, és ezért van szükség többájtos karakterkódolásra (MultiByte-Character Set: MBCS). A többájtos karakter egy vagy több bájt sorozata, de minden bájt sorozat egyetlen karaktert reprezentál a ki-bővített karakterkészletben. A többájtos karakter lehet egybájtos (egy karakter az alap C karakterkészletből), lehet kétbájtos, vagy többájtos sorozat.

Két vagy többájtos bájtsorozatokat tartalmazó többájtos kódolás ese-tén a bájtok értelmezése a konverziós állapottól (conversion state) függ. A konverziós állapotot a bájtsorozatban előbb álló bájt határozza meg a következőre. A kezdeti konverziós állapot szerint, ha a bájt egyezik az alap C karakterkészlet egyik karakterével, akkor a bájt azt a karaktert ábrá-zolja.

Például az EUC kódolás magába foglalja az ASCII–t. A [0xA1, 0xFE] intervallumba eső értékű bájt a kétbájtos sorozat első bájtja, mely sorozat második bájtja köteles a [0x80, 0xFF] értéktartományba esni. Minden más bájt érték egybájtos sorozat. Miután az alap C karakterkészlet minden tag-

Page 19: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 19 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 19 ►

jának bájt értéke a [0x00, 0x7F] ASCII kódtartományba esik, az EUC ki-elégíti „a többájtos kódolás szabványos C–ben” követelményeit is. [0xA1, 0xFE] intervallumba eső értékű bájt után azonban nincs kezdeti konverzi-ós állapot, s így az ilyenkor következő [0x00, 0x7F] értékű bájt nem egy-bájtos sorozat, hanem egy hibás kétbájtos sorozat része.

Az előző bekezdés értelmében mind az ASCII, mind a többájtos karakterláncokban 0x00 értékű a karakterláncokat záró, egybájtos null karakter ('\0').

A többájtos karakterek állapotfüggő kódolásúak is lehetnek. Ilyen kó-dolásban egy bájt értelmezése attól a konverziós állapottól függ, mely ma-gába foglalja az elemzési állapotot, mint az előbb, és a bájt sorozatok korábbi bájtjai által meghatározott váltó állapotot. Új többájtos karakter kezdetén kezdeti váltó állapot van, mely egyben kezdeti konverziós álla-pot is. Egy későbbi váltó sorozat majd más váltó állapotot határozhat meg, mely aztán minden bájt sorozatot – az egybájtosokat is beleértve – különböző értelmezésűvé tehet. Zérusértékű bájt azonban mindig null karaktert ábrázol, és nem lehet valamely többájtos karakter része.

Például a JIS (Japan Industrial Standard) kódolás is magába foglalja az ASCII–t. Kezdeti váltó állapotban minden bájt egyetlen karaktert ábrázol, eltekintve a két következő hárombájtos váltó sorozattól:

• A hárombájtos ”\x1B$B” sorozat kétbájtos üzemmódba vált. Ez után két, egymást követő bájt (mindkettő [0x21, 0x7E] értéktartományban) képez egy többájtos karaktert.

• A hárombájtos ”\x1B(B” sorozat visszavált kezdeti váltó állapotba.

A JIS is kielégíti „a többájtos kódolás szabványos C–ben” követelmé-nyeit. Ilyen sorozat sincs kezdeti konverziós állapotban azonban három-bájtos váltó sorozat után, ill. kétbájtos üzemmódban.

Többájtos karakterek beírhatók a C forrásszövegbe megjegyzés, karak-ter konstans, karakterlánc konstans, vagy #include direktívabeli fájlazo-nosító részeként. Implementációtól függ, hogy e karakterek, hogy jelennek meg nyomtatásban. Minden leírt többájtos karaktersorozatnak azonban kezdeti váltó állapotban kell kezdődnie és végződnie! Null lezárású többájtos karakterláncokat több könyvtári függvény is használhat. Például ilyen lehet a printf és a scanf formátum karakterlánca. E karakterláncok is kötelesek kezdeti váltó állapotban kezdődni és végződni!

Page 20: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 20 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 20 ►

3.2.1. Egybájtos és többájtos adattípusok Bármely könyvtári rutin, mely csak egy többájtos karaktert, vagy ennek egyetlen bájtját kezeli, előjeltelen egész paramétert vár. A többájtos bájto-kat vagy karaktereket karakterláncként kezelő MBCS függvények előjelte-len char mutatóként reprezentált többájtos karakteres karakterláncokra számítanak paraméterként.

A többájtos karakter mindenegyes bájtja reprezentálható 8 bites char–ként is. Egy SBCS vagy egy MBCS egybájtos char típusú, 0x7F–nél nagyobb értékű karakter azonban negatív. A fordító az ilyen karaktert elő-jel kiterjesztéssel konvertálja int–té vagy long–gá, ami előre megjósolha-tatlan eredményre is vezethet. A többájtos karakter egy bájtját ezért célsze-rű unsigned char típusúként kezelni, vagy az int–té, long–gá konverzió előtt explicit módon unsigned char–ré típusmódosítani.

3.3. Széles karakterek (Wide Characters) A legelterjedtebben használt széles karakter kettő vagy négybájtos, több-nyelvű karakterkód. A belső ábrázolás Unicode, ill. UCS (Universal Character Set – univerzális karakterkészlet, ISO 10646). Az Unicode eredetileg 16 bitesre tervezett karakter készlet, míg az UCS 32 bites. A két szabvány az alacsonyabb helyiértékű szavában azonban gyakorlatilag azo-nos karakter repertoárral és kódtáblával rendelkezik, s ez az ú.n. Basic Multilingual Plane – BMP. A 16 bites tartományon kívül eső, specializált karakterek alkotása és kódolása jelenleg is folyamatban van. Mi a további-akban csak az Unicode–dal foglalkozunk!

A modern számítástechnikában világszerte használt bármilyen karak-ter, beleértve a technikai szimbólumokat és a speciális publikációs karakte-reket, reprezentálható az Unicode specifikációnak megfelelően széles ka-rakterként. A többájtos karakter lehet ugyan 2 bájt méretű, de nem Unicode és nem széles karakter! Az Unicode szabvány ma széles körben elfogadott. Miután a széles karakterek fix méretűek, egyszerűsítik a nem-zetközi karakterkészleteket alkalmazó programok készítését.

Az Unicode alsó 128 értéke átvette az ASCII–t. Ezután 256–ig a La-tin–1 kódlap következik. A magyar ő és ű betűk így aztán 256–nál nagyobb kódot kaptak. Az ó például 0X00F3 (243), s az ő pedig 0X0151 (337).

A char széles karakterek ábrázolására alkalmatlan, ezért az ANSI/ISO C szabvány új típust vezetett be:

Page 21: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 21 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 21 ►

3.3.1. wchar_t típus Egyetlen széles karakter ebben a típusban ábrázolandó. Az adattípust de-finiálja az STDDEF.H fejfájl is, és például:

typedef unsigned short wchar_t;

A széles karakterlánc wchar_t [] tömb, melynek elemei wchar_t* tí-pusú mutatókkal is elérhetők. Bármely ASCII karakter reprezentálható széles karakterként, csak az L előtagot elé kell írni. Például az L'\0' a szé-les karakterláncot záró 16 bites null karakter. Tehát a széles karakter konstans írásszabálya:

L’karakter’

Hasonlóan: az ASCII karakterlánc konstans is reprezentálható széles karakterlánc állandóként, csak elé kell tenni az L előtagot így: L"Hello". Az ábrázolás hatelemű széles karakteres (wchar_t) tömbben történik, melynek tartalma:

{L'H', L'e', L'l', L'l', L'o', 0}

A széles karakterek általában több helyet foglalnak a memóriában, mint a többájtos karakterek, de feldolgozásuk gyorsabb. Többájtos kódo-lással egyszerre csak egy hely (locale) reprezentálható, míg az Unicode karakterkészlettel a világ minden karakterkészlete megvalósul szimultán módon.

Teljesen új függvény családot készítettek, ahogy majd e dokumen-tumban is látjuk, a széles karakterek és karakterláncok kezelésére. Formá-tumspecifikációként esetükben a %lc, ill. a %ls alkalmazandó!

3.3.2. UTF–8 kódolás A nyolcbites Unicode átalakítási formátum (8–bit Unicode Transformation Format) veszteségmentes, változó hosszúságú Unicode karakterkódolási módszer, mely bármilyen Unicode karaktert képes ábrá-zolni, és visszafele kompatibilis az ASCII kódolással. Egy karakter kódolá-sára 1–6 bájtot vesz igénybe. Pontosabban az ASCII karaktereket egy–egy ASCII bájt írja le (0X00–0X7F), és ezt a kategóriát a bájt legmagasabb helyiértékű bitjének zérus értéke jelzi. A kódolásban az összes többi bájt legmagasabb helyiértékű bitje egy. Pontosabban a karaktert leíró bájtsoro-zat első bájtjának – magasabb helyiértéktől lefelé haladva – annyi bitpozí-ciója 1, mint ahány bájtból a bájtsorozat áll, s ezt aztán egy zérusértékű bit zárja. A bájtsorozat további tagjainak két legmagasabb helyiértékű bitpozí-

Page 22: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 22 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 22 ►

ciója 10. A karakter Unicode–ját aztán a kódolás a bájtsorozat fennmaradt bitjeiben helyezi el.

A magyar ékezetes betűket két bájtban 16 bites Unicode–jából átala-kítva ábrázolja az UTF–8, azaz az Unicode–ot egy 110xxxxx és egy 10xxxxxx bájtba rakja szét. Az ó (0X00F3) két bájtja így 11000011 és 10110011 (195 179). Az ő (0X0151) viszont 11000101 10010001 (197 145).

Az UTF–8 eléggé gazdaságtalan a latin betűket alkalmazó nyelvekre, de ugyanakkor a karakterkijelzésben megszűnik a beállított kódlaptól való függés.

Elterjedt Linux rendszerekben, és XP–től fölfelé a Windows is támogatja. Különösen alkalmas 8 bites átviteli közegekre, mint például az e–mail,

vagy a weblapok.

3.4. Konverzió többájtos és széles karakterek közt A kibővített karakterek többájtos és széles karakteres ábrázolásai közti konverzióban, a teljesség igénye nélkül, a következőkben felsorolt makrók, típusok és függvények segítenek. Használatukhoz bekapcsolandó a WCHAR.H fejfájl, ill. az STDLIB.H az mblen, az mbstowcs, az mbtowc, a wcstombs, és a wctomb függvényekhez!

A konverzió mindig az aktuális hely LC_CTYPE kategória beállí-tásának megfelelően történik.

3.4.1. MB_LEN_MAX, MB_CUR_MAX makrók Az MB_LEN_MAX makró megadja az implementáció támogatta, összes helyre vonatkozóan az egyetlen többájtos sorozatot alkotó bájtok maximá-lis számát. Az MB_CUR_MAX ugyanez az érték, de csak az aktuális hely vonatkozásában. Tehát bizonyosan igaz, hogy MB_LEN_MAX >= MB_CUR_MAX.

Az MB_LEN_MAX makródefiníció a LIMITS.H, s az MB_CUR_MAX az STDLIB.H fejfájlban található!

3.4.2. mbstate_t típus A típus a konverziós állapotot képes ábrázolni az mbrlen, az mbrtowc, az mbsrtowcs, a wcrtomb, vagy a wcsrtombs függvények számára. Például:

typedef int mbstate_t;

Az mbstate_t mbst = {0};

Page 23: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 23 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 23 ►

definíció biztosítja, hogy az mbst kezdeti konverziós állapotot ábrázoljon. Ezt az állapotot persze a változó más értéke is jelezheti. Megbízhatóan rákérdezni erre az állapotra az mbsinit függvénnyel lehet.

3.4.3. wint_t típus adattípus olyan WCHAR.H–ban definiált egész típus, mely ábrázolni képes minden wchar_t értéket, valamint a WEOF makró értékét, és egész–előléptetéskor nem változhat meg az érték. Például a következő típusdefiníciók teljesítik a mondott kritériumokat:

typedef unsigned short wchar_t; typedef wchar_t wint_t;

3.4.4. WEOF makró A makró wint_t típusú visszatérési érték a széles folyam végének, ill. va-lamilyen hibafeltétel jelzésére. Például:

#define WEOF (wint_t)(0xFFFF)

3.4.5. btowc wint_t btowc(int c);

A függvény WEOF–ot ad vissza, ha c EOF. Egyébként az (unsigned char)c–t kezdeti konverziós állapotban kezdődő, egybájtos többájtos ka-rakterként konvertálja, mint az mbrtowc. Ha a konverzió sikeres, a rutin a konvertált széles karakterrel tér vissza. Máskülönben WEOF–ot szolgáltat.

3.4.6. mblen int mblen(const char *s, size_t n);

Ha s nem NULL, akkor a függvény visszaadja az s többájtos karakter bájtszámát. Ha s NULL, vagy lánczáró nullára mutat, akkor zérust ka-punk. Ha az s címtől kezdődő, n (vagy lezáró nulla bekövetkezése estén kevesebb) bájt nem alkot az aktuális helynek megfelelő, érvényes többájtos karaktert, akkor a rutintól –1 jön.

3.4.7. mbrlen size_t mbrlen(const char *s, size_t n, mbstate_t *ps);

Sikeres esetben az mbrlen visszaadja az s mutatta, érvényes, széles karak-terré konvertálható, többájtos karakter összegyűjtéséhez felhasznált bájtok számát. A függvény ekvivalens az

mbrtowc(NULL, s, n, ps!=NULL ? ps : &internal);

Page 24: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 24 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 24 ►

hívással, ahol az internal az mbrlen mbstate_t típusú, statikus belső vál-tozója, melyet a programindítás kezdeti konverziós állapotba hoz, és a benne tárolt értéket más könyvtári függvény nem is változtathatja meg.

A rutin visszatérési értéke:

• –2: Ha mind az n char összeszedése után az eredmény konverziós állapot hiányos többájtos karaktert jelez.

• –1: Ha a függvény a következő többájtos karakter teljessé válása előtt kódhibát észlel, amikor is az errno–t EILSEQ–be állítja, és az ered-mény konverziós állapotot meghatározatlanul hagyja.

• zérus: Ha a következő teljes többájtos karakter null, amikor is az eredmény konverziós állapot a kezdeti konverziós állapot.

• a teljes többájtos karakter bájtszáma: Ez a sikeres eset. Ilyenkor az eredmény konverziós állapot e bájtok átkonvertáltságát jelzi.

3.4.8. mbrtowc size_t mbrtowc(wchar_t *pwc, const char *s, size_t n,

mbstate_t *ps);

A függvény meghatározza, ha lehet, az s többájtos karakterláncban a kö-vetkező többájtos karaktert adó bájtok számát.

Ha a ps nem NULL, akkor a rutin feltételezi, hogy a többájtos karak-terlánc konverziós állapota *ps. Ha a ps NULL, akkor viszont internal–nak tekinti. Az internal az mbrtowc mbstate_t típusú, statikus belső változója, melyet a programindítás kezdeti konverziós állapotba hoz, és a benne tárolt értéket más könyvtári függvény nem változtathatja meg. A többájtos karakter bájtjainak feldolgozása közben a konverziós állapotot bájtról–bájtra haladva aktualizálja a függvény.

Ha az s többájtos karakterlánc nem NULL, s az s–sel elért n, vagy ke-vesebb bájt érvényes többájtos karaktert alkot, akkor az mbrtowc szolgál-tatja a többájtos karakter bájtszámát. Ha ilyenkor a pwc sem NULL, a többájtos karaktert konvertálja az aktuális helynek megfelelően széles ka-rakterré a függvény, majd magát a széles karaktert elhelyezi a pwc címen. Máskülönben a rutin elvetve pwc–t és n–t, mbrtowc(0, "", 1, ps)–vel tér vissza ténylegesen. A visszatérési érték csak akkor zérus, ha a konverziós állapot azt jelzi, hogy nincs függőben megelőző mbrlen, mbrtowc, vagy mbsrtowcs hívásból származó nem teljes többájtos karakter ugyanarra a láncra és konverziós állapotra.

A rutin visszatérési értéke:

Page 25: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 25 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 25 ►

• –2: Ha mind az n char összeszedése után az eredmény konverziós állapot hiányos többájtos karaktert jelez, s ilyenkor nincs letárolás pwc címre.

• –1: Ha a függvény a következő többájtos karakter teljessé válása előtt kódhibát észlel, amikor is az errno–t EILSEQ–be állítja, és az ered-mény konverziós állapotot meghatározatlanul hagyja. Természetesen ez esetben sincs letárolás pwc címre.

• zérus: Ha a következő teljes többájtos karakter null, amikor is az eredmény konverziós állapot a kezdeti konverziós állapot, és az L’\0’ kikerül a pwc címre, ha az nem NULL.

• a következő teljes többájtos karakter bájtszáma: Ez a sikeres eset. Ilyenkor az eredmény konverziós állapot e bájtok átkonvertáltságát jelzi.

3.4.9. mbsinit int mbsinit(const mbstate_t *ps);

A rutin nem zérust szolgáltat, ha a ps NULL mutató, vagy a *ps kezdeti konverziós állapotot jelöl. Máskülönben zérust kapunk tőle.

3.4.10. mbsrtowcs size_t mbsrtowcs(wchar_t *pwstr, const char **forras,

size_t hossz, mbstate_t *ps);

A függvény széles karaktersorozattá konvertálja *forras címen kezdődő többájtos karakterláncot az aktuális helynek megfelelően, mintha ismétel-ten hívná az:

x=mbrtowc(pwstr, *forras, n, ps != 0 ? ps : &internal),

ahol n egy bizonyos, zérusnál nagyobb érték, s az internal az mbsrtowcs mbstate_t típusú, statikus belső változója, melyet a programindítás kezde-ti konverziós állapotba hoz, és a benne tárolt értéket más könyvtári függ-vény nem is változtathatja meg.

Ha a pwstr nem NULL, az mbsrtowcs legfeljebb hossz széles karaktert tárol ismételt mbrtowc hívásokkal. A függvény eggyel növeli a pwstr és x–szel *forras címet minden egyes mbrtowc hívás után. Zérusértékű mbrtowc visszatéréskor az mbsrtowcs széles null karaktert rak a pwstr–re és NULL mutatót a *forras–ra.

Ha a pwstr NULL, a hossznak nagy értéket tulajdonít a rutin, és a kon-verzió eredménye nem áll rendelkezésre.

A rutin visszatérési értéke:

Page 26: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 26 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 26 ►

• –1: Ha egy mbrtowc hívás ezzel tért vissza a következő többájtos ka-rakter teljessé válása előtti kódhibát jelezve. Az errno ilyenkor EILSEQ.

• a sikeresen konvertált széles karakterek száma: A záró null karakter nem értendő ebbe bele!

3.4.11. mbstowcs size_t mbstowcs(wchar_t *pwstr, const char *forras,

size_t hossz);

A függvény a forras mutatta többájtos karakterlánc legfeljebb hossz karakte-rét átkonvertálja az aktuális helynek megfelelően széles karakterekké, az eredmény széles karaktersorozatot el is helyezi a pwstr címtől, és visszaadja az átkonvertált többájtos karakterek számát. Ha a pwstr paraméter NULL, a függvény a cél karakterlánc szükséges méretével tér vissza. Ha az mbstowcs a konverzió során érvénytelen többájtos karakterrel találkozik, –1–et szolgáltat. Ha a visszaadott érték hossz, a széles karakterlánc nem null–lezárású.

A konverzió eredménye egyezik azzal, mintha a forrás karakterlánc mindenegyes karakterére meghívták volna az mbtowc–t. Ha az mbstowcs egybájtos null karakterrel ('\0') találkozik még a hossz kimerülé-se előtt, a '\0'–t L'\0'–lá alakítja, és leállítja a konverziót. Ha a pwstr és a forras átfedik egymást, a függvény viselkedése kiszámíthatatlan.

3.4.12. mbtowc Többájtos karakter konverziója a megfelelő széles karakterré.

int mbtowc(wchar_t *pwc, const char *s, size_t n);

Ha az s nem NULL, s az s–sel elért n, vagy kevesebb bájt érvényes többájtos karaktert alkot az aktuális helyen, akkor az mbtowc szolgáltatja a többájtos karakter bájtszámát. Ha ilyenkor a pwc sem NULL, a többájtos karaktert konvertálja az aktuális helynek megfelelően széles karakterré a függvény, majd magát a széles karaktert elhelyezi a pwc címen. Ha az s NULL, vagy lánczáró null karakter konverziójáról van szó, az eredmény L'\0' lesz, és zérust kapunk a függvénytől. –1 jön viszont, ha az s–sel elért objektum első n bájtján belül nem képez érvényes többájtos karaktert.

Az mbtowc sehogyan sem vizsgál MB_CUR_MAX–nál több bájtot.

3.4.13. wcrtomb size_t wcrtomb(char *s, wchar_t wc, mbstate_t *ps);

Page 27: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 27 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 27 ►

A függvény meghatározza, ha lehetséges, a wc széles karakter többájtos karakterként való ábrázolásához szükséges bájtok számát. Nem minden wchar_t típusban ábrázolható érték képez érvényes széles karakterkódot!

Ha a ps nem NULL, akkor a rutin feltételezi, hogy *ps a konverziós ál-lapot a többájtos karakterlánc számára. Ha a ps NULL, akkor viszont internal–nak tekinti. Az internal az wcrtomb mbstate_t típusú, statikus belső változója, melyet a programindítás kezdeti konverziós állapotba hoz, és a benne tárolt értéket más könyvtári függvény nem is változtathatja meg. A többájtos karakter bájtjainak kialakítása közben a konverziós álla-potot bájtról–bájtra haladva aktualizálja a függvény.

Ha az s nem NULL, és a wc érvényes széles karakterkód, akkor a wcrtomb szolgáltatja az aktuális helynek megfelelően konvertált többájtos karakter bájtszámát (ez nem lehet nagyobb MB_CUR_MAX–nál), és a többájtos karakter bájtjait letárolja az s címen kezdődő char tömbbe.

Ha a wc null széles karakter, akkor a függvény a kezdeti váltó állapot visszaállításához szükséges, null karakterrel lezárt váltó sorozatot tárol le. Az eredmény konverziós állapot a kezdeti konverziós állapot.

Ha az s NULL mutató, a rutin ténylegesen wcrtomb(puff, L’\0’, ps)–vel tér vissza, ahol a puff valamilyen belső puffer a függvényben. A függ-vény ekképpen a kezdeti konverziós állapot helyreállításához és az előző, ugyanerre a láncra és konverziós állapotra vonatkozó wcrtomb, vagy wcsrtombs hívásból származó többájtos karakterlánc lezárásához szüksé-ges bájtszámmal tér vissza. Magyarán a rutin a *ps–t (vagy az internal–t) kezdeti állapotba hozza.

A rutin visszatérési értéke:

• –1: Ha a wc érvénytelen széles karakterkód, akkor az errno–t EILSEQ–be állítja, és az eredmény konverziós állapotot meghatáro-zatlanul hagyja a függvény. Természetesen ez esetben nincs letárolás az s címre.

• a konvertált többájtos karakter bájtszáma: Ez a sikeres eset. Ilyenkor az eredmény konverziós állapot a többájtos karakter bájtjainak legene-ráltságát jelzi.

3.4.14. wcsrtombs size_t wcsrtombs(char *s, const wchar_t **forras,

size_t hossz,mbstate_t *ps);

Page 28: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 28 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 28 ►

A függvény a *forras címen kezdődő széles karakterláncot az aktuális hely-nek megfelelő többájtos karaktersorozattá konvertálja, mintha ismételten hívná az:

x=wcrtomb(s? s: puff, *forras, ps!=0? ps: &internal),

ahol a puff egy char tömb, s az internal a wcsrtombs mbstate_t típusú, statikus belső változója, melyet a programindítás kezdeti konverziós álla-potba hoz, és a benne tárolt értéket más könyvtári függvény nem is változ-tathatja meg. A wcsrtombs a széles karakterláncot záró null karaktert is konvertálja.

Ha az s nem NULL, a wcsrtombs legfeljebb hossz bájtot tárol ismételt wcrtomb hívásokkal. A függvény eggyel növeli a *forras címet és x–szel az s–t minden egyes teljes többájtos karaktert tároló wcrtomb hívás után. A teljes null többájtos karaktert (beleértve a kezdeti váltó állapot visszaállítá-sához szükséges váltó sorozatot) tároló wcrtomb hívás után a rutin NULL mutatót ír a *forras–ra.

Ha az s NULL, a hossznak nagy értéket tulajdonít a rutin. A konverzió ilyenkor is megtörténik, de az eredmény többájtos karaktersorozat nem áll rendelkezésre.

A rutin visszatérési értéke:

• –1: Ha egy wcrtomb hívás ezzel tért vissza érvénytelen széles karak-terkódot jelezve, vagy a konvertált többájtos karakter nem fért el hossz bájton. Az errno ilyenkor EILSEQ.

• a sikeresen konvertált, összes bájtok száma: A záró null karakter nem értendő ebbe bele!

3.4.15. wcstombs size_t wcstombs(char *s, const wchar_t *forras, size_t

hossz);

A függvény a forras széles karakterláncot konvertálja az aktuális helynek megfelelő többájtos karakterlánccá, az eredmény legfeljebb hossz bájtját el is helyezi az s címen, és sikeres esetben visszaadja a többájtos karakter-láncba kiírt bájtok számát (a lezáró null karaktert – ha egyáltalán van – nem számítva). Ha az s paraméter NULL, a wcstombs a cél karakterlánc szükséges méretét szolgáltatja. Ha a rutin a konverzió során olyan széles karakterrel találkozik, mely nem konvertálható többájtos karakterré, size_t–vé típusmódosított –1–et kapunk.

A hossz paraméter praktikusan az s céltömb maximális bájtmérete. Ál-talában nem lehet pontosan tudni, hogy hány bájtra lesz majd szükség a

Page 29: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 29 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 29 ►

széles karakterlánc konverziójánál. Bizonyos széles karakterek ugyanis egy bájtot, mások meg kettőt, vagy többet igényelnek a cél karakterláncban. A 2*(wcslen(forras)+1) tömbméret persze többnyire elégséges. A +1 szoká-sosan a lánczáró null karakter miatt van a képletben.

Ha a wcstombs a konverzió során széles karakteres null karakterrel (L'\0') találkozik, 8 bites null karakterré alakítja, és leállítja a konverziót még a hossz kimerülése előtt. Ez praktikusan azt jelenti, hogy az s többájtos karakterlánc csak akkor null lezárású, ha az L'\0' a konverzió során követ-kezett be. Ha a forras és az s átfedik egymást, a wcstombs viselkedése ki-számíthatatlan.

3.4.16. wctob int wctob(wint_t c);

A rutin megvizsgálja, hogy a c ábrázolható–e kezdeti váltó állapotban kez-dődő, egybájtos többájtos karakterként. Ha igen, akkor ezt a karaktert szolgáltatja. Ha nem, WEOF–ot kapunk tőle. A konverziót a függvény wcrtomb hívással végzi.

3.4.17. wctomb int wctomb(char *s, wchar_t wc);

A függvény a széles karakteres wc paramétere értékét konvertálja át az ak-tuális helynek megfelelő többájtos karakterré, és elhelyezi az eredményt az s címtől, ha az nem NULL. Vissza a karakter bájtszáma jön, mely soha-sem nagyobb MB_CUR_MAX–nál. Ha a wc széles karakteres null karak-ter (L'\0'), a wctomb 1–et szolgáltat. –1–et kapunk, ha az aktuális helyen a konverzió nem lehetséges.

Az MBSTOWCS.C szemlélteti a széles és többájtos karakterláncok kon-verzióját oda és vissza:

#include <stdio.h> #include <stdlib.h> #include <locale.h> #include <limits.h> #include <string.h> #include <wchar.h> #define WLANC L"Hahó" int main(void){ int hossz = wcslen(WLANC)+1, i; size_t h; char *pmbhaho = (char *)malloc(MB_CUR_MAX*hossz);

Page 30: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 30 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 30 ►

wchar_t *pwchaho = (wchar_t *) malloc(sizeof(wchar_t)*hossz); wcscpy(pwchaho, WLANC); setlocale(LC_ALL, ""); printf("MB_LEN_MAX: %d és MB_CUR_MAX: %d\n\n", MB_LEN_MAX, MB_CUR_MAX); printf("\"%ls\" konverziója szélesről többájtos " "karakterlánccá:\n", WLANC); printf("\tA konvertált többájtos karakterek bájtszáma:" " %4u\n", h=wcstombs(pmbhaho, (const wchar_t *) pwchaho, MB_CUR_MAX*hossz)); printf("\tA többájtos karakterlánc bájtjainak hex. " "értéke:\n"); for(i=0; i<=h; ++i) printf("%.2X ", (unsigned char) pmbhaho[i]); printf("\n\nA negyedik többájtos karakter bájt száma:" " %d\n", mblen(pmbhaho+3, MB_CUR_MAX)); printf("\nKonverzió vissza többájtosról széles " "karakterlánccá:\n" ); pwchaho = (wchar_t *)realloc(pwchaho, sizeof(wchar_t)*h); printf("\tA konvertált karakterek száma: %u\n", h=mbstowcs(pwchaho, (const char *)pmbhaho, MB_CUR_MAX*hossz)); printf("\tA széles karakterek hex. értéke:\n"); for(i=0; i<=h; ++i) printf("%.*X ", 2*sizeof(wchar_t), pwchaho[i]); printf("\n\n"); return 0; }

Az eredmény például a következő lehet: MB_LEN_MAX: 2 és MB_CUR_MAX: 1 "Hahó" konverziója szélesről többájtos karakterlánccá: A konvertált többájtos karakterek bájtszáma: 4 A többájtos karakterlánc bájtjainak hex. értéke: 48 61 68 F3 00 A negyedik többájtos karakter bájt száma: 1 Konverzió vissza többájtosról széles karakterlánccá: A konvertált karakterek száma: 4 A széles karakterek hex. értéke: 0048 0061 0068 00F3 0000

Page 31: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 31 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 31 ►

3.5. Széles karakterek osztályozása A [2] jegyzet „Karaktervizsgáló függvények (makrók)” fejezete tárgyal-ja a karakterosztályozó függvényeket és a tolower, toupper konverziós rutinokat. E függvények mindegyike azt vizsgálja, hogy a paraméter – ami egybájtos, vagy széles karakter – kielégíti–e a feltételt. E rutinok rendsze-rint gyorsabb végrehajtásúak, mint a saját készítésű kód. Például az isalpha(c) gyorsabb a következő kódnál:

if(c>='A' && c<='Z' || c>='a' && c<='z') return TRUE; else return FALSE;

Az is–zel kezdődő nevű rutinok prototípusai a CTYPE.H fejfájlban helyezkednek el, és értelmes eredményt produkálnak bármely –1 (EOF) és 0xFF (UCHAR_MAX) közötti értékű, egész paraméterrel. Az elvárt pa-raméter típus int, s prototípusaik az isalpha példáján bemutatva a követ-kezők:

#include <ctype.h> int isalpha(int c);

Az is és a to függvényeknek átadott char típusú paraméter megjó-solhatatlan eredményre is vezethet. A char típusú, 0x7F–nél nagyobb ér-tékű SBCS vagy MBCS egybájtos karakter negatív. A paraméterként át-adott char értékét a fordító signed int–té vagy signed long–gá konver-tálja előbb előjel kiterjesztéssel, s ez okozhatja a problémát.

Az isw karakterrel kezdődő nevű függvények mindig is párjuk széles karakteres változatai, és működésük ettől eltekintve egyezik párjukéival. Prototípusaik a WCTYPE.H fejfájlban helyezkednek el. Az isw rutinok értelmes eredményt produkálnak bármely –1 (WEOF), vagy WCHAR_MIN (0) és WCHAR_MAX (0xFFFF) közötti értékű, egész paraméterrel.

#include <wctype.h> int iswalpha(wint_t c); Mindenegyes is függvény vizsgálatának eredménye függ az aktuális

hely LC_CTYPE kategória beállításától is. A “C” hely vizsgálati feltételeit tekintettük főleg []–ben az elkövetkező felsorolásban. Az isw rutinok vi-szont függetlenek a helytől.

3.5.1. is és isw függvények Rutin Karaktervizsgálati feltétel

Page 32: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 32 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 32 ►

• isalnum, iswalnum: Alfanumerikus–e [A – Z , a – z, 0 – 9, vagy más hely–specifikus alfanumerikus karakter]?

• isalpha, iswalpha: Alfabetikus? [A – Z, a – z, vagy más hely–specifikus alfabetikus karakter] Az is(w)alpha akkor igaz, ha az is(w)cntrl, is(w)digit, is(w)punct és is(w)space egyike sem igaz, de az is(w)upper vagy az is(w)lower valamelyike igaz.

• iscntrl, iswcntrl: Vezérlő karakter–e [BEL, BS, CR, FF, HT, LF, VT, vagy más implementáció–definiálta vezérlő karakter]? Az iswcntrl–nél a kérdés az, hogy c széles vezérlő karakter–e.

• isdigit, iswdigit: Decimális számjegy–e [0 – 9]? • isgraph, iswgraph: Szóköztől (széles esetben L' '–től) különböző

nyomtatható karakter-e? Pontosabban a függvények olyan karakterre szolgáltatnak nem zérust, melyre vagy az is(w)alnum, vagy az is(w)punct nem zérus.

• islower, iswlower: Kisbetű–e [a – z, vagy más hely–specifikus kisbetű karakter]?

• isprint, iswprint: Nyomtatható karakter–e? A karakter szóköz (L’ ’), vagy olyan karakter, melyre az is(w)graph és az is(w)space valamelyi-ke nem zérus.

• ispunct, iswpunct: Elválasztójel-e? Az is(w)punct esetében minden nyomtatható (széles) karakter a szóköz (L' ') és azok kivételével, me-lyekre az is(w)alnum igazat szolgáltat. Konkrétabban elválasztójelek a következők: ! " # % & ' ( ) ; < = > ? [ \ ] * + , - . / : ^ _ { | } ~ vagy más implementáció–definiálta elválasztójel karakterek.

• isspace, iswspace: Fehér karakter–e? Az is(w)space igazat szolgáltat a szabvány fehér karakterekre és más hely–specifikus fehér karakterek-re. A szabványos (széles) fehér karakterek: a szóköz (L' '), a lapdobás (L'\f'), a soremelés (L'\n'), a kocsi vissza (L'\r'), a horizontális (L'\t'), és a vertikális tab (L'\v').

• isupper, iswupper: Nagybetű–e [A – Z, vagy más hely–specifikus nagybetű karakter]?

• isxdigit, iswxdigit: Hexadecimális számjegy–e [A – F, a – f, vagy 0 – 9] a karakter?

A tolower, toupper konverziós rutinok széles karakteres párjai a #include <wctype.h> wint_t towlower(wint_t c);

Page 33: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 33 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 33 ►

wint_t towupper(wint_t c); A függvények visszaadják a kisbetűs, ill. a nagybetűs betűt, ha van ilyen

és az iswupper(c), ill. az iswlower(c) nem zérus megfelelően, máskülön-ben c–t szolgáltatják változatlanul vissza.

Az iswprint függvény az isprint–től eltérően nem zérus értéket szol-gáltathat a széles karakteres szóközön (L’ ’) kívül további fehér karakterek-re. E további fehér karakterekre nem zérust ad az iswspace, de zérust szolgáltat az iswprint, vagy az iswpunct.

Az implementáció további karaktereket definiálhat, melyekre e függ-vények némelyike nem zérust szolgáltat. Bármely karakterkészlet tartal-mazhat olyan további karaktereket, melyekre nem zérussal tér vissza az:

• iswpunct (feltéve, hogy e karakterek az iswalnum–ot zérus vissza-adására kényszerítik).

• iswcntrl (feltéve, hogy e karakterek az iswprint–et zérus visszaadására kényszerítik).

Azon kívül egy ”C”–től különböző hely további karaktereket definiálhat:

• az iswalpha–ra, az iswupper–re és az iswlower–re (feltéve, hogy e karakterek az iswcntrl–t, az iswdigit–et, az iswpunct–ot és az iswspace–t zérus visszaadására kényszerítik).

• az iswspace–re (feltéve, hogy e karakterek az iswpunct–ot zérus visz-szaadására kényszerítik).

Megjegyezzük még, hogy az implementáció definiálhat olyan ”C”–től különböző helyet is, ahol egy karakterre az iswalpha (és ennél fogva az iswalnum) nem zérust ad, de az iswupper és az iswlower zérust szolgáltat.

A LONGE.C példában a karaktereket osztályozó rutinok felhasználásával készítünk egy longe függvényt (és egy ezt kipróbáló programot), mely az első karakterlánc paraméterét decimális egész számnak tekinti, és formai-lag ellenőrzi. Ha helyesnek találja, egyet, máskülönben zérust ad vissza. Ha az egész szám legfeljebb HOSSZ jegyű, és belefér a long típus ábrázolási határaiba, akkor a függvény konvertálja is, és elhelyezi az értéket a máso-dik paraméter címen.

/* LONGE.C példa. */ #include <stdio.h> #include <ctype.h> #include <string.h>

Page 34: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 34 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 34 ►

#include <limits.h> #define HOSSZ 10 /* long számjegyeinek max. száma. */ int longe(char *s, long *pn){ /* A longe ellenőrzi az s egész szám karakterláncot. Ha hibás, zérust ad vissza. Ha hibátlan, 1-et, és az értéket konvertálja is a pn címre. */ int sign=1, ok=0, numdb; double d; /* Az elöl levő fehér karakterek átlépése: */ while(isspace(*s)) ++s; /* Az előjel feljegyzése: */ if(*s=='+'|| *s=='-') sign=(*s++=='+')?1:-1; /* A számrész konverziója: */ for(d=0.,numdb=HOSSZ;numdb>0&&isdigit(*s);--numdb,++s){ d=10.*d+*s-'0'; ok=1; } /* Szorozva az előjellel: */ d*=sign; /* Ha nincs egyetlen számjegy sem, vagy a szám nem fér az ábrázolási határokba, vagy nem lánczáró null és nem fehér karakter következik a karakterláncban: */ if(numdb==HOSSZ || d>LONG_MAX || d<LONG_MIN || *s!=0 && !isspace(*s)) ok=0; /* Ha a szám rendben, kikerül a 2. paraméter címre: */ if(ok) *pn=d; return(ok); } int main(void){ char s[2*HOSSZ]; long l; /* Programcím: */ printf("\n\nA longe() függvényt kipróbáló program:\n" "Befejezés: üres sorral, vagy EOF-al!\n\n"); /* Egész szám bekérése befejezésig: */ while(printf("Adjon meg egy egész számot: "), fgets(s, 2*HOSSZ, stdin)) if(longe(s, &l)) printf("A szám jó: %ld\n\n", l); else if(!strcmp(s,"\n")) break; else printf("A szám rossz!\n\n"); return 0; }

A kimenet a következő lehet: A longe() függvényt kipróbáló program: Befejezés: üres sorral, vagy EOF-al! Adjon meg egy egész számot: Halihó! A szám rossz!

Page 35: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 35 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 35 ►

Adjon meg egy egész számot: 12kőműves A szám rossz! Adjon meg egy egész számot: 4000000000 A szám rossz! Adjon meg egy egész számot: -345 A szám jó: -345 Adjon meg egy egész számot:

A WLONGE.C példában elkészítjük a longe függvény széles karakteres változatát wlonge néven:

/* WLONGE.C példa. */ #include <stdio.h> #include <stdlib.h> #include <wctype.h> #include <wchar.h> #include <limits.h> #include <string.h> #define HOSSZ 10 /* long számjegyeinek max. száma. */ int wlonge(wchar_t *s, long *pn){ /* A longe ellenőrzi az s egész szám karakterláncot. Ha hibás, zérust ad vissza. Ha hibátlan, 1-et, és az értéket konvertálja is a pn címre. */ int sign=1, ok=0, numdb; double d; /* Az elöl levő fehér karakterek átlépése: */ while(iswspace(*s)) ++s; /* Az előjel feljegyzése: */ if(*s==L'+'|| *s==L'-') sign=(*s++==L'+')?1:-1; /* A számrész konverziója: */ for(d=0.,numdb=HOSSZ; numdb>0&&iswdigit(*s); --numdb,++s){ d=10.*d+*s-'0'; ok=1; } /* Szorozva az előjellel: */ d*=sign; /* Ha nincs egyetlen számjegy sem, vagy a szám nem fér az ábrázolási határokba, vagy nem lánczáró null és nem fehér karakter következik a karakterláncban: */ if(numdb==HOSSZ || d>LONG_MAX || d<LONG_MIN || *s!=0 && !iswspace(*s)) ok=0; /* Ha a szám rendben, kikerül a 2. paraméter címre: */ if(ok) *pn=d;

Page 36: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 36 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 36 ►

return(ok); } int main(void){ wchar_t s[2*HOSSZ]; char sc[2*HOSSZ]; size_t i; long l; /* Programcím: */ printf("\n\nA wlonge() függvényt kipróbáló program:\n" "Befejezés: üres sorral, vagy EOF-al!\n\n"); /* Egész szám bekérése befejezésig: */ while(printf("Adjon meg egy egész számot: "), fgets(sc, 2*HOSSZ, stdin)){ i=mbstowcs(s, sc, 2*HOSSZ); if(wlonge(s, &l)) printf("A szám jó: %ld\n\n", l); else if(!wcscmp(s, L"\n")) break; else printf("A szám rossz!\n\n"); } return 0; }

A TOUPP.C példában nagybetűssé alakítunk egy minden ékezetes kisbe-tűt tartalmazó szöveget az alapértelmezett ”C”, majd átállítás után az aktu-ális helyen:

/* TOUPP.C példa. */ #include <ctype.h> #include <stdio.h> #include <locale.h> /*#include <windows.h>*/ void main(void){ char string[]="A kürtös és ő is megízlelte a várakozás" " hosszú, óráinak örömét.\nMeglátjuk, mire jutunk a " "szöveg nagybetűs átalakításával.\n\n"; char *ptr = string-sizeof(char); /*SetConsoleOutputCP(1250);*/ printf("Ez lesz belőle a \"C\" helyen " "átalakítva:\n\n"); while(*++ptr) putchar(toupper(*ptr)); setlocale(LC_ALL, ""); ptr = string-sizeof(char); printf("Ugyanez az aktuális helyen (\"\") " "átalakítva:\n\n"); while(*++ptr) putchar(toupper(*ptr)); }

Az eredmény Ez lesz belőle a "C" helyen átalakítva: A KüRTöS éS ő IS MEGíZLELTE A VáRAKOZáS HOSSZú, óRáINAK öRöMéT.

Page 37: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 37 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 37 ►

MEGLáTJUK, MIRE JUTUNK A SZöVEG NAGYBETűS áTALAKíTáSáVAL. Ugyanez az aktuális helyen ("") átalakítva: A KüRTöS éS ő IS MEGÍZLELTE A VÁRAKOZÁS HOSSZú, óRÁINAK öRöMéT. MEGLÁTJUK, MIRE JUTUNK A SZöVEG NAGYBETŰS ÁTALAKÍTÁSÁVAL. jó indulattal is legfeljebb fél sikernek nevezhető. Mi a probléma vajon? Emlékezzünk csak vissza a fejezet eleji bombás bekezdésben mondottak-ra, azaz:

Az is és a to függvényeknek átadott char típusú paraméter megjó-solhatatlan eredményre is vezethet. A char típusú, 0x7F–nél nagyobb ér-tékű SBCS vagy MBCS egybájtos karakter negatív. A paraméterként át-adott char értékét a fordító signed int–té vagy signed long–gá konver-tálja előbb előjel kiterjesztéssel, s ez okozhatja a problémát.

Írjuk át, tehát a program deklarációs részében levő char típust unsigned char–ra, és szemléljük meg újra az eredményt!

Ez lesz belőle a "C" helyen átalakítva: A KüRTöS éS ő IS MEGíZLELTE A VáRAKOZáS HOSSZú, óRáINAK öRöMéT. MEGLáTJUK, MIRE JUTUNK A SZöVEG NAGYBETűS áTALAKíTáSáVAL. Ugyanez az aktuális helyen ("") átalakítva: A KÜRTÖS ÉS Ő IS MEGÍZLELTE A VÁRAKOZÁS HOSSZÚ, ÓRÁINAK ÖRÖMÉT. MEGLÁTJUK, MIRE JUTUNK A SZÖVEG NAGYBETŰS ÁTALAKÍTÁSÁVAL.

3.6. Széles karakterláncok és memóriaterületek kezelése

A [2] jegyzet „Karakterlánc kezelő függvények” fejezete tárgyalja az str névkezdetű karakterláncokat kezelő, és a mem névkezdetű memóriaterü-letekkel foglalkozó rutinokat. A karakterlánc kezelő függvények mindegyi-ke null–lezárású egybájtos, széles és többájtos karakterláncokat manipulál. A nem null–lezárású karaktertömbökkel a memóriaterületekkel foglalkozó rutinok dolgoznak.

Az str–rel és mem–mel kezdődő nevű rutinok prototípusai a STRING.H fejfájlban helyezkednek el. A wcs karakterekkel kezdődő nevű függvények mindig str párjuk széles karakteres változatai, és műkö-

Page 38: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 38 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 38 ►

désük ettől eltekintve egyezik párjukéval. Ugyanez igaz mem és wmem párosításban a memóriaterületeket manipuláló rutinokra is. A széles karak-teres wcs és wmem kezdetű függvények prototípusai a WCHAR.H fej-fájlban találhatók.

Ha a forrás és a célterület átfedi egymást, a teljes forrást garantál-tan csak a (w)memmove másolja át megfelelően.

3.6.1. strcat, wcscat, strncat, wcsncat char *strcat(char *cel, const char *forras); wchar_t *wcscat(wchar_t *cel, const wchar_t *forras); char *strncat(char * cel, const char *forras, size_t

n); wchar_t *wcsncat(wchar_t * cel, const wchar_t *forras,

size_t n);

A függvények a cel karakterlánchoz fűzik a forras–t, és visszatérnek az egye-sített cel karakterlánc címével. Nincs hibát jelző visszaadott érték! Nincs túlcsordulási vizsgálat a karakterláncok másolásakor és hozzáfűzésekor. A rutinok viselkedése megjósolhatatlan, ha a forras és a cel átfedik egymást.

Az strncat és a wcsncat a forras legfeljebb első n karakterét fűzik a cel–hoz. Ha a forras rövidebb n–nél, akkor csak a forras hozzáfűzése történik meg.

3.6.2. strchr, wcschr, memchr, wmemchr char *strchr(const char *string, int c); wchar_t *wcschr(const wchar_t *string, wint_t c); void *memchr(const void * string, int c, size_t n); wchar_t *wmemchr(const wchar_t * string, wchar_t c,

size_t n);

A függvények c karakter string–beli első előfordulásának címével térnek vissza, ill. NULL mutatóval, ha nincs is c karakter a string karakterláncban, memóriaterületen. A lánczáró null karakter is lehet c paraméter.

A mem függvények a string puffer legfeljebb első n elemét nézik át.

Lásd az STRRCHR.C példaprogramot az strrchr–nél!

3.6.3. strcmp, wcscmp, strncmp, wcsncmp, memcmp, wmemcmp

int strcmp(const char *string1, const char *string2); int wcscmp(const wchar_t *string1, const wchar_t

*string2);

Page 39: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 39 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 39 ►

int strncmp(const char *string1, const char *string2, size_t n);

int wcsncmp(const wchar_t *string1, const wchar_t *string2, size_t n);

int memcmp(const void *string1, const void *string2, size_t n);

int wmemcmp(const wchar_t *string1, const wchar_t *string2, size_t n);

A függvények lexikografikusan összehasonlítják string1 és string2 karakter-láncokat, és negatív értéket szolgáltatnak, ha string1 < string2. Pozitív érték jön, ha string1 > string2. Az egyenlőséget a visszaadott zérus jelzi.

Az strcmp abban különbözik az strcoll függvénytől, hogy az összeha-sonlítást nem befolyásolja a helyi információ, míg a másik függvény össze-hasonlítási módszerét az aktuális hely LC_COLLATE kategóriája hatá-rozza meg.

A “C” helyen a karakterkészlet (ASCII) karaktereinek sorrendje ugyan-az, mint a lexikografikus karaktersorrend. Más helyek karaktersorrendje azonban eltérhet ettől. Például bizonyos európai helyeken az 'a' (0x61) karakter megelőzi az 'ä'–t (0xE4) a karakterkészletben, holott lexikografikusan az 'ä' előzi meg az 'a' karaktert.

Azokon a helyeken, ahol a karakterkészlet és a lexikografikus karakter-sorrend eltér, az aktuális hely LC_COLLATE kategória-beállítása szerinti lexikografikus karakterlánc–összehasonlításhoz az strcoll használandó inkább az strcmp helyett.

Az strncmp és a wcsncmp függvények a lexikografikus hasonlítást legföljebb az első n karakterig végzik. Az összehasonlítás befejeződik ak-kor is, ha a záró null karakter mindkét karakterláncban n karakternél előbb következik be. A két karakterlánc ilyenkor egyezik. Ha a záró null karakter egyik karakterláncban előbb következik, mint a másikban, akkor a rövi-debb karakterlánc a kisebb.

A mem függvények is összehasonlítják a string1 és string2 pufferek leg-feljebb első n elemét. Az strcmp–nél megszokott módon negatív értéket szolgáltatnak, ha string1 < string2. Pozitív érték jön, ha string1 > string2. Az egyenlőséget a visszakapott zérus jelzi.

Lásd az STRXFRM.C példaprogramot az strxfrm leírásánál!

3.6.4. strcoll, wcscoll int strcoll(const char *string1, const char *string2);

Page 40: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 40 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 40 ►

int wcscoll(const wchar_t *string1, const wchar_t *string2);

Az strcoll és a wcscoll függvény két karakterláncot hasonlít össze az ak-tuális hely szabályai szerint, azaz az LC_COLLATE kategória–beállítása szerint. A coll rutinokat akkor kell használni karakterláncok összehasonlí-tására, ha különbség van a karakterkészlet sorrendje és az aktuális hely lexikografikus karaktersorrendje közt, s ez az eltérés érdekes az összeha-sonlítás szempontjából is. A megfelelő cmp függvény csak a karakterlán-cok egyezőségének vizsgálatára való.

Bizonyos kódlapokra és a megfelelő karakterkészletekre a karak-terkészletbeli karaktersorrend eltérhet a lexikografikustól. A “C” helyen nem ez a helyzet: az ASCII karakterkészletben a karakterek sorrendje egyezik a lexikografikus karaktersorrenddel. Bizonyos európai kódlapok-ban azonban például az 'a' (0x61) karakter megelőzi az 'ä'–t (0xE4) a ka-rakterkészletben, holott lexikografikusan az 'ä' előzi meg az 'a' karaktert. Ilyen esetekben lexikografikus karakterlánc–összehasonlításhoz inkább az strcoll használata javasolható az strcmp–vel szemben. Alternatív mód-szerként ajánlható az eredeti karakterláncok strxfrm–mel konvertált válto-zatainak összehasonlítása strcmp–vel.

A coll függvények lexikografikusan vetik egybe a karakterláncokat összehasonlításkor, míg a cmp rutinok egyszerűen karakterlánc–egyezőségre tesztelnek, ezért a coll függvények sokkal lassabbak megfelelő cmp társaiknál.

A rutinok egyébként a string1 és a string2 karakterláncokat vetik egybe, és negatív értéket szolgáltatnak, ha string1 < string2. Pozitív érték jön, ha string1 > string2. Az egyenlőséget viszont a visszaadott zérus jelzi.

Lásd az STRXFRM.C példaprogramot az strxfrm leírásánál!

3.6.5. strcpy, wcscpy, strncpy, wcsncpy, memcpy, wmemcpy, memmove, wmemmove

char *strcpy(char *cel, const char *forras); wchar_t *wcscpy(wchar_t *cel, const wchar_t *forras); char *strncpy(char *cel, const char *forras, size_t

n); wchar_t *wcsncpy(wchar_t *cel, const wchar_t *forras,

size_t n); void *memcpy(void *cel, const void *forras, size_t n);

Page 41: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 41 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 41 ►

wchar_t *wmemcpy(wchar_t *cel, const wchar_t *forras, size_t n);

void *memmove(void *cel, const void * forras, size_t n);

wchar_t *wmemmove(wchar_t *cel, const wchar_t *forras, size_t n);

Az strcpy és a wcscpy függvények a forras karakterláncot másolják a lezá-ró null karakterével együtt a cel karaktertömbbe, és visszatérnek a cel cím-mel. Nincs hibát jelző visszatérési érték. Nincs túlcsordulás ellenőrzés a karakterláncok másolásánál. A függvények viselkedése előre nem jósolható meg, ha a cel és a forras átfedik egymást.

Az strncpy és a wcsncpy rutinok a forras legfeljebb első n karakterét másolják. Ha az n nem nagyobb, mint a forras mérete, a null karakter nem kerül automatikusan a másolt karakterlánc végére. Ha az n nagyobb a forras hosszánál, a cel karakterlánc null karakterrel párnázott az n eléréséig. A függvények egyebekben úgy viselkednek, mint az strcpy és wcscpy társaik.

A (w)memcpy és a (w)memmove rutinok átmásolják a forras n elemét a cel–ba, és visszaadják a cel–t. Ha a forrás és a cél átlapolja egymást, akkor a (w)memcpy esetén nincs semmilyen biztosíték sem az átlapoló régióbeli eredeti forras elemek felülírás előtti átmásolására. Átfedéses területek hibát-lan másolására a (w)memmove használatos, mert biztosítja az átlapoló régióbeli eredeti forras elemek felülírás előtti átmásolását!

3.6.6. strcspn, wcscspn size_t strcspn(const char *string, const char

*strCharSet); size_t wcscspn(const wchar_t *string, const wchar_t

*strCharSet);

A függvények egész értékként visszaadják a string azon kezdőszegmen-sének méretét, melyben az strCharSet–beli karakterek egyike sem fordul elő. Tehát, ha a string olyan karakterrel indul, mely benne van az strCharSet–ben, akkor zérust kapunk. Nincs hibát jelző visszatérési érték. Más megfogalmazással: A rutinok az strCharSet–beli karakterek valame-lyikét keresik a string–ben, és visszaadják az első előfordulás indexét. A keresésbe beleértendők a lánczáró null karakterek is! Az /* STRCSPN.C példa. */ #include <string.h> #include <stdio.h> void main(void){ char string[] = "xyzabc";

Page 42: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 42 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 42 ►

char strCharSet[] = "abc", *ptr = strCharSet-1; printf("\"%s\" karakterláncban az első ", string); while(*++ptr) printf("%c%s", *ptr, ptr[1]?(ptr[2]?", ": ", vagy "):""); printf(" indexe %d.\n", strcspn(string, strCharSet)); }

futásának eredménye: ”xyzabc” karakterláncban az első a, b, vagy c indexe 3.

3.6.7. strlen, wcslen size_t strlen(const char *string); size_t wcslen(const wchar_t *string);

A függvények a string karakterlánc karaktereinek számával térnek vissza a lezáró null karaktert be nem számítva. Nincs hibát jelző visszaadott értékük!

3.6.8. strpbrk, wcspbrk char *strpbrk(const char *string, const char

*strCharSet); wchar_t *wcspbrk(const wchar_t *string, const wchar_t

*strCharSet);

A függvények az strCharSet–beli karakterek valamelyikét keresik a string–ben, és visszaadják az első előfordulás címét, ill. NULL mutatót, ha a két paraméternek közös karaktere sincs. A keresésbe nem értendők bele a lánczáró null karakterek.

A függvények hasonlítanak az strcspn és wcscspn rutincsaládra, de mutatót szolgáltatnak a size_t típusú visszatérési érték helyett.

Az STRPBRK.C példában számjegy karaktereket keresünk egy szöveg-ben. A megtalálási helyektől kezdve visszaírjuk a szöveg részeket besor-számozva.

/* STRPBRK.C példa: */ #include <string.h> #include <stdio.h> void main(void){ char string[]="3 hapsi, s 2 fiu 4 nyulat ettek meg.\n"; char *eredm = string; int i=1; /* Számok keresése: */ while(eredm = strpbrk(eredm, "0123456789")) printf("%2d: %s\n", i++, eredm++); }

Az eredmény: 1: 3 hapsi, s 2 fiu 4 nyulat ettek meg.

Page 43: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 43 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 43 ►

2: 2 fiu 4 nyulat ettek meg. 3: 4 nyulat ettek meg.

3.6.9. strrchr, wcsrchr char *strrchr(const char *string, int c); wchar_t *wcsrchr(const wchar_t *string, wchar_t c);

A függvények c karakter string–beli utolsó előfordulásának címével térnek vissza, ill. NULL mutatóval, ha nincs is c karakter a string karakterláncban. A lánczáró null karakter is lehet c paraméter.

Az STRRCHR.C példaprogram megállapítja 'r' karakter első és utolsó előfordulásának indexét a string karakterláncban.

/* STRRCHR.C: Karakter keresése karakterláncban előre és vissza. */ #include <string.h> #include <stdio.h> void main(void){ int c = 'r', i; char string[]="A gyors kutya átugorja a lusta rókát.", *pcel; printf("%c karakter keresése a következő " "karakterláncban:\n%s\n ", c, string); for(i=1; i<5; ++i)printf("%10d", i); printf("\n"); for(i=0; i<50; ++i)printf("%1d", i%10); /* Keresés előre: */ if(pcel = strchr(string, c)) printf("\nEredmény:\tAz első %c a(z) %d indexű " "helyen található.\n", c, pcel - string); else printf("\nEredmény:\tNincs %c a " "karakterláncban.\n", c); /* Keresés hátra: */ if(pcel = strrchr(string, c)) printf("Eredmény:\tAz utolsó %c a(z) %d. indexű " "helyen található.\n\n", c, pcel - string); else printf("Eredmény:\tNincs %c a karakterláncban.\n", c); }

Az eredmény: r karakter keresése a következő karakterláncban: A gyors kutya átugorja a lusta rókát. 1 2 3 4 01234567890123456789012345678901234567890123456789

Page 44: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 44 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 44 ►

Eredmény: Az első r a(z) 5 indexű helyen található. Eredmény: Az utolsó r a(z) 31. indexű helyen található.

3.6.10. memset, wmemset void *memset(void *string, int c, size_t n); wchar_t *wmemset(wchar_t *string, wchar_t c, size_t

n);

A függvény átírja string első n elemét c karakterre, és visszaadja a string címet.

3.6.11. strspn, wcsspn size_t strspn(const char *string, const char

*strCharSet); size_t wcsspn(const wchar_t *string,const wchar_t

*strCharSet);

Az strspn és a wcsspn függvények annak a string elején levő, maximális alkarakterláncnak a méretét szolgáltatják, mely teljes egészében csak az strCharSet–beli karakterekből áll. Ha a string nem strCharSet–beli karakterrel kezdődik, akkor zérust kapunk. Nincs hibát jelző visszatérési érték.

A függvények visszaadják az első olyan karakter indexét a string–ben, mely nincs benn az strCharSet karakterlánccal definiált karakterkészletben. A keresésbe nem értendők bele a lánczáró null karakterek!

3.6.12. strstr, wcsstr char *strstr(const char *string1, const char

*string2); wchar_t *wcsstr(const wchar_t *string1, const wchar_t

*string2);

A függvények string2 karakterlánc első előfordulásának címét szolgáltatják string1–ben, ill. NULL mutatót kapunk, ha string2 nincs meg string1–ben. Ha string2 üres karakterlánc, akkor a rutinok string1–gyel térnek vissza. A keresésbe nem értendők bele a lánczáró null karakterek.

Az STRSTR.C példaprogram string2 karakterláncot keresi string1–ben.

/* STRSTR.C: Karakterlánc keresése másik karakterláncban. */ #include <string.h> #include <stdio.h> void main(void){ int i; char string1[] = "A gyors kutya hamar elkapja a lusta " "rókát.",

Page 45: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 45 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 45 ►

string2[] = "lusta", *p; printf("A(z) \"%s\" karakterlánc keresése a következő " "karakterláncban:\n%s\n ", string2, string1); for(i=1; i<5; ++i)printf("%10d", i); printf("\n"); for(i=0; i<50; ++i)printf("%1d", i%10); if(p = strstr(string1, string2)) printf("\nA(z) \"%s\" megtalálható a(z) %d indexű " "helytől kezdve.\n\n", string2, p - string1); else printf("\nA(z) \"%s\" nincs a karakterláncban.\n", string2); }

Az eredmény: A(z) "lusta" karakterlánc keresése a következő karakterláncban: A gyors kutya hamar elkapja a lusta rókát. 1 2 3 4 01234567890123456789012345678901234567890123456789 A(z) "lusta" megtalálható a(z) 30 indexű helytől kezdve.

3.6.13. strtok, wcstok char *strtok(char *strToken, const char *strDelimit); wchar_t *wcstok(wchar_t *strToken, const wchar_t

*strDelimit, wchar_t **ptr);

Az strtok a következőleg megtalált, strToken–beli szimbólum (token) cí-mével tér vissza, ill. NULL mutatóval, ha nincs már további szimbólum az strToken karakterláncban. Mindenegyes hívás módosítja az strToken ka-rakterláncot, úgy hogy lánczáró null karaktert tesz a bekövetkezett elvá-lasztójel (delimiter) helyére. Az strDelimit karakterlánc az strToken–beli szimbólumok lehetséges elválasztó karaktereit tartalmazza.

Az első strtok hívás átlépi a vezető elválasztójeleket, visszatér az strToken–beli első szimbólum címével, és ezelőtt a szimbólumot null ka-rakterrel zárja. Az strToken maradék része további szimbólumokra bontha-tó újabb strtok hívásokkal. Mindenegyes strtok hívás módosítja az strToken karakterláncot, úgy hogy null karaktert tesz az aktuálisan vissza-adott szimbólum végére. Az strToken következő szimbólumát az strToken paraméter helyén NULL mutatós strtok hívással lehet elérni. A NULL mutató első paraméter hatására az strtok megkeresi a következő szimbó-lumot a módosított strToken–ben. A lehetséges elválasztójeleket tartalmazó strDelimit paraméter, s így maguk az elválasztó karakterek is, változhatnak hívásról–hívásra.

Az strtok statikus mutatót használ a karakterlánc szimbólumokra bontásához. Ne kíséreljük meg ennél fogva ugyanazt a függvényt szimul-

Page 46: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 46 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 46 ►

tán különböző karakterláncokra hívni! Tartózkodjunk attól is, hogy a ru-tint olyan ciklusból hívjuk, ahol más olyan függvény hívása is bekövetkez-het, mely ugyanezt a rutint idézi meg!

Ha a wcstok hívásban az strToken nem NULL mutató, akkor a függ-vény megkezdi a széles karakterlánc vizsgálatát. Különben annak a „mara-dék” karakterláncnak az elemzésébe fog, melynek címét egy korábbi hívá-sakor a *ptr–en legutóbb letárolta.

Vegyük észre, hogy a wcstok nem statikus területet használ a „Hol is tartok?” cím tárolására, hanem a harmadik (ptr) paraméter mutatta mu-tatót.

A wcstok a széles karakterláncban először a kezdetet kutatja, azaz az első olyan elem címét keresi, mely nem egyezik az strDelimit széles karak-terlánc (a lehetséges elválasztójeleket tartalmazza) egyik elemével sem. A lánczáró null karaktert az elemzett széles karakterlánc részének tekinti.

Ha a vizsgálat nem talál elemet, a rutin a lánczáró null széles karakter címét tárolja a *ptr–be (azért, hogy a rákövetkező, e címmel induló keresés kudarcba fulladjon), és NULL mutatót ad vissza. Máskülönben a függ-vény a címtől indulva végre keres, azaz az strDelimit széles karakterlánc elemeivel egyező, első elem címét kívánja megállapítani. A lánczáró null karaktert ismét az elemzett széles karakterlánc részének tekinti. Ez után széles null karaktert helyez a vég címre, elteszi a vég utáni elem címét a *ptr–be, hogy a következő szimbólum keresése ettől a címtől kezdődjék a még hátralevő karakterláncban, és visszaadja a kezdet címét.

Az STRTOK.C programban ciklusban hívjuk az strtok függvényt, hogy megjelentethessük a string karakterlánc összes szóközzel, vesszővel, stb. elválasztott szimbólumát:

/* STRTOK.C: */ #include <string.h> #include <stdio.h> void main( void ){ char string[] = "\t Szimbólumok\tkarakterlánca,\n ,, " "és még néhány további szimbike."; char elv[] = " ,.\t\n", *szimb; printf("%s\n\nA fenti karakterláncban a szimbólumok a " "következők:\n", string); /* Míg vannak szimbólumok a karakterláncban, */ szimb = strtok(string, elv); while(szimb){ /* addig megjelentetjük őket, és */

Page 47: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 47 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 47 ►

printf("\t%s\n", szimb); /* vesszük a következőt. */ szimb = strtok(NULL, elv); } }

A kimenet a következő: Szimbólumok karakterlánca, ,, és még néhány további szimbike. A fenti karakterláncban a szimbólumok a következők: Szimbólumok karakterlánca és még néhány további szimbike

3.6.14. strxfrm, wcsxfrm size_t strxfrm(char *cel, const char *forras, size_t

n); size_t wcsxfrm(wchar_t *cel, const wchar_t *forras,

size_t n);

A függvények átalakítják a hely–specifikus forras karakterláncot, és a kon-vertált alakot elhelyezik a cel karakterláncban. A lánczáró null karaktert is beleértve azonban legfeljebb n karaktert alakítanak át az aktuális hely LC_COLLATE kategória–beállításának megfelelően. A rutinok az át-konvertált karakterlánc hosszával – a lezáró null karaktert nem tekintve – térnek vissza. Ha a visszakapott érték nem kisebb a n–nél, akkor a cel tar-talma megjósolhatatlan. Hiba esetén a függvények beállítják errno–t, és (size_t) – 1–t adnak vissza.

Az átalakítás végrehajtása után az strcmp–t (wcscmp–t) a két konver-tált karakterláncra meghívva ugyanazt az eredményt kapjuk, mint az erede-ti karakterláncokra az strcoll–t (wcscoll–t) alkalmazva.

A “C” helyen a karakterkészlet (ASCII) karaktereinek sorrendje ugyan-az, mint a lexikografikus karaktersorrend. Más helyek karaktersorrendje azonban eltérhet ettől. Azokon a helyeken, ahol a karakterkészlet és a lexikografikus karaktersorrend eltér, az aktuális hely LC_COLLATE ka-tegória-beállítása szerinti lexikografikus karakterlánc–összehasonlításhoz az strcoll használandó inkább az strcmp helyett. Alternatív módszerként ajánlható az eredeti karakterláncok strxfrm–mel konvertált változatainak összehasonlítása strcmp–vel.

Page 48: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 48 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 48 ►

A következő kifejezés az strxfrm–mel átalakított forras karakterlánc tá-rolásához szükséges tömb méretét szolgáltatja:

1 + strxfrm(NULL, string, 0)

Csak a “C” helyen az strxfrm a következővel ekvivalens: strncpy(_string1, _string2, _n); return(strlen(_string1));

Az STRXFRM.C példaprogram szemlélteti az strcmp, az strxfrm és az strcoll használatát.

/* STRXFRM.C: Karakterláncok rendezése. */ #include <string.h> #include <stdio.h> #include <locale.h> void main(void){ int i; char egy[10] = "bárka", ket[10] = "birka", kegy[10], kket[10]; printf("Karakterláncok vizsgálata a C helyen:\n"); if((i=strcmp(egy, ket))<0) printf("\"%s\" kisebb, mint \"%s\"\n", egy, ket); else if(i>0) printf("\"%s\" nagyobb, mint \"%s\"\n", egy, ket); else printf("\"%s\" ugyanaz, mint \"%s\"\n", egy, ket); printf("Karakterláncok vizsgálata a \"\" helyen " "strxfrm konverzióval:\n"); setlocale(LC_COLLATE, ""); strxfrm(kegy, egy, 10); strxfrm(kket, ket, 10); if((i=strcmp(kegy, kket))<0) printf("\"%s\" kisebb, mint \"%s\"\n", egy, ket); else if(i>0) printf("\"%s\" nagyobb, mint \"%s\"\n", egy, ket); else printf("\"%s\" ugyanaz, mint \"%s\"\n", egy, ket); printf("Karakterláncok vizsgálata a \"\" helyen " "strcoll-lal:\n"); if((i=strcoll(egy, ket))<0) printf("\"%s\" kisebb, mint \"%s\"\n", egy, ket); else if(i>0) printf("\"%s\" nagyobb, mint \"%s\"\n", egy, ket); else printf("\"%s\" ugyanaz, mint \"%s\"\n", egy, ket); }

Az eredmény a következő: Karakterláncok vizsgálata a C helyen: "bárka" nagyobb, mint "birka"

Page 49: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 49 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 49 ►

Karakterláncok vizsgálata a "" helyen strxfrm konverzióval: "bárka" kisebb, mint "birka" Karakterláncok vizsgálata a "" helyen strcoll-lal: "bárka" kisebb, mint "birka"

3.7. Adatkonverzió A [2] jegyzet az adatkonverzióra saját függvényeket ír, ill. a szabványos atof, atoi, atol stb. rutinokat használja.

Az említett és a későbbiekben tárgyalt rutinok konverziót végeznek, s valószínűleg gyorsabban, mint egy saját készítésű függvény. A függvények prototípusai az STDLIB.H, ill. a széles karaktereseké a WCHAR.H fej-fájlban találhatók.

Összefoglaló példa a fejezet végén található!

3.7.1. HUGE_VAL makró A végtelen jelzésére a MATH.H fejfájlban definiált szimbolikus állandó.

3.7.2. strtod, wcstod double strtod(const char *s, char **vegptr); double wcstod(const wchar_t *s , wchar_t **vegptr);

A rutinok az s karakterláncot konvertálják double értékké, és a *vegptr–n szolgáltatják a láncban már nem konvertált első karakter címét is, ha a vegptr nem NULL. Túlcsordulás esetén +/–HUGE_VAL–t kapunk a függvényektől. Az előjel a már nem reprezentálható lebegőpontos érték előjele. Ha nincs konverzió, vagy alulcsordulás van, zérust kapunk. Túl vagy alulcsordulás bekövetkeztekor az errno ERANGE.

A rutinok leállnak az s karakterlánc olvasásával (elemzésével) az első olyan karakternél, mely nem fogadható el a konvertálandó valós szám–karakterlánc részeként. Ez persze lehet a lánczáró null karakter is helyes esetben.

A valós (lebegőpontos) szám–karakterlánc általános alakja a kö-vetkező:

<fehérkarakter><előjel><számjegyek><.számjegyek><{d| D|e|E}<előjel> számjegyek> A fehérkarakter szóköz vagy tabulátor karakterekből áll, melyeket elhagy

a konverziós függvény. Az előjel plusz (+) vagy mínusz (–). A számjegyek egy vagy több decimális számjegy. Ha a decimális pont előtt nincs szám-jegy, akkor utána legalább egynek lennie kell, ill. megfordítva. A decimális

Page 50: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 50 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 50 ►

számjegyeket exponens rész követheti, mely egy bevezető d, D, e, vagy E karakterből és egy opcionális előjeles decimális egészből áll.

Az aktuális hely LC_NUMERIC kategória–beállítása befolyásolja a szám–karakterlánc alakját. Például az is lehet, hogy az adott helyen nem decimális pont, hanem tizedes vessző van, stb.

3.7.3. strtol, wcstol, strtoul, wcstoul long strtol(const char *s, char **vegptr, int alap); long wcstol(const wchar_t *s, wchar_t **vegptr, int

alap); unsigned long strtoul(const char *s, char **vegptr,

int alap); unsigned long wcstoul(const wchar_t *s, wchar_t

**vegptr, int alap);

A függvények az s karakterláncot konvertálják long, ill. unsigned long típusú egésszé, és szolgáltatják a láncban már nem konvertált első karakter címét is. Túlcsordulás estén LONG_MAX, vagy LONG_MIN jön az strtol, wcstol párostól, ill. ULONG_MAX az strtoul, wcstoul csoport-tól. Zérust akkor is kapunk, ha konverzió sem történik. Túl vagy alulcsor-dulás bekövetkeztekor az errno ERANGE.

A rutinok leállítják az s karakterlánc olvasását (elemzését) annál a ka-rakternél, mely nem fogadható el a konvertálandó egész szám–karakterlánc részeként. Ez persze lehet a lánczáró null karakter is helyes esetben, vagy bármely az alapnál nem kisebb számjegy.

Ha a vegptr nem NULL, akkor arra a karakterre mutat az s karakter-láncban, mely leállította az elemzést. Ha nem hajtható végre konverzió (nincsenek érvényes számjegyek, vagy érvénytelen alapot adtak meg), a vegptr s lesz.

Az egész szám–karakterlánc általános alakja a következő: <fehérkarakter> <előjel> <0 <{ x | X }>> számjegyek A fehérkarakter szóköz vagy tabulátor karakterekből áll, melyeket el-

hagynak a konverziós függvények. Az előjel plusz (+) vagy mínusz ( – ). A számjegyek egy vagy több decimális számjegy. Az első, e formának meg nem felelő karakter leállítja az s karakterlánc olvasását (elemzését). Ha az alap 2 és 36 közötti értékű, akkor a számrendszer alapjaként használják a függvények. Ha zérus az alap, akkor az s karakterlánc kezdő karakterei határozzák meg a számrendszer alapját. Ha az első karakter ’0’ és a máso-dik nem 'x' és nem 'X', akkor a rutinok az s karakterláncot oktális egésznek tekintik. Ha az első karakter ’0’ és a második 'x' vagy 'X', akkor az s karak-

Page 51: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 51 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 51 ►

terlánc hexadecimális egész. Ha az első karakter '1' és '9' közötti, akkor az egész decimális. Az 'a'–tól 'z' (ill. az 'A'–tól 'Z') betűket hozzárendelték 10–től 35–ig a számokhoz, de a rutinok csak azokat a betűket fogadják el érvényesnek, melyek hozzárendelt számértéke kisebb az alapnál.

Az strtoul is megengedi a szám–karakterlánc elején az előjelet. A mí-nusz előjel azt jelzi, hogy a visszatérési érték negált.

Az STRTOUDL.C példában az strtod–dal double értékké konvertálta-tunk egy karakterláncot. Az strtol long int–té alakít egy másik karakter-láncot. Az strtoul unsigned long int–té konvertálja ugyanazon abszolút értékű pozitív és negatív karakterláncot 2, 4 és 8–as számrendszerben.

/* STRTOUDL.C példa. */ #include <stdlib.h> #include <stdio.h> void main(void){ char *string, *leallitas; double x; long l; int alap; unsigned long ul; string = "3.1415926Ez leállítja."; x = strtod(string, &leallitas); printf("string = %s\n", string); printf(" strtod = %.7f\n", x); printf("A vizsgálat leállt: %s\n\n", leallitas); string = "-1101012493Ez is leállítja."; l = strtol(string, &leallitas, 10); printf("string = %s\n", string); printf(" strtol = %10ld (alap %d)\n", l, 10); printf("A vizsgálat leállt: %s\n\n", leallitas); printf("string = %s\n", string); /* string konverziója 2, 4 és 8 alappal: */ for(alap=2; alap<10; alap<<=1){ ul = strtoul( string, &leallitas, alap); printf(" strtoul= %10ld (alap %d)\n", ul, alap); printf("A vizsgálat leállt: %s\n", leallitas); } string = "1101012493"; printf("\nstring = %s\n", string); /* string konverziója 2, 4 és 8 alappal: */ for(alap=2; alap<10; alap<<=1){ ul = strtoul( string, &leallitas, alap); printf(" strtoul= %10ld (alap %d)\n", ul, alap); printf("A vizsgálat leállt: %s\n", leallitas); } }

A kimenet a következő:

Page 52: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 52 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 52 ►

string = 3.1415926Ez leállítja. strtod = 3.1415926 A vizsgálat leállt: Ez leállítja. string = -1101012493Ez is leállítja. strtol = -1101012493 (alap 10) A vizsgálat leállt: Ez is leállítja. string = -1101012493Ez is leállítja. strtoul= -53 (alap 2) A vizsgálat leállt: 2493Ez is leállítja. strtoul= -5190 (alap 4) A vizsgálat leállt: 493Ez is leállítja. strtoul= -2363476 (alap 8) A vizsgálat leállt: 93Ez is leállítja. string = 1101012493 strtoul= 53 (alap 2) A vizsgálat leállt: 2493 strtoul= 5190 (alap 4) A vizsgálat leállt: 493 strtoul= 2363476 (alap 8) A vizsgálat leállt: 93

3.8. Széles (Unicode) folyam (stream) B/K szöveges és bináris módban

Emlékezzünk a [2] jegyzet „MAGAS SZINTŰ BEMENET, KIME-NET” című nagy fejezetében tárgyaltakra!

Az ott említett „Bemeneti műveletek”–nek megvan a széles karakte-res párja, melyek a folyam következő széles karaktereit olvassák formázat-lanul, ill. formázottan. A fájlvéget, vagy hibát a WEOF jelzi. Az unget(w)c visszateszi a c karaktert a folyamba (ha az nem WEOF), és törli a fájlvég jelzőt.

int fgetc(FILE *stream); wint_t fgetwc(FILE *stream); int getc(FILE *stream); wint_t getwc(FILE * stream); int getchar(void); /* A folyam: stdin. */ wint_t getwchar(void); char *fgets(char *s, int n, FILE *stream); wchar_t *fgetws(wchar_t *s, int n, FILE *stream); int ungetc(int c, FILE * stream);

Page 53: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 53 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 53 ►

wint_t ungetwc(wint_t c, FILE *stream); int fscanf(FILE *stream, const char * format<, cim, …>); int fwscanf(FILE *stream,const wchar_t *format<,cim, …>); int scanf(const char *format<,cim, ...>);/*A folyam: stdin.*/ int wscanf(const wchar_t *format<, cim, ...>); int sscanf(const char *puffer, const char * format<, cim, ...>); /* Bemenet a pufferből. */ int swscanf(const wchar_t * puffer, const wchar_t * format

<, cim, ...>); A „Kimeneti műveletek” is rendelkeznek széles karakteres függvény

párjaikkal, melyek széles karaktereket írnak a folyamba formázatlanul, ill. formázottan. A hibát itt is a WEOF jelzi többnyire.

int fputc(int c, FILE *stream); wint_t fputwc(wint_t c, FILE * stream); int putc(int c, FILE * stream); wint_t putwc(wint_t c, FILE * stream) int putchar(int c); /* A folyam: stdout. */ wint_t putwchar(wchar_t c); int fputs(const char *s, FILE *stream); int fputws(const wchar_t *s, FILE *stream); int fprintf(FILE *stream, const char *format

<, parameter, ...>); int fwprintf(FILE * stream, const wchar_t *format

<, parameter,…>); int printf(const char *format<, parameter, ...>); /* A folyam: stdout. */ int wprintf(const wchar_t * format<, parameter, ...>); int sprintf(char * puffer, const char * format

<, parameter, ...>); /* Kimenet a pufferbe. */ int swprintf(wchar_t *puffer, const wchar_t * format

<, parameter, ...>); Természetesen a függvények használatakor bekapcsolandó a szab-

ványos STDIO.H, ill. széles karakteres párjaikhoz a WCHAR.H fejfájlt! Persze a változó paraméterlistás printf–eknek is megvannak a széles

karakteres párjaik: #include <STDARG.H> int vfprintf(FILE *stream, const char *format, va_list parlist); int vfwprintf(FILE * stream, const wchar_t *format,

Page 54: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 54 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 54 ►

va_list parlist); int vprintf(const char *format, va_list parlist); int vwprintf(const wchar_t * format, va_list parlist); int vsprintf(char * puffer, const char * format, va_list parlist); int vswprintf(wchar_t *puffer, const wchar_t * format, va_list

parlist); Mikor a széles folyam B/K rutin szöveges módban (alapértelmezés!)

nyitott fájlon dolgozik, két fajta karakter konverzió megy végbe:

• Unicode–MBCS, vagy • MBCS–Unicode átalakítás.

A széles (Unicode) folyam B/K függvények szöveges módban mű-ködve a forrás vagy a cél folyamot többájtos karakterek sorozatának tekin-tik. Az Unicode folyam bemeneti rutinok ezért a többájtos karaktereket széles karakterekké konvertálják, mintha meghívták volna a mbtowc függvényt. Ugyanilyen okból az Unicode folyam kimeneti függvények a széles karaktereket többájtos karakterekké alakítják (mint a wctomb rutin).

A CR–LF transzláció bemenetnél az MBCS–Unicode konverzió előtt történik meg, s outputnál az Unicode – MBCS átalakítás után. Az input során minden CR–LF párból egy LF karakter lesz, s az output folyamán minden LF karakter CR–LF párrá alakul.

Ha azonban a széles folyam B/K rutinok bináris módban dolgoznak, a fájlt Unicode folyamnak tekintik, és így nincs CR–LF transzláció sem ki-menetkor, sem bemenetkor.

Pontosítsunk kicsikét! Vannak bájt és széles folyamok.

3.8.1. Bájt és széles folyamok A bájt folyam bájtok sorozatának tekinti a fájlt. A programon belül a fo-lyam ugyanannak a bájt sorozatnak látszik, eltekintve a már megemlített transzlációtól.

Ezzel szemben a széles folyam a fájlt olyan általánosított többájtos karakterek sorozatának nézi, mely karakterek kódolási szabályai tág hatá-rok között mozoghatnak. A programon belül a folyam széles karakterek megfelelő sorozatának látszik. A két reprezentáció közötti konverzió (Unicode–MBCS és MBCS–Unicode) szabályai elvileg LC_TYPE kategó-riás setlocale hívással változtathatók. Minden széles folyam széles orien-tálttá válásakor határozza meg konverziós szabályait, melyek aztán meg-maradnak akkor is, ha az LC_TYPE kategória később változik.

Page 55: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 55 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 55 ►

A széles folyamon belüli pozícionálás ugyanolyan korlátoktól szenved, mint a szöveges folyamoké, aztán jön a reprezentációk közti konverzió. A sok megfontolást félretéve a fájlpozíció lekérdezésének egyetlen megbíz-ható módja az fgetpos rutin, ill. a korábban lekérdezett pozíció visszaállí-tásának egyetlen biztos módja az fsetpos hívás.

3.8.2. Folyam állapotok és állapot átmenetek Három érvényes folyam állapot van:

• Kötetlen (unbound). Megnyitását követően minden folyam ilyen. Ebben az állapotban legfeljebb pozícionálni lehet a folyamban, de B/K műveletek nem végezhetők, s ez nem stabil állapota a folyamnak. Pontosabban a folyamra meghívott első B/K függvény eldönti majd, hogy bájt vagy széles orientálttá válik, s aztán ez is marad a stabil álla-pota.

• Bájt orientált. Stabil állapot, mely bájt folyamkezelést jelent. • Széles orientált. Szintén tovább nem változtatható állapot, mely szé-

les folyamkezelést eredményez.

A folyam állapot átmenet a folyamon műveletet végző függvény hí-vásának eredményeként következhet be. Öt függvény csoport okozhat állapot átmenetet, melyek közül az első háromhoz az STDIO.H–t kell bekapcsolni, s a többihez a WCHAR.H–t:

• Bájt olvasó függvények: fgetc, fgets, fread, fscanf, getc, getchar, gets, scanf és ungetc.

• Bájt író rutinok: fprintf, fputc, fputs, fwrite, printf, putc, putchar, puts, vfprintf és vprintf.

• Pozícionálók: fflush, fseek, fsetpos és rewind. Egyetlen pozícioná-lási művelet sem csökkenti soha az érvényesen következhető B/K függvényhívások számát.

• Széles olvasó függvények: fgetwc, fgetws, fwscanf, getwc, getwchar, ungetwc és wscanf.

• Széles író rutinok: fwprintf, fputwc, fputws, putwc, putwchar, vfwprintf, vwprintf és wprintf.

A kötetlen folyamra alkalmazott első bájt olvasó vagy író rutin bájt orientálttá teszi a folyamot, s aztán ez lesz a továbbiakban a stabil állapota. A kötetlen folyamra meghívott első széles olvasó vagy író függvény széles orientált állapotba helyezi a folyamot, s aztán ez az állapot nem változik. A

Page 56: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 56 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 56 ►

bájt orientált és széles orientált állapotok közt nincs átmenet. A pozícioná-lás nem okoz állapot átmenetet, de adatirányváltás nem következhet egyik orientációban sem közbenső pozícionálás nélkül.

A stream folyam orientációját explicit módon a nem szabványos int fwide(FILE *stream, int mod);

függvény határozza meg, ill. kérdezi le. Az fwide(stream, 0) hívás mindig érvényes, és semmilyen változást nem okoz a folyam állapotában. Ha a mod paraméter eltér zérustól, akkor a rutin megkísérli a folyam orientáció-ját a kívántra állítani. Ha a mod negatív, akkor a bájt orientáltság, ha pozi-tív, akkor viszont a széles orientáltság volt a kívánalom. A függvény visz-szatérési értéke minden esetben jelzi a folyam állapotát:

• pozitív: széles orientált, • zérus: kötetlen és • negatív: bájt orientált.

Az fwide azonban semmi esetre sem fogja változtatja a folyam orientációját, ha egyszer már beállították.

Írást nem követhet olvasás közbenső pozícionálás nélkül. Az olva-sást követő írásra ugyanez igaz a fájl vég ((W)EOF) elérését kivéve, mert ilyenkor nem kell közbenső pozícionálás ahhoz, hogy az olvasást írás kö-vethesse.

A dolgokat szemléltetendő az SZFOLY.C példában, széles orientációban készítünk egy szövegfájlt, mely zérustól kilencig a szám karaktereket tar-talmazza soronként egyet–egyet. Bezárjuk, majd megnyitjuk binárisan bájt orientációban, és hexadecimálisan kijelezzük a tartalmát.

/* SZFOLY.C: széles folyam. */ #include <stdio.h> #include <wchar.h> /* Ameddig max. a fájlba írjuk a számokat. */ #define MAX 8 #define FAJL "SZFOLY.TXT" int main(void){ FILE *outin; int i; /*Az output fájl megnyitása kötetlen szöveges módban:*/ if(!(outin=fopen(FAJL, "wt"))){ fprintf(stderr, "A(z) %s fájl nem nyitható meg " "outputra!\n", FAJL); return(1); }

Page 57: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 57 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 57 ►

/* Orientáció legyen széles: fwide(outin, 1); */ for(i=0; i<=MAX; ++i) fwprintf(outin, L"%d\n", i); fclose(outin); /* Az input fájl megnyitása bináris módban: */ if((outin=fopen(FAJL, "rb")) == NULL){ fprintf(stderr, "A(z) %s fájl nem nyitható meg " "inputra!\n", FAJL); return(1); } /* Olvasás bájtonként: fwide(outin, -1); */ while((i=fgetc(outin))!=EOF) printf("%02X", i); printf("\n"); for(i=0; i<=3*(MAX+1); ++i) printf("%2d", i%10); fclose(outin); return (0); }

Az eredmény: 300D0A310D0A320D0A330D0A340D0A350D0A360D0A370D0A380D0A 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7

3.9. Ellenőrző kérdések:

• Miben különbözik a függvények és széles karakteres párjaik neve? • Melyik szabványos fejfájlban helyezkednek el a széles karakteres függ-

vények prototípusai? • Mi az egybájtos karakterkészlet, s mik a kódlapok? • Beszéljen a többájtos karakterkészletről, s a többájtos karakterkódolás-

ról! • Jellemezze az egy és többájtos adattípusokat! • Mik a széles karakterek és a széles karakteres adattípus? • Mit jelentenek az MB_LEN_MAX, az MB_CUR_MAX, a

WCHAR_MIN és a WCHAR_MAX szimbolikus állandók? • Mely függvény segítségével tudja megállapítani egy többájtos karakter

bájtszámát! • Mely függvények végeznek konverziót többájtos és széles karakterek, s

láncaik között? • Mire való a wint_t adattípus és a WEOF szimbolikus állandó? • Ismertesse a széles karakterek osztályozását és kis/nagybetűs átalakítá-

sát végző rutinokat! • Mire való a mem és wmem függvényekben az utolsó size_t típusú

paraméter?

Page 58: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 58 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 58 ►

• Mely rutinokkal lehet (széles) karakterláncok hosszát megállapítani, melyekkel egyesíteni, másolni és összehasonlítani őket? Az összeha-sonlítást hogy végezhetjük hely–specifikusan, s hogyan lexikografikusan?

• Mely függvények alkalmasak karakterláncokban karakterek és karakter-láncok keresésére?

• Melyik rutinok segítségével lehetne egy bizonyos karakterrel feltölteni egy karakterláncot?

• Melyik függvényekkel lehet egyszerre több karakter valamelyikét ke-resni egy karakterláncban?

• Miben különböznek az strtod (wcstod), az strtol (wcstol) és az strtoul (wcstoul) adatkonverziós függvények atof, atol és atoi társaik-tól?

• Milyen részekből állhat az strtod (wcstod) konvertálandó lebegőpon-tos szám–karakterlánca, s mit változtathat ezen az aktuális hely LC_NUMERIC kategória–beállítása?

• Milyen részekből állhat az strto(u)l (wcsto(u)l) konvertálandó egész szám–karakterlánca, s milyen értékeket vehet fel a függvények alap pa-ramétere, és milyen következményekkel jár ez?

• Mely magas szintű B/K függvénynek van meg a széles karakteres pár-ja, s a párok miben különböznek egymástól?

• A széles folyam B/K rutinok szöveges módban milyen konverziókat hajtanak végre, s melyik helyi kategória beállítása hat az átalakításokra?

• Mi a bájt és mi a széles folyam? • Ismertesse a folyam állapotokat és a lehetséges állapot átmeneteket!

3.10. Megoldandó feladatok:

Írja át a LONGE.C példában szereplő longe rutint úgy, hogy visszatérési értéke külön–külön jelezze a hibafajtákat (formai hiba, túl sok számjegy, egy számjegy sincs, nem fér az ábrázolási határokba stb.), s érzékeltesse is ezeket a kipróbáló programban.

Készítsen egy doublee függvényt (és egy ezt kipróbáló programot), mely első karakterlánc paraméterét decimális valós számnak tekinti, és formailag ellenőrzi. Mérvadó a valós konstans írásszabálya! Ha helyesnek találja, egyet, máskülönben zérust ad vissza. Ha a belefér a double típus ábrázolá-

Page 59: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Többájtos és széles karakterek

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 59 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 59 ►

si határaiba, akkor a függvény konvertálja is, és elhelyezi az értéket a má-sodik paraméter címen.

Írja át a doublee függvényt úgy, hogy visszatérési értékével külön–külön jelezze a hibafajtákat (formai hiba, mantissza probléma, kitevő gond, nem fér az ábrázolási határokba stb.), s érzékeltesse is ezeket a kipróbáló prog-ramban. Csinálja meg a doublee rutin széles karakteres változatát is wdoublee néven! Készítsen programot, mely ékezetes betűket is tartalmazó nevekből álló szövegfájlt névsorba rendez! Mind a bemeneti, mind a kimeneti fájl legyen a programot indító parancssorban megadható! Bontson szavakra egy parancssorban adott szövegfájlt úgy, hogy minden fehér karaktert és írásjelet kitisztít a szövegből, s csak a puszta szavakat helyezi el (soronként egyet) egy másik szövegfájlban! Készítsen strrstr és wcsrstr függvényeket (és egy őket kipróbáló progra-mot), melyek az strstr–hez és wcsstr–hez hasonlóan működnek, de a má-sodik karakterlánc elsőbeli utolsó előfordulásának címét szolgáltatják! Írjon strlwr (wcslwr), ill. strupr (wcsupr) rutinokat tesztelő programmal egyetemben, melyek paraméter karakterláncukat a saját helyükön kisbetűs-sé (lwr), ill. nagybetűssé (upr) alakítják, s szolgáltatják az eredmény karak-terlánc címét. Készítsen strset és wcsset függvényeket (és egy őket kipróbáló progra-mot), melyek az első paraméter karakterlánc minden pozícióját feltöltik a második paraméterként adott karakterrel, s visszaadják az eredmény karak-terlánc címét! Készítsen szövegfájlt, melynek soraiban egymástól fehér karakterekkel elválasztottan több egész szám áll! Adja meg e fájl azonosítóját parancssori paraméterként a most kódolandó programjának! A szoftver olvassa be e számokat egy tömbbe, képezze összegüket, átlagukat (legalább két tizedes pontossággal), majd számlálja le, hogy hány átlag alatti és hány átlag feletti elem van a tömbben! Az eredményeket természetesen alkalmas módon jelezze is ki! Csinálja meg az előző pontban ismertetett feladatot a valós számok köré-ben is!

Page 60: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Az idő kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 60 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 60 ►

4. Az idő kezelése

A következő ANSI/ISO szabványos függvények lekérdezik, letárolják, konvertálják, stb. az aktuális időt. Az aktuális idő a rendszer idő. Két időt feltétlenül meg kell különböztetnünk:

• Az egyik az időzónának megfelelő helyi idő.

• A másik az egyezményes idő (koordinált univerzális – Universal Ti-me Coordinated – UTC), amit korábban greenwichi közép időnek (Greenwich Mean Time – GMT) neveztek. Az idő kezelésében használatos típusok, struktúrák definíciói és a

függvény prototípusok a TIME.H fejfájlban találhatók. A következő ábra összefoglalja a típusokat (ezek láthatók a téglalapokban) és a függvényeket, melyek konverziót hajtanak végre a típusok közt.

Több függvény osztozik két statikus élettartamú pufferen, melyek a

függvények által kiszámított értékeket tartalmaznak. Ezek:

• az idő karakterlánc és • az idő struktúra.

struct tm*

char *

double

time_t

time

difftime ctime

mktime

asctime strftime

gmtimelocaltime

Page 61: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Az idő kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 61 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 61 ►

4.1. Az idő karakterlánc char * típusú, statikus karaktertömbben foglal helyet az idő karakterlánc, mely pontosan 26 karakteres, és Tue Jan 05 12:03:55 1999\n\0 formájú. 24-órás az időszámítás. Minden mező konstans szélességű. Az új sor és a lezáró null karakter mindig a karakterlánc két utolsó pozícióját foglalja el.

4.2. Az idő struktúra (struct tm) A másik statikus puffer egy struct tm típusú struktúrában elhelyezett idő struktúra. A struktúra minden tagja int típusú, de a tagok sorrendje nem kötött a szabvány szerint. A tagok:

• tm_sec: Másodperc (0–59). • tm_min: Perc (0–59). • tm_hour: Óra (0–23). • tm_mday: Nap a hónapon belül (1–31). • tm_mon: Hónap az éven belül (0–11, a január 0). • tm_year: Év – 1900. • tm_wday: A hét napja (0–6, a vasárnap a 0). • tm_yday: Napszám az éven belül (0–365, a január 1. a zérus). • tm_isdst: Nyári időszámítás jelző, mely pozitív, ha a nyári időszámí-

tás hatályban van, és 0, ha nincs. Negatív a jelző, ha a nyári időszámí-tás állapota ismeretlen.

A rutinok egyikének hívása megváltoztathatja a statikus élettarta-mú puffer tartalmát, mely egy másik ilyen függvény hívásával került oda. Szóval, ha a programban hosszabb ideig van szükség a függvénytől a stati-kus pufferben kapott értékre, akkor el kell menteni innét.

4.3. time time_t time(time_t *sectime);

A time függvény az aktuális rendszer időt 1970. január elseje éjfél óta el-telt másodpercek számában, time_t típusban szolgáltatja, ha a cél környe-zet meg tudja határozni ezt az értéket, máskülönben –1–et kapunk. A szo-kásos operációs rendszereknél nincs hibás visszatérés. Az idő egyezmé-nyes naptári időben értendő. A visszatérési értéket a sectime címen is elhe-lyezi a rutin, ha a paraméter nem NULL mutató. NULL mutató aktuális paraméter esetén nincs letárolás.

Page 62: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Az idő kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 62 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 62 ►

A függvényt hívjuk a fejezet minden példájában.

4.4. time_t típus typedef aritmetikai–típus time_t;

Az aritmetikai–típus (szokásosan long) a time függvény szolgáltatta, 1970. január elseje éjfél óta eltelt másodpercek számában mért, egyezményes idő tárolására való.

4.5. difftime double difftime(time_t ido1, time_t ido0);

A difftime visszaadja az ido1 és az ido0 naptári idők különbségét, azaz a közben eltelt időt másodpercekben.

Vigyázzunk a visszatérési érték típusára, mert az double!

A DIFFTIME.C példaprogram meghatározza egymilliárd lebegőpontos szorzás és hozzárendelés műveleti idejét:

#include <stdio.h> #include <stdlib.h> #include <time.h> void main(void){ time_t kezdet, veg; long i; double ered; printf("1 milliárd lebegőpontos szorzás és " "hozzárendelés elvégzése\n"); time(&kezdet); for(i = 0l; i < 1000000000l; i++) ered = 3.63 * 5.27; time(&veg); printf("%6.0f másodpercet igényel.\n", difftime(veg, kezdet));}

4.6. ctime Az ido mutatta, time_t típusú időt alakítja a helyi időzóna beállításoknak is megfelelő idő karakterlánccá a

char *ctime(const time_t *ido);

függvény, elhelyezi a statikus pufferben, és erre mutató mutatót ad vissza. Ha az ido érték egyezményes időben (UTC) 1970. január 1. éjfél előtti időt reprezentál, akkor a rutin NULL mutatót szolgáltat. Az ido értéknek

Page 63: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Az idő kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 63 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 63 ►

UTC–ben mért 1970. január 1. éjfél (00:00:00) óta eltelt másodpercek számának kell lennie, azaz többnyire time hívásból kell származnia.

A ctime hívás módosítja a gmtime és a localtime függvényeknek statikusan allokált puffert. A rutinok bármelyikének hívása felülírja az elő-ző hívás eredményét. A ctime az asctime rutinnal is megosztja statikus pufferét, azaz a ctime hívás felülírja bármely megelőző asctime, localtime, vagy gmtime hívás eredményét, magyarán: az idő karakter-láncnak statikusan allokált puffert.

A ctime(ido); ekvivalens az asctime(localtime(ido)) hívással.

Példaként lásd az IDOK.C programot az strftime–nál!

4.7. gmtime, localtime struct tm *gmtime(const time_t *ido); struct tm *localtime(const time_t *ido);

A függvények a paraméter, time_t típusú, GMT (UTC, vagy egyezmé-nyes) idő értéket statikus, tm típusú struktúrába konvertálják, és ennek az idő struktúrának a címét visszatérési értékként szolgáltatják. Ha az ido ér-ték 1970. január elseje éjfél előtti, akkor a rutinok NULL mutatót adnak vissza. A localtime korrigálja az időt a helyi időzónára, míg a gmtime ilyet nem tesz.

A paraméter ido értéket rendszerint time hívással állítjuk elő. A gmtime és a localtime függvények ugyanazt a statikusan allokált

tm struktúrát használják a konverzió eredményének elhelyezésére, s így egymást követő hívásuk mindig felülírja az előző hívás eredményét.

Példaként jelentessük meg az aktuális dátumot és időt ÉÉÉÉ.HH.NN ÓÓ:PP:SS alakban!

/* IDOKI.C példa. */ #include <stdio.h> #include <time.h> void main(void){ struct tm *tmptr; time_t ido=time(NULL); tmptr=localtime(&ido); printf("%4d.%.2d.%.2d %02d:%02d:%02d\n", tmptr->tm_year+1900, tmptr->tm_mon+1,

Page 64: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Az idő kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 64 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 64 ►

tmptr->tm_mday, tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec); }

4.8. mktime A gmtime és a localtime függvényekhez képesti fordított konverziót az

time_t mktime(struct tm *idoptr);

rutin valósítja meg, mely az idoptr mutatta (lehet, hogy nem teljes) tm struktúrát normalizált értékeket tartalmazó, teljesen kitöltött struktúrává alakítja, és visszaadja a time_t típusúvá konvertált idő értéket. Ha az idoptr ábrázolhatatlan (például. 1970. január elseje éjfél előtti) időre hivatkozik, akkor az mktime time_t–vé típusmódosított –1–et szolgáltat.

A konvertált idő ugyanolyan kódolású, mint a time függvény által visz-szaadott érték. Az aktuális paraméter struktúra tm_wday, s tm_yday tag-jainak eredeti értékét elhagyja a rutin, és a többi tagokra sem érvényesek az idő struktúra leírásánál részletezett korlátozások.

Az mktime 1970. január 1. éjfél és 2038. január 18. 19:14:07 közötti időket kezel. A felső korlát akkor ennyi, ha a time_t típus tulajdonképpen long. Sikeres esetben beállítja a tm_wday, a tm_yday tagok értékét, és a többi tagot is a szokásos értéktartományba normálja. Ez ugye azt jelenti a dátum vonatkozásában, hogy a tm_mday–t nem állapítja meg addig, míg tm_mon és tm_year értéke nem meghatározott. A tm struktúra megadá-sakor állítsuk zérusra a tm_isdst tagot, ha normál időszámítást kívánunk! Nyári időszámításhoz a tm_isdst pozitív kell, hogy legyen. Ha a C szab-vány könyvtárral akarjuk megállapíttatni, hogy normál vagy nyári időszá-mítás van–e, akkor válasszuk negatívnak a tm_isdst értékét! A tm_isdst érték megadásával azonban mindenképp foglalkozni kell, mert rossz érték hatására az mktime előre megjósolhatatlan eredményt szolgáltat. Ha az idoptr megelőző asctime, gmtime, vagy localtime hívásból származó értékű tm struktúrára mutat, akkor a tm_isdst helyes értéket tartalmaz.

A gmtime és a localtime rutinok egyazon statikusan allokált puf-fert használják a konverzióhoz. Ha paraméterül ezt adjuk át az mktime–nak, akkor az előző tartalom megváltozhat!

Példa az asctime–nál található!

Page 65: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Az idő kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 65 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 65 ►

4.9. asctime Az idoptr mutatta, tm típusú idő struktúrában tárolt időt alakítja a helyi időzóna beállításoknak is megfelelő idő karakterlánccá az

char *asctime(const struct tm *idoptr);

és erre a karakterláncra mutató mutatót ad vissza. Nincs hibás visszatérés. Az idoptr érték rendszerint gmtime vagy localtime hívásból származik.

Az eredmény karakterlánc pontosan 26 karakteres, és Tue Jan 05 12:03:55 1999\n\0 formájú angol nyelv szerinti ábrázolású. Az időszámí-tás 24 órás. Minden mező konstans szélességű. Az új sor és a lezáró null karakter mindig az eredmény karakterlánc két utolsó pozícióját foglalja el. Az asctime az eredményt egy statikusan allokált pufferben szolgáltatja, s így minden újabb asctime hívás felülírja az előző hívás eredményét.

Az idoptr mutatta idő struktúrát az asctime nem normalizálja, és nem alakítja teljesen kitöltötté, mint az mktime.

A MAJUS40.C példa 2037. május 40. 31 óra 82 perc 0 másodpercet jelez-tet ki. Bemutatja, hogy az aktuális hely beállítása mit sem változtat az idő karakterlánc formáján. Szemlélteti, hogy ez az idő time_t típusban közel esik a long ábrázolás felső határához. Láttatja, hogy az mktime normali-zálja az idő struktúrát, s kiszámítja a meg nem adott tagokat is.

#include <stdio.h> #include <stdlib.h> #include <time.h> #include <locale.h> void main(void){ struct tm maj40; /* A tm struktúra tagjainak sorrendjét nem rögzítette a szabvány, ezért a tagonkénti értékadás az inicializálás helyett. */ maj40.tm_year=137;/* Év: 2037 - 1900. */ maj40.tm_mon=4; /* Hónap az éven belül (0-11). */ maj40.tm_mday=40; /* Nap a hónapon belül. */ maj40.tm_hour=31; /* Óra (0-23). */ maj40.tm_min=82; /* Perc (0-59). */ maj40.tm_sec=0; /* Másodperc. */ /* A hét napját (0-6, a vasárnap a 0), és az éven belüli napszámot (0-365, a január 1 a zérus) úgyis számítja. */ maj40.tm_wday=maj40.tm_yday=0; maj40.tm_isdst=1; /*Nyári időszámítás van hatályban.*/

Page 66: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Az idő kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 66 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 66 ►

printf("2037. május 40. 31 óra 82 perc: %s\n", asctime(&maj40)); printf("A hét napja: %d\nAz év napja: %d\n\n", maj40.tm_wday, maj40.tm_yday); setlocale(LC_ALL, ""); printf("Az aktuális hely beállítása az idő " "karakterláncon mit sem változtat:\n"); printf("2037. május 40. 31 óra 82 perc: %s\n", asctime(&maj40)); printf("Az mktime megadja a time_t időt, és " "normalizálja az idő struktúrát:\n"); printf("time_t: %ld\n", mktime(&maj40)); printf("2037. május 40. 31 óra 82 perc: %s\n", asctime(&maj40)); printf("A hét napja: %d\nAz év napja: %d\n\n", maj40.tm_wday, maj40.tm_yday); }

Az eredmény: 2037. május 40. 31 óra 82 perc: Sun May 40 31:82:00 2037 A hét napja: 0 Az év napja: 0 Az aktuális hely beállítása az idő karakterláncon mit sem változtat: 2037. május 40. 31 óra 82 perc: Sun May 40 31:82:00 2037 Az mktime megadja a time_t időt, és normalizálja az idő struktúrát: time_t: 2128227720 2037. május 40. 31 óra 82 perc: Wed Jun 10 08:22:00 2037 A hét napja: 3 Az év napja: 160

4.10. strftime, wcsftime size_t strftime(char *puffer, size_t maxmeret, const

char *format, const struct tm *idoptr); size_t wcsftime(wchar_t *puffer, size_t maxmeret,

const wchar_t *format, const struct tm *idoptr);

Az strftime és a wcsftime függvények az idoptr mutatta, struct tm típusú időértéket megformázzák a format vezérlő karakterláncban előírtak szerint, és az eredmény karakterláncot elhelyezik a legfeljebb maxmeret karakteres pufferben. A rutinok a pufferben letárolt karakterek számával térnek vissza,

Page 67: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Az idő kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 67 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 67 ►

ha az eredmény nem haladja meg a maxmeretet. Máskülönben zérust ka-punk, és a puffer tömb tartalma meghatározatlan.

A format paraméter egy vagy több karaktert tartalmaz, s mint a printf–ben a formátumspecifikációkat az itteni, úgy nevezett formátumkódokat is százalék jel (%) vezeti be. A % jellel meg nem előzött karaktereket a rutinok változatlanul átmásolják a pufferbe. Az aktuális hely LC_TIME kategória beállítása befolyásolja a (w)strftime által képzett eredményt. A formátumkódok – melyek után zárójelben a ”C” hely formátumkódnak megfelelő értéke, vagy a lehetséges tartomány áll – a következők:

• %a: A hét napja nevének rövidítése (Sun). • %A: A hét napjának teljes neve (Sunday). • %b: Hónapnév rövidítés (Dec). • %B: Teljes hónapnév (December). • %c: A helynek megfelelő dátum és idő reprezentáció (Dec 11 06:55:15

2005). • %d: Decimális napszám a hónapon belül (01 – 31). • %H: Óra 24-órás formában (00 – 23). • %I: Óra 12-órás alakban (01 – 12). • %j: Decimális napszám az éven belül (001 – 366). • %m: Decimális hónapszám (01 – 12). • %M: Decimális percszám (00 – 59). • %p: Az aktuális hely A.M./P.M. jelzője a 12–órás időkijelzésnél

(AM/PM). • %S: Decimális másodpercszám (00 – 59). • %U: Decimális hétszám az éven belül, vasárnap hét első napot feltéte-

lezve (00 – 53). • %w: Decimális napszám a héten belül (0 – 6; a vasárnap a 0). • %W: Decimális hétszám az éven belül, hétfő hét első napot feltételez-

ve (00 – 53). • %x: Az aktuális hely dátum reprezentációja (Dec 11 2005). • %X: Az aktuális hely idő reprezentációja (06:55:15). • %y: Évszázad nélküli decimális évszám (00 – 99). • %Y: A teljes decimális évszám. • %Z: Az időzóna neve. Üres karakterlánc, ha az időzóna ismeretlen. • %%: Százalék jel.

Page 68: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Az idő kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 68 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 68 ►

Mint a printf függvénynél a nem szabványos # jelző megelőzheti a tí-puskaraktert bármelyik formátumspecifikációban. A formátumkódok ér-telmezése is változik ilyenkor a következőképp:

• %#a, %#A, %#b, %#B, %#p, %#X, %#z, %#Z, %#%: A # jelző figyelmen kívül marad.

• %#c: Az aktuális helynek megfelelő, hosszú dátum és idő reprezentá-ció. Például: “Tuesday, March 14, 1995, 12:41:29”.

• %#x: Az aktuális helynek megfelelő, hosszú dátum reprezentáció. Például: “Tuesday, March 14, 1995”.

• %#d, %#H, %#I, %#j, %#m, %#M, %#S, %#U, %#w, %#W, %#y, %#Y: Eltávolítja a vezető zérusokat, ha vannak.

Az IDOK.C példaprogram illusztrálja jó néhány időkezelő függvény használatát.

#include <stdio.h> #include <string.h> #include <time.h> #include <locale.h> void main(){ char idpuff[128], dedu[] = "de"; time_t tt; /* Feltételezzük a tm struktúra tm_sec, tm_min, tm_hour, tm_mday tm_mon, tm_year, s így tovább tagsorrendjét! */ struct tm *ma, *gmt, kcsony = {0, 0, 12, 25, 11, 105}; /* A UNIX-stílusú idő lekérdezése és megjelentetése: */ time(&tt); printf("1970. január 1. óta eltelt másodpercek " "száma:\t%ld\n", tt); printf("A UNIX idő és dátum:\t\t\t%s", ctime(&tt)); /* Az UTC kijelzése. */ gmt = gmtime(&tt); printf("Az egyezményes idő:\t\t\t%s", asctime(gmt)); /* Az idő struktúra konverziója és 12 órás átalakítása: */ ma = localtime(&tt); if(ma->tm_hour > 12){ strcpy(dedu, "du"); ma->tm_hour -= 12; } if(ma->tm_hour == 0) ma->tm_hour = 12; /* Éjfélkor. */ /* Mutatónöveléssel átlépjük az első 11 karaktert, és a

Page 69: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Az idő kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 69 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 69 ►

printf formátumspecifikációval levágjuk a hátsó karaktereket. */ printf("12-órás idő:\t\t\t\t%.8s %s\n", asctime(ma) + 11, dedu ); /* Karácsony déli idejének megállapítása: */ if(mktime(&kcsony) != (time_t)-1) printf("Karácsony\t\t\t\t%s", asctime(&kcsony)); /* Mi van karácsonytól 10 napra. */ kcsony.tm_mday = kcsony.tm_mday + 10; if((tt = mktime(&kcsony)) != (time_t)-1) printf("Karácsony + 10 nap: \t\t\t%s", asctime(&kcsony)); else perror("Az mktime sikertelen!"); /* Átszabott idő karakterlánc előállítása az idő struktúrából az strftime segítségével: */ ma = localtime(&tt); strftime(idpuff, 128, "Ma %Y. %B %#d. %A van a(z) %Z " "időzónában.\n", ma); printf(idpuff); setlocale(LC_TIME, ""); strftime(idpuff, 128, "Ma %Y. %B %#d. %A van a(z) %Z " "időzónában.\n", ma); printf(idpuff); }

Az eredmény: 1970. január 1. óta eltelt másodpercek száma: 1130419458 A UNIX idő és dátum: Thu Oct 27 15:24:18 2005 Az egyezményes idő: Thu Oct 27 13:24:18 2005 12-órás idő: 03:24:18 du Karácsony Sun Dec 25 12:00:00 2005 Karácsony + 10 nap: Wed Jan 04 12:00:00 2006 Ma 2006. January 4. Wednesday van a(z) Közép-európai téli idő időzónában. Ma 2006. január 4. szerda van a(z) Közép-európai téli idő időzónában.

4.11. clock clock_t clock(void);

A clock függvény a hívó folyamat által elhasznált processzor időt szolgáltatja.

4.11.1. clock_t típus Aritmetikai típus (többnyire long), mely a clock rutin által visszaadott,

eltelt processzor idő értéket ábrázolni, tárolni képes.

Page 70: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Az idő kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 70 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 70 ►

4.11.2. CLOCKS_PER_SEC A makró a clock függvény által egy másodperc alatt visszaadott óra

ütemek száma. A clock rutin által visszaadott értékből úgy kapunk má-sodpercet, hogy elosztjuk a CLOCKS_PER_SEC szimbolikus állandóval. Magyarán a clock az eltelt processzor timer számlálások számát szolgáltat-ja. Egy timer ütem közelítőleg 1/CLOCKS_PER_SEC másodperc.

Ha a rendszerben nem áll rendelkezésre az eltelt idő, a clock clock_t–vé típusmódosított –1–t szolgáltat.

A hívó folyamat indulása óta eltelt idő nem szükségképpen egyen-lő a folyamat által aktuálisan elhasznált processzor idővel, hisz egyszerre több konkurens folyamat futhat.

A következő sleep függvény sec másodpercet vár. #include <time.h> void sleep(clock_t sec){ clock_t ig = sec*CLOCKS_PER_SEC + clock(); while(ig > clock()); }

4.12. Ellenőrző kérdések:

• Melyik fejfájlt kell bekapcsolni a szabványos időkezelő rutinok haszná-latához?

• Mi az idő karakterlánc, s milyen részei vannak? • Milyen tagjai vannak az időstruktúrának? • Mi a hasonlóság és a különbség a ctime és az asctime függvények

között? • Mit csinál a gmtime és a localtime rutin, miben térnek el egymástól? • Mire való az mktime függvény? Miért csak 1970. január 1. éjfél és

2038. január 18. 19:14:07 közötti időket kezel? • Ismertesse az strftime és a wcsftime rutinokat! • Mit állít be az LC_TIME kategória? • Mire való a clock függvény? • Nézze meg, hogy fejlesztő rendszerében mennyi a

CLOCKS_PER_SEC szimbolikus állandó értéke!

Page 71: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Nem helyi vezérlésátadás és jel(zés)kezelés

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 71 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 71 ►

4.13. Megoldandó feladatok:

Készítsen programot, mely megjelenteti az strftime összes formátumkód-jának értékét egy idő struktúrával adott és a pillanatnyi időpontban az ANSI konform C környezetben, valamint az aktuális helyen.

Írjon szoftvert a clock függvény segítségével, mely a képernyő valamelyik sorában kijelzi ÓÓ:PP:MM alakban folyamatosan az időt!

5. Nem helyi vezérlésátadás és jel(zés)kezelés

Ebben a fejezetben két témakört veszünk alaposabban szemügyre. Első-ként a nem helyi vezérlésátadás (non-local exit) lehetőségével ismerke-dünk meg, amit a jel(zés)ek (signal) kezelésének bemutatása követ. E két terület szorosan kapcsolódik egymáshoz, mert a nem helyi vezérlésátadást tipikusan jelzéskezelőkben alkalmazzák.

5.1. Nem helyi vezérlésátadás A setjmp.h fejfájlban definiált függvényszerű makrók és gépfüggő puffer lehetővé teszik, hogy függvényeinkből ne csak a már ismert return utasí-tással térhessünk vissza. Például mélyen egymásba ágyazott rutinhívások-nál, vagy a később ismertetendő jel(zés)kezelőknél hasznos lehet, ha hely-reállíthatatlan hiba után egyetlen utasítással vissza tudunk térni a program egy olyan pontjára, ahonnét a futás folytatható.

int setjmp(jmp_buf allapot);

A setjmp zérust ad vissza a verem környezet és a program állapot allapot–ba mentése után. Ezt az allapot–ot később a longjmp lesz képes visszaállí-tani. Ha a vezérlés a longjmp hívását követően került ismét a setjmp-ra, akkor a visszatérési érték zérustól különböző lesz. Nincs hibás visszatérés.

A setjmp makró csak olyan kifejezésekben használható, melyekben

• nincsenek operátotok, vagy • csak az egyoperandusos logikai nem operátor (!) fordul elő, vagy • kétoperandusos reláció műveletek (>, >=, <, <=, ==, !=) találhatók

legfeljebb benne úgy, hogy a másik operandus egész értékű konstans kifejezés.

Page 72: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Nem helyi vezérlésátadás és jel(zés)kezelés

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 72 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 72 ►

A setjmp makrós kifejezéseket aztán while, for, do, if és switch uta-sítások feltétel részében alkalmazhatjuk.

A jmp_buf aritmetikai tömb típus, mely tárolni képes a setjmp–pal elmentett és longjmp–pal visszaállított verem környezetet és program állapotot.

void longjmp(jmp_buf allapot, int vissza);

A rutin visszaállítja az allapot paraméter tartalma alapján a setjmp előző hívásakor elmentett programállapotot, s a szoftver futása a setjmp-ból való visszatérésnél folytatódik.

A longjmp második paramétere azt határozza meg, hogy mi legyen a setjmp visszatérési értéke az ugrás után. Ha a vissza nem zérus, akkor visz-sza a visszatérési érték. Ha a vissza zérus, akkor viszont 1. (Nem jó prog-ramozói magatartás a vissza értéket zérusnak választani.)

Csak olyan függvényen belülre kerülhet vissza a vezérlés, ami még nem tért vissza a hívójához! Ha valamely nem volatile, dinamikus élettar-tamú változó értéke módosult a setjmp hívása után, akkor értéke a longjmp hívást követően definiálatlan lesz!

A jump.c program szabvány kimenetre írja 0-tól indulva a 10-nél kisebb páros számokat.

/* jump.c */ #include <stdio.h> #include <setjmp.h> #define MAX 10 jmp_buf cimke; void masodik(int); void elso(int i) { masodik(i); printf("Ez a szöveg soha nem fog megjelenni."); } void masodik(int i) { printf("%d ", i); longjmp(cimke, i+1); printf("Ez a szöveg soha nem fog megjelenni."); } int main(void) { volatile int i; printf("A %d-nél kisebb páros számok: ", MAX);

Page 73: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Nem helyi vezérlésátadás és jel(zés)kezelés

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 73 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 73 ►

if((i = setjmp(cimke)) > 0) i++; if(i < MAX) elso(i); else { putchar('\n'); return 0; } }

5.2. Jel(zés)kezelés Az operációs rendszer képes jel(zés)t (signal) küldeni a folyamatnak, ha valamilyen különleges esemény (külső forrástól érkező megszakításkérés, végrehajtási hiba, zérussal osztás stb.) következik be. A jeleket az alkalma-zás figyelmen kívül hagyhatja, használhatja a gépi megvalósítástól függő alapértelmezett jelzéskezelést, de saját jelkezelést is megvalósíthat.

A különféle jel értékeket és a vonatkozó függvények prototípusait a signal.h fejfájl definiálja.

void (*signal(int jelzes, void (*kezelo)(int))) (int);

A rutin segítségével beállíthatjuk, hogyan reagáljon a program a jelzes típu-sú jelekre. Ha a kezelo értéke SIG_DFL, akkor alapértelmezés szerinti kezelést végez, ha SIG_IGN, akkor figyelmen kívül hagyja a jelzést. Har-madik lehetőségként saját jelzéskezelő függvényünk címét is átadhatjuk.

Ennek int típusú paramétere arról tájékoztatja a rutint, hogy milyen jelzés érkezése idézte elő futását. (Ugyanazt a függvényt többféle jelzés kezelésére is használhatjuk.) A jelzéskezelőt az operációs rendszer hívja, megfelelően paraméterezve. A rutinból való visszatérést követően a prog-ram futása ott fog folytatódni, ahol a jelzés érkezése következtében félbe-szakadt.

A signal visszatérési értéke sikeres beállítást követően a korábbi jel-zéskezelő címe, vagy a fenti két szimbolikus állandó egyike. A hibás beállí-tási kísérletet a SIG_ERR visszatérési érték jelzi.

A megengedett jelzes értékek és jelentéseik a következők:

• SIGABRT: Abnormális befejezés, amit pl. az abort függvény válthat ki.

• SIGFPE: Lebegőpontos hiba (nullával való osztás, túl– vagy alulcsor-dulás, stb.)

• SIGILL: Illegális utasítás, pl. hibás címmel adott függvény meghívása. • SIGINT: Interaktív jelzés, megszakításkérés. • SIGSEGV: Illegális tár elérés, pl. tömb túlindexelése.

Page 74: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Nem helyi vezérlésátadás és jel(zés)kezelés

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 74 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 74 ►

• SIGTERM: Befejezési igény küldése a programnak.

A jelzések alapértelmezett kezelési módja operációs rendszerenként el-térő lehet.

int raise(int jelzes);

A függvény az előidézendő jelzes-t küldi a programnak. Ha hívása sikerrel járt, zérus értékkel tér vissza.

A UNIX és GNU/Linux operációs rendszerek a jelzéskezelés ANSI C szabványban rögzített lehetőségeit jelentősen kibővítették. To-vábbi jeleket vezettek be, melyek precíz használatát új függvények segítik. Részletesebb információkkal a [3] irodalom és a kézikönyv oldalai szolgál-nak:

man 2 signal man 3p signal man 7 signal

A SIGINT jelzést általában a Ctrl+C billentyűkombinációval a fel-használó küldi a futó alkalmazásnak.

A Win32 (Windows XP, Windows 95, stb.) alkalmazásokban gyakor-latilag csak a SIGFPE jelzés használható, aminek jelkezelője viszont egy második, opcionális int típusú paraméter segítségével pontosabban tudja felderíteni a jelzés keletkezésének okát. A többi jelet csak a raise explicit hívásával lehet előidézni. A SIGINT jel kezelését egyáltalán nem támogat-ják. A Win32 operációs rendszer új szálat generál specifikusan a Ctrl+C megszakítás kezelésére. Ez azt eredményezi, hogy olyan egyszálas alkalma-zások, mint a UNIX-éi, többszálasokká válnak, ami előre megjósolhatatlan viselkedéshez vezethet. Részletesebb információkkal az MSDN Library szolgál. A 2003-as változatban a következő témakört érdemes áttanulmányozni:

Visual Studio .NET/Visual C++/Reference/Visual C++ Librar-ies/Run-Time Library Reference/Alphabetical Function Reference/S through Z/signal

A SIGFPE jelzés használatához operációs rendszerenként eltérő to-vábbi intézkedések is szükségesek lehetnek, például a lebegőpontos vezér-lő szó kivétel bitjeinek beállítása, a lebegőpontos csomag inicializálása, stb.

Mielőtt jelzéskezelő függvény írásába fognánk, át kell tanulmányozni az operációs rendszer ide vonatkozó előírásait! Saját rutinjaink készítésénél általában jó gyakorlat, ha a kezelő csak a legszükségesebb műveleteket végzi el. Ez lehet pl. egy volatile állapotjelző globális változó beállítása, amit a megfelelő helyen és időközönként kiolvasunk. Nem szabad elfeled-

Page 75: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Nem helyi vezérlésátadás és jel(zés)kezelés

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 75 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 75 ►

kezni arról, hogy a jelzések bármikor, bárhol megszakíthatják a folyamat futását (akár jelzéskezelő függvényen belül, vagy értékadás közben), ezzel inkonzisztens állapotot idézve elő. Kerülendő a nem újrahívható (non-reentrant) függvények használata. Tipikusan ilyenek az alacsonyszintű vagy folyam B/K rutinok (printf, fread, stb.), és a heap kezelő függvények (malloc, free, stb.).

A division.c program két lebegőpontos értéket kér a felhasználótól, majd megjeleníti ezek hányadosát és szorzatát. Nullával való osztás esetén a program SIGFPE jelzést kap, és a lebegőpontos csomag újrainicializálását követően megjeleníti a „Lebegőpontos hiba” üzenetet.

A programot úgy készítettük el, hogy Win32 és GNU/Linux rendsze-reken is futtatható legyen. A rendszerek közötti különbségeket a feltételes fordítással hidaltuk át. gcc-s fordításnál be kell szerkeszteni a matematikai függvénykönyvtárat is: gcc –o division –lm division.c /* division.c */ #include <stdio.h> #include <signal.h> #include <setjmp.h> #include <stdlib.h> #include <math.h> #include <string.h> #ifdef _WIN32 #include <float.h> #endif #ifdef __GNUC__ #include <fenv.h> #define _GNU_SOURCE fenv_t allapot; #endif jmp_buf cimke; /* Jelzéskezelő függvény */ void fphandler(int jelzes) { #ifdef _WIN32 /* Lebegőpontos csomag alaphelyzetbe állítása */ _fpreset(); #endif #ifdef __GNUC__

Page 76: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Nem helyi vezérlésátadás és jel(zés)kezelés

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 76 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 76 ►

/* Lebegőpontos környezet visszaállítása a hiba előtti állapotra */ fesetenv(&allapot); #endif longjmp(cimke, -1); } int main( void ){ double s1, s2, e; #ifdef _WIN32 /* Lebegőpontos vezérlő szó beállítása; csak zérussal osztás esetén hívja a rendszer a jelzéskezelő függvényt. */ _control87(~_EM_ZERODIVIDE, _MCW_EM); #endif #ifdef __GNUC__ /* Zérussal osztás esetén meg kell hívni a jelzéskezelő függvényt. */ feenableexcept(FE_DIVBYZERO); /* Lebegőpontos környezet mentése. */ if(fegetenv(&allapot)) fprintf(stderr, "Állapot mentése nem sikerült.\n"); #endif /* Jelzéskezelő függvény regisztrálása. */ if(signal(SIGFPE, fphandler) == SIG_ERR) { fprintf(stderr, "Nem állítható be a lebegőpontos " "hibakezelő!\n"); abort(); } if(!setjmp(cimke)) { printf("Teszt érvénytelen lebegőpontos " "műveletre:\n"); printf("Adjon meg két számot! "); scanf("%lf %lf", &s1, &s2); e = s1 / s2; printf("\n\n%4.3g / %4.3g = %4.3g\n", s1, s2, e); e = s1 * s2; printf("\n\n%4.3g * %4.3g = %4.3g\n", s1, s2, e); } else printf("Lebegőpontos hiba.\n"); return 0; }

5.3. Ellenőrző kérdések

• Mely fejfájlokat kell bekapcsolni, ha nem helyi vezérlésátadást és jel-zéskezelést szeretnénk megvalósítani?

• Mire használatos a nem helyi vezérlésátadás, és mely makrók segítsé-gével valósítható meg?

Page 77: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Nem helyi vezérlésátadás és jel(zés)kezelés

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 77 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 77 ►

• Sorolja fel a szabványos jelzéseket! • Melyik függvénnyel jegyeztethetjük be saját jelzéskezelőnket? Hogyan

lehet a már bejegyeztetett jelzéskezelő helyett ismét az alapértelmezett kezelést kérni?

• Hogyan lehet explicit módon jelzést létrehozni? • Milyen bővítéseket és megszorításokat alkalmaznak az egyes operációs

rendszerek a jelzések kezelésével kapcsolatban? • Milyen szempontokat kell figyelembe venni jelzéskezelő függvény ké-

szítésekor?

5.4. Megoldandó feladatok

Készítsen programot, amely a szabványos jelzések bármelyikének érkezése esetén megjeleníti, milyen jelzést kapott a program! A felhasználó legyen képes explicit módon is jelzéseket küldeni!

Page 78: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 78 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 78 ►

6. Fájlok és könyvtárak kezelése

A többi operációs rendszerrel ellentétben UNIX–ban a C fordító szerves része az operációs rendszernek. Visszagondolva az előző féléves tanulmá-nyokra, s a C nyelv történetére, a UNIX–ot magát C nyelven írták, és a C nyelv azért született, hogy az első UNIX–ot megírhassák vele.

Mi a UNIX operációs rendszer család Linux tagjával dolgozunk. A Linux rendszereken használatos C fordító általában a GNU C (pontosab-ban a GCC), mely magába foglalja az előfeldolgozót, a fordítót és a kap-csoló–szerkesztőt (linker). A GCC igazából a GNU Compiler Collection (GNU fordító gyűjtemény) rövidítése, s a fordító valójában C, C++, Ada, Fortran, Java stb. nyelveken képes dolgozni. Természetesen a forrásfájl kiterjesztése (.c) dönti el, milyen nyelvű legyen a fordítás. C fejlesztőkör-nyezetünk másik fontos része a GNU LIBC (GLIBC) könyvtár fájlok, melyek tartalmazzák az ANSI/ISO szabványos C könyvtári függvényeket. A GLIBC számos fejlesztéssel, bővítéssel is rendelkezik természetesen, ilyenek a különböző POSIX (Portable Operating System Inteface for UNIX) szabványok is. A POSIX az egyik legismertebb alapszoftverrel kapcsolatos szabvány, mely rögzíti, hogy:

• Mit tudjon az alapszoftver. • Milyen interfészei legyenek az alapszoftver moduljainak, és így tovább.

A GCC legegyszerűbb indítása: gcc forras.c,

mely készít egy a.out végrehajtható fájlt. Igazából fordíttathatunk egy forras.o tárgymodult, és ebből készíttethetjük el az a.out végrehajtható fájlt a következőképp:

gcc -c forras.c gcc forras.o

Az említetteknél kicsit bonyolultabb eset a gcc -o fajlnev forras1.c forras2.c ...,

mely több forrásmodult fordít, és készít egy fajlnev nevű végrehajtható fájlt is belőlük. A továbbiak megtudhatók:

man gcc

Ne feledjük azonban, hogy az aktuális könyvtárbeli végrehajtható fájlt kell futtatnunk, azaz az indítás:

Page 79: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 79 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 79 ►

./a.out

UNIX rendszerekben nincsenek meghajtónevek, de a fa-struktúrájú, hierarchikus könyvtárszerkezet használata innét származik.

Kapcsolatba kerülési (például: megnyitás), vagy műveletvégzési (példá-ul: törlés) céllal valahogy hivatkozni kell a fájlra. Csaknem minden fájl rendelkezik nevekkel, még az olyan eszközök is, mint a mágnesszalagos meghajtó, vagy a terminálok. A fájlnév karakterlánc. Meg kell adni, tehát annak a fájlnak a nevét, amelyet meg kívánunk nyitni, vagy amelyen vala-milyen műveleteket szeretnénk végezni.

A fájlnevek szintaxisának megértéséhez tudnunk kell, hogy szervezi könyvtárak hierarchiájává a fájlrendszert a Linux. Van gyökérkönyvtár, melynek jele /. A gyökérkönyvtárban fájlok és könyvtár fájlok lehetnek. A könyvtárakban újabb fájlok és könyvtár fájlok foglalhatnak helyet, és így tovább.

A könyvtár fájltársítási információt tartalmaz más nevekkel ellátott fáj-lokhoz. E társítási információt hivatkozásnak (link), vagy könyvtári be-jegyzésnek (directory entry) nevezzük. Beszélünk „fájlokról a könyvtár-ban”, holott a valóságban a könyvtár csak fájlokra mutató mutatókból áll, és nem tartalmazza magukat a fájlokat. A könyvtári bejegyzés pontos szer-kezetéhez lásd a dirent struktúrát!

A könyvtári bejegyzésbeli fájlnevet szokás fájlnév komponensnek nevezni. Általánosságban a fájlnév egy vagy több / jellel elválasztott kom-ponensből áll. Magyarán: a fájlra, vagy könyvtárra hivatkozhatunk abszolút módon, azaz a gyökérkönyvtárból induló elérési úttal, melynek végén ott van a konkrét fájl, ill. könyvtár neve. Például:

/konyvtar/konyvtar/…/fajlnev

Az elérési út elemeit / jel és nem \ választhatja csak el! Van természetesen beállítható aktuális könyvtár, vagy munkakönyv-

tár (working directory), melyből indulva relatív módon is elérhetők a fáj-lok és a könyvtárak. Az ilyen út tehát nem / jellel kezdődik, és a fájl, ill. könyvtár elérését az aktuális könyvtárból kiindulva írja le:

konyvtar/…/fajlnev

A UNIX fájlrendszere alaposan eltér a FAT16–tól, vagy az NTFS–től. A UNIX fájlrendszer a szuperblokkal kezdődik, mely a fájlrendszer alapvető (méret)adatait rögzíti. Ezt a legfontosabb elem, az ún. inode tábla követi. Majd fájlok, könyvtár fájlok és szabad területek jönnek.

Page 80: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 80 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 80 ►

Az index–node rövidítés inode arra utal, hogy az operációs rendszer a fájlok attribútumait (jellemzőit) tartalmazó inode–okat indexként használ-ja. Az inode tábla fix hosszúságú rekordokból áll, és a fájlrendszerbeli minden fájlhoz és könyvtárhoz egy és csak egy inode bejegyzés tartozik. Magyarán a UNIX rendszerben a fájlt az inode száma (indexe) azonosítja. Az inode rekordban megtalálható a fájl elhelyezési információja és olyan attribútum adatai, mint a fájl mérete, típusa, elérési jogosultságai, tulajdo-nosa stb. Lásd a stat struktúra leírását! Nem tartalmazza azonban a fájl nevét az inode bejegyzés. A név fájlhoz rendelése könyvtár fájlban törté-nik meg. [1]

A [2] jegyzet „MAGAS SZINTŰ BEMENET, KIMENET” fe-jezetében tárgyalt fogalmak és más ismeretek tudását feltételezzük az olvasóról!

Az alacsony szintű B/K–t végző függvények prototípusai az unistd.h

fejfájlban találhatók. Opcionálisan szükség lehet még az errno.h, az fcntl.h, a sys/types.h és a sys/stat.h fejfájlok behozatalára is.

Ezek a függvények az alacsony szintű művelet elvégzéséhez közvetle-nül az operációs rendszert idézik meg (kernel rendszerhívások), és bizto-sítják az alapvető, alkalmazói programok rendelkezésére is álló rendszer képességek legközvetlenebb elérését. Az alacsony szintű bemenet és ki-menet nem pufferelt, nem formázza az adatokat, nem tekinti a mögöttes fájlt sorokból állónak. Szóval: bináris fájlkezelést valósít meg.

Az alacsony szintű B/K függvények fájlleíróval dolgoznak. A fájlleíró egyedi, azonosító jellegű, nem negatív egész érték, melyet megnyitásakor, létrehozásakor rendel a fájlhoz az operációs rendszer, és érvényes marad egészen a fájl lezárásáig. Az alacsony szintű rutinok elérhetik az indító program által megnyitott, szabványos folyamokat is a következő előrede-finiált fájlleírók használatával:

stdin 0 stdout 1 stderr 2 A folyam B/K–ra visszagondolva meg kell jegyeznünk, hogy a

6.1.1. fileno #include <stdio.h> int fileno(FILE *folyam);

Page 81: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 81 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 81 ►

függvény kinyeri, s visszaadja a folyamhoz kapcsolt fájlleírót. Nincs hibás visszatérés. Ha a folyam paraméter nem megnyitott fájlt specifikál, az eredmény előre meghatározhatatlan.

Az fleirok.c példaprogram visszaadja a szabványos C folyamok fájlleíróit. Az eredmény értékeket korábban felsoroltuk!

#include <stdio.h> int main(void){ printf("Az stdin fájlleírója %d.\n", fileno(stdin)); printf("Az stdout fájlleírója %d.\n", fileno(stdout)); printf("Az stderr fájlleírója %d.\n ", fileno(stderr)); return 0; }

A fileno függvényként és makróként is létezik. Ha mindenképp a függvény változatot szeretnénk megidézni, akkor vagy tegyük külön záró-jelbe a függvény nevét, vagy #undef direktívával tegyük definiálatlanná a makrót.

A alacsony szintű B/K függvények hiba bekövetkeztekor ugyancsak az errno globális változó értékét állítják be. Az stdio.h fejfájlt akkor kell csak behozni alacsony szintű B/K függvények használata esetén, ha a program olyan, ebben a fejfájlban definiált konstansokat is igényel, mint az EOF fájlvég jelző például.

6.1.2. Szokásos fájlnév hibák A fájlnév paramétert fogadó függvények esetében az errno a fájlnév szin-taxis megsértése, vagy a fájl keresése során fellépő gondok következtében a következő szimbolikus állandó értékeket veheti fel:

• EACCES: A folyamatnak nincs keresési joga (search permission) a fájlnév valamely könyvtár komponensére.

• ELOOP: Túl sok közvetett hivatkozást kellett feloldani a fájlnév kere-sése közben. Szóval valószínűleg hurok van a könyvtárszerkezetben. A ciklikus hivatkozásokat elkerülendő a rendszer mesterségesen korlá-tozza a közvetett link feloldások számát fájlnév keresés közben. A sys/param.h fejfájlban definiált MAXSYMLINKS makró rögzíti ezt a maximális értéket.

• ENAMETOOLONG: Ha a fájlnév teljes hossza nagyobb PATH_MAX–nál, vagy egy egyedi fájlnév komponens haladja meg a

Page 82: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 82 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 82 ►

NAME_MAX korlátot. A GNU rendszerben nincsenek ilyen korláto-zások, de más fájlrendszerekben ez a lehetőség fennáll.

• ENOENT: Az útban egy könyvtár komponens nem létezik. Ez a probléma akkor is fellép, ha a közvetett hivatkozás komponens célfájl-ja nincs meg.

• ENOTDIR: A fájlnévben könyvtár komponensként hivatkozott fájl létezik ugyan, de nem könyvtár.

6.1.3. Érvénytelenedési (cancellation) pont Az érvénytelenedés lehetővé teszi a szál számára, hogy a folyamatbeli bármely alkalmazási szál futását befejeztesse. Az érvénytelenedés akkor hasznos, mikor egy vagy több szál további műveletvégzése nem kívánatos, vagy szükségtelen. Például aszinkron módon generált érvénytelenedési szituáció, mikor a felhasználó be akar zárni bizonyos futó művelet, vagy ki kíván lépni belőle. Másik példa lehet egy, több szállal keresett megoldás elérése valamely (mondjuk: labirintus) problémára. A megoldáshoz az egyik szál jutott el. A többi szál további futása (megoldás keresése) teljesen felesleges, mindőjüket törölni kell.

A rendszer definiál bizonyos pontokat, amikor az érvénytelenedés, tör-lés bekövetkezhet, ill. magunk is képezhetünk érvénytelenedési pontokat. Bizonyos függvények (Ezt az illető rutin leírásánál is megemlítjük!) érvénytelenedési (cancellation) pontot képeznek többszálas programok-ban. Probléma akkor áll fenn, mikor a szál néhány erőforrást (memória, fájlleírók, szemaforok, stb.) allokált a rutin hívása idején. Ha a szál töröltté válik, az erőforrások allokáltak maradnak a program végéig. Ezt elkerülen-dő a függvényhívást érvénytelenedési kezelőkkel kell megvédeni.

A jegyzetben helytakarékossági okból csupán a 32 bites fájlméretű fájlrendszereket kezelő függvényekkel foglalkozunk. A Linux rendszerek természetesen támogatják a 64 bites méretű fájlrendszereket (large file system) is úgy, hogy

• a szükséges adattípusoknak létezik 64 bites változata is (például a könyvtár fájlok rekordjainak beolvasásához használatos dirent struk-túrából létezik dirent64 variáns is), és

• a megfelelő függvényeknek elkészítették a 64 bites verzióját is. Például a könyvtáraknál maradva, a rekord beolvasását a readdir rutin végzi, de 64 bites esetben a readdir64.

Page 83: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 83 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 83 ►

Felkérjük az olvasót, hogy e változatoknak nézzen utána az info, a man, vagy más programokkal, ill. az irodalomban!

6.2. Fájlok megnyitása és bezárása A fejezetben a fájlleírót használó fájlt megnyitó és lezáró primitívekről

lesz szó. A függvények hívásához bekapcsolandók az unistd.h és az fcntl.h fejfájlok.

6.2.1. mode_t típus A fájl módok (fájltípus és elérési jogosultság) ábrázolására használatos egész típus. A GNU rendszerben az unsigned int típussal ekvivalens. Lásd a stat struktúra leírását!

6.2.2. RLIMIT_NOFILE A folyamat egyszerre legfeljebb RLIMIT_NOFILE fájlt nyithat meg, azaz legfeljebb ennyi fájlleíróval rendelkezhet. E határ túllépésekor az open sikertelen, és az errno EMFILE. E korlát az erőforrások használa-tának korlátozását végző rutinokkal változtatható.

6.2.3. open int open(const char *fajlnev, int jelzok<, mode_t mod>);

A függvény megnyitja a fajlnev fájlt a jelzok paraméterrel meghatározott módon, és visszaad hozzá egy fájlleírót. Sikertelen esetben –1 a visszatéré-si érték, és az errno a szokásos fájlnév hibákon túl még következő értékek egyike lehet:

• EACCES: A fájl létezik, de nem olvasható, írható úgy, ahogyan a jelzok paraméter megköveteli. A fájl nem létezik, de nem hozható létre, mert a könyvtár nem írható. Szóval az a probléma, hogy a folyamat nem rendelkezik a kellő jogokkal.

• EEXIST: O_CREAT és O_EXCL jelzőket specifikáltak, de a fajlnev fájl már létezik.

• EINTR: A megnyitási műveletet jel (signal) szakította meg. • EISDIR: A jelzok írási elérést specifikál, de a fajlnev könyvtár. • EMFILE: A folyamat túl sok fájlt nyitott meg. Nem áll rendelkezésre

további fájlleíró.

Page 84: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 84 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 84 ►

• ENFILE: Az egész rendszer, vagy talán a könyvtárat tartalmazó fájl-rendszer pillanatnyilag nem képes további fájlok megnyitására. Ez a probléma GNU rendszerben nem történhet meg.

• ENOENT: A fájl nem létezik, és O_CREAT jelzőt nem specifikál-tak.

• ENOSPC: A könyvtár, vagy a fájlrendszer – mely az új fájlt tartal-mazná majd – nem bővíthető, mert nincs elég üres lemezterület.

• ENXIO: A jelzok paraméterben bebillentették mind az O_NONBLOCK–t, mind az O_WRONLY–t, de a fajlnevvel elért fájl cső, melyet egyetlen folyamat sem nyitott meg olvasásra.

• EROFS: A fájl csak olvasható fájlrendszeren helyezkedik el, és az O_WRONLY, az O_RDWR és az O_TRUNC valamelyikét bebil-lentették a jelzok paraméterben, vagy O_CREAT–et és a fájl még nem létezik.

Mikor az open megnyitja a fajlnev fájlt, akkor egyben előkészíti olvasás-ra vagy írásra, ahogyan a fájl állapot jelzok paraméterben a megengedett műveletek típusa specifikálja. A jelzok az fcntl.h fejfájlban definiált szim-bolikus konstansok kombinációjából képzett egész kifejezés. A fájl állapot-jelzők három csoportba sorolhatók:

• Fájl elérési mód jelzők. • B/K műveleti mód jelzők. • Nyitás idejű jelzők.

Fáj elérési mód jelzők meghatározzák, hogy a fájlleíró a fájl milyen típusú elérésére használható: olvasásra, írásra, vagy mindkettőre. A GNU rendszerben egy jelző a fájl programkénti végrehajtását engedélyezheti. A fájl elérési módja zérus is lehet, amikor is B/K művelet nem engedélyezett a fájlra, de más művelet (például: fchmod) megvalósítható. A fájl elérési módjának beállítása aztán a megnyitás után nem változtatható. A fájl eléré-si módjának rögzítéséhez specifikálni kell valamelyik jelzőt. Nincs alapér-telmezett érték az elérési módra! Ha jelzok paraméterként több szimboli-kus konstanst szeretnénk kombinálni, akkor vagy művelet írandó közéjük. A szimbolikus állandók:

• O_RDONLY: Csak olvasásra nyitja meg a fájlt. • O_WRONLY: Csak írásra nyitja meg a fájlt.

Page 85: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 85 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 85 ►

• O_RDWR: A fájlt megnyitja mind olvasásra, mind írásra. A jelző O_RDONLY|O_WRONLY tulajdonképpen.

• O_EXEC: A fájl megnyitása végrehajtásra. Ez a jelző nem szabvá-nyos, csak GNU C könyvtári bővítés!

B/K műveleti mód jelzők az olvasási és írási tevékenységre hatnak. E jelzők megnyitási értéke később változtatható fcntl–lel.

• O_APPEND: Megnyitás hozzáfűzésre, azaz minden írási művelet előtt a fájl végére mozgatja a fájlmutatót a rendszer tekintet nélkül az aktuális fájlpozícióra. Ez a megbízható módja a fájl bővítésének. Hoz-záfűzéses üzemmódban az adatok írása garantáltan az aktuális fájlvé-gen történik tekintet nélkül arra, hogy más folyamatok is írnak–e a fájlhoz. Megfordítva: ha egyszerűen a fájl végére pozícionálunk, s aztán írunk, akkor más folyamat pozícionálásunk és írásunk közt bővítheti a fájlt, s adataink valahova a valódi fájlvég elé kerülhetnek.

• O_NONBLOCK: A megnyitás nem blokkoló módban történjék. A blokkoló mód azt jelenti, hogy amíg a kért B/K vagy fájlt záró művelet be nem fejeződik a rendszer blokkolja (felfüggeszti) a folyamat futását. Az O_NONBLOCK–ot bebillentve a B/K igény hibát jelezve tér vissza, ha a művelet nem realizálható azonnal. Megjegyezzük, hogy az O_NONBLOCK fájlnév transzlációs jelzőként is használatos.

A további B/K műveleti mód jelzők mind GNU bővítések.

• O_ASYNC: Megnyitás aszinkron bemeneti módban. Az input meg-szakítás vezérelt, azaz a rendszer SIGIO jelet generál, ha a bemenet rendelkezésre áll.

• O_FSYNC: Megnyitás szinkron írásra. A meghívott írási művelet addig nem tér vissza, míg az adatok valóban ki nem kerültek a lemezre.

• O_NOATIME: Megnyitás a fájl utolsó hozzáférési idejének frissítése nélkül. Nem keverendő az utolsó módosítás időpontjával! Kimentést (backup) készítő programok használják a bitet, hogy a fájl kimentése ne számítson olvasásnak. A bit használatához legalább a fájl tulajdono-sának kell lenni!

Nyitás idejű jelzők a megnyitás viselkedésének részleteit vezérlik, s nem őrzi meg őket a rendszer az open hívás után az egyetlen O_NONBLOCK kivételével, mely B/K műveleti mód jelző is. Kétféle

Page 86: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 86 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 86 ►

nyitás idejű opciócsoport létezik: a nyitás idejű tevékenység, ill. a fájlnév transzlációs jelzők.

• O_TRUNC: Az egyetlen POSIX szabványos nyitás idejű tevékeny-ség jelző, melynek hatására megnyitása után zérusméretűre csonkítja a rendszer a fájlt. Az opció csak szabályos fájlokkal használatos, s nem speciálisakra, mint a könyvtárak, vagy a csövek. A fájlhoz írási jogo-sultsággal kell rendelkezni, de nem szükséges írásra megnyitni a fájlt. Nem túl értelmes tevékenység persze, olvasásra nyitni a fájlt, majd rög-tön zérusméretűre vágni, s aztán reménykedni, hogy majd valamit le-het belőle olvasni! Az O_TRUNC O_CREAT–tel megnyitja a létező fájlt, vagy újat hoz létre. Az O_TRUNC jelző megsemmisíti a meg-adott, már létező fájl tartalmát! A fájlnév transzlációs jelzők befolyásolják, hogy az open hogyan ke-

resse, lokalizálja a fájlt, és hogy létrehozhatja–e.

• O_CREAT: Új fájlt hoz létre, ha még nem létezik, és nyit meg jobbá-ra írásra. Az olvasásra nyitásnak nem sok értelme lenne, hisz üres fájl-ból nem sok mindent lehet olvasni. Nincs hatása, ha a fajlnev paramé-terrel specifikált fájl már létezik. A mod paramétert kötelező meg-adni, ha O_CREAT–et előírtak!

• O_EXCL|O_CREAT: Megnyitás akkor, ha a fájlt létre lehet hozni. Hibát jelezve tér vissza az open, ha a fajlnev paraméterrel megadott fájl létezik. Az O_EXCL–t csak O_CREAT–tel együtt van értelme hasz-nálni.

• O_NONBLOCK: Megakadályozza az open–t, hogy túl sokáig blok-kolja a folyamatot a fájl megnyitására várva. A jelző csak bizonyos fájl-típusoknál értelmes. Rendszerint olyan eszközöknél, mint például a so-ros port. Ha a fájllal kapcsolatban nincs értelme a jelzőnek, akkor ár-talmatlan és elhagyja a rendszer. Tudjuk, hogy az O_NONBLOCK B/K műveleti mód jelzőként is használatos, azaz ebben az értelmében megadva a B/K is nem blokkoló módban történik. Ha blokkoló B/K–t szeretnénk, akkor a sikeres O_NONBLOCK jelzős open után kap-csoljuk ki e bitet fcntl hívással.

• O_NOLINK: Ha a fájl közvetett hivatkozás, akkor magát a szimboli-kus linket kell megnyitni az általa hivatkozott fájl helyett. A közvetett hivatkozás ily módon megnyitva a hivatkozott könyvtár bejegyzés el-érési útját és nevét tartalmazza. A bit GNU bővítés.

Page 87: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 87 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 87 ►

A mod paraméter csak a fájl létrehozatalakor (O_WRONLY| O_CREAT|O_TRUNC) használatos, de bármikor máskor is megadha-tó. Ha a fájl már létezik, az open elhagyja a modot. Máskülönben a mod a fájl elérési jogosultsági beállításait határozza meg.

Ha a fajlnev paraméterrel specifikált fájl nem létezik, akkor az O_WRONLY|O_CREAT|O_TRUNC–os open (az elavult creat) a mod jogosultsági beállításokat kielégítő, írásra nyitott, új fájlt hoz létre. Ha a fájl már létezik, és jogosultsági beállításai megengedik az írást, akkor ez az open zérusméretűre csonkítja korábbi tartalmát megsemmisítve, és írásra nyitja meg. A mod jogosultsági beállítások csak újonnan létrehozott fájlokra érvényesek, s csak akkor jutnak érvényre, ha a fájlt először lezár-ják. A mod a sys/stat.h–ban definiált, és a stat struktúránál ismertetett szimbolikus állandókból vagy művelettel létrehozott kifejezés lehet. Példá-ul: S_IRWXU| S_IRGRP| S_IXGRP| S_IROTH.

Az O_WRONLY|O_CREAT|O_TRUNC–os open az aktuális fájl létrehozási maszkot alkalmazza a modra a jogosultságok tényleges beállítása előtt (lásd umask!).

Az open függvény érvénytelenedési (cancellation) pont többszálas programokban.

Az open az alapvető primitív a folyamlétesítő fopen és freopen függ-vényekhez.

A megnyitott fájl pozíciója (fájlmutatója) kezdetben a fájl elején áll.

Az open.c példa bemenetre nyitja meg az open.c fájlt és kimenetre az OPEN.OUT–ot, s aztán bezárja őket. Az OPEN.OUT elérési jogosult-sága: –rwxr–xr––. Ha a mod paramétert elhagytuk volna, akkor –rw–r–x––T lenne.

/* open.c példa. */ #include <stdio.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <sys/stat.h> #define INP "OPEN.C" /* open.c-re cserélve sikerülni fog a megnyitás! */ #define OUT "OPEN.OUT" int main(void){ int fh; /* Fájlleíró. */ printf("Fájlok megnyitása bemenetre, kimenetre:\n");

Page 88: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 88 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 88 ►

if((fh = open(INP, O_RDONLY )) == -1) fprintf(stderr, "A(z) %s fájl megnyitása sikertelen." "\n\tA hiba száma: %d\n\tA hiba szövege:%s\n", INP, errno, strerror(errno)); else{ printf("Sikerült a(z) %s fájl megnyitása!\n", INP); close(fh); } if((fh = open(OUT, O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH)) == -1) perror("Az output fájl megnyitása nem sikerült.\n" "\tA hiba szövege:"); else{ printf("Sikerült a(z) %s fájl megnyitása!\n", OUT); close(fh); } return 0; }

Az eredmény: Fájlok megnyitása bemenetre, kimenetre: A(z) OPEN.C fájl megnyitása sikertelen. A hiba száma: 2 A hiba szövege:No such file or directory Sikerült a(z) OPEN.OUT fájl megnyitása!

6.2.4. close int close(int leiro);

függvény zárja a leiro leírót. A fájl lezárásnak az alábbi következményei vannak:

• A fájlleíró felszabadul, s nem használható a továbbiakban a fájl eléré-séhez.

• A folyamat fájlon levő rekord zárait is feloldja a rendszer. • Ha a csővel kapcsolatban levő, összes leírót lezárták, bármely belőle

kiolvasatlan adatot elvet a rendszer.

Sikeres esetben zérust kapunk a rutintól, s a –1 visszatérési érték jelzi ez esetben is a hibát. Az errno tartalmazza most is pontos hiba okot:

• EBADF: Érvénytelen fájlleíró paraméter. • EINTR: A lezárási műveletet jel (signal) szakította meg.

A close függvény érvénytelenedési (cancellation) pont többszálas programokban.

Szemléltető példaként lásd az open.c–t előbbre!

Page 89: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 89 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 89 ►

6.3. Olvasás és írás A fejezetben a fájlleírót használó B/K–t megvalósító rutinokat ismer-

tetjük. A függvények hívásához be kell hozni az unistd.h fejfájlt.

6.3.1. ssize_t típus Az adattípus az egyetlen művelettel beolvasható, vagy kiírható blokkok

méretét ábrázolja. Hasonló a size_t típushoz, de előjeles. Az int típus megfelel a célnak például.

6.3.2. read ssize_t read(int leiro, void *puffer, size_t szlo);

A read maximálisan szlo bájtot olvas be a pufferbe a leiro paraméterrel meg-határozott, nyitott fájlból.

A beolvasás eredménye nem karakterlánc, s nincs lánczáró null ka-rakter a végén!

Az olvasási művelet az aktuális fájlpozíciótól kezdődik. Az olvasás végrehajtása után aztán a fájlmutató a következő, be nem olvasott bájtra áll a fájlban. A függvény a valóban beolvasott bájtok számával tér vissza, ami a szlo értékénél kevesebb is lehet, és ez nem minősül feltétlenül hibá-nak. Hogyan lehetséges ez?

• Ha a fájl végén olvasva egyszerűen nála kevesebb bájt volt már a fájlban. • Ha nem áll közvetlenül ennyi bájt rendelkezésre. Szóval a pontos vi-

selkedés nagyban függ a fájl fajtájától. • Ha a függvény a fájl végén kísérli meg az olvasást, zérust kapunk vissza

tőle. Fájl végén többször híva a read–et, mindig zérust kapunk tőle, és nem történik semmi. Zérust persze úgy is kaphatunk, hogy a szlo pa-raméter volt nulla.

Ha a read legalább egy karaktert olvasott, akkor nincs más mód a fájl vég megállapítására, mint az újabb, zérus visszatérési értékű olvasás.

Ha a művelet végrehajtása során hiba lépett fel, negatív értéket szolgál-tat a read, és megfelelően beállítja az errno–t:

• EBADF: A leiro érvénytelen, vagy a fájl nem olvasásra nyitott. • EAGAIN: Normál esetben, ha nem áll input közvetlenül rendelkezés-

re, a read vár a bemenetre. Ha azonban O_NONBLOCK jelzőt állí-tottak be a fájlra, a művelet rögtön visszatér anélkül, hogy olvasott volna, s jelzi ezt a hibát. Bármely EAGAIN–hez vezető feltétel e he-

Page 90: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 90 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 90 ►

lyett sikeres, a kért bájtszámnál kevesebbel visszatérő olvasással is vég-ződhet. Aztán az azonnal ismételt read hívás eredményez EAGAIN–t.

• EINTR: Az olvasási műveletet jel (signal) szakította meg a bemenetre való várakozás közben. A jel nem feltétlenül okoz EINTR–es read–et, hanem helyette sikeres, a kért bájtszámnál kevesebbel visszatérő le-het az olvasás.

• EIO: A legtöbb eszközre és lemezes fájlra ez a hibakód hardverhibát jelez.

A függvény érvénytelenedési (cancellation) pont többszálas programokban. A read alapvető primitív minden folyamból olvasó (például: fgetc)

függvény számára.

Az olvas.c példa megnyitja saját magát, s aztán kísérletet tesz 60000 bájt beolvasására, és végül megjelenteti a ténylegesen beolvasott bájtok számát:

/* olvas.c példa. */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #define INP "olvas.c" char puffer[60000]; int main(void){ int fh; /* Fájlleíró. */ unsigned int nbajt = 60000; int beobajt; /* A fájl megnyitása csak bemenetre: */ printf("Olvasás a(z) %s fájlból:\n", INP); if((fh = open(INP, O_RDONLY )) == -1){ fprintf(stderr, "A(z) %s fájl megnyitása sikertelen." "\n\tA hiba száma: %d\n\tA hiba szövege:%s\n", INP, errno, strerror(errno)); exit(1); } /* Beolvasás: */ if((beobajt=read(fh, puffer, nbajt))<=0) perror("Probléma az olvasásnál.\n\tA hiba szövege:"); else printf("%u bájt beolvasva a fájlból.\n", beobajt); close(fh); return 0; }

Az eredmény: Olvasás a(z) olvas.c fájlból:

Page 91: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 91 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 91 ►

714 bájt beolvasva a fájlból.

6.3.3. off_t típus Ez a fájlméret ábrázolására használatos aritmetikai típus. A GNU

rendszerben ez azonos az fpos_t–vel, praktikusan long int.

6.3.4. pread ssize_t pread(int leiro, void *puffer, size_t szlo, off_t

offset);

A pread működése és visszaadott értéke tulajdonképpen egyezik a read–ével. Az eltérés az új negyedik paraméter, s annak kezelése. Az adat-blokkot a rutin nem a leiro leírójú fájl aktuális fájlpozíciójától olvassa, ha-nem az offset pozíciótól kezdődően. A művelet hatására a leirohoz tartozó fájlmutató nem változik, azaz megtartja a hívás előtti értékét.

Mondottuk már, hogy a pread visszaadott értéke is a valóban beolva-sott bájtok száma. Hiba esetén szintén ugyanúgy tér vissza, mint a read és az errno–ban beállított hibakódok is megvannak. Ez utóbbiakhoz azon-ban két újabb csatlakozik:

• EINVAL: A offset értéke negatív és ez által érvénytelen.

• ESPIPE: A leiro csőhöz kapcsolt, és ez az eszköz nem teszi lehetővé a fájlmutató pozícionálását.

Ha az olvas.c példában a read hívást átírjuk a következőre, ugyanazt az eredményt érjük el:

if((beobajt=pread(fh, puffer, nbajt, 0))<=0)

6.3.5. write ssize_t write(int leiro, const void *puffer, size_t

szlo);

A függvény szlo bájtot ír a pufferből a leiro paraméterrel meghatározott, nyitott fájlba.

A pufferbeli adat nem feltétlenül karakterlánc, ill. ha az, a null karak-ter outputja is megtörténik, mint bármely más karakteré.

Az írási művelet a megadott fájl fájlmutatója aktuális pozíciójától kez-dődik Ha a fájl hozzáfűzésre nyitott, akkor az írás a mindenkori fájlvégtől indul. A művelet végrehajtása után a fájlmutató az aktuálisan kiírt bájtok számával nő.

Page 92: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 92 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 92 ►

Sikeres esetben a write visszaadja az aktuálisan kiírt bájtok számát. Ez lehet a szlo értéke, de lehet kisebb is ennél. A program célszerűen olyan ciklusban hívja a write–ot, mely addig iterál, míg minden adat kiírása meg nem történik.

Ha a write visszatér, az adatok kikerültek a kiírandó várakozási sorba, és máris visszaolvashatók, holott az operációs rendszer nem feltétlenül írta ki őket fizikailag is a fájlba. Ha azt szeretnénk, hogy a write a kiírandó adatokat fizikailag is kivigye a fájlba visszatérése előtt, akkor használjunk megnyitáskor O_FSYNC B/K műveleti mód jelzőt. A másik lehetőség az fsync, mely biztosítja az adatok fizikai kikerülését a fájlba. Szükségtelenül ne alkalmazzuk azonban egyik módszert sem, mert a rendszer hatéko-nyabb, ha hagyjuk, hogy az összevárt kiírandó adatok fizikai kivitelét egy neki alkalmas pillanatban teheti meg. Normál esetben ekkor is kikerülnek az adatok a fizikai tárolóra egy–két percen belül.

Ha a write sikertelen, a hibát jelző visszatérési értéke –1, és az errno a következő szimbolikus állandók valamelyike:

• EAGAIN: Normál esetben a write addig blokkolja a folyamatot, míg az írási művelet nem kész. Ha azonban O_NONBLOCK jelzőt állí-tottak be a fájlra, a függvény rögtön visszatérhet adat írás nélkül ezzel a hibával. A write–ot célszerű újrahívni, s akkor az írás esetleg elvégezhető.

• EBADF: A fájlleíró paraméter érvénytelen, vagy a fájl nem írásra nyitott. • EFBIG: Az írás elvégzése után akkorára nőne a fájl, amekkorát a

rendszer már nem tud kezelni. • EINTR: Az írási műveletet jel (signal) szakította meg befejeződésére

való várakozás közben. A jel nem feltétlenül okoz EINTR–es write–ot, hanem helyette sikeres, a kért bájtszámnál kevesebbel visszatérő le-het a művelet.

• EIO: A legtöbb eszközre és lemezes fájlra ez a hibakód hardverhibát jelez. • ENOSPC: Nincs elég hely a lemezen a művelet végrehajtásához. Pre-

cízebben a fájlt tartalmazó eszköz megtelt. • ESPIPE: Ez a hiba akkor következik be, ha a folyamat olyan csőbe ír,

melyet semmilyen folyamat sem nyitott meg olvasásra. Ha ez történik, akkor SIGPIPE jel is megy a folyamatnak.

Ha más módon nem akadályoztuk meg az EINTR hiba bekövetkezé-sét, akkor minden hibával záruló írás után ellenőrizni kell az errno–t, és ha

Page 93: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 93 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 93 ►

EINTR, egyszerűen meg kell ismételni az írást. Ezt a legegyszerűbben a Linux rendszerekben rendelkezésre álló

6.3.6. TEMP_FAILURE_RETRY makró TEMP_FAILURE_RETRY(kifejezes)

segítségével tehetjük meg. A makró kiértékeli a kifejezest. Ha sikertelen és EINTR hibakódot jelez, akkor mindaddig újra kiértékeli a makró, míg e helyzet nem áll fenn. A makró praktikusan a paraméterében megadott függvényhívást addig ismétli, míg az EINTR hibakódot ad. #define _GNU_SOURCE #include <errno.h> #include <unistd.h> ssize_t beobajtok; beobajtok=TEMP_FAILURE_RETRY(write(leiro, puffer, szlo));

A TEMP_FAILURE_RETRY visszaadott értéke a kifejezes értéke. Visszatérve a write–hoz! A függvény érvénytelenedési (cancellation) pont többszálas programokban. A write alapvető primitív minden folyamba író függvény (például:

fputc) számára.

Az ir.c program kimenetre nyitja meg az irt.o fájlt, s néhány bájtot ír ki bele:

/* ir.c példa. */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #define OUT "irt.o" char puffer[] = "Ez a \'write\' függvény kipróbálása."; int main(void){ int fh; /* Fájlleíró. */ unsigned int kiirtbajt; /* A fájl megnyitása kimenetre: */ printf("Irás a(z) %s fájlba:\n", OUT); if((fh = open(OUT, O_RDWR|O_CREAT)) == -1){ fprintf(stderr, "A(z) %s fájl megnyitása sikertelen." "\n\tA hiba száma: %d\n\tA hiba szövege:%s\n", OUT, errno, strerror(errno)); exit(1); } /* Kiírás: */

Page 94: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 94 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 94 ►

if((kiirtbajt=TEMP_FAILURE_RETRY(write(fh, puffer, sizeof(puffer))))<=0) perror("Probléma az írásnál.\n\tA hiba szövege:"); else printf("%u bájtot írt a fájlba.\n", kiirtbajt); close(fh); return 0; }

A kimenet: Irás a(z) irt.o fájlba: 40 bájtot írt a fájlba.

6.3.7. pwrite ssize_t pwrite(int leiro, const void *puffer, size_t

szlo, off_t offset);

A pwrite működése és visszaadott értéke tulajdonképpen egyezik a write–ével. Az eltérés az új negyedik paraméter, s annak kezelése. Az adatblokkot a rutin nem a leiro leírójú fájl aktuális fájlpozíciójától írja, ha-nem az offset pozíciótól kezdődően. A művelet hatására a leirohoz tartozó fájlmutató nem változik, azaz megtartja a hívás előtti értékét.

Mondottuk már, hogy a pwrite visszaadott értéke is a valóban kiírt bájtok száma. Hiba esetén szintén ugyanúgy tér vissza, mint a write és az errno–ban beállított hibakódok is megvannak. Ez utóbbiakhoz azonban két újabb csatlakozik:

• EINVAL: A offset értéke negatív és ez által érvénytelen. • ESPIPE: A leiro csőhöz kapcsolt, és ez az eszköz nem teszi lehetővé a

fájlmutató pozícionálását.

Ha az ir.c példában a write hívást átírjuk a következőre, ugyanazt az eredményt érjük el:

if((kiirtbajt=TEMP_FAILURE_RETRY(pwrite(fh, puffer, sizeof(puffer), 0)))<=0)

6.4. Pozícionálás a fájlban Ahogyan a folyam fájlpozíciója állítható az fseek–kel, úgy a leírós fájlkeze-lés fájlmutatója lseek–kel pozícionálható. A fájlpozíció meghatározza a következő read vagy write művelet helyét a fájlban. A fájlpozíció a fájl elejétől számított bájtok száma, s ez tulajdonképpen a fájlmutató értéke. Megnyitás után a fájlmutató zérus, azaz a fájl elején van.

A függvények hívásához be kell hozni az unistd.h fejfájlt.

Page 95: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 95 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 95 ►

6.4.1. lseek off_t lseek(int leiro, off_t offset, int honnan);

A függvény a nyitott, leiro leírójú fájl mutatóját mozgatja el offset bájtnyit a honnan kezdeti pozíciótól. A következő művelet a fájlon ezen az új pozíci-ón következik be. Az stdio.h fejfájlban definiált konstansok egyike lehet csak a honnan paraméter:

• SEEK_SET: A fájl kezdetétől. • SEEK_CUR: A fájlmutató aktuális pozíciójától. Az offset lehet pozitív

és negatív. • SEEK_END: A fájl végétől. A negatív offset az aktuális fájl kiterjedé-

sen belül, a pozitív viszont az aktuális fájlvégen túl határozza meg az új fájlmutató értéket.

A fájlmutató tehát beállítható az aktuális fájlvégen túlra. Ez maga nem teszi hosszabbá a fájlt, mert az lseek semmilyen körülmények között sem módosítja az állományt. A rákövetkező írás ettől a pozíciótól azonban kibővíti majd a fájlt. A korábbi fájlvég és az új pozíció közti bájtokat mind zérussal tölti fel a rendszer. A fájl ilyetén bővítése „lyukat” hozhat létre: a tiszta zérus blokkot nem foglalja le a rendszer a lemezen, s így a fájl keve-sebb helyet foglalhat el, mint amekkorának tűnik. Ez a fájl a

6.4.2. ritka (sparse) fájl A ritka fájlban, tehát lehetnek zérustartalmú „lyukak”, melyek aktuálisan nem foglalnak helyet a lemezen.

Térjünk vissza az lseek–hez! Sikeres esetben az lseek–től a fájlmutató fájl elejétől számított, bájtban

mért, új pozícióját kapjuk vissza. Ez lehetőséget ad az aktuális fájlpozíció lekérdezésére:

lseek(leiro, 0, SEEK_CUR);

A –1 visszatérési érték hibát jelez. Az errno:

• EBADF: A leiro paraméter érvénytelen fájlleíró. • EINVAL: A honnan értéke érvénytelen, vagy az offset–tel specifikált

pozíció a fájl elé mutat. • ESPIPE: Ez a hiba akkor következik be, ha a leiro pozícionálni képte-

len eszköznek (cső, terminál, nyomtató stb.) felel meg.

Page 96: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 96 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 96 ►

Ha hozzá szeretnénk fűzni a fájlhoz, akkor a fájlmutató írást meg-előző SEEK_END–es aktuális fájlvégre állítása nem elégséges és nem megfelelő. Másik folyamat több adatot írhat a fájlhoz a mi pozícionálásunk után és az írásunk megkezdődése előtt. Aztán mi következvén felülírjuk a másik folyamat által kitett adatokat, sőt esetleg tovább is bővíthetjük a fájlt. A hozzáfűzéses fájlhoz írás szabályosan csak O_APPEND B/K műveleti módban valósítható meg.

A függvény érvénytelenedési (cancellation) pont többszálas programokban. Az lseek alapvető primitív a folyamot pozícionáló fseek, ftell és

rewind függvények számára.

Az lseek.c program először megnyitja az lseek.c fájlt, s aztán az lseek függvénnyel a fájl elejére, egy kis olvasás utáni aktuális pozícióra, és legvé-gül a fájl végére pozícionál:

#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> int main(void){ int fh; /* Fájlleíró. */ off_t poz; /* A fájlpozíció. */ char puffer[10]; /* Az input puffer. */ printf("Pozícionálás az \"lseek.c\" fájlban:\n"); if((fh=open("lseek.c", O_RDONLY)) == -1){ perror("A megnyitás sikertelen!"); exit(1); } /* Pozícionálás a fájl elejétől: */ if((poz=lseek(fh, 0, SEEK_SET)) == -1) perror("Az lseek nem sikerült a fájl elejétől"); else printf("Fájlmutató a fájl elejétől való " "pozícionálás után =%5ld\n", poz); /* A fájlmutató kis mozgatása: */ if(TEMP_FAILURE_RETRY(read(fh, puffer, 10)) == -1){ perror("Olvasási hiba!"); exit(1); } /* Az aktuális pozíció lekérdezése: */ if((poz=lseek(fh, 0, SEEK_CUR)) == -1) perror("Az lseek az aktuális pozícióhoz nem sikerült" ); else printf("Fájlmutató az aktuálistól induló "

Page 97: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 97 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 97 ►

"pozícionálás után =%5ld\n", poz); /* Fájl végére állás: */ if((poz=lseek(fh, 0, SEEK_END)) == -1) perror("Az lseek a fájl végére nem sikerült"); else printf("Fájlmutató a fájl végétől való " "pozícionálás után =%5ld\n", poz); close(fh); return 0; }

Az eredmény kimenet a következő: Pozícionálás az "lseek.c" fájlban: Fájlmutató a fájl elejétől való pozícionálás után = 0 Fájlmutató az aktuálistól induló pozícionálás után = 10 Fájlmutató a fájl végétől való pozícionálás után = 1225

Egynél többször megnyitva, vagy a dup függvénnyel duplikálva több leíróval is rendelkezhetünk ugyanarra a fájlra. A megnyitással létesített leírók külön, egymástól független fájlpozícióval bírnak. Az egyik leíróval végrehajtott lseek–nek nincs hatása a többi leíróra. A rövidség kedvéért elhagyva a valódi programban elengedhetetlen hibaellenőrző kódot, a kö-vetkező példarészlet a huhu.txt fájl első tíz bájtját fogja beolvasni: int fh1, fh2; char puff[10]; /* . . . */ fh1=open("huhu.txt", O_RDONLY); fh2=open("huhu.txt", O_RDONLY); /* . . . */ lseek(fh1, 100, SEEK_SET); read(fh2, puff, 10); /* . . . */

A duplikálással létrehozott leírók ezzel ellentétben az eredeti leíróval közös fájlpozíción osztoznak. Az egyik leíróval végrehajtott bármilyen fájlmutatót változtató művelet, ugyanúgy hat a többi duplikált leíróra. Az első read 10 karaktert olvas a huhu.txt fájl 100. karakterétől kezdve, a második ugyancsak tíz bájtot olvas a 110. pozíciótól, s legvégül a fájlmuta-tó éppen 120: int fh1, fh2, fh3; char puff[10]; /* . . . */ fh1=open("huhu.txt", O_RDONLY); fh2=dup(fh1); fh3=dup(fh2); /* . . . */ lseek(fh3, 100, SEEK_SET); read(fh2, puff, 10);

Page 98: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 98 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 98 ►

/* . . . */ read(fh1, puff, 10);

6.4.3. dup Második leírót rendel a leiro paraméterrel meghatározott, nyitott fájlhoz a

int dup(int leiro);

függvény. Visszaadja a következő rendelkezésre álló, új fájlleírót, mely tulajdonságok tekintetében a leiro pontos másolata. Hiba esetén a rutin –1–et szolgáltat, és beállítja a globális errno változót a következő értékek va-lamelyikére:

• EBADF: Érvénytelen fájlleíró. • EMFILE: Nem áll rendelkezésre fájlleíró. Túl sok nyitott fájl van, lásd

az RLIMIT_NOFILE korlátot!

A függvény olyan előredefiniált fájlleíró hozzárendelésére is használha-tó, mint az stdout–é. Műveletek aztán a fájlon mindkét fájlleíró segítségé-vel kivitelezhetők. A fájl megengedett elérés típusát nem befolyásolja az új fájlleíró hozzárendelése.

6.5. Fájl attribútumok A fejezetben szereplő rutinok nem megnyitott fájlokkal dolgoznak, ha mégis, akkor az illető függvény leírásánál ezt külön kihangsúlyozzuk.

A fejezet típusainak, szimbolikus állandóinak és függvényeinek haszná-latához behozandók az unistd.h, a sys/types.h és a sys/stat.h fejfájlok.

Lássuk előbb a használatos típusokat és a stat struktúrát!

6.5.1. blkcnt_t típus A fájl blokkszámlálójának (szektorszámlálójának) ábrázolására használatos, aritmetikai adattípus. GNU rendszerben: unsigned long int.

6.5.2. dev_t típus A fájl adathordozójának eszköz számát ábrázoló, aritmetikai adattípus. GNU rendszerben: int.

6.5.3. gid_t típus A csoport azonosítóját (group ID) ábrázoló, egész adattípus. GNU rend-szerben: unsigned int. A csoport további adatai (név, beletartozó fel-használók) a getgrgid és a getgrnam rutinokkal kérdezhetők le.

Page 99: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 99 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 99 ►

6.5.4. ino_t típus Fájl sorszámot ábrázoló, aritmetikai adattípus. UNIX zsargonban ez az inode szám. GNU rendszerben a típus unsigned long int.

6.5.5. nlink_t típus A fájl hivatkozás (link) számlálóját ábrázoló, aritmetikai adattípus. GNU rendszerben: unsigned short int.

6.5.6. uid_t típus A felhasználói azonosítót (user ID) ábrázoló, egész adattípus. GNU rend-szerben: unsigned int. A felhasználó további adatai (neve, kódolt jelszava stb.) a getpwuid és a getpwnam függvények segítségével nyerhetők ki.

6.5.7. stat struktúra Mikor beolvassuk a fájl attribútumait, struct stat struktúrában bocsátja rendelkezésünkre a stat függvény. E fejezet az attribútumok neveit, adat-típusait és jelentését kívánja ismertetni.

A stat struktúra, mely tehát a fájl inode bejegyzése felhasználó számára is elérhető része, legalább a következő tagokat tartalmazza:

mode_t st_mode: Fájltípus és elérési jogosultság

A fájl módját rögzíti ez a GNU rendszerben az unsigned int típusú tag, mely tartalmazza a fájltípus és az elérési jogosultsági biteket. A fájltípus az inode típusa is tulajdonképpen, mely megmondja, hogy a fájl könyvtár, foglalat stb. A fájltípus lekérdezése történhet előredefiniált makrókkal, melyek int típusban nem zérust szolgáltatnak, ha a feltett kérdésre a válasz igen, ill. zérust, ha nem. A makrók paramétere a stat struktúra mode_t típusú st_mode tagja:

• int S_ISDIR(mode_t): A bejegyzés könyvtár–e? • int S_ISCHR(mode_t): A fájl karakteres eszköz–e? Például: terminál. • int S_ISBLK(mode_t): A fájl blokkos eszköz–e? Például: lemez. • int S_ISREG(mode_t): A rekord szabályos fájl–e? • int S_ISFIFO(mode_t): A fájl cső–e? • int S_ISLNK(mode_t): A fájl közvetett hivatkozás–e? Szimbolikus

link–e? • int S_ISSOCK(mode_t): A fájl foglalat(socket)–e? Kapu–e?

Page 100: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 100 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 100 ►

Van egy másik, nem POSIX szabványos módszer is. Feltéve, hogy p egy struct stat struktúrára mutató mutató, az st_mode tagot és kapcso-latba hozva az S_IFMT szimbolikus állandóval (p–>st_mode & S_IFMT), ki tudjuk vágni a mode_t–ből az összes fájltípust leíró bitet. A fájltípus bitekre egyenként definiáltak szimbolikus állandókat, melyek neve egyezik a fájltípust lekérdező makrók nevével, azzal a kis eltéréssel, hogy az S_IS…–ből S_IF… lett. Az

S_ISREG(p–>st_mode)

makróhívás azonos a (p–>st_mode&S_IFMT) == S_IFREG

Későbbi POSIX szabványok szerint a fájlrendszerbe újabb objek-tumok (üzenet várakozási sor, szemafor, osztott memória terület stb.) is beépülhetnek, ezért mind a fájltípust lekérdező makrók, mind a fájltípus bitek bővülhetnek.

A mode_t st_mode tag további bitjei a fájl elérési jogosultságait írják le. A sys/stat.h fejfájlban definiált, vonatkozó szimbolikus állandók (zá-rójelben az elavult változatot is közöljük) a következők:

• S_IRUSR (S_IREAD): A tulajdonos olvasási joga. Sok rendszerben ez a 0X400–as bit.

• S_IWUSR (S_IWRITE): A fájl tulajdonosának írási jogosultsága. Rendszerint 0X200.

• S_IXUSR (S_IEXEC): Közönséges fájlokra végrehajtási (futtatási), könyvtárakra keresési (belépési) jog a tulajdonos számára. Többnyire 0X100.

• S_IRWXU: S_IRUSR | S_IWUSR | S_IXUSR. • S_IRGRP: A tulajdonos csoportjának olvasási jogosultsága. Jobbára

0X40. • S_IWGRP: A tulajdonoscsoport írási joga. Általában 0X20. • S_IXGRP: A tulajdonoscsoport futtatási vagy belépési joga. Főként 0X10. • S_IRWXG: S_IRGRP | S_IWGRP | S_IXGRP. • S_IROTH: Más felhasználók olvasási jogosultsága. Leggyakrabban 0X4. • S_IWOTH: Mások írási joga. Rendszerint 0X2. • S_IXOTH: Végrehajtási vagy keresési jogosultság más felhasználók

számára. Általában 0X1. • S_IRWXO: S_IROTH | S_IWOTH | S_IXOTH. • S_ISUID: A SUID bit. Jobbára 0X4000.

Page 101: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 101 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 101 ►

• S_ISGID: Az SGID bit. Többnyire 0X2000. • S_ISVTX: Ez a „sticky” (ciki) bit. Rendszerint 0X1000.

S_ISUID és S_ISGID: Bizonyos esetekben kívánatos programoknak lehetővé tenni fájlok vagy eszközök elérését még akkor is, ha a futtató felhasználó ilyen jogosultságokkal egyébként nem rendelkezik. Az egyik lehetséges megoldás a problémára a programfájl SUID (set user ID), ill. SGID (set group ID) bitjeinek bebillentése. Ha ilyen program indul, a folyamat tényleges felhasználói ID–je a programfájl tulajdonosának ID–jére változik. Így például olyan fájlok írási elérésének engedélyezése céljá-ból, mint az /etc/passwd, melyet normálisan csak a rendszergazda írhat, a módosító szoftvert a root tulajdonává kell tenni a SUID bitet is bebillentve.

E fájlokon kívül azonban a felhasználót váltó programnak is tilos elér-ni más olyan fájlt, melyhez semmilyen elérési joga sincs. A fájl olvasása vagy írása előtt tehát a szoftvernek meg kell vizsgálnia, hogy a felhasználó rendelkezik–e hozzá a szükséges elérési jogosultsággal. Erre való az access függvény, mely az elérési jogosultságról a folyamat valódi (nem a tényleges) felhasználói ID–je alapján győződik meg.

Van másik mód is az elérhetőség elemzéséhez. Leutánozva a rend-szer elérés–számításait, vizsgáljuk meg mi magunk a fájl módbitjeit! A dolgot leírni könnyebb, mint megvalósítani. A különféle rendszerek járu-lékos elérés vezérléssel is rendelkezhetnek, melyeket lekódolva nem portábilis programokat képzünk. Szóval ez a megoldás nem javasolható!

S_ISVTX: Könyvtár vonatkozásában a „sticky” bit törlési jogot ad a könyvtárbeli fájlokra, de csak a tulajdonosnak. Szokásosan a felhasználó vagy törölni tud minden fájlt egy könyvtárban, vagy nem képes törölni egyetlen egyet sem. A dolog a felhasználó e könyvtárra vonatkozó írási jogosultságán múlik. Ez ugyanaz a megkötés: a törölni kívánt fájl tulajdo-nosának kell lenni, és írási jogosultsággal kell rendelkezni a fájlt tartalmazó könyvtárra. Az egyetlen kivétel a könyvtár tulajdonosa, aki törölni képes minden fájlt a könyvtárában tekintet nélkül a fájlok tulajdonosaira. Ez a közös használatú /tmp könyvtár esete, ahol bárki létrehozhat fájlokat, de nem törölheti a mások által kreált állományokat.

Eredetileg a „sticky” bit végrehajtható fájlokra a rendszer lapcsere eljá-rásmódját módosította. Normálisan, amikor a program befejeződött, lapjai a memóriában rögtön felszabadultak és újrahasznosíthatta őket a rendszer. Ha a „sticky” bitet bebillentették, a lapokat a memóriában tartotta a rend-szer egy ideig, mintha a program még futna. Ez előnyös volt a feltehetőleg

Page 102: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 102 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 102 ►

többször egymás után futtatandó programokra. A kezelésmód azonban mára idejétmúlttá vált, mert modern rendszerekben a befejeződött prog-ram lapjai mindaddig a tárban maradnak, míg memóriahiány nem lesz. Mikor a program aztán legközelebb megint fut, lapjai már a tárban van-nak, ha a legutóbbi futás óta nem lépett fel memóriahiány.

Bizonyos modern rendszerekben a „sticky” bitnek nincs hasznos ér-telme végrehajtható fájlokra, így nem könyvtárakra egyáltalán nem billent-hetők be. Ha mégis kísérletet teszünk rá, a chmod EFTYPE hibával zárul.

Bizonyos más rendszerek (főleg a Sun operációs rendszerei) más ér-telmet tulajdonítanak a „sticky” bitnek. Ha nem végrehajtható fájlra billen-tik be, épp az eddig írtak ellenkezőjét jelenti: egyáltalán ne tartsa a rendszer cache–ben a fájl lapjait.

ino_t st_ino: A fájl inode száma

A fájl azonosító sorszáma, mely ugyanazon az eszközön (egy fájlrendsze-ren belül) különbözik minden más fájlétól.

dev_t st_dev: Eszközazonosító

A fájlt tartalmazó eszközt azonosító szám. Az st_ino és az st_dev együtt egyedileg azonosítják a fájlt. Az st_dev érték azonban nem szükségképpen változatlan rendszer újraindításokon, összeomlásokon át.

nlink_t st_nlink: Közvetlen hivatkozások száma

A fájlra (az inode–jára) való közvetlen hivatkozások (hard link) száma. A számláló azt követi nyomon, hogy hány könyvtár rendelkezik bejegyzéssel erre a fájlra. Ha a számláló valaha zérusra csökken, akkor a rendszer elveti a fájlt, mihelyt egyetlen folyamat sem tartja nyitva. A közvetett hivatkozá-sok (symbolic link) nincsenek beszámítva.

A közvetett hivatkozásokkal később külön fejezet foglalkozik!

uid_t st_uid: Tulajdonos azonosítója

A fájl tulajdonosának felhasználói azonosítója (user ID).

gid_t st_gid: Csoport azonosítója

A fájl csoport azonosítója (group ID).

off_t st_size: Fájlméret

A szabályos fájl mérete bájtban. Ha a fájl igazából eszköz, a tag általában nem tartalmaz értelmes értéket. Közvetett hivatkozás (symbolic link) ese-tén a struktúra tag a hivatkozott fájlnév hosszát tárolja.

Page 103: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 103 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 103 ►

A fájl idő adatai

A fájl idő adataival később külön fejezet foglalkozik, és a dolgokhoz lásd még „Az idő kezelése” szakaszt!

time_t st_atime: A fájl utolsó elérésének ideje. unsigned long int st_atime_usec: A fájl utolsó elérése idejének mik-

ró szekundum része. time_t st_mtime: A fájltartalom utolsó módosításának ideje. unsigned long int st_mtime_usec: A fájltartalom utolsó módosítási

idejének mikró másodperc része. time_t st_ctime: A fájl attribútumai utolsó módosításának ideje. unsigned long int st_ctime_usec: A fájlt attribútumai utolsó módo-

sítási idejének mikró másodperc része.

blkcnt_t st_blocks: Elfoglalt blokkok száma

A helyfoglalásának mennyisége a lemezen 512 bájtos blokkokban mérve. A lemezblokkok száma nem szigorúan arányos a fájl méretével két okból:

• a fájlrendszer felhasználhat néhány blokkot belső feljegyzései tárolásá-hoz, és

• a fájl ritka (sparse) is lehet, azaz lehetnek benne zérustartalmú „lyu-kak”, melyek aktuálisan nem foglalnak helyet a lemezen. Ritka fájl ese-tén megközelítőleg fennáll– a p struct stat struktúrára mutató mutatót megint feltételezve – a következő reláció: p–>st_blocks*512 < p–>st_size

unsigned int st_blksize: Ideális blokkméret

E fájl olvasására és írására használatos optimális, bájtban mért blokkméret. A tag értéke felhasználható az adatátviteli puffer allokálásakor.

6.5.8. stat Az

int stat(const char *fajlnev, struct stat *puffer);

függvény a fajlnev fájl vagy könyvtár attribútum információját helyezi el a puffer mutatta stat struktúrába. A fajlnev fájlnak vagy könyvtárnak termé-szetesen léteznie kell.

Ha a fajlnev közvetett hivatkozás (symbolic link), a kinyert attribútu-mok arra a fájlra vonatkoznak, ahova a hivatkozás mutat.

Page 104: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 104 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 104 ►

A függvény zérust szolgáltat, ha az attribútum információt sikeresen kinyerte. A –1 visszatérési érték hibát jelez, amikor is az errno a szokásos fájlnév hibákon túl még lehet:

• ENOENT: A fájl vagy az út nem létezik.

A stat.c példaprogram kijelzi a parancssori paraméterként megadott fájl néhány attribútumát:

#include <stdio.h> #include <time.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int main(int argc, char *argv[]){ struct stat puff; /* Megadtak parancssori paramétert? */ if(argc<2){ fprintf(stderr, "A program indítása:\nstat fájl vagy" " könyvtárnév\n"); return 1; } /* Az argv[1] attribútum információ lekérdezése: */ printf("A(z) %s fájl vizsgálata:\n", argv[1]); if(stat(argv[1], &puff) != 0){ perror("Probléma van az attribútum infó " "kinyerésével"); return 1; } else { /* Néhány tag értékének kijelzése: */ printf("A(z) %s ", argv[1]); if(S_ISDIR(puff.st_mode)) printf("könyvtár.\n"); else if(S_ISCHR(puff.st_mode)) printf("karakteres eszköz.\n"); else if(S_ISBLK(puff.st_mode)) printf("blokkos eszköz.\n"); else if(S_ISREG(puff.st_mode)) printf("szabályos fájl.\n"); else if(S_ISFIFO(puff.st_mode)) printf("cső.\n"); else if(S_ISLNK(puff.st_mode)) printf("közvetett hivatkozás.\n"); else if(S_ISSOCK(puff.st_mode)) printf("foglalat.\n"); else printf("típusa ismeretlen.\n"); printf("Fájlméret:\t\t%ld\n", puff.st_size); printf("Utsó módosítás ideje:\t%s",

Page 105: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 105 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 105 ►

ctime(&puff.st_mtime)); } return 0; }

Az eredmény, ha a programot stat.c paraméterrel indítjuk: A(z) stat.c fájl vizsgálata: A(z) stat.c szabályos fájl. Fájlméret: 1207 Utsó módosítás ideje: Mon Nov 21 13:22:24 2005

6.5.9. lstat int lstat(const char *fajlnev, struct stat *puffer);

Ha a fajlnev közvetett hivatkozás neve, az lstat magáról a hivatkozásról szolgáltat információt, s nem követi le a közvetett hivatkozást. Az lstat egyébként úgy dolgozik, mint a stat.

6.5.10. fstat int fstat(int leiro, struct stat *puffer);

A leiro leírójú nyitott fájlra vonatkozó attribútum információt helyezi el a függvény a puffer mutatta stat struktúrába. A rutin egyebekben ugyanúgy dolgozik, mint a stat függvény. Sikeres esetben zérust szolgáltat. A –1 visszatérési érték hibát jelez, amikor is az errno:

• EBADF: A megadott fájlleíró érvénytelen.

Az fstat.c példaprogram kijelzi az F_STAT.OUT fájl méretét és utolsó módosítási idejét az fstat segítségével.

#define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <string.h> #include <time.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #define OUT "F_STAT.OUT" int main(void){ struct stat puff; int fh; /* Fájlleíró. */ char puffer[] = "Ez egy kimeneti sor lesz."; printf("fstat kipróbáló program:\n\n"); if((fh=open(OUT, O_CREAT|O_RDWR|O_TRUNC,

Page 106: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 106 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 106 ►

S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH)) == -1){ fprintf(stderr, "A(z) %s fájl megnyitása " "sikertelen.\n\tA hiba száma: %d\n\tA hiba " "szövege:%s\n", OUT, errno, strerror(errno)); return 1; } /* Kiírás: */ if(TEMP_FAILURE_RETRY(write(fh, puffer, strlen(puffer)))<=0) perror("Probléma az írásnál.\n\tA hiba szövege:"); /* Az attribútum információ lekérdezése: */ if(fstat(fh, &puff) != 0) perror("Probléma van az attribútum információ " "kinyerésével"); else { /* Néhány struktúratag értékének kijelzése: */ printf("A(z) %s ", OUT); if(S_ISDIR(puff.st_mode)) printf("könyvtár.\n"); else if(S_ISCHR(puff.st_mode)) printf("karakteres eszköz.\n"); else if(S_ISBLK(puff.st_mode)) printf("blokkos eszköz.\n"); else if(S_ISREG(puff.st_mode)) printf("szabályos fájl.\n"); else if(S_ISFIFO(puff.st_mode)) printf("cső.\n"); else if(S_ISLNK(puff.st_mode)) printf("közvetett hivatkozás.\n"); else if(S_ISSOCK(puff.st_mode)) printf("foglalat.\n"); else printf("típusa ismeretlen.\n"); printf("Fájlméret:\t\t%ld\n", puff.st_size); printf("Utsó módosítás ideje:\t%s", ctime(&puff.st_mtime)); } close(fh); return 0; }

Az eredmény kimenet a következő: fstat kipróbáló program: A(z) F_STAT.OUT szabályos fájl. Fájlméret: 25 Utsó módosítás ideje: Tue Nov 22 15:40:04 2005

6.5.11. Elérési jogosultság vizsgálata és módosítása

access int access(const char * fajlnev, int mod);

Page 107: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 107 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 107 ►

A függvény megvizsgálja, hogy a fajlnev fájl (vagy könyvtár) elérhető–e a mod paraméterben megfogalmazott módon. E rutin a hívó folyamat valódi felhasználói és csoport ID–jét használja a tényleges ID–k helyett az elérési jogosultság vizsgálatához. A mod egész érték, mely az unistd.h fejfájlban definiált makrókat vagy kapcsolatba hozva képezhető. A makrók a fájl létezését, ill. a specifikált jogosultsággal való elérhetőségét vizsgálják. A jelzők a következők:

• F_OK: A fájl létezését vizsgálja. • W_OK: Az írási jogosultságot teszteli a fájlra. • R_OK: Olvasási jogosultságra vizsgálja a fájlt. • X_OK: A jelző végrehajtási/keresési jogosultságot tesztel.

A függvény zérussal tér vissza, ha a fájl rendelkezik a megadott moddal. A –1 visszaadott érték jelzi, hogy a fájl nem létezik, vagy a megadott modon nem elérhető. Ilyenkor a szokásos fájlnév hibákon túl az errno lehet:

• EACCES: Elérés elutasítva: A fájl jogosultsági beállítása nem engedi meg a specifikált elérést.

• ENOENT: A fajlnev fájl, vagy út nem található. • EROFS: Írási jogosultságot kértek a fájlra csak olvasható fájlrendszeren.

Az access csak SUID, ill. SGID programokban való használatra alkalmas. A nem ilyen szoftverek mindig a tényleges felhasználói ID–t alkalmazzák a valódi helyett.

Példaként a chmod függvény leírásánál található chmod.c–t említjük meg!

chmod int chmod(const char *fajlnev, int mod);

A függvény megváltoztatja a létező, fajlnev fájl elérési jogosultsági bitjeinek beállítását a mod paraméterrel előírtra. Lásd a stat struktúra st_mode tag-jának leírását a mod paraméterhez!

Ha a fajlnev közvetett hivatkozás (symbolic link), a jogosultsági változá-sok arra a fájlra vonatkoznak, ahova a hivatkozás mutat.

Ha a jogosultsági beállítást sikeresen megváltoztatta, akkor a rutin zé-russal tér vissza. A –1 visszaadott érték hibát jelez. A szokásos fájlnév hibákon túl az errno még a következő lehet:

Page 108: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 108 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 108 ►

EFTYPE: A mod be kívánja billenteni az S_ISVTX (a „sticky”) bitet, de a fajlnevvel adott fájl nem könyvtár. Bizonyos rendszerek nem teszik lehetővé nem könyvtár fájlokra a sticky bit bebillentését, mások viszont megengedik, de utóbbiak közül is csak néhány rendszer rendel értelmes jelentést a dologhoz. A hiba természetesen csak olyan operációs rendsze-rekben jelentkezik, ahol a sticky bitnek nem könyvtár fájlokra nincs értel-mes jelentése.

ENOENT: A fájl nem létezik. EPERM: E folyamatnak nincs joga e fájl elérési jogosultságainak vál-

toztatásához. Csak a fájl tulajdonosa (a folyamat tényleges felhasználói ID–je), vagy valamely privilegizált felhasználó módosíthatja.

EROFS: A fájl csak olvasható fájlrendszeren van, tehát semmije sem módosítható.

A chmod.c példaprogramban a fájlt csak olvashatóvá tesszük előbb, majd helyreállítjuk korábbi állapotát.

/* chmod.c példaprogram. */ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #define FAJL "chmod.c" int main(void){ char parancs[128]; struct stat puff; sprintf(parancs, "ls -la %s", FAJL); printf("A(z) %s fájt előbb csak olvashatóvá tesszük, " "majd\nvisszaállítjuk induló állapotát:\n\n", FAJL); /* Létezik-e a fájl? */ if((access(FAJL, F_OK))!=-1){ printf("A(z) %s fájl létezik.\n", FAJL); /* A fájl attribútumainak lekérdezése: */ if(stat(FAJL, &puff)){ perror("Balhé van az attribútum infó " "kinyerésével"); return 1; } /* Irható-e? */ if((access(FAJL, W_OK))!=-1) printf("A(z) %s írható.\n", FAJL);

Page 109: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 109 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 109 ►

system(parancs); /* A fájl csak olvashatóvá tétele: */ if(chmod(FAJL, S_IREAD)==-1) fprintf(stderr, "A(z) %s fájl (R) nem található.\n" "\tA hiba száma: %d\n\tA hiba szövege:%s\n", FAJL, errno, strerror(errno)); if((access(FAJL, W_OK))==-1) printf("A(z) %s fájl csak olvasható lett.\n", FAJL); system(parancs); /* Visszaállítás olvashatóra és írhatóra: */ if(chmod(FAJL, puff.st_mode)==-1) fprintf(stderr, "A(z) %s fájl (WR) nem " "található.\n\tA hiba száma: %d\n\tA hiba " "szövege:%s\n", FAJL, errno, strerror(errno)); else printf("A(z) %s fájl újra írható, olvasható " "stb.\n", FAJL); system(parancs); } else printf("A(z) %s fájl nem létezik.\n", FAJL); return 0; }

Az eredmény: A(z) chmod.c fájt előbb csak olvashatóvá tesszük, majd visszaállítjuk induló állapotát: A(z) chmod.c fájl létezik. A(z) chmod.c írható. -rwxr--r-- 1 bauer teacher 1505 2005-11-23 10:42 chmod.c A(z) chmod.c fájl csak olvasható lett. -r-------- 1 bauer teacher 1505 2005-11-23 10:42 chmod.c A(z) chmod.c fájl újra írható, olvasható stb. -rwxr--r-- 1 bauer teacher 1505 2005-11-23 10:42 chmod.c

fchmod int fchmod(int leiro, int mod);

Ugyanaz, mint a chmod, de a leiroval adott, nyitott fájlra. Sikeres esetben ez is zérust szolgáltat. –1 jön hiba esetén, amikor is az errno a következők egyike:

• EBADF: Érvénytelen fájlleíró paraméter. • EINVAL: A leiro cső, foglalat, vagy valami más, aminek igazából nin-

csenek elérési jogosultságai. • EPERM: E folyamatnak nincs joga e fájl elérési jogosultságainak vál-

toztatásához. Csak a fájl tulajdonosa (a folyamat tényleges felhasználói ID–je), vagy valamely privilegizált felhasználó módosíthatja.

Page 110: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 110 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 110 ►

• EROFS: A fájl csak olvasható fájlrendszeren van, tehát semmije sem módosítható.

6.5.12. Fájl létrehozási maszk (umask) A fájlokat létrehozó függvényeknek (open, mkdir stb.) van mode_t típu-sú mod paramétere, mely az újonnan létesítendő fájlnak adandó jogosultsá-gokat specifikálja. Felhasználás előtt azonban ezt a modot változtatja a fo-lyamat fájl létrehozási maszkja (umask). Az umask 1 értékű bitjei jelzik azokat a jogosultsági biteket, melyek bizonyosan nincsenek engedélyezve az újonnan létrehozott fájlokra. Például, ha „mások” minden elérési bitjét (S_IRWXO) bebillentjük a maszkban, akkor a „mások” kategóriába eső folyamatok egyáltalán nem tudják elérni az újonnan létesített fájlokat még akkor sem, ha az őket létrehozó függvény mod paraméterében expliciten engedélyeznénk ezeket a jogokat. Magyarán: a fájl létrehozási maszk a megadható jogosultságok egyes komplemense.

A fájlokat létesítő programok a mod paraméterrel általában minden ér-telmes jogosultságot meg szoktak adni nekik. Szabályos fájlok esetén ez olvasási és írási jogot jelent minden felhasználó kategóriában (tulajdonos, csoport és mások). E jogosultságokat aztán a felhasználó saját fájl létreho-zási maszkja korlátozza, azaz a tényleges jogosultság mindig a:

mod & ~umask

Létező fájl jogosultságai módosíthatók a chmod–dal. E függvény a megadott jogosultsági biteket használja és elveti a fájl létrehozási maszkot.

Normál esetben a fájl létrehozási maszkot a felhasználó bejelentkezési shell–je (umask shell parancs) látja el kezdőértékkel, és ezt megörökli minden alfolyamat.

Ha a programnak fájlt kell létesítenie, és el kívánja kerülni az umask hatását a megadott elérési jogosultságaira, akkor nem az umask–ot kell változtatni, hanem megnyitása után fchmod–dal módosítani kell a jogo-sultságokat a kívántra. Az umask–ot rendszerint csak a shell (parancsér-telmező) változtatja az umask függvénnyel.

A következő függvények használatakor bekapcsolandó sys/stat.h fejfájl is.

umask mode_t umask(mode_t maszk);

A függvény az aktuális folyamat fájl létrehozási maszkját maszkra állítja, és visszaadja a maszk korábbi értékét. Nincs sikertelen visszatérés. A maszk a

Page 111: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 111 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 111 ►

stat struktúránál ismertetett jogosultsági bitek szimbolikus állandóiból vagy művelettel összeállított kifejezés. A

getumaszk

függvény segítségével változtatása nélkül a következőképp kérdezhető le az umask: mode_t getumaszk(void){ mode_t maszk = umask(0); umask(maszk); return maszk; }

GNU bővítésként el is készítették ezt a függvényt.

Az umaszk.c program az umask függvénnyel lekérdezi előbb a fájl létre-hozási maszkot, majd beállítja úgy, hogy minden jövőben létrehozandó fájl csak olvasható lesz, s legvégül visszaállítja a kiindulási állapotot.

/* umaszk.c példaprogram. */ #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int main(void){ /* Az aktuális fájl létrehozási maszk lekérdezése. */ unsigned long seged; mode_t regimaszk = umask(S_IWUSR), ujmaszk; /* Az aktuális fájl létrehozási maszk kijelzése. */ printf("Az aktuális fájl létrehozási maszk: "); for(seged=0X80000000; seged; seged>>=1) if(regimaszk&seged) putchar('1'); else putchar('0'); /* A jövőben csak olvasható fájlok hozhatók létre: */ printf("\nCsak olvasható fájlok hozhatók létre: "); ujmaszk = umask(regimaszk); for(seged=0X80000000; seged; seged>>=1) if(ujmaszk&seged) putchar('1'); else putchar('0'); regimaszk=umask(0); umask(regimaszk); printf("\nAz indulási umask visszaállítása: "); for(seged=0X80000000; seged; seged>>=1) if(regimaszk&seged) putchar('1'); else putchar('0'); putchar('\n'); return 0; }

Az eredmény kimenet lehet a következő: Az aktuális fájl létrehozási maszk:

Page 112: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 112 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 112 ►

00000000000000000000000000010010 Csak olvasható fájlok hozhatók létre: 00000000000000000000000010000000 Az indulási umask visszaállítása: 00000000000000000000000000010010

6.5.13. Fájlok elnevezése, átnevezése, törlése POSIX rendszerekben egy fájlnak egy időben több neve lehet. A fájl az operációs rendszer szempontjából egy azonosítószám, melyhez több egyenrangú név tartozhat.

Közvetlen hivatkozás (hard link)

A név hozzáadás a fájlhoz a link függvény segítségével történik Az új ne-vet közvetlen hivatkozásnak nevezik. Új közvetlen hivatkozást létesítve, nem készül másolat a fájl tartalmáról, csak új név születik (új könyvtári bejegyzés készül), mellyel hivatkozni lehet ettől fogva az eddig már létező nevein túl az állományra.

Egy fájl nevei több különböző könyvtárban helyezkedhetnek el, minek következtében a fájlrendszer szervezése nem szigorúan hierarchikus, nem szigorúan fa–struktúrájú. A fájl közvetlen hivatkozásainak számát az inode tábla bejegyzésében (st_nlink stat struktúratag) tartja nyilván a rendszer.

A legtöbb UNIX rendszerben nem megengedett, hogy ugyanarra a fájlra az övétől különböző fájlrendszerből is készíthető legyen közvetlen hivatkozás. Ha ez az aktuális operációs rendszerben is így van, akkor a link hívás hibát fog jelezni.

Egy fájlnak legfeljebb LINK_MAX (rendszer korlát) név adható!

link int link(const char *reginev, const char *ujnev);

A rutin a létező, reginev nevű fájlra új közvetlen hivatkozást készít ujnev néven.

A sikert a függvény zérust visszaadott értékkel jelzi. Hiba esetén vi-szont –1 jön tőle, amikor is a szokásos fájlnév hibákon (mind a reginev–re, mind az ujnev–re) túl az errno még a következő értékek egyike lehet:

• EACCES: A folyamatnak nincs írási joga abba a könyvtárba, ahova az ujnev közvetlen hivatkozás kerülne.

• EIO: Hardverhiba történt a fájlrendszer olvasása, vagy írása közben. • EEXIST: Van már ujnev nevű fájl. Előbb törölni kell az ujnev hivatko-

zást, s csak aztán hajtatható végre a link.

Page 113: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 113 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 113 ►

• EMLINK: A reginev fájlnak már túl sok neve van, nem hozható létre újabb.

• ENOENT: A reginev fájl nem létezik, s nem létező állományhoz nem készíthető hivatkozás.

• ENOSPC: Az ujnev–et majdan tartalmazó könyvtárban nincs hely további bejegyzéshez, és a fájlrendszerben sincs további hely a könyv-tár fájl bővítéséhez.

• EPERM: GNU rendszerben (és még néhány másban) nem készíthető közvetlen hivatkozás könyvtárakhoz. Sok operációs rendszerben csak privilegizált felhasználó tehet ilyet. A hibakód ezt a problémát jelzi.

• EROFS: Az új hivatkozást majdan tartalmazó könyvtár csak olvasható fájlrendszeren helyezkedik el.

• EXDEV: Az ujnev–vel adott könyvtár más fájlrendszeren van, mint a reginev fájl.

Közvetett hivatkozás (soft link, symbolic link)

A GNU rendszerben lehetséges a közvetett hivatkozás „fájlfajta” is, mely mutató lényegében egy másik fájlnévre. A közvetlentől eltérően ez a hivat-kozás korlátozások nélkül létesíthető akár könyvtárakra, vagy akár fájl-rendszereken át is. Közvetett hivatkozás, mi több, olyan névhez is létre-hozható, mely semmilyen fájlnak nem neve. Az ilyen hivatkozás megnyitá-sa persze mindaddig sikertelen, míg ezt a nevű fájlt létre nem hozzák. Ha-sonlóképpen, ha a korábban létező, de most már törölt fájlra mutató köz-vetett hivatkozás továbbra is a volt fájlnévre irányul, holott ezen a néven most már nincs is fájl.

A közvetett hivatkozás értelme megnyitásakor jön elő például. Ha az open függvény ilyen hivatkozást nyit, akkor beolvassa a hivatkozásbeli fájlnevet, és ezt a fájlt nyitja meg, nem a hivatkozást. A stat rutin is azon a fájlon dolgozik, amire a hivatkozás mutat és nem magán a hivatkozáson, s így tovább.

Ezzel szemben vannak olyan műveletek is, melyek magára a linkre hatnak, például a fájl törlése, vagy átnevezése. Az lstat és a readlink is tartózkodik a közvetett hivatkozás lekövetésétől, mert épp az a dolguk, hogy a linkről nyerjenek ki információt. A közvetlen hivatkozást előállító link rutin is így tesz: közvetlen hivatkozást létesít a közvetetthez, ami na-gyon ritkán lehet csak „kívánatos”.

Page 114: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 114 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 114 ►

symlink int symlink(const char *reginev, const char *ujnev);

A függvény ujnev közvetett hivatkozást hoz létre a reginev–hez. Sikeres esetben a rutin zérust szolgáltat. Hiba esetén viszont –1–et,

amikor is a szokásos fájlnév hibákon túl az errno még a következő értékek egyike lehet:

• EACCES: A szokásos értelmezésen túl, az ujnev–et majdan tartalmazó könyvtárhoz nincs írási jogosultság.

• EEXIST: Van már ujnev nevű fájl. Előbb törölni kell az ujnev hivatko-zást, s csak aztán hajtatható végre a symlink.

• EFAULT: A reginev vagy az ujnev könyvtárbejegyzés valamelyike nem érhető el.

• EIO: Hardverhiba történt a fájlrendszer olvasása, vagy írása közben. • ENOSPC: Az ujnev–et majdan tartalmazó könyvtárban nincs hely

további könyvtárbejegyzéshez, és a fájlrendszerben sincs további hely a könyvtár fájl bővítéséhez.

• ENOTDIR: Az ujnev–et majdan tartalmazó könyvtár nem létezik, vagy létezik, de nem könyvtár.

• EPERM: A fájlrendszer nem támogatja a közvetett hivatkozásokat, így nem hozható létre.

• EROFS: Az ujnev fájl nem létezhet csak olvasható fájlrendszeren.

readlink int readlink(const char *fajlnev, char *puffer, size_t

meret);

A függvény kinyeri a fajlnev közvetett hivatkozás tartalmát. A hivatkozás mutatta fájlnevet bemásolja a puffer tömbbe. Az eredmény nem null–lezárású karakterlánc, de a rutin visszatérési értéke a pufferbe másolt karak-terek száma. A meret paraméter a puffer tömb mérete, a függvény legfeljebb ennyi karaktert másol, s nem többet.

Ha a readlink visszatérési értéke éppen meret, nem lehetünk bizto-sak benne, hogy elfért–e a teljes név. Ezen például a Biztireadlink függ-vényben leírt módon segíthetünk. A rutin minden problémás esetben NULL mutatót szolgáltat. Sikeres visszatéréskor viszont a fájlnév karak-terlánc kezdőcíme jön tőle. Ha a karakterláncra már nincs tovább szükség a programban, fel kell szabadítani a dinamikusan foglalt memóriablokkot. #include <stdlib.h>

Page 115: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 115 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 115 ►

char * Biztireadlink(const char *fajlnev){ size_t meret = 32; char *puffer = NULL; int hsz; while(puffer=(char *)malloc(meret)) if((hsz=readlink(fajlnev, puffer, meret))<0){ free(puffer); return NULL; } else if(hsz==meret){ free(puffer); meret*=2; } else { puffer[hsz]=0; return puffer; } return NULL; }

A readlink hiba esetén –1–et szolgáltat, amikor is a szokásos fájlnév hibákon túl az errno még a következő értékek egyike lehet:

• EFAULT: A puffer memóriaterület nem elérhető a folyamat számára. • EINVAL: A fajlnev nem közvetett hivatkozás. Lehet az is, hogy a meret

paraméter negatív. • EIO: Hardverhiba történt a fájlrendszer olvasása, vagy írása közben.

canonicalize_file_name

Bizonyos esetekben szeretnénk a valódi fájlnévhez jutni az összes közve-tett hivatkozás feloldásával. A

char * canonicalize_file_name(const char *fajlnev);

visszaadja a fajlnev fájl abszolút nevét, mely nem tartalmaz ., .. komponen-seket, közvetett hivatkozásokat, ill. ismételt útelválasztókat (/). A vissza-kapott karakterlánchoz a canonicalize_file_name malloc–kal foglalt memóriaterületet, azaz amennyiben nincs tovább szükség a fájl abszolút nevére, a lefoglalt memóriaterület free–vel felszabadítandó!

A problémát a függvény NULL visszatérési értékkel jelzi. Probléma az is, ha az abszolút fájlnév karaktereinek száma eléri, vagy meghaladja a PATH_MAX értéket. Az errno lehetséges értékei:

• EACCES: Az út legalább egy komponense nem olvasható. • ELOOP: Több, mint MAXSYMLINKS közvetett hivatkozás lekö-

vetve. • ENAMETOOLONG: Az eredmény fájlnév túl hosszú. • ENOENT: A fajlnev üres karakterlánc.

Page 116: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 116 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 116 ►

• ENOTDIR: A fajlnevben legalább egy könyvtár komponens nem létezik.

A canonicalize_file_name nem szabványos, de Linux rendsze-rekben elérhető, ha definiáljuk a _GNU_SOURCE makrót az stdlib.h betöltése előtt!

A linkek.c példában bemutatjuk a közvetlen és közvetett hivatkozásokat kezelő függvények használatát:

/* linkek.c példaprogram. */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int main(void){ int i, hsz; char parancs[128], fajl[] = "linkek.c", *cim, *uj[] = {"linkhd.c","linksy1.c", "linksy2.c"}; printf("Hivatkozások készítése a(z)\"%s\" fájlhoz:\n", fajl); if(link(fajl, uj[0])) fprintf(stderr, "A(z) \"%s\" közvetlen hivatkozás " "létesítése a(z) \"%s\" fájlhoz sikertelen!\n" "\tA hiba száma: %d\n\tA hiba szövege:%s\n", uj[0], fajl, strerror(errno)); else printf("Közvetlen hivatkozás: %s\n", uj[0]); if(symlink(fajl, uj[1])) fprintf(stderr, "A(z) \"%s\" közvetett hivatkozás " "létesítése a(z) \"%s\" fájlhoz sikertelen!\n" "\tA hiba száma: %d\n\tA hiba szövege:%s\n", uj[1], fajl, strerror(errno)); else printf("Közvetett hivatkozás: %s\n", uj[1]); if(symlink(uj[1], uj[2])) fprintf(stderr, "A(z) \"%s\" közvetett hivatkozás " "létesítése a(z) \"%s\" fájlhoz sikertelen!\n" "\tA hiba száma: %d\n\tA hiba szövege:%s\n", uj[2], uj[1], strerror(errno)); else printf("Közvetett hivatkozás: %s <- %s\n", uj[1], uj[2]); /* A munkakönyvtár *uj[0] kezdetű fájljainak kijelzése: */

Page 117: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 117 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 117 ►

strcpy(parancs, "ls -li l*"); parancs[(hsz=strlen(parancs))-2]=*uj[0]; system(parancs); /* readlink az uj[2]-re: */ if((i=readlink(uj[2], &parancs[++hsz], sizeof(parancs)- hsz))<0) fprintf(stderr, "A(z) \"%s\" közvetett hivatkozás " "readlink-je sikertelen!\n\tA hiba száma: " "%d\n\tA hiba szövege:%s\n", uj[2], errno, strerror(errno)); else { parancs[hsz+i]=0; printf(" readlink(\"%s\",...): %s\n", uj[2], &parancs[hsz]); } /* canonicalize_file_name az uj[2]-re: */ if((cim=canonicalize_file_name(uj[2]))==NULL) fprintf(stderr, "A(z) \"%s\" közvetett hivatkozás " "canonicalize_file_name-je sikertelen!\n" "\tA hiba száma: %d\n\tA hiba szövege:%s\n", uj[2], errno, strerror(errno)); else { printf("canonicalize_file_name(\"%s\"): %s\n", uj[2], cim); free(cim); } /* A hivatkozások törlése: */ for(i=sizeof(uj)/sizeof(uj[0])-1; i>=0; --i) if(unlink(uj[i])) fprintf(stderr, "A(z) \"%s\" hivatkozás törlése " "sikertelen!\n\tA hiba száma: %d\n\tA hiba " "szövege:%s\n", uj[i], errno, strerror(errno)); else printf("\"%s\" törölve.\n", uj[i]); system(parancs); return 0; }

Egy lehetséges eredmény kimenet: Hivatkozások készítése a(z)"linkek.c" fájlhoz: Közvetlen hivatkozás: linkhd.c Közvetett hivatkozás: linksy1.c Közvetett hivatkozás: linksy1.c <- linksy2.c 7243 –rwxr--r-- 2 bauer teacher 2508 2005-12-02 16:49 linkek.c 7243 –rwxr--r-- 2 bauer teacher 2508 2005-12-02 16:49 linkhd.c 4396 lrwxrwxrwx 1 bauer teacher 8 2005-12-02 16:51 linksy1.c -> linkek.c 4440 lrwxrwxrwx 1 bauer teacher 9 2005-12-02 16:51 linksy2.c -> linksy1.c

Page 118: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 118 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 118 ►

4173 –rwxr--r-- 1 bauer teacher 1283 2005-11-15 15:29 lseek.c readlink("linksy2.c",...): linksy1.c canonicalize_file_name("linksy2.c"): /home/bauer/FajlokMappak/linkek.c "linksy2.c" törölve. "linksy1.c" törölve. "linkhd.c" törölve. 7243 –rwxr--r-- 1 bauer teacher 2508 20051202 16:49 linkek.c 4173 –rwxr--r-- 1 bauer teacher 1283 20051115 15:29 lseek.c

Az ls paranccsal az inode számokat is megjelentettük!

rename int rename(const char *reginev, const char *ujnev);

A reginev nevű fájl vagy könyvtár átnevezése ujnev–re. A reginev–nek létező fájlt vagy könyvtárt kell specifikálnia. A fájl vagy könyvtár a továbbiakban nem érhető el a reginev–en, csak az ujnev–en, ill. a korábban létező, reginev–től különböző nevein.

Az ujnev–et tartalmazó könyvtárnak ugyanazon a fájlrendszeren kell lennie, mint a reginev–et tartalmazó könyvtárnak.

Ha a reginev nem könyvtár, akkor az átnevezési művelet eltávolít min-den létező ujnev nevű fájlt. A rename hibával zárul viszont ilyenkor, ha az ujnev könyvtár.

Ha a reginev könyvtár, az ujnev vagy nem létezhet, vagy üres könyvtár neve lehet csak. A művelet előbb törli ezt az ujnev nevű, üres könyvtárt. Az ujnev nem lehet alkönyvtára sem az átnevezendő reginev könyvtárnak.

Szóval, sokkal jobb, ha ujnev–en sem fájl, sem könyvtár nem létezik! A rename–nek megvan az a hasznos sajátossága, hogy az ujnev jelenté-

se „automatikusan” bármely előzetesen létező, ilyen nevű fájlról az új ér-telmére (azaz a reginev–nek hívott fájlra) változik. Nincs olyan pillanat, amikor az ujnev nem létezik a régi és az új jelentés között. Ha rendszer–összeomlás van a művelet folyamán, akkor lehet, hogy még mind a két név létezik, bár az ujnev mindig ép, ha létezik egyáltalán.

Ha az ujnev paraméter a fájl eredeti helyétől eltérő utat tartalmaz, és az ugyanezen a fájlrendszeren van, akkor az átnevezésen túl megtörténik a fájl átmozgatása is. A rename nem használható viszont könyvtárak át-mozgatására. A könyvtárak átnevezhetők, de nem mozgathatók át.

A függvény egyik paramétere sem lehet globális fájlnév!

Page 119: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 119 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 119 ►

A rename speciális esete az, amikor a reginev és az ujnev ugyanarra a fájlra vonatkozó, két név. Az eset konzisztens kezelésmódja: a reginev törlé-se. A POSIX előírásai szerint a rename–nek nem kell tennie semmit, de sikert kell visszajeleznie, ami persze nem konzisztens dolog. Szóval ki kell deríteni a konkrét operációs rendszer viselkedését erre a helyzetre!

Sikeres esetben a függvény zérust szolgáltat. Hiba esetén viszont –1–et, amikor is a szokásos fájlnév hibákon túl az errno még a következő értékek egyike lehet:

• EACCES: A szokásos értelmezésen túl, az ujnev–et vagy a reginev–et tartalmazó könyvtár egyikéhez nincs írásjog, vagy az ujnev és a reginev könyvtárak, és valamelyikre nincs írási jogosultság.

• EBUSY: A reginev vagy az ujnev nevű könyvtár valamelyikét valamilyen módon használja a rendszer, s ez nem teszi lehetővé az átnevezést. Ilyen eset, ha a könyvtár valamely folyamat aktuális (munka) könyvtá-ra, vagy ha a könyvtár fájlrendszer csatlakoztatási pontja, stb.

• EINVAL: A reginev könyvtár, mely tartalmazza az ujnev–et. • EISDIR: Az ujnev könyvtár, de a reginev nem. • EMLINK: Az ujnev szülőkönyvtára túl sok közvetett hivatkozással

(bejegyzéssel) rendelkezik. • ENOENT: Nincs reginev fájl vagy könyvtár. • ENOSPC: Az ujnev–et majdan tartalmazó könyvtárban nincs hely

további könyvtárbejegyzéshez, és a fájlrendszerben sincs további hely a könyvtár fájl bővítéséhez.

• ENOTEMPTY vagy EEXIST: Az ujnev könyvtár nem üres. A GNU rendszerben ezt a problémát mindig az ENOTEMPTY jelzi, más rendszerekben az EEXIST–et használják ugyanerre a célra.

• EROFS: A művelet írni kíván egy csak olvasható fájlrendszeren elhe-lyezkedő könyvtárba.

• EXDEV: Az ujnev és a reginev különböző fájlrendszereken vannak.

remove és unlink int remove(const char *fajlnev); int unlink(const char * fajlnev);

Fájlokat az unlink, vagy a remove rutinokkal törölhetünk. A törlés valójában csak a fajlnevet távolítja el. Ha a fájlnak a törölt

fajlneven kívül maradnak még nevei, akkor ezeken a neveken továbbra is elérhető marad. Ha ez volt az állomány egyedüli neve, akkor a fájltartalom

Page 120: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 120 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 120 ►

is megszűnik. Ha valamely más folyamat eközben nyitva tartja a fájlt, ak-kor a tartalom megszűntetését elhalasztja az operációs rendszer, míg a folyamat be nem zárja az állományt.

Az ANSI/ISO C szabványos remove fájlt távolít el ugyancsak. Fájlok-ra úgy dolgozik, mint az unlink, s könyvtárakra, mint az rmdir. Prototí-pusa viszont az stdio.h fejfájlban található.

Sikeres esetben a függvények zérust szolgáltatnak. Hiba esetén viszont –1–et, amikor is a szokásos fájlnév hibákon túl az errno még a következő értékek egyike lehet:

• EACCES: A szokásos értelmezésen túl, a fajlnevet tartalmazó könyv-tárhoz nincs írásjog, vagy a könyvtár „sticky” bitje bebillentett, és a fo-lyamat nem tulajdonosa a fájlnak.

• EBUSY: A fajlnev fájlt valamilyen módon használja a rendszer, s ez nem teszi lehetővé a törlését. Ilyen eset, ha a fajlnev a gyökérkönyvtárat specifikálja, vagy ha fájlrendszer csatlakoztatási pontja, stb.

• EIO: Hardverhiba történt a művelet közben. • EISDIR: Újabb rendszerek ezzel jelzik, hogy a fajlnev könyvtár. • ENOENT: A törlendő fajlnev nem létezik. • EPERM: Bizonyos rendszerekben az unlink nem használható könyv-

tárak törlésére, vagy legalább is csak privilegizált felhasználó alkalmaz-hatja erre a célra. GNU rendszerben könyvtárat nem unlink–kel, ha-nem rmdir–rel szabad törölni.

• EROFS: A fajlnevet tartalmazó könyvtár csak olvasható fájlrendszeren helyezkedik el.

A renamov.c példaprogram a rename és az unlink függvényeket szem-lélteti. A szoftver előbb lemásolja az aktuális könyvtárbeli renamov.c–t a szülő mappába. Majd átnevezi ren.cic–re úgy, hogy át is mozgatja közben a szülő könyvtárba. Ezt követően törli a program a ren.cic–et, majd visz-szamozgatja a szülőbeli renamov.c–t az aktuális könyvtárba.

/* renamov.c példaprogram. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h>

Page 121: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 121 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 121 ►

#include <unistd.h> int main(void){ int hl; char parancs[128]; char regi[] = "renamov.c", uj[] = "ren.cic", hova[]="../"; /* regi másolása a hova könyvtárba: */ strcpy(parancs, "cp "); strcat(parancs, regi); strcat(parancs, " "); system(strcat(parancs, hova)); /* regi átnevezése uj-ra a hova könyvtárba mozgatással: */ strcpy(parancs, hova); strcat(parancs, uj); printf("\"%s\" átnevezése \"%s\"-re(a):\n", regi, parancs); if(rename(regi, parancs)){ fprintf(stderr, "A(z) \"%s\" fájl nem volt " "átnevezhető!\n\tA hiba száma: %d\n\tA hiba " "szövege:%s\n", regi, errno, strerror(errno)); return 1; } else { /* A szülő könyvtár uj[0] kezdetű fájljainak kijelzése: */ strcpy(parancs+100, "ls -l "); strcat(parancs+100, hova); hl=strlen(parancs+100); strcat(parancs+100, "r*"); *(parancs+100+hl)=*uj; system(parancs+100); /* A hova könyvtárbeli uj törlés: */ printf("\"%s\" törlése:\n", parancs); if(unlink(parancs) == -1) fprintf(stderr, "A(z) \"%s\" fájl nem törölhető!\n" "\tA hiba száma: %d\n\tA hiba szövege:%s\n", parancs, errno, strerror(errno)); /* A szülő könyvtár uj[0] kezdetű fájljainak kijelzése: */ system(parancs+100); } /* A másolat regi visszamozgatása a hova könyvtárból az aktuálisba: */ strcpy(parancs, hova); strcat(parancs, regi); if(rename(parancs, regi)){ fprintf(stderr, "A(z) \"%s\" fájl nem volt " "átnevezhető!\n\tA hiba száma: %d\n\tA hiba "

Page 122: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 122 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 122 ►

"szövege:%s\n", parancs, errno, strerror(errno)); return 1;} return 0; }

A program kimenete a következő lehet: "renamov.c" átnevezése "../ren.cic"-re(a): –rwxr--r-- 1 bauer teacher 1776 2005-11-28 17:07 ../renamov.c –rwxr--r-- 1 bauer teacher 1776 2005-11-28 17:06 ../ren.cic "../ren.cic" törlése: –rwxr--r-- 1 bauer teacher 1776 2005-11-28 17:07 ../renamov.c

6.5.14. A fájl tulajdonosa, csoportja és az elérési jogosultság megállapítása

Minden fájlnak van tulajdonosa, aki a rendszerben regisztrált felhasználói nevek egyikével rendelkezik. Minden fájlnak van csoportja is, mely a defi-niált csoportok egyike. A fájl tulajdonosa hasznos lehet az állomány készí-tőjének kiderülésében, de a fő szerepe a fájl elérés vezérlésében nyilvánul meg. A tulajdonos és a csoport szerepet játszik az elérés meghatározásá-ban, mert a fájl elérési jogosultsági bitekkel rendelkezik a tulajdonos, a csoporthoz tartozó felhasználók és mindenki más számára. A jogosultsági részletek a stat struktúra leírásánál találhatók!

Az operációs rendszer normál esetben a fájl elérési jogosultságáról a folyamat tényleges (effektív) felhasználói és csoport ID–je, a másodlagos (supplementary) csoportok ID–jei alapján dönt, tekintetbe véve a fájl tu-lajdonosát, csoportját és jogosultsági bitjeit.

Ha a folyamat tényleges felhasználói ID–je egyezik a fájl tulajdonos felhasználói ID–vel, akkor az olvasási, írási, végrehajtási vagy keresési jo-gosultságot a megfelelő felhasználói (tulajdonos) bit vezérli. Hasonlóan, ha a folyamat valamely tényleges vagy másodlagos csoport ID–je egyezik a fájl tulajdonos csoportjának ID–jével, akkor a csoport bitek vezérlik a jogosultságokat. Máskülönben a „mások” bitjei határozzák meg a jogokat.

A privilegizált felhasználók (mint a root) bármely fájlt elérhetik, tekin-tet nélkül jogosultsági bitjeik beállítására. Egyetlen kivétel van: fájlt végre-hajtatni a privilegizált felhasználó is csak akkor képes, ha valamelyik vég-rehajtást engedélyező bitje be van billentve.

A fájl létrejövetelekor a tulajdonosa a létrehozó folyamat tényleges fel-használói ID–je lesz. A fájl csoportazonosítója az állományt tároló rend-szertől függően vagy a folyamat tényleges csoport ID–je, vagy a fájlt tar-

Page 123: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 123 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 123 ►

talmazó könyvtár csoport ID–je lesz. Távoli fájlrendszer elérésekor a viselkedés nem a programunkat futtató, hanem a távoli rendszer szabályaitól függ.

A folyamat valódi felhasználói azonosítója (real user ID) a getuid, a tényleges felhasználói azonosítója (effective user ID) a geteuid függvény-nyel tudható meg. Az ugyanilyen rutin páros a valódi és tényleges csoport-azonosítóra a getgid és a getegid.

chown

A függvénnyel módosítható egy létező fájl tulajdonosa és/vagy tulajdonos csoportja a paraméterként megadottakra.

int chown(const char *fajlnev, uid_t tulaj, gid_t csoport);

Bizonyos rendszerekben a fájl tulajdonosának változtatása törli a SUID (set–user–ID) és az SGID (set–group–ID) biteket is, miután e bitek beállítása nem biztos, hogy megfelel az új tulajdonosnak. A többi jogosult-sági bit viszont változatlan.

A rutin visszatérési értéke zérus sikeres esetben, és a –1 hibát jelez, amikor is az errno a szokásos fájlnév hibákon túl a következő értékeket veheti még fel:

• EPERM: E folyamatnak nincs joga e fájl tulajdonosváltásához. Csak a fájl tulajdonosa (a folyamat tényleges felhasználói ID–je), vagy vala-mely privilegizált felhasználó módosíthatja a fájl csoportját. A legtöbb fájlrendszerben csak a privilegizált felhasználók változtathatják meg a fájl tulajdonosát, más rendszerekben ezt az aktuális tulajdonos is meg-teheti. Távoli fájlrendszer elérésekor a viselkedés nem a programunkat futtató, hanem a fájlt tároló, távoli rendszer szabályaitól függ.

• EROFS: A fájl csak olvasható fájlrendszeren van, tehát semmije sem módosítható.

A _POSIX_CHOWN_RESTRICTED makró definiáltsága ese-tén nem privilegizált folyamatokban a chown rutinnal tényleges, vagy va-lamely másodlagos csoport ID–vel csak a fájl tulajdonos csoport ID–je módosítható.

fchown int fchown(int leiro, uid_t tulaj, gid_t csoport);

Ugyanaz, mint a chown, de a tulajdonosváltás a leiro leírójú, nyitott fájlra vonatkozik.

Page 124: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 124 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 124 ►

A rutin sikeres esetben zérussal tér vissza. A szolgáltatott –1 hibát je-lez, amikor is az errno a következő értékek egyike:

• EBADF: Érvénytelen fájlleíró paraméter. • EINVAL: A leiro cső, vagy foglalat, nem szabályos fájl. • EPERM: E folyamatnak nincs joga e fájl tulajdonosváltásához. Rész-

letek a chmod–nál! • EROFS: A fájl csak olvasható fájlrendszeren van, tehát semmije sem

módosítható.

A tulaj.c példaprogram váltja az első parancssori paraméter fájl tulajdono-sát a második paraméterben megadottra:

/* tulaj.c példaprogram */ #include <stdio.h> #include <pwd.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int main(int argc, char *argv[]){ struct passwd *pwdmut; struct stat puff; uid_t ujtul; gid_t ujcsop; /* Megadtak parancssori paramétert? */ if(argc!=3){ fprintf(stderr, "A program indítása:\n" "tulaj fájlnév újtulaj\n"); return 1; } /* Az argv[1] fájl létezik és írható? */ printf("A(z) %s fájl tulajdonosváltása:\n", argv[1]); if(access(argv[1], F_OK|W_OK) != 0){ fprintf(stderr,"A(z) %s fájl nem létezik, vagy nem " "írható!\n", argv[1]); return 1; } /* Az argv[2] felhasználó létezik-e? */ if(!(pwdmut=getpwnam(argv[2]))){ fprintf(stderr,"A(z) %s felhasználó ismeretlen!\n" "\tA hiba száma: %d\n\tA hiba szövege:%s\n", argv[2], errno, strerror(errno)); return 1; } ujtul=pwdmut->pw_uid; ujcsop=pwdmut->pw_gid;

Page 125: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 125 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 125 ►

/* Az eredeti tulajdonos elérése: */ if(stat(argv[1], &puff)){ fprintf(stderr,"Gond van a(z) %s attribútumainak " "lekérdezésével!\n\tA hiba száma: %d\n\tA hiba " "szövege:%s\n", argv[1], errno, strerror(errno)); return 1; } if(!(pwdmut=getpwuid(puff.st_uid))){ fprintf(stderr,"A(z) eredeti felhasználó " "ismeretlen!\n\tA hiba száma: %d\n\tA hiba " "szövege:%s\n", errno, strerror(errno)); return 1; } printf("\t%s --> %s\n", pwdmut->pw_name, argv[2]); if(chown(argv[1], ujtul, ujcsop)){ fprintf(stderr, "A váltás sikertelen!\n\tA hiba " "száma: %d\n\tA hiba szövege:%s\n\n", errno, strerror(errno)); return 1;} printf("A váltás sikeresen megtörtént.\n\n"); return 0; }

Egy lehetséges kimenet „tulaj ki.txt wajzy” indítás után: A(z) ki.txt fájl tulajdonosváltása: bauer --> wajzy A váltás sikertelen! A hiba száma: 1 A hiba szövege:Operation not permitted

Az eredményből aztán kitűnően látszik, hogy a GNU C csak privi-legizált felhasználónak, vagy a root–nak engedi meg a fájl tulajdonosváltását.

6.5.15. Fájlméret A fájl aktuális méretét a stat struktúra st_size tagja tartalmazza. A fájlmé-retet szokásosan a rendszer tartja karban automatikusan, azaz első létreho-zásakor létrejön valamilyen méretben az állomány, majd a hozzáírások következtében bővül. Bizonyos esetekben szükség lehet azonban a fájlmé-ret redukálására is. Erre való a lezárt fájlokkal foglalkozó truncate, és a megnyitottakat kezelő ftruncate függvény.

Néhány operációs rendszer lehetővé teszi e függvények segítségével a fájlbővítést „lyukak” létesítésével (ritka fájlok!).

Ne feledjük, hogy open vagy fopen hívásokkal a fájl tartalma ma-radéktalanul el is távolítható, azaz mérete zérusra vágható!

A truncate és az ftruncate eredménye nem szabályos fájlokra nem definiált. Sok rendszerben azonban az ilyen hívás sikeresnek látszik, holott hatására semmi sem történik.

Page 126: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 126 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 126 ►

truncate int truncate(const char *fajlnev, off_t meret);

A függvény a fajlnev fájl méretét meret–re változtatja. Ha a meret rövidebb az állomány korábbi méreténél, akkor a fájlvégen helyet foglaló adatok el-vesznek. A fájlnak írhatónak kell lennie a felhasználó szempontjából a művelet elvégezhetőségéhez.

Ha a meret nagyobb az induló fájlméretnél, a rendszer „lyukakat” ad az állomány végéhez.

A sikert jelző visszatérési érték zérus, s a hibás a –1, amikor is a szoká-sos fájlnév hibákon túl az errno a következők egyike lehet:

• EACCES: A fajlnev könyvtár, vagy nem írható. • EFBIG: A meret nagyobb, mint az operációs rendszerben megengedett

maximális fájlméret, így ez utóbbi határig képes a függvény a fájl bőví-tésére.

• EINTR: A műveletet jel (signal) szakította meg. • EINVAL: A meret negatív. • EIO: Hardver B/K hiba történt. • EISDIR: Újabb rendszerek ezzel jelzik, hogy a fajlnev könyvtár. • ENOTDIR: A fájlnévben valamely könyvtár komponens nem létezik. • EPERM: A fájl „csak hozzáfűzős”, vagy megváltoztathatatlan méretű. • EROFS: A fájl csak olvasható fájlrendszeren van, tehát semmije sem

módosítható. • ETXTBSY: A fajlnev foglalt. A fájl végrehajtható, s a rendszer éppen

futtatja.

ftruncate

A leiro leírójú, nyitott fájl méretét változtatja meg meret bájt méretűre az int ftruncate(int leiro, long meret);

rutin. A függvényhívás bővítheti és csonkíthatja a fájlt. Az állománynak a művelet sikeres kivitelezéséhez írásra nyitottnak kell lennie. Csonkuláskor a fájl végén az eredeti és az új méret közötti információ elvész.

Bővítéskor a POSIX szabvány tulajdonképpen az implementációra hagyja, hogy mi történjék, ha a megadott, új fájlméret nagyobb az eredeti-nél. Az ftruncate békén hagyhatja a fájlt, semmit sem téve, vagy megnö-velheti a kívánt méretűre. Utóbbi esetben a bővítő terület zérus feltöltésű

Page 127: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 127 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 127 ►

lesz. Ezek szerint az ftruncate portábilis értelemben nem megbízható útja a fájlméret növelésének, de egyébként valószínűleg a lehető leggyorsabb módja.

Sikeres esetben zérussal tér vissza a rutin. A –1 visszaadott érték hibát jelez. A lehetséges hibákhoz irányadók a truncate–nél ismertetettek, s ezeken túl az errno lehet még a következők egyike is:

• EACCES: A leiro könyvtárt ér el, vagy a fájl nem írásra nyitott. • EBADF: A leiro érvénytelen.

A chsize.c példaprogram kijelzi saját hosszát, majd két másolatot készít a forrásfájlról. Az egyik másolat (chsize.fel) méretét megfelezi, a másikét (chsize.ket) megkétszerezi, s láthatóvá teszi az eredményt. Végül törli a két átméretezett másolatfájlt.

/* chsize.c példaprogram. */ #include <stdio.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <unistd.h> #define FAJL "chsize" int main(void){ char parancs[2*NAME_MAX+10]= "cp ", fajlnev[][NAME_MAX]={{FAJL}, {FAJL}, {FAJL}}, *kit[]={".c", ".fel", ".ket"}; #define TMERET sizeof(kit)/sizeof(kit[0]) struct stat puff; int i, hsz; /* A fájlnevek előállítása: */ for(i=0; i<TMERET; ++i) strcat(fajlnev[i], kit[i]); printf("%s másolat fél- és kétszeres fájlméretben:\n", fajlnev[0]); /* A stat struktúra feltöltése: */ if(stat(fajlnev[0], &puff)){ perror("Gond van a fájlattribútumok kinyerésével"); return 1; } printf("A(z) %s fájl mérete %ld bájt.\n", fajlnev[0], puff.st_size); /* Felezés és kétszerezés: */ strcat(parancs, fajlnev[0]); hsz=strlen(strcat(parancs, " ")); for(i=1; i<TMERET; ++i){

Page 128: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 128 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 128 ►

strcpy(&parancs[hsz], fajlnev[i]); system(parancs); if(truncate(fajlnev[i], i>1?puff.st_size*2:puff.st_size/2)) fprintf(stderr, "A(z) %s átméretezése " "sikertelen.\n\tA hiba száma: %d\n\tA hiba " "szövege: %s\n", fajlnev[i], errno, strerror(errno)); } /* Eredmény kijelzése: */ strcpy(parancs, "ls -li "); strcat(parancs, FAJL); system(strcat(parancs, "*")); /* A két új fájl törlése: */ for(i=1; i<TMERET; ++i) if(unlink(fajlnev[i])) fprintf(stderr, "A(z) %s törlése sikertelen.\n" "\tA hiba száma: %d\n\tA hiba szövege: %s\n", fajlnev[i], errno, strerror(errno)); return 0; }

Az eredmény kimenet: chsize.c másolat fél- és kétszeres fájlméretben: A(z) chsize.c fájl mérete 1607 bájt. 4394 –rwxr—r-- 1 bauer teacher 1607 2005-12-08 15:57 chsize.c 4392 –rwxr—r-- 1 bauer teacher 803 2005-12-08 16:00 chsize.fel 4396 –rwxr—r-- 1 bauer teacher 3214 2005-12-08 16:00 chsize.ket

6.5.16. Fájlok idő adatai Minden fájl három idő adattal (időbélyeggel) rendelkezik:

• az utolsó elérés idejével, • a fájltartalom utolsó módosításának idejével és • a fájlt attribútumai utolsó változtatásának idejével.

Ezeknek a stat struktúra st_atime, st_mtime és st_ctime tagjai felel-nek meg, melyek mind a time.h fejfájlban definiált time_t típusúak. A típus pontos részletezése „Az idő kezelése” szakaszban található!

Az olvasás a fájlból felújíttatja az elérési idő attribútumot, s az írás pe-dig a módosítás idejét aktualizálja. Az állomány létrejövetelekor mind a három idő adat beáll az akkori aktuális időre. Ráadásul a fájl miatti új be-jegyzést tartalmazó könyvtár attribútum változtatási és módosítási ideje is megváltozik

Page 129: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 129 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 129 ►

Az új név adása a fájlnak link függvénnyel felújíttatja a hivatkozott ál-lomány attribútum változtatási idejét, és az új nevet tartalmazó könyvtár attribútum változtatási és módosítási idő adatait. Ugyanezen időkre hat a fájlnév törlése unlink, remove vagy rmdir rutinnal. A rename–s fájl–átnevezés csak a két érintett szülőkönyvtár attribútum változtatási és mó-dosítási idejét korrigálja, de az átnevezett állomány idő adatait nem.

A fájl attribútumainak módosítása (például chmod–dal) az attribútum változtatási időt aktualizálja.

Az attribútum változtatási idő adattól eltekintve, a fájl többi időbélyege átírható explicit módon az utime függvény segítségével. A rutin használa-tához be kell kapcsolni az utime.h fejfájlt.

utimbuf struktúra

Az utime függvénnyel használatos struktúra a fájl elérési és módosítási időinek megadását biztosítja. Tagjai:

• time_t actime: A fájl elérési ideje. • time_t modtime: A fájl utolsó módosításának ideje.

utime int utime(const char *fajlnev, struct utimbuf *

fajlidok);

A függvény a fajlnev fájl elérési és módosítási idejét átállítja a fajlidok mutat-ta utimbuf struktúra actime és modtime tagjaira. Ha a fajlidok NULL mutató, akkor mindkét időbélyegbe beírja a rutin az aktuális helyi időt. A fajlnev fájl attribútum változtatási idejét mindkét esetben az aktuális helyi időre módosítja, hisz a két időbélyeg módosítása is attribútum változtatás volt.

Az utime zérussal tér vissza sikeres esetben. –1 jön tőle hiba esetén, és a szokásos fájlnév hibákon túl az errno a következők értékek egyike lehet:

• EACCES: Jogosultsági probléma van fajlidok==NULL esetén. A fájl időbélyegei változtatásához vagy írási jogosultsággal rendelkező tulaj-donosnak kell lenni, vagy privilegizált felhasználónak.

• ENOENT: A fájl nem létezik. • EPERM: Jogosultsági gond van fajlidok!=NULL esetben. A művelet

végrehajtásához fájl tulajdonosnak, vagy privilegizált felhasználónak kell lenni.

• EROFS: A fájl csak olvasható fájlrendszeren helyezkedik el.

Page 130: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 130 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 130 ►

Emlékezzünk! Mindhárom időbélyeg rendelkezik mikró szekundum résszel is. Ezek a stat struktúra st_atime_usec, st_mtime_usec és st_ctime_usec tagjai, melyek a 0 és 999999 tartományból vehetnek fel értéket. A két utime–nál ismertetett időbélyeget és mikró másodperc pár-jait az utimes, a lutimes és az futimes függvények segítségével lehet átírni.

Az idos.c példa az első parancssori paraméter fájl elérési és módosítási idejének dátum részét az ÉÉÉÉ.HH.NN alakú második paraméter 12 órára állítja, majd rekonstruálja a kiindulási helyzetet.

/* idos.c példaprogram: */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <errno.h> #include <time.h> #include <utime.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> /* Formailag helyese az ÉÉÉÉ.HH.NN alakú s dátum? */ int datume(const char *s){ static int honap[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30,31,30,31}; int i, ho; if(!s[10]&&!isdigit(s[4])&&s[4]==s[7]){ for(i=0; i<10; ++i){ if(i==4||i==7) ++i; if(!isdigit(s[i])) return 0; } if((i=atoi(s))>0){ honap[2]=28+(!(i%4)&&i%100||!(i%400)); if((ho=atoi(s+5))>=1&&ho<=12&&(i=atoi(s+8))>=1&& i<=honap[ho]) return 1; } } return 0; } /* time_t időből ÉÉÉÉ.HH.NN ÓÓ:PP:SS alakú karakterlánc: */ char *idolanc(time_t t){ struct tm *struki; static char lanc[25]; struki=localtime(&t); strftime(lanc, 24, "%F %T", struki); lanc[4]=lanc[7]='.'; return lanc; } /* A fajl időattribútumainak kijelzése: */ struct stat attr;

Page 131: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 131 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 131 ►

void idokijelzes(const char *fajl){ if(stat(fajl, &attr)){ fprintf(stderr, "Gond van a(z) %s attribútum " "információjának kinyerésével!\nA hiba(%d): %s.\n", fajl, errno, strerror(errno)); exit(1); } printf("%s fájl idő adatai:\n elérési:%36s", fajl, idolanc(attr.st_atime)); printf("\n módosítási:%33s\n", idolanc(attr.st_mtime)); printf(" attribútum változtatási:%20s\n", idolanc(attr.st_ctime)); } /* Főprogram: */ int main(int argc, char *argv[]){ /* Az eredeti elérési és módosítási idő. */ time_t atim, mtim; struct tm struki; struct utimbuf ujido; int n; /* Parancssori paraméterek rendben? */ if(argc!=3){ fprintf(stderr, "A program indítása:\nidos fájlnév " "ÉÉÉÉ.HH.NN\n"); return 1; } /* A fájl rendben? */ if(access(argv[1], F_OK|W_OK)){ fprintf(stderr, "A(z) %s fájl nem létezik, vagy nem " "írható!\n", argv[1]); return 1; } /* Dátum rendben? */ if(!datume(argv[2])){ fprintf(stderr, "A %s dátum hibás!\n", argv[2]); return 1; } /* Programcím: */ n=printf("%s idő adatainak átállítása %s 12:00:00-ra, " "s vissza:\n", argv[1], argv[2]); while(n--) putchar('-'); putchar('\n'); /* Kiindulás. */ printf("********** A kiindulási állapot:\n"); idokijelzes(argv[1]); atim=attr.st_atime; /* Mentés a visszaállításhoz. */ mtim=attr.st_mtime; printf("********** Átállítás %s 12:00:00-ra:\n", argv[2]); /* Átállítás. */ struki.tm_sec=struki.tm_min=struki.tm_isdst=0; struki.tm_hour=12; struki.tm_year=atoi(argv[2])-1900;

Page 132: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 132 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 132 ►

struki.tm_mon=atoi(argv[2]+5)-1; struki.tm_mday=atoi(argv[2]+8); ujido.actime=ujido.modtime=mktime(&struki); if(utime(argv[1], &ujido)){ fprintf(stderr, "Az időváltoztatás nem sikerült!\n"); return 1; } idokijelzes(argv[1]); printf("********** Visszaállítás a kiindulási " "időre:\n"); /* Visszaállítás. */ ujido.actime=atim; ujido.modtime=mtim; if(utime(argv[1], &ujido)) fprintf(stderr, "Nem sikerült az idő " "visszaállítása!\n"); else idokijelzes(argv[1]); return 0; }

„idos idos.c 2016.01.23” indítás után egy lehetséges eredmény kime-net a következő: idos.c idő adatainak átállítása 2016.01.23 12:00:00-ra, s vissza: -------------------------------------------------------- ********** A kiindulási állapot: idos.c fájl idő adatai: elérési: 2005.12.13 10:41:05 módosítási: 2005.12.13 10:40:51 attribútum változtatási: 2005.12.13 10:41:09 ********** Átállítás 2016.01.23 12:00:00-ra: idos.c fájl idő adatai: elérési: 2016.01.23 12:00:00 módosítási: 2016.01.23 12:00:00 attribútum változtatási: 2005.12.13 10:41:40 ********** Visszaállítás a kiindulási időre: idos.c fájl idő adatai: elérési: 2005.12.13 10:41:05 módosítási: 2005.12.13 10:40:51 attribútum változtatási: 2005.12.13 10:41:40

6.6. Fájl zárak A fejezetben a tanácsolt és nem a kötelező zárolásokról lesz szó. A záro-lás vonatkozhat az egész fájlra, vagy annak egy kisebb részére, például rekordokra.

A rekordzárolás megakadályozza, hogy több ugyanazon a fájlon dol-gozó szoftver hiba–gyanús módon egyszerre érje el, esetleg változtassa az állomány egyazon részét. Példaként vegyünk egy programot, melyet egy-

Page 133: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 133 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 133 ►

szerre több felhasználó is futtat, és állapot információját egy közös fájlba naplózza! Ilyen például egy játékszoftver, mely az elért eredményeket egy fájlban követi. Ha több példányban futtatják a programot, a szimultán fájlba–írás tönkreteheti az eredményeket nyilvántartó fájl tartalmát.

E probléma úgy hárítható el, hogy írás zárat kell kihelyezni a fájl írni kívánt régiójára. Az írás sikeres lebonyolítása után aztán vissza kell vonni a fájlterület zárolását.

Ha a program konzisztens módon szeretne beolvasni egy fájldarabot, akkor az olvasás végrehajtatása előtt olvasási zárat kell kitennie a kérdéses fájlterületre. Az olvasási zárolás megvéd attól, hogy a művelet során más folyamat erre a fájlrégióra írjon. Az olvasási zárat is törölni kell a művelet befejeződése után.

Nézzük precízebben! A kizárólagos (exclusive) zár, vagy írás zár biztosítja a folyamatnak,

hogy a fájl megadott részét egyedül ő érhesse el írási céllal. Amíg az írás zár fennáll, más processz nem tudja zárolni a fájl ugyanezen részét.

Az osztott (shared) zár, vagy olvasási zár megakadályoz más folya-matokat abban, hogy írás zárat tegyenek az állomány adott részére. Ter-mészetesen olvasási zárat más program is igényelhet ugyanerre a fájlterü-letre.

Mint mondottuk, a zárolás tanácsolt, azaz az olvasó és író függvények pillanatnyilag nem ellenőrzik, és nem veszik figyelembe a zárolásokat. Ha több processz közt megosztott fájlra zárolási protokollt kívánunk megva-lósítani, akkor az alkalmazásnak magának kell explicit fcntl hívásokkal az alkalmas programpontokon a zárakat igényelnie és törölnie.

A zárak a processzekkel vannak kapcsolatban. Egy folyamat csak egyfajta zárral rendelkezhet a fájl bájtjaira. Ha az állomány bármely fájlle-íróját bezárja a program, a processz által fenntartott összes zárat feloldja a rendszer még akkor is, ha a zárolásokat a nyitva maradt leírókkal hozták létre. Ha a folyamat befejeződik, a zárak felszabadulnak, és nem öröklik meg a fork–kal létesített gyermekprocesszek.

Zároláskor az flock struktúrával specifikálandó a zár fajtája és helye. Ezt az adattípust, és az fcntl függvénnyel kapcsolatos makrókat az fcntl.h fejfájlban deklarálták.

6.6.1. flock struktúra A típus az fcntl függvénnyel fájl zárak leírására használatos. Tagjai:

Page 134: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 134 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 134 ►

• short int l_type: A zár típusa. Lehet olvasási (osztott) zár: F_RDLCK, írási (kizárólagos): F_WRLCK, vagy nem zárolt (zár fel-oldása): F_UNLCK.

• short int l_whence: Az fseek vagy az lseek honnan paraméterének felel meg, és megmondja, hogy mihez képesti az eltolás (offset): SEEK_SET, SEEK_CUR, vagy SEEK_END.

• off_t l_start: A zárolandó terület kezdetének eltolása. Bájtban értendő az l_whence ponthoz képest.

• off_t l_len: A zárolandó régió bájtban mért hossza. A zérus érték spe-ciális, s azt jelenti, hogy a terület innét a fájl végéig tart.

• pid_t l_pid: A zárat fenntartó folyamat azonosítója (process ID). A pid_t a folyamat azonosító ábrázolására alkalmas, előjeles egész típus. A GNU C–ben int. Az l_pid kitöltése F_GETLK parancsos fcntl hí-vással történhet, de zár létesítésekor nem veszi tekintetbe ezt a tagot a rendszer.

6.6.2. fcntl Az fcntl függvény különféle műveleteket képes végezni fájlleírókon. Ilyen műveletek a fájlleíró állapotát taglaló jelzők lekérdezése és beállítása, a rekord zárolás kezelése, és így tovább. A függvényt és a jelzők szimbolikus állandóit az fcntl.h fejfájlban deklarálták. E jelzők közül sok az open–nel is használatos.

int fcntl(int leiro, int parancs, …);

A függvény végrehajtja a parancs műveletet a leiro fájlleírón. Az fcntl függvény érvénytelenedési (cancellation) pont többszálas programokban! Bizonyos parancsok további paramétereket igényelnek. E járulékos paramé-terek, a visszatérési érték, ill. a lehetséges hibakódok az egyes parancsok részletes leírásánál találhatók!

Itt most csak a fájlzárolással foglalkozunk, így a lehetséges parancs faj-ták a következők:

• F_GETLK: Fájlzár kinyerése. • F_SETLK: Fájlzár beállítás vagy törlése. • F_SETLKW: Mint az F_SETLK, de megvárja a befejeződést.

A zárolással foglalkozó fcntl harmadik paramétere struct flock * típu-sú, s a zár adatait tartalmazza. Tehát a zároláshoz használatos fcntl proto-típusa a következő:

Page 135: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 135 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 135 ►

int fcntl(int leiro, int parancs, struct flock *zar);

Nézzük át parancskódonként a dolgokat!

F_GETLK: Információ kinyerése a zárról

Ha van már zár, akkor az blokkolja a zar zárolást, és információja felülírja a zar paraméterrel mutatott struktúra tagjait. Létező zárakról nem tudósít a függvény, ha kompatibilisek az flock struktúra paraméterben specifikált zárral. Ilyenformán F_WRLCK zártípust kell előírni, ha minden zárra (olvasási és írási) kíváncsiak vagyunk. Ha F_RDLCK–t adunk meg, csak az írás zárak érdekelnek bennünket.

Ha a zar paraméterrel megadott régióban egynél több zárolás is hatály-ban van, akkor is csak egyikkőjükről szolgáltat információt az fcntl. Ha az l_whence SEEK_SET a struktúrában, akkor az l_start és az l_len azo-nosítja a zárolt területet.

Ha nincs zárolás, akkor a zar struktúra l_type tagját állítja át F_UNLCK–ra a rutin.

A függvény visszatérési értéke normál esetben –1–től eltérő, mert ez az érték foglalt a hibajelzésre, amikor is az errno lehet:

• EBADF: A leiro paraméter érvénytelen. • EINVAL: Vagy a zar paraméter nem képez érvényes zár információt,

vagy a leirohoz kapcsolt fájl nem támogatja a zárolást.

F_SETLK: Zár beállítása, vagy törlése

Ha a folyamat rendelkezik már zárolással a megadott régió bármelyik ré-szén, akkor a kérdéses területen a régi zárat helyettesíteni fogja az új. A zár eltávolításához zártípusként F_UNLCK–ot kell megadni.

Ha a zárolás sikeres, –1–től eltérő értéket szolgáltat az fcntl. Ha a zár nem állítható be, a függvény azonnal –1–gyel tér vissza. Ez a parancs nem várja meg, míg más folyamatok felengedik a zárakat a zárolni kívánt terüle-ten. A lehetséges errno értékek:

• EAGAIN és EACCES: A zár kihelyezését meggátolja egy már létező zárolás a fájlon. Az egyik rendszer EACCES, a másik (és a GNU C is idetartozik) EAGAIN hibakódot állít. A probléma programbeli leke-zelése persze azonos.

• EBADF: A leiro paraméter érvénytelen. Olvasási zárat kívántak kihe-lyezni, de a leiro nincs olvasási elérésre megnyitva. A leiro nem írási el-érésű, és írási zárolást akartak megvalósítani.

Page 136: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 136 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 136 ►

• EINVAL: A zar paraméter nem zárolási információ, vagy a leirohoz csatlakozó állomány nem támogatja a zárakat.

• ENOLCK: A rendszer kifogyott a fájlzár erőforrásokból, azaz túl sok zárolás van hatályban. A GNU esetében erre a hibalehetőségre nem kell felkészülni, de hálózaton át elért, másik gépen levő fájlrendszer produkálhat ilyen hibajelzést.

F_SETLKW: Zár beállítása, vagy törlése várakozással

Ugyanaz, mint az F_SETLK parancs, de a folyamatot blokkolja (várako-zásra kényszeríti) az fcntl, míg az igényelt zárolás végre nem hajtható.

Az fcntl visszatérési értékei és a hibák is megegyeznek az F_SETLK–s függvényhíváséival, s az errno még a következő értékeket is felveheti:

• EINTR: A rutint várakozás közben megszakította egy jel (signal). • EDEADLK: A megadott régiót másik folyamat zárolja. A másik

processz viszont egy olyan terület zárolására vár, melyet ez a program zárolt, s így a zárásra várakozások holtponton vannak. A rendszer nem garantálja az összes ilyen szituáció felfedezését, de ha észre vesz egyet, rögtön értesít róla.

A zarolas.c példaprogram olvasási zárat kihelyezve beolvassa a forrásfájl egy darabját, majd írási zárat alkalmazva vissza is írja ezt.

/* zarolas.c példaprogram. */ #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #define FAJL "zarolas.c" #define MERET 10 /* Mennyit. */ #define POZICIO 10 /* Honnét. */ #define ISMETLES 3 /* Max. kísérletszám sikertelen esetben. */ int main(void){ int leiro, i, szlo; char puffer[MERET+1]; struct flock zar={F_RDLCK, SEEK_SET, POZICIO, MERET,0}; /* Programcím: */ i=printf("A(z) \"%s\" fájl zárolt olvasása és " "írása:\n", FAJL);

Page 137: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 137 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 137 ►

while(--i) putchar('-'); putchar('\n'); /* A fájl megnyitása: */ if((leiro=open(FAJL, O_RDWR))==-1){ fprintf(stderr, "A(z) %s fájl megnyitása " "sikertelen!\n\tA hiba szövege:%s\n", FAJL, strerror(errno)); return 1; } /* Olvasási zár kihelyezése: */ for(i=1; i<=ISMETLES&&fcntl(leiro, F_SETLK, &zar)==-1; ++i, sleep(1)) printf("%d. olvasási zár kirakási kísérlet " "sikertelen!\n", i); /* Olvasás: */ if(i<=ISMETLES){ if((szlo=TEMP_FAILURE_RETRY(pread(leiro, puffer, MERET, POZICIO)))<=0){ perror("Az olvasási kísérlet sikertelen!\n\tA hiba" " szövege:"); close(leiro); return 1; } puffer[10]=0; printf("Beolvasva:\"%s\"\n", puffer); /* Az olvasási zár visszavonása: */ zar.l_type=F_UNLCK; for(i=1; i<=ISMETLES&&fcntl(leiro,F_SETLK, &zar)==-1; ++i, sleep(1)) printf("%d. zár törlési kísérlet sikertelen!\n",i); /* Az írási zár kirakása: */ zar.l_type=F_WRLCK; for(i=1; i<=ISMETLES&&fcntl(leiro, F_SETLK,&zar)==-1; ++i, sleep(1)) printf("%d. írási zár kihelyezési kísérlet " "sikertelen!\n", i); /* Irás: */ if(i<=ISMETLES){ if((i=TEMP_FAILURE_RETRY(pwrite(leiro, puffer, szlo, POZICIO)))<=0) perror("Az írási kísérlet sikertelen!\n\tA hiba " "szövege:"); else { puffer[i]=0; printf("Kiírva :\"%s\"\n", puffer); } /* Az írás zárat nem kell visszavonni, hisz a close törli. */ } } close(leiro); return 0; }

Page 138: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 138 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 138 ►

Az eredmény kimenet: A(z) "zarolas.c" fájl zárolt olvasása és írása: ---------------------------------------------------- Beolvasva:".c példap" Kiírva :".c példap"

6.7. Könyvtárak

6.7.1. Aktuális, vagy munkakönyvtár Minden folyamat rendelkezik aktuális, vagy munkakönyvtárral, ahová dol-gozik, ill. ez az a könyvtár, ahonnét a relatív utak indulnak. Bejelentkezés-kor a felhasználó saját (home) könyvtára, vagy bejelentkezési könyv-tára lesz az aktuális, melyet később megváltoztathat, s mindenkor le is kérdezhet.

A vonatkozó függvények prototípusai az unistd.h fejfájlban helyez-kednek el.

getcwd char *getcwd(char *puffer, int maxhsz);

A függvény az aktuális munkakönyvtárat kérdezi le. Az eredmény abszolút (gyökérkönyvtárból induló!) elérési utat aztán null lezárású karakterlánc formájában elhelyezi a puffer címen. A maxhsz a puffer tömb mérete, azaz a maxhsz paraméter az eredmény út maximális hosszát specifikálja a lezáró null karaktert is beleértve. Hiba következik be, ha az út mérete meghaladja ezt.

A rutin GNU könyvtári változata megengedi, hogy a puffer NULL mu-tató legyen, amikor is az út tárolásához legalább maxhsz bájtot allokál au-tomatikusan a rendszer a malloc segítségével, ha a maxhsz nagyobb zérus-nál. Ha a puffer NULL és a maxhsz is az, akkor a malloc–kal foglalt puffer bizonyosan elegendően nagy a visszaadandó út tárolásához. free hívással fel is szabadítandó puffer, ha már nincs szükség a továbbiakban az útra.

A rutin a (malloc–kal foglalt) puffer címével tér vissza sikeres esetben. A NULL visszaadott érték hibát jelez, és az errno:

• EACCES: A fájlnév egy komponenséhez hiányzik az olvasási, vagy keresési jogosultság.

• EINVAL: A maxhsz zérus, és a puffer nem NULL mutató. • ERANGE: Az eredmény út a lezáró null karaktert is beleértve hosz-

szabb maxhsz karakternél. Nagyobb tömböt kell allokálni, s újra lehet próbálkozni!

Page 139: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 139 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 139 ►

chdir

Az aktuális könyvtárat (a folyamat munkakönyvtárát) a fajlnev paraméterrel megkívántra a

int chdir(const char *fajlnev);

függvénnyel állíthatjuk át. Sikeres esetben zérust szolgáltat a rutin Hiba bekövetkeztekor –1 jön tőle, és a szokásos fájlnév hibákon túl az errno még a következő lehet:

• ENOTDIR: A fajlnev nem könyvtár.

fchdir int fchdir(int leiro);

A folyamat munkakönyvtárát a leiroval megadottra állítja át a rutin. A nor-mál visszatérés itt is zérus, és a hibát jelző a –1, amikor is az errno a kö-vetkezők egyike:

• EACCES: Elérés elutasítva: A könyvtár jogosultsági beállítása nem engedi meg az olvasási (keresési) elérést.

• EBADF: A leiro paraméter nem érvényes fájlleíró. • EINTR: A függvényt jel (signal) szakította meg. • EIO: B/K hiba következett be. • ENOTDIR: A leiro fájlleíró nem könyvtárral van kapcsolatban.

Az akonyvtar.c példa lekérdezi és kijelzi az aktuális könyvtárat. A getcwd(NULL, 0) hívás biztosítja, hogy bizonyosan elegendő hely legyen a leghosszabb abszolút út tárolásához is. Eztán két szinttel feljebb visszük az aktuális munkakönyvtárat, majd visszaállítjuk a kiindulási állapotot.

/* akonyvtar.c példaprogram. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main(void){ char *puffer; int maxhsz; printf("Az aktuális könyvtár lekérdezése és " "változtatása:\n"); /* Az aktuális könyvtár lekérdezése és kijelzése: */ if(!(puffer=getcwd(NULL, 0)))

Page 140: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 140 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 140 ►

perror("getcwd hiba!\n\tA hiba szövege:"); else { maxhsz=strlen(puffer)+1; printf("%s\n", puffer); } /* A munkakönyvtár a szülő szülőkönyvtára: */ printf("A munkakönyvtár a szülő szülőkönyvtára:\n"); if(chdir("../..")) perror("chdir hiba!\n\tA hiba szövege:"); if(!getcwd(puffer, maxhsz)) perror("getcwd hiba!\n\tA hiba szövege:"); else { printf("%s\n", puffer); /* A kiindulási aktuális könyvtár visszaállítása: */ printf("Az eredeti aktuális könyvtár " "visszaállítása:\n"); if(chdir(&puffer[strlen(puffer)+1])) perror("chdir hiba!\n\tA hiba szövege:"); if(!getcwd(puffer, maxhsz)) perror("getcwd hiba!\n\tA hiba szövege:"); else printf("%s\n", puffer); } return 0; }

Egy lehetséges kimenet: Az aktuális könyvtár lekérdezése és változtatása: /home/bauer/FajlokMappak A munkakönyvtár a szülő szülőkönyvtára: /home Az eredeti aktuális könyvtár visszaállítása: /home/bauer/FajlokMappak

6.7.2. Könyvtár létrehozása, törlése Az itt ismertetett függvények használatához bekapcsolandó az unistd.h és a sys/stat.h fejfájl.

mkdir

Új könyvtárakat az int mkdir(const char *fajlnev, mode_t mod);

függvénnyel hozhatunk létre. A rutin egyetlen, üres, fajlnev nevű könyvtárat hoz létre. Az új könyvtárnév a fajlnev paraméter utolsó komponense lehet csak. A létrejött könyvtár fájl jogosultságait a mod paraméter rögzíti. Lásd az open függvényt és a stat struktúra leírását! Sikeres esetben zérust szol-gáltat a függvény, s hiba bekövetkeztekor –1 jön tőle, amikor is az errno a szokásos fájlnév hibákon túl még a következő értékeket veheti fel:

• EACCES: Nincs írási jog a leendő új könyvtár szülő könyvtárában.

Page 141: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 141 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 141 ►

• EEXIST: A könyvtár azért nem jött létre, mert az fajlnev létező fájl, könyvtár vagy eszköz neve.

• EMLINK: A szülőkönyvtár túl sok hivatkozást (bejegyzést) tartalmaz. Normál fájlrendszerben (és a GNU is ilyen) ez a hiba nem fordulhat elő, mert több hivatkozást engedélyez, mint amennyi a lemezen egyál-talán elfér. A dologra azonban tekintettel kell lenni, mert a hálózaton át elért, másik gépen levő fájlrendszer produkálhat ilyen problémát.

• ENOSPC: Nincs elég hely a fájlrendszerben az új könyvtár létrehoza-talához.

• EROFS: A létesítendő új könyvtár szülőkönyvtára csak olvasható fájlrendszeren helyezkedik el.

rmdir

Egyetlen, létező, üres könyvtárat – ha az nem az aktuális és nem a gyökér-könyvtár – az

int rmdir(const char *fajlnev);

függvénnyel szüntethetünk meg. Minden más vonatkozásban az rmdir úgy viselkedik, mint az unlink. A fajlnev paraméterrel megadott könyvtár sikeres törlése után a rutin zérust szolgáltat. –1–gyel jelzi a hibát és az errno:

• EACCES: A szokásos értelmezésen túl, a fajlnevet tartalmazó könyv-tárhoz nincs írásjog, vagy a könyvtár „sticky” bitje bebillentett, és a fo-lyamat nem tulajdonosa a fájlnak.

• EBUSY: A fajlnev könyvtárat valamilyen módon használja a rendszer (aktuális könyvtár, gyökérkönyvtár stb.), s ez nem teszi lehetővé a tör-lését.

• EIO: Hardverhiba történt a művelet közben. • ENOENT: A törlendő fajlnev könyvtár nem létezik. • ENOTEMPTY és EEXIST: A fajlnev könyvtár nem üres. A szimbo-

likus állandók szinonimák. A GNU rendszer az ENOTEMPTY–t használja.

Az üres könyvtár ugye az, amiben csak egy . és egy .. bejegyzés van.

• EPERM: Bizonyos rendszerekben az rmdir nem használható nem könyvtárak törlésére, és a fajlnev nem könyvtár. GNU rendszerben ál-lományt csak unlink–kel szabad törölni.

Page 142: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 142 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 142 ►

• EROFS: A fajlnev könyvtár csak olvasható fájlrendszeren helyezkedik el, s így nem törölhető.

Az mkrd.c példában az aktuálisban létrehozunk egy könyvtárat, megjelen-tetjük a tartalmát, majd töröljük:

/* mkrd.c példaprogram. */ #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <unistd.h> int main(void){ /* A Proba könyvtár létrehozása az aktuálisba: */ printf("A \"Proba\" könyvtár létrehozása és törlése:\n"); if(mkdir("Proba", S_IRWXU| S_IRGRP| S_IXGRP| S_IROTH| S_IXOTH)) perror("Gond van a \"Proba\" könyvtár " "létrehozatalával!\n\tA hiba szövege:"); else { printf("A \"Proba\" könyvtár sikeresen létrehozva és" " tartalma:\n"); system("ls -lia Proba"); } if(!rmdir("Proba")) printf("A \"Proba\" könyvtár sikeresen törölve.\n"); else perror("Probléma van a \"Proba\" könyvtár " "megszüntetésével!\n\tA hiba szövege:"); return 0; }

Egy lehetséges kimenet: A "Proba" könyvtár létrehozása és törlése: A "Proba" könyvtár sikeresen létrehozva és tartalma: összesen 1 7444 drwxr-xr-x 2 bauer teacher 48 2005-12-16 10:45 . 4474 drwxr-xr-x 3 bauer teacher 584 2005-12-16 10:45 .. A "Proba" könyvtár sikeresen törölve.

6.7.3. Könyvtárak megnyitása, olvasása és bezárása Az itt ismertetett függvények és szimbólumok használatához bekapcso-landó a dirent.h fejfájl.

Először nézzük meg, hogy mi található egy könyvtár bejegyzésben!

dirent struktúra

A struktúra tagjai a könyvtár fájl rekord felhasználó számára is elérhető adatai.

Page 143: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 143 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 143 ►

• char *d_name: A könyvtár neve (fájlnév komponens) null lezárású karakterláncként. Ez az a struktúratag, melyre minden POSIX rend-szerben bizton’ számíthatunk.

• ino_t d_fileno: A fájl sorszáma (inode száma). A GNU és egy sereg POSIX rendszerben a legtöbb fájlra ez ugyanaz, mint a stat struktúra st_ino tagja az állományra. Más rendszerekkel való kompatibilitási ok-ból a tag d_ino névvel is elérhető.

• unsigned char d_namlen: A d_name karakterlánc hossza a lánczáró ’\0’ karakter nélkül.

• unsigned char d_type: A fájl típusa. A lehetséges értékei: • DT_BLK: A fájl blokkos eszköz. • DT_CHR: A fájl karakteres eszköz. • DT_DIR: A bejegyzés könyvtár. • DT_FIFO: A bejegyzés névvel ellátott cső. • DT_LNK: A bejegyzés közvetett hivatkozás. • DT_REG: A fájl szabályos állomány. • DT_SOCK: A bejegyzés hálózati kapcsolatot ellátó foglalat. • DT_UNKNOWN: A fájltípus ismeretlen, nem meghatározható.

Bizonyos rendszerek mindenképp ezt adják vissza.

Ha a fájlnak több neve van, akkor minden név saját könyvtár bejegy-zéssel rendelkezik. A könyvtár rekordok viszont akkor tartoznak egyetlen fájlhoz, ha a d_fileno tagjuk azonos értékű.

A további fájl attribútumok (méret, utolsó módosítás ideje stb.) nem a könyvtár rekordban, hanem a fájl inode tábla bejegyzésében helyezkednek el. Lásd a stat struktúra leírását!

DIR típus

Könyvtár folyamot ábrázoló adattípus. A DIR típust általában fájlleírót használva valósították meg, és az opendir egyfajta open függvény.

A programozónak nem kell explicit módon sem DIR típusú, sem dirent struktúra típusú változókat létrehoznia, ezek címét a könyvtár el-érést biztosító függvények bocsátják rendelkezésére.

opendir DIR *opendir(const char *konyvtarnev);

Megnyitja olvasásra a konyvtarnev fájlnevű könyvtárat, s visszaadja azt a DIR * típusú mutatót, melyen át elérhető lesz a könyvtár. Hiba esetén

Page 144: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 144 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 144 ►

NULL mutatót szolgáltat a rutin, s az errno a szokásos fájlnév hibákon túl még a következő értékeket veheti fel:

• EACCES: A folyamatnak nincs olvasási joga a konyvtarnev könyvtárra. • EMFILE: A program már túl sok fájlt nyitott meg, s ezért a művelet

nem végrehajtható. • ENFILE: Az egész operációs rendszer, vagy a könyvtárat tartalmazó

fájlrendszer pillanatnyilag nem képes további fájlok megnyitására. Ez a probléma GNU rendszerben nem történhet meg.

readdir struct dirent *readdir(DIR *konyvtar);

A függvény a következő rekordot olvassa az opendir–rel megnyitott konyvtarból. A rekord adatait elhelyezi a statikusan allokált dirent struktú-rába, majd visszaadja ennek címét. Az újabb readdir hívás aztán felülírja ezt a struktúrát a konyvtar következő bejegyzése adataival.

Bizonyos rendszerekben a readdir nem adja vissza a . és a .. re-kordok adatait, holott ezek érvényes fájlnevek bármely könyvtárban.

Ha a könyvtárban már nincs további bejegyzés, vagy valamilyen hiba történt, a rutin NULL mutatót szolgáltat. Hiba esetén az errno a követ-kező lehet: • EBADF: A konyvtar paraméter érvénytelen.

A readdir nem szál–biztos. Ha több végrehajtási szál ugyanazt a konyvtarat olvassa, akkor felülírhatják egymás visszatérési értékeit. Ilyenkor a függvény szál–biztos változatát (readdir_r) célszerű használni.

closedir int closedir(DIR *konyvtar);

A rutin zárja a konyvtarat, s belőle tovább könyvtárrekordok nem olvasha-tók readdir–rel, csak újabb opendir után.

A closedir sikeres esetben zérust szolgáltat, s a problémát –1–gyel jel-zi, amikor is esetén az errno a következő lehet: • EBADF: A konyvtar paraméter érvénytelen.

6.7.4. Pozícionálás a könyvtár fájlban

rewinddir void rewinddir(DIR *konyvtar);

Page 145: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 145 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 145 ►

A függvény újrainicializálja a nyitott konyvtarat úgy, hogy a readdir ismét az első könyvtár bejegyzést olvassa. A rutin a könyvtár fájlt is újraolvassa, azaz az opendir óta hozzáadott rekordokat el fogja érni, míg az időköz-ben törölt bejegyzéseket már nem.

telldir off_t telldir(DIR *konyvtar);

A rutin a nyitott konyvtar fájl pillanatnyi fájlpozícióját szolgáltatja, melyet aztán a

seekdir void seekdir(DIR *konyvtar, off_t pozicio);

függvénnyel újra be lehet állítani. A konyvtar nyitott, s a pozicio korábbi telldir hívással lekérdezett érték.

A könyvtár bezárása és újabb megnyitása érvénytelenítheti a telldir–rel korábban lekérdezett értékeket.

A dir.c példa az „ls –lia” parancsot valósítja meg (névsorba rendezés nél-kül) a parancssori paraméterként megadott könyvtárra. Ha paraméter nél-kül indítják, akkor az aktuális könyvtárral foglalkozik a program.

/* dir.c példaprogram: */ #include <stdio.h> #include <pwd.h> #include <grp.h> #include <errno.h> #include <time.h> #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> #include <unistd.h> /*time_t időből ÉÉÉÉ.HH.NN ÓÓ:PP:SS alakú karakterlánc:*/ char * idolanc(time_t t){ struct tm *struki; static char lanc[25]; struki=localtime(&t); strftime(lanc, 24, "%F %T", struki); lanc[4]=lanc[7]='.'; return lanc; } /* Főprogram: */ int main(int argc, char *argv[]){ DIR *k; /* A könyvtár. */

Page 146: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 146 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 146 ►

struct dirent *r; /* A könyvtárrekord. */ struct stat s; /* Az inode tábla rekord. */ struct passwd *t; /* A felhasználók. */ struct group *cs; /* A csoportok. */ char *aktkonyv=NULL; /* Aktuális könyvtár. */ /* Programcím: */ printf("A(z) %s könyvtár tartalomjegyzéke:\n", argc>1?argv[1]:"."); /* Aktuális a megadott könyvtár: */ if(argc>1){ if(!(aktkonyv=getcwd(NULL, 0))){ perror("getcwd hiba: "); return 1; } if(chdir(argv[1])){ perror("A könyvtár nem tehető aktuálissá!\n" "\tA hiba: "); return 1; } } /* A könyvtár fájl megnyitása: */ if(!(k=opendir("."))) perror("A könyvtár nem nyitható meg!\n\tA hiba: "); else { while(r=readdir(k)){ if(stat(r->d_name, &s)) printf("%s: ???\n", r->d_name); else { /* Az inode száma: */ printf("%5ld ", s.st_ino); /* Fájltípus: */ switch(r->d_type){ case DT_BLK: putchar('b'); break; case DT_CHR: putchar('c'); break; case DT_DIR: putchar('d'); break; case DT_FIFO: putchar('p'); break; case DT_LNK: putchar('l'); break; case DT_REG: putchar('-'); break; case DT_SOCK: putchar('s'); break; case DT_UNKNOWN: if(S_ISBLK(s.st_mode)) {putchar('b'); break;} if(S_ISCHR(s.st_mode)) {putchar('c'); break;} if(S_ISDIR(s.st_mode)) {putchar('d'); break;} if(S_ISFIFO(s.st_mode)){putchar('p'); break;} if(S_ISLNK(s.st_mode)) {putchar('l'); break;} if(S_ISREG(s.st_mode)) {putchar('-'); break;} if(S_ISSOCK(s.st_mode)){putchar('s'); break;} default: putchar('?'); } /* Elérési jogosultságok: */ if(s.st_mode&S_IRUSR) putchar('r');

Page 147: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 147 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 147 ►

else putchar('-'); if(s.st_mode&S_IWUSR) putchar('w'); else putchar('-'); if(s.st_mode&S_IXUSR) putchar('x'); else putchar('-'); if(s.st_mode&S_IRGRP) putchar('r'); else putchar('-'); if(s.st_mode&S_IWGRP) putchar('w'); else putchar('-'); if(s.st_mode&S_IXGRP) putchar('x'); else putchar('-'); if(s.st_mode&S_IROTH) putchar('r'); else putchar('-'); if(s.st_mode&S_IWOTH) putchar('w'); else putchar('-'); if(s.st_mode&S_IXOTH) putchar('x'); else putchar('-'); /* Közvetlen hivatkozások száma: */ printf("%3hd ", s.st_nlink); /* Tulajdonos és csoport: */ t=getpwuid(s.st_uid); cs=getgrgid(s.st_gid); printf("%-7s %-7s", (t?t->pw_name:"???"), (cs?cs->gr_name:"???")); /* Fájlméret: */ printf("%8d ", s.st_size); /* Az utolsó módosítás ideje: */ printf("%s ", idolanc(s.st_mtime)); /* Fájlnév: */ printf("%s\n", r->d_name); } } } /* Eredeti munkakönyvtár helyreállítása: */ if(argc>1) chdir(aktkonyv); return 0; }

Egy lehetséges kimenet: A(z) . könyvtár tartalomjegyzéke: 4474 drwxr-xr-x 2 bauer teacher 592 2005.12.21 14:51:56 . 4449 drwxr-xr-- 9 bauer teacher 648 2005.12.21 14:42:11 .. 7269 –rwxr--r-- 1 bauer teacher 826 2005.12.05 14:38:34 ir.c 7430 -rwxr--r-- 1 bauer teacher 1085 2005.12.16 10:13:20 akonyvtar.c 42 –rwxr-xr-x 1 bauer teacher 9940 2005.12.21 14:51:37 a.out . . .

Page 148: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 148 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 148 ►

6.8. Ellenőrző kérdések:

• Hogyan fordíthatunk tárgymodult a GCC–vel? Hogyan végrehajtható fájlt?

• Beszéljen a következő fogalmakról: fájlnév, fájlnév komponens, könyvtár fájl, aktuális könyvtár!

• Ismertesse vázlatosan a UNIX fájlrendszer elemeit! • Mi a fájlleíró? Mire használható? Rendelkeznek–e a folyamok fájlleíróval? • Beszéljen az alacsony szintű B/K függvények hibakezeléséről! • Mire való, milyen paraméterei, és visszatérési értéke van az open ru-

tinnak? • Sorolja fel a fájl elérési mód jelzőket, és ismertesse funkcióikat! • Tegye meg ugyanezeket a B/K műveleti mód jelzőkkel kapcsolatban is! • Mi a hatása az O_TRUNC nyitás idejű tevékenység jelzőnek? • Sorolja fel a nyitás idejű fájlnév transzlációs jelzőket, és tájékoztasson

funkcióikról! • Mire való és mikor kell megadni az open függvény harmadik paramé-

terét? • Hogyan lehet zárni a fájlleírót? • Ismertesse a fájlleírót használó B/K rutinokat különös tekintettel a

fájlpozícióra! • Mire való a TEMP_FAILURE_RETRY makró? • Beszéljen a fájlpozícióról! Mihez tartozik a fájlpozíció? Létezhet–e egy

programban egy fájlon több fájlpozíció is? Hogy tudjuk változtatni a fájlmutató értékét?

• Milyen fájltípusok és elérési jogosultságok vannak? • Mire való a stat struktúra és a stat függvény? • Milyen tagjai vannak a stat struktúrának? • Hogyan lehet feltöltetni a stat struktúrát megnyitott fájl, ill. közvetett

hivatkozás adataival? • Hogy lehet lekérdezni, vizsgálni és módosítani a fájl elérési jogosultságait? • Mi a fájl létrehozási maszk? Mi a hatása? Hogy lehet állítani, ill. lekér-

dezni? • Mi a közvetlen hivatkozás? Hogy lehet létrehozni? Honnan lehet meg-

tudni, hogy egy bizonyos fájlra hány darab közvetlen hivatkozás létezik? • Mi a közvetett hivatkozás? Hogyan létesíthető?

Page 149: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 149 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 149 ►

• Mire való a readlink és canonicalize_file_name rutin? • Beszéljen a rename függvény funkcióiról! • Ismertesse a remove és az unlink függvényeket! • Hogyan állapítja meg a rendszer a fájl elérési jogosultságát a fájl tulaj-

donosa és csoportja segítségével? • Módosítható–e a fájl tulajdonosa, ill. csoportja és hogyan? • Hogy lehet megtudni a fájl aktuális méretét? Hogy lehet ezt bővíteni,

ill. redukálni? • Hogyan lehet lekérdezni és változtatni a fájlok időadatait? • Beszéljen a rekordzárolásról, az flock struktúráról és arról, hogyan

lehet lekérdezni, beállítani és törölni a rekordzárakat? • Hogy tudja lekérdezni és változtatni az aktuális, vagy munkakönyvtárat? • Ismertesse az új könyvtárat létrehozó, és a létező könyvtárat megszün-

tető függvényeket! • Milyen adatszerkezettel és függvényekkel történik a könyvtárak meg-

nyitása, olvasása, bezárása, és hogyan lehet pozícionálni egy könyvtár fájlban?

6.9. Megoldandó feladatok:

Írjunk fájlméretet megállapító függvényt!

Készítsünk szoftvert komplett hibakezeléssel, mely az első parancssori paramétere fájlt átmásolja a második paramétereként megadott fájlba! Ha a programot nem elég parancssori paraméterrel indítják, akkor ismertesse használatát! A másolási folyamat előrehaladásáról tájékoztasson feltétlenül!

Fokozzuk kicsit az előbbi feladatot! A fájlba írás normál esetben akkor nem megy, ha betelik a lemez. Ezen próbáljunk meg úgy segíteni, hogy a forrásfájl megnyitása után állapítsuk meg a méretét! A célfájlnak is foglal-junk helyet ugyanekkora méretben, majd írjuk ki rá a forrás tartalmát!

Készítsen programot, mely paraméterül a tervezett fa–struktúrájú könyv-társzerkezet egy komplett ágát kapja, és ezt a könyvtárágat létre is hozza! A könyvtárág gyökér felé eső komponensei létezhetnek, és csak a hátrale-vő, még nem kész ágrészt kell létrehozni.

Page 150: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Fájlok és könyvtárak kezelése

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 150 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 150 ►

Írjon szoftvert, mely létező utat kap paraméterül! A program az út utolsó komponense könyvtárt törölje annak minden alkönyvtárával, fájljával!

Fejlessze tovább a dir.c példát!

• Legyen a tartalomjegyzék névsorba rendezett!

• Lehessen kapcsolókkal szabályozni, hogy melyik fájl attribútum szerint legyen rendezett a tartalomjegyzék!

• Lehessen – globális fájlnevet megadva az út végén – korlátozni a megjelenő tartalomjegyzék tartalmát!

Az idos.c példaprogram felhasználásával készítsen olyan szoftvert,

• mely nem csak a dátumot, hanem az időt is fogadja ellenőrzötten a parancssorból, s a fájl elérési és módosítási idejét ezek szerint változtatja!

• mely külön–külön dátum–idő adat pár alapján változtatja a meg-adott fájl elérési és módosítási idejét!

Írjon szoftvert, mely az indító parancssorban megadott fájlokat egyesíti a megadás sorrendjében, a parancssorban utolsóként előírt fájlba! Ha pa-rancssori paraméter nélkül indítják a programot, akkor ismertesse a képer-nyőn, hogyan kell használni! Ha csak egy fájlnév van a parancssorban, akkor a szabvány bemenet másolandó bele. A fájlok egyesítése során a folyamat előrehaladásáról tájékoztatni kell a képernyőn! A szabvány be-menet másolása esetén végül közlendő még az eredményfájl mérete!

Page 151: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 151 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 151 ►

7. Folyamatok

Pontosítsuk fogalmainkat, és tegyünk különbséget programok valamint folyamatok (process) között! A programra ezentúl úgy tekintünk, mint egy, valamely háttértárolón elhelyezett bináris állományra, amelyet az ope-rációs rendszer futtatni képes. A folyamat azonban a program futtatásához kapcsolódó fogalom; a program ugyanis működése közben erőforrásokat foglal: állományokat nyit meg, memóriát allokál, stb. A folyamat tehát az operációs rendszer szemszögéből nézve nem más, mint egy erőforrások foglalására szolgáló elemi egység. Minden folyamat saját címterülettel ren-delkezik. Ugyanazt a programot több folyamat is futtathatja egyszerre. Mindegyik folyamat saját programmásolattal rendelkezik, s így a program-példányok külön címterületeken, egymástól függetlenül futnak.

A folyamatok hierarchikusan felépített fastruktúrákba szervezettek. Minden folyamat rendelkezik szülő folyamattal (kivéve a fa csúcsán lévő init nevűt), mely létrehozta, elindította az aktuális folyamatot. A szülő által indított folyamatokat gyermek folyamatoknak nevezzük. A gyermek szá-mos tulajdonságot örököl szülőjétől.

A továbbiakban áttekintjük, hogy hogyan lehet gyermek folyamatokat indítani, leállítani vagy működésüket befolyásolni.

A folyamatokat kezelő típusok, szimbolikus állandók és függvények használatához be kell kapcsolni az unistd.h és a sys/types.h fejállomá-nyokat.

7.1. Parancsok végrehajtása Az egyik legmagasabb szintű, folyamatok indítására szolgáló függvény az ANSI/ISO szabványos system, mely könnyen használható, de a részfel-adatok finomhangolására nem ad lehetőséget.

int system(const char *parancs);

Használatához be kell kapcsolni az stdlib.h fejfájlt. A függvény elindítja /bin/sh -c–vel az operációs rendszer parancsér-

telmezőjének (shell) egy újabb példányát. A /bin/sh általában csak hivat-kozás (link), mely az alapértelmezett héjprogramra mutat. A rutin a para-méter parancsot az újonnan indított héjprogrammal hajtatja végre. Ha a parancs nem teljes elérési úttal specifikált, a shell a PATH környezeti vál-tozóban megadott mappákban is keresi.

Page 152: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 152 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 152 ►

Csak a parancs végrehajtását követően folytatódik a system-et hívó folyamat futása. Végrehajtás alatt a hívó folyamat blokkolja a SIGCHLD jelzések fogadását, a SIGINT és SIGQUIT jelzéseket pedig figyelmen kívül hagyja.

A függvény −1 visszatérési értékkel jelzi, ha nem sikerült elindítani a héjprogramot, egyébként a héj állapotára utaló értéket ad. Ennek értelme-zése megegyezik a wait függvénynél ismertetettel.

Ha parancsként NULL mutatót adunk át, a függvény a zérus visszaté-rési értékkel jelezi, ha nincs elérhető parancsértelmező.

Óvatosan kell eljárni a system hívással többszálú programok ese-tén, mert a függvényhívás érvénytelenedési pontot (cancellation point) képez!

A system.c példa konzolra listázza a /usr mappa tartalmát:

/* system.c */ #include <stdio.h> #include <stdlib.h> #define MAPPA "/usr" #define MAX 128 int main(void) { char parancs[MAX]; int allapot; sprintf(parancs,"ls %s -l",MAPPA); printf("A %s mappa tartalma:\n\n",MAPPA); switch(allapot = system(parancs)){ case -1: fprintf(stderr,"Héjprogram indítása " "sikertelen.\n"); exit(EXIT_FAILURE); case 0: printf("A parancsot sikeresen végrehajtottuk.\n"); exit(EXIT_SUCCESS); default: fprintf(stderr,"A system() által visszaadott " "érték: %d\n",allapot); exit(EXIT_FAILURE); } }

A program lehetséges kimenete: A /usr mappa tartalma: összesen 98 drwxr-xr-x 2 root root 41576 2005-10-27 15:58 bin drwxr-xr-x 2 root root 72 2005-07-11 16:23 games

Page 153: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 153 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 153 ►

drwxr-xr-x 5 root root 120 2005-07-11 16:08 i586-suse-linux . . . drwxr-xr-x 8 root root 192 2005-07-11 16:14 X11R6 A parancsot sikeresen végrehajtottuk.

7.2. Folyamatok létrehozásának módjai A UNIX és GNU/Linux operációs rendszereken a folyamatokra a folya-matazonosítók (Process Identifier, PID) segítségével hivatkozhatunk. Minden folyamat saját azonosítóval rendelkezik. A folyamat akkor áll le, amikor ezt a szándékát jelezte szülő folyamatának. Ezzel egyidejűleg az összes általa eddig használt erőforrás is felszabadul, beleértve ebbe PID-jét is.

Folyamatokat fork rendszerhívással lehet indítani. A fork által létreho-zott gyermek folyamat az eredeti folyamat másolata azzal az egyetlen lé-nyegi különbséggel, hogy új, saját PID-et kap.

A fork hívása után mind a szülő, mind a gyermek folyamat a fork-ot tartalmazó utasítástól folytatja, vagy kezdi meg futását. Ettől kezdve egy-mástól teljesen függetlenül futnak tovább mindketten. A fork visszatérési értékének segítségével mindig eldönthető, hogy melyik az eredeti, szülő folyamat, és melyik a gyermek.

Ha a szülő meg szeretné várni a gyermek befejeződését, mielőtt maga tovább futna, a fork-ot követően meg kell hívnia a wait vagy waitpid függvények valamelyikét. A wait és a waitpid viszonylag korlátozottan szolgáltat információt a gyermek folyamat befejeződési körülményeiről.

Amennyiben nem ugyanazt a programot szeretnénk több példányban futtatni, használjuk az exec függvényt! Az exec hívást követően a folya-mat által eddig végrehajtott programmal kapcsolatos minden információ elveszik. Az exec-kel indított program leállása után tehát nincs arra lehe-tőség, hogy folytatódjék az exec hívást tartalmazó program, mert ilyenkor az egész folyamat befejeződik.

Mielőtt elmerülnénk az új folyamatok létrehozási módjainak részletei-ben, tekintsük át a folyamatok azonosításának lehetőségeit!

7.2.1. A pid_t típus A pid_t típus folyamatazonosítók tárolására képes egész típus. A GNU/Linux rendszereken ez az int típussal egyezik meg.

Page 154: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 154 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 154 ►

7.3. Folyamatok azonosítása Folyamatok azonosítóit szolgáltatják az alábbi függvények:

pid_t getpid(void);

A függvény az aktuális folyamat azonosítójával tér vissza. pid_t getppid(void);

Visszaadja a hívó folyamat szülőjének azonosítóját. Mindkét függvény része a POSIX szabványnak. A getpid-et néhány

program ideiglenes fájlok egyedi nevének képzéséhez is használja.

7.4. Folyamatok létrehozása pid_t fork(void);

A függvény új folyamatot hoz létre, pontosabban lemásolja a fork-ot hí-vót. Ezután mindkét folyamat egymástól függetlenül futhat tovább. Ha a művelet sikerrel jár, a szülő visszakapja a gyermek azonosítóját, a gyermek pedig zérust. Ha nem sikerült a folyamat elindítása, a fork ezt -1 visszaté-rési értékkel jelzi a szülőnek. A probléma okának pontosabb felderítésére az errno változó értékének vizsgálata ad lehetőséget, mely mindössze két-féle értéket vehet fel:

• EAGAIN: Nem áll rendelkezésre elegendő erőforrás az új folyamat elindításához, vagy a felhasználó elérte a rá vonatkozó erőforrás korlá-tot. A hívónak érdemes lehet később ismét próbálkoznia.

• ENOMEM: A folyamatnak több memóriára lenne szüksége, mint amennyit a rendszer számára biztosítani tud.

A gyermek folyamat néhány tulajdonsága eltér a szülőétől.

• Saját, egyedi folyamatazonosítóval rendelkezik. • Szülő folyamatának azonosítója az őt indító folyamat azonosítója. • Saját másolattal rendelkezik a szülő által megnyitott állományok fájlle-

íróiról. Következésképpen, ha a szülő megváltoztatja a fájlleírók vala-melyik tulajdonságát, annak semmiféle következménye nem lesz a gye-rek leíróira nézve, és fordítva. Ez alól egyedüli kivétel az aktuális fájl-pozíció, mely közös a szülő és gyermek folyamatban.

• A gyermek által igénybe vett processzoridő számlálása nulláról indul, nem a szülő által eddig igénybe vett mennyiség növekszik tovább.

• A gyermek nem örökli meg a szülő állomány zárolásait.

Page 155: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 155 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 155 ►

• A gyermek nem örökli a szülő riasztásait. • A felfüggesztett jelzések listája üres lesz a gyermekfolyamatnál. (Ettől

függetlenül megörökli a blokkolt jelzések maszkját, valamint a szülő jelzéskezelőit.)

GNU/Linux alatt a fork az ún. copy-on-write lapokkal került megva-lósításra, ami azt jelenti, hogy a fork nagyon gyorsan lefuthat, mert csak a szülő laptáblájának lemásolása, valamint a gyermek egyedi feladatstruktú-rájának kialakítása igényel némi processzoridőt és memóriát. A fork a POSIX szabvány része.

Létezik a fork függvény egy módosított változata is, mely néhány meg-szorítás ellenében hatékonyabb működést ígér.

pid_t vfork(void);

A fork lemásolja a hívó teljes címterületét, majd a szülő és a gyermek egymástól függetlenül futhat tovább. A vfork azonban nem hajtja végre ezt a másolást. A vfork által indított gyermekfolyamat a szülő címterületét használja, amíg meg nem hívja az _exit függvényt, vagy az exec függvé-nyek valamelyikét. A gyermek végrehajtásának idejére a szülő futása szünetel.

A gyermek nem módosíthatja a globális és lokális változókat. Ezen kí-vül a gyermek nem térhet vissza a vfork-ot hívó függvényből, valamint a longjmp-pal sem hagyhatja el azt.

Néhány operációs rendszer nem valósítja meg a vfork-ot. A GNU C könyvtár minden platformon lehetővé teszi a függvény meghívását, és ha a vfork az adott rendszeren nem használható, a fork-ot használja majd helyette.

Érdemes itt megemlíteni a fork függvény egy speciális változatát, amelynek démonok készítésekor láthatjuk hasznát. (A démon olyan fo-lyamat, ami a háttérben futva valamilyen szolgáltatás megvalósítását végzi; pl.: HTTP vagy FTP kiszolgáló.)

int daemon(int nincsKonyvtarValtas, int nincsLezaras);

A daemon függvény meghívásakor a folyamat lemásolja önmagát fork hívás segítségével, majd a szülő leáll. A gyermek a termináltól függetlenít-ve, a háttérben fut tovább.

Ha nincsKonyvtarValtas értéke nulla, a program saját munkakönyvtárát a gyökérkönyvtárra állítja. Ennek az a jelentősége, hogy a hosszú ideig (adott esetben hónapokig) futó szolgáltatás működése közben az eredeti munka-könyvtár megszűnhet.

Ha nincsLezaras értéke nulla, akkor a folyamat a szabványos kimenetét, bemenetét és hibafolyamát átirányítja a

Page 156: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 156 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 156 ►

/dev/null

eszközre, ami minden információt elnyel. A szolgáltatást indító felhaszná-ló általában nincs bejelentkezve a démon futása alatt, így nem lenne értel-me számára üzeneteket küldeni. (A szolgáltatások inkább konfigurációs és naplóállományok segítségével tartják a kapcsolatot a felhasználóval.)

Sikeres hívás esetén a függvény nullát ad, egyébként −1-et. Ha hiba lé-pett fel, az errno változó ugyanazoknak az értékeknek valamelyikét veszi fel, amelyeket a fork-nál már megismerhettünk.

A fork.c a fork és a getpid függvények működését illusztrálja: relációje-lekből rajzol mintát a szabványos kimeneten.

/* fork.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <errno.h> #define CIKLUS 3 void szulof(pid_t gyermek) { int i; printf("A szülő PID-je: %d\n",getpid()); printf("A gyermek PID-je: %d\n\n",gyermek); for(i=0;i<CIKLUS;i++) { putchar('<'); fflush(stdout); sleep(2); } } void gyermekf(void) { int i; sleep(1); for(i=0;i<CIKLUS;i++) { putchar('>'); fflush(stdout); sleep(2); } } void hiba(void) { fprintf(stderr,"Hiba lépett fel a fork() függvény " "hívása során.\nA hiba oka: "); if(errno == EAGAIN) { fprintf(stderr,"jelenleg nincs elegendő erőforrás " "\na folyamat elindításához, de később " "próbálkozzon újra.\n"); } else if(errno == ENOMEM) { fprintf(stderr,"nincs elegendő memória.\n"); } } int main(void) {

Page 157: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 157 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 157 ►

pid_t gyermek; printf("Folyamatok indítása a fork() hívásával\n" "A szülő < jelet, a gyermek > jelet ír a " "kimenetre.\n\n"); switch(gyermek = fork()) { case -1: hiba(); exit(EXIT_FAILURE); case 0: gyermekf(); exit(EXIT_SUCCESS); default: szulof(gyermek); putchar('\n'); exit(EXIT_SUCCESS); } }

Az eredmény kimenet a következő lehet: Folyamatok indítása a fork() hívásával A szülő < jelet, a gyermek > jelet ír a kimenetre. A szülő PID-je: 12269 A gyermek PID-je: 12270 <><><>

7.5. Fájlok futtatása Az alább ismertetendő exec függvénycsalád a többfázisú programok megvalósítására szolgál. Ez utóbbi fogalom azt jelenti, hogy a szülő által indított új folyamat felülírja a szülőt memóriában. Ebből következően a gyermek nem is adhatja vissza a vezérlést a szülőnek, de saját maga helyére betölthet újabb programot (amit az angol irodalom process image-nek nevez). A függvénycsaládot sokszor a fork-kal együtt használják; miután új folyamatot indítottak, a gyermek lecseréli az általa futtatott programot.

7.5.1. A globális environ változó A [2] jegyzet „Parancssori paraméterek” fejezetében már tárgyaltuk

az environ változót. Az exec függvénycsaládnak azon tagjai, melyeknél nem lehet explicit

módon megadni a gyermek környezetét, a gyermekfolyamat environ vál-tozóját a szülő változójának lemásolásával állítják elő.

Page 158: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 158 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 158 ►

7.5.2. Az exec függvénycsalád A különféle függvényvariánsok csak paraméterezésükben, és néhány

megvalósítási részletben különböznek egymástól. int execv(const char *fajlnev, char *const argv[]);

Az execv-t hívó folyamat lecseréli az általa futtatott programot a fajlnev paraméterében adottra. Az argv már ismert a main paramétereként. Az utolsó argv tömbelemnek a NULL értéket kell tartalmaznia. Az újonnan betöltött és elindított program main függvénye ezt a mutatótömböt fogja megkapni második paramétereként. A konvenciók szerint a tömb első eleme az indítandó állomány nevét tartalmazza az elérési út nélkül. A be-töltött új program a környezetét a szülőtől örökli.

int execl(const char *fajlnev char *arg0, …);

A paramétereket nem tömbként, hanem egyenként kell átadni. Az utolsó paraméternek itt is a NULL mutatónak kell lennie.

A függvénycsaládnak ezt a tagját akkor érdemes használni, ha a pa-rancssori paraméter mutatók száma konstans, vagy ismert fordítási időben.

int execve(const char *fajlnev, char *const argv[],

char *const env[]); Annyiban tér el az execv-től, hogy lehetőséget ad a gyermek környezeté-nek explicit beállítására az env paraméter segítségével. Ez ugyanúgy karak-terláncokat címző mutatók tömbje kell legyen, mint az environ változó esetében.

int execle(const char *fajlnev,const char *arg0, …,char *const env[]);

Paraméterezése abban különbözik az execl függvényétől, hogy utolsóként az environ változóéval megegyező formában várja az újonnan indítandó folyamat környezetét. Az utolsó parancssori paramétert követő NULL mutató természetesen itt sem maradhat el.

int execvp(const char *fajlnev, char *const argv[]);

Ez a rutin az execv–hez hasonlóan működik, azzal a különbséggel, hogy-ha a fajlnev paraméter nem tartalmaz per jelet, a rendszer a futtatható fájlt a PATH környezeti változóban jelzett elérési utakon kezdi keresni. Ameny-nyiben a PATH környezeti változó nincs beállítva, az alapértelmezett keresési helyek a /bin és a /usr/bin mappák.

A különféle rendszerek esetleg kereshetnek az aktuális könyvtárban is. A GNU/Linux rendszerek ezt a helyet még a fent említett két könyvtár előtt ellenőrzik (trójai ló típusú támadások elleni védekezés).

Page 159: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 159 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 159 ►

Ez a függvény különösen az operációs rendszer segédprogramjai-nak elindításánál hasznos, mert azokon a helyeken végzi el a keresést, amit a felhasználó beállított. A héjprogramok is ezt a függvényt hívják, amikor a felhasználó el szeretne indítani valamit.

int execlp(const char *fajlnev, const char *arg0, …);

Paraméterezése megegyezik az execl függvénynél olvashatóakkal, azonban megvalósítja ugyanazt a keresési mechanizmust, amit az execvp is használ.

Általában, a POSIX kompatibilis rendszereken a parancssori paramé-terek listájának és a környezeti változók listájának együttes mérete nem haladhatja meg az ARG_MAX bájtot. A GNU rendszereken ebbe a mé-retbe beleszámítanak a karakterláncok jelei, a lezáró nullák, illetve a karak-tereket címző mutatók is. Mindezt ráadásul felfelé kerekítik char* méreté-nek egész számú többszörösére. Más rendszerek ettől eltérő módon is számolhatnak.

A függvények általában nem adnak vissza semmit, hiszen a hívó futása a hívás helyén megszakad. A −1 visszaadott érték hibát jelez, amelynek természetéről az errno változó értéke tájékoztat. Leggyakrabban az alábbi értékek és hibajelenségek valamelyike fordul elő:

• E2BIG: Az új program paraméter- és környezeti változó listájának összesített mérete túl nagy (meghaladja az ARG_MAX állandóban rögzített értéket). A GNU rendszerekben nincs felső korlát a parancs-sori paraméterek listájának méretére nézve (ARG_MAX nem defini-ált), így ha a paraméterek elhelyezésére nincs elég memória, az ENOMEM hibakódot kapjuk.

• ENOEXEC: A fajlnev paraméterben adott állomány formátuma nem megfelelő (nem futtatható program).

• ENOMEM: A megadott program végrehajtásához nem áll rendelke-zésre elegendő memória.

• EACCESS: Csak a p végződésű változatoknál jelentkezhet. Amennyi-ben a PATH-ban jelzett könyvtárak valamelyikéhez való hozzáférés tiltott, a függvények folytatják a kutatást a további mappákban. Ha egyik könyvtárban sem lelik a keresett állományt, beállítják az errno változót az EACCESS értékre.

• ENOENT: A fajlnev paraméterben adott állomány nem található.

Ha az új fájl futtatása lehetséges, akkor módosul az utolsó hozzáférés időpontja.

Page 160: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 160 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 160 ►

Az új állomány betöltésével a rendszer lecseréli a végrehajtandó kódot, a parancssori paramétereket és környezeti változókat tartalmazó karakter-láncokat új helyre teszi a memóriában. A folyamat több tulajdonsága azonban változatlan marad:

• a folyamat és szülő folyamatának azonosítója (PID), • a munkamenet (session) és folyamat csoport tagságok, • a valódi felhasználói azonosító, a csoportazonosító és a másodlagos

csoportazonosítók, • a felfüggesztett riasztások, • az aktuális vagy munkakönyvtár és a gyökérkönyvtár. A GNU rendsze-

rekben a gyökérkönyvtárat nem másolják, ha SUID bites program fut-tatásáról van szó; helyette a rendszer gyökérkönyvtárát használja a program.

• Nem változik továbbá a fájl létrehozási maszk, • a folyamat által kezelt jelzések maszkja, • a felfüggesztett jelzések listája, • a folyamat által felhasznált processzoridő.

Ha a futtatandó program SUID és SGID bitjei beállítottak, ez megha-tározza a folyamat tényleges felhasználói és csoport ID tulajdonságait.

Azok a jelzések, amelyeket a szülő figyelmen kívül hagyott, a gyermek is figyelmen kívül fogja hagyni. A többi jelzésre az alapértelmezések érvé-nyesek.

A szülő által megnyitott fájlleírók a gyermeknél is nyitva maradnak, mert alapértelmezés szerint nincs beállítva az FD_CLOEXEC (close-on-exit) jelzőbitjük. A nyitva maradt fájlok minden tulajdonsága öröklődik, ideértve a zárolásokat is.

Az FD_CLOEXEC, ún. fájlleíró jelzőbit, beállítására az fcntl függ-vény használható. Pl.: int jelzok = fcntl(fajlleiro, F_GETFD, 0); jelzok |= FD_CLOEXEC; fcntl(fajlleiro, F_SETFD, jelzok);

Ezzel ellentétben a megnyitott folyamok „nem élik túl” az exec hívást, mert adataikat a szülő folyamat memóriaterületén tárolják (leszámítva azo-kat, amelyeket a gyermek is épp úgy létrehoz indulásakor, mint szülője tette). Mindazok a folyamok, melyekhez a szülőnek volt belső leírója, az exec hívás után is használhatóak maradnak (feltéve, hogy az

Page 161: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 161 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 161 ►

FD_CLOEXEC nem volt beállítva). A gyermek használhatja ezeket a folyamokat az fdopen függvény segítségével.

Az exec1.c példa az execv és execl függvények használatát mutatja be; egy egyszerű menürendszer segítségével paraméterezetten elindítja az exec2 nevű programot, ami kiírja első paraméterét a kimenetre.

/* exec1.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <errno.h> int main(void) { int c, hiba; char* parameter[] = {"./exec2", "szamár", NULL}; do { printf("Szülő elindult. PID: %d\nVálasszon a " "menüből!\n\n" "1) Program indítása execv függvénnyel\n" "2) Program indítása execl függvénnyel\n" "k) Kilépés\n", getpid()); while ((c=getchar()) != '1' && c != '2' && c!= 'k'); switch (c) { case '1': hiba = execv(*parameter, parameter); break; case '2': hiba = execl("./exec2", "exec2", "ló", NULL); break; default: hiba = 0; break; } if (hiba == -1) { switch (errno) { case E2BIG: fprintf(stderr, "A paraméter és környezeti " "változók összesített mérete túl nagy.\n"); break; case ENOEXEC: fprintf(stderr, "Az indítandó állomány " "formátuma nem megfelelő.\n"); break; case ENOMEM: fprintf(stderr, "A megadott program "

Page 162: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 162 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 162 ►

"futtatásához nincs elegendő memória.\n"); break; case ENOENT: fprintf(stderr, "Nem találom a megadott " "állományt.\n"); break; default: fprintf(stderr, "Ismeretlen hiba lépett fel a " "futtatás során.\nHibakód: %d\n", errno); break; } } } while(c != 'k'); exit(EXIT_SUCCESS); }

Az exec2.c fájl tartalma alább olvasható. /* exec2.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> int main(int argc, char** argv) { if (argc>1) { printf("Üdvözlöm! Én az exec2 program vagyok %s " "paraméterrel!\nPID: %d\n", *(argv+1), getpid()); exit(EXIT_SUCCESS); } else { fprintf(stderr, "Hibás paraméterezés!\n"); exit(EXIT_FAILURE); } }

Egy lehetséges kimenet: Szülő elindult. PID: 6903 Válasszon a menüből! 1) Program indítása execv függvénnyel 2) Program indítása execl függvénnyel k) Kilépés 1 Üdvözlöm! Én az exec2 program vagyok szamár paraméterrel! PID: 6903

7.6. Folyamatok befejeződése Az ismertetett függvények arra szolgálnak, hogy megvárják a gyermek folyamat állapotának megváltozását, majd visszaadják azt. Ilyen állapotvál-tozást idézhet elő, ha a gyermek befejeződik (terminate), ha a gyermeket megállítja (stop) vagy újraindítja egy jelzés. Használatukhoz be kell kap-csolni a sys/types.h és sys/wait.h fejfájlokat.

pid_t waitpid(pid_t pid, int *allapot, int opciok);

Page 163: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 163 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 163 ►

A waitpid függvényt a pid folyamatazonosítójú gyermek állapotának le-kérdezésére használják. Alapértelmezés szerint a hívó folyamat futása szü-netel addig, amíg a gyermek elő nem állítja kilépési kódját, azaz be nem fejezi futását.

A pid paraméter speciális értékeket is felvehet. A WAIT_ANY (-1) használatával bármely gyermekfolyamatról kaphatunk információt. A WAIT_MYGRP (0) segítségével bármely, a hívóval azonos folyamatcso-portban lévő gyermek állapota felől tájékozódhatunk. Bármely, −1-nél kisebb −pgid egész megadásával lehetőség nyílik bármely olyan gyermek-folyamatról állapotinformációt kérni, amelynek folyamatcsoport-azonosítója pgid.

Ha a gyermekfolyamat állapotinformációja azonnal elérhető, akkor a függvény rögtön vissza is tér. Ha egynél több gyermekfolyamat állapota is hozzáférhető egyszerre, akkor ezek közül egy véletlenszerűen választott-nak a kilépési kódját kapjuk meg. Ha a többiére is kíváncsiak vagyunk, hívjuk meg újra a waitpid-et.

Az opciok paraméter bit maszk. Értékét könnyedén kialakíthatjuk a zé-rus, a bitenkénti vagy operátor, és két szimbolikus állandó (WNOHANG, WUNTRACED) felhasználásával. A WNOHANG annak jelzésére szol-gál, hogy a wait ne blokkolja a programunk futását; ha van befejezett fo-lyamat, az allapot-tal adott helyen elhelyezi annak állapotát és visszaadja folyamatazonosítóját. Ha nincs, nullával tér vissza. Ha a WUNTRACED-et is beállítjuk, nem csak a befejezett, hanem a megállított programokról is kapunk információt. E két beállítás csak akkor hatásos, ha a SIGCHLD jelzéshez nem állították be az SA_NOCLDSTOP kapcsolót (lásd a sigaction függvényt!). A GNU/Linux 2.6.10-es kernelverziójától kezdő-dően még egy újabb konstanst lehet használni; ez a WCONTINUED. Ennek beállítása esetén a függvény akkor is visszatér, ha egy leállított gyermek a neki küldött SIGCONT jelzés hatására újraindul.

A függvény a gyermek állapotinformációját az allapot paraméterrel adott helyre írja, hacsak nem NULL a paraméter.

A függvény hívása a többszálú programok érvénytelenedési pontja (cancellation point).

Alapértelmezés szerint a függvény annak a gyermekfolyamatnak a fo-lyamatazonosítójával tér vissza, amelynek az állapotinformációját szolgál-tatta. Ha léteznek gyermekfolyamatok, de még mindegyik fut, és a waitpid-et a WNOHANG kapcsolóval hívtuk meg, zérust ad vissza.

Page 164: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 164 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 164 ►

Ha a waitpid-et konkrét gyermekfolyamat PID-ével hívják, akkor ter-mészetesen a szülő többi gyermekének állapotától függetlenül, a meghatá-rozott gyermek befejeződéséig blokkolni fogja a szülőt, vagy nullával tér vissza a fent írottaknak megfelelően.

A −1 visszatérési érték hibát jelez, melynek természetéről az errno vál-tozó értéke tájékoztat. Lehetséges értékei a következők:

• EINTR: A függvényt hívó folyamatnak címzett jelzés továbbítása megszakította a függvény lefutását.

• ECHILD: A hívónak nincsenek gyermekfolyamatai, vagy a pid-del adott folyamat nem a hívó gyermekfolyamata.

• EINVAL: Az opciok paraméter nem megengedett értéket tartalmaz.

A wait hívást nem tudja megszakítani a szülőnek küldött jelzés, ha er-ről előzetesen gondoskodunk a sigaction függvény megfelelően paramé-terezett (SA_RESTART) hívásával.

pid_t wait(int *allapot);

A rutin a waitpid egyszerűsített változata, melyet gyermekfolyamat befe-jeződésének megvárására használnak. A wait(&allapot) hívás teljesen egyenértékű a waitpid(WAIT_ANY, &allapot, 0)–val.

pid_t wait3(int *allapot, int opciok, struct rusage *efhasznalat);

pid_t wait4(pid_t pid,int *allapot,int opciok,struct rusage *efhasznalat);

Ezek a függvények eredetileg a BSD Unix bővítményei voltak. A wait és waitpid függvényeknél megismert lehetőségeket annyival egészítik ki, hogy a befejeződött folyamatok erőforrás használatáról is szolgáltatnak információt, ha az efhasznalat értéke nem NULL. A wait3 tetszőleges, a wait4 pedig egy konkrét gyermekre várakozik.

A legújabb Linux változatoknál lehetőségünk van az eddigieknél precí-zebb módon is meghatározni, hogy a gyermekfolyamatok milyen állapot-változásaira várakozzunk.

A témakörben részletes információkat találunk a Linux súgójában (info waitpid, info wait4 stb.).

Fontos tudni, hogy az operációs rendszer nem törli folyamattáblá-jából a már befejezett folyamatokat, amíg azok állapotinformációit a szülő a wait (vagy valamelyik változatának) hívásával át nem vette. Az ilyen fo-lyamatokat nevezik „zombi” folyamatoknak, utalva élőhalott állapotukra. Ezek feleslegesen foglalják a rendszer erőforrásait.

Page 165: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 165 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 165 ►

Ha olyan szülő folyamat fejezi be futását, amelynek voltak „zombi” gyermekei, akkor azokat az init folyamat „adoptálja”, majd egy-egy wait hívással megsemmisíti őket. A GNU/Linux a clone rendszerhívással is képes újabb folyamatok készítésére. Az ilyen módon létrejött folyamatok precíz kezelésére újabb jelzőket használhatunk az opciok paraméterben, de ennek részleteit ebben a jegyzetben nem tárgyaljuk.

Az wait.c mintaprogram bemutatja, hogyan készül el a gyermekfolyamat, melynek befejezését megvárja a szülőfolyamat. Ezt követően a szülő meg-felelő üzenetet helyez el a szabványos kimeneten.

/* wait.c */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main(void) { printf("Gyermekáldás van kilátásban...\n"); if (fork()) { waitpid(WAIT_ANY,NULL,0); printf("Borzalmas tragédia eltemetni saját " "gyermekünk...\n"); } else { printf("\tMegszülettem! Én vagyok a gyermek!\n" "\tBoldog gyermekévek...\n" "\tDe jaj, eljött a szörnyű vég.\n"); } exit(EXIT_SUCCESS); }

A program futtatása az alábbi kimenetet eredményezi. Gyermekáldás van kilátásban... Megszülettem! Én vagyok a gyermek! Boldog gyermekévek... De jaj, eljött a szörnyű vég. Borzalmas tragédia eltemetni saját gyermekünk...

7.7. Állapotinformációk értelmezése A waitpid által visszaadott állapotinformáció értelmezését különféle mak-rók könnyítik meg, melyeket a sys/wait.h fejfájl bekapcsolása után hasz-nálhatunk.

int WIFEXITED(int allapot)

Page 166: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 166 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 166 ►

A makró visszatérési értéke nem zérus, ha a gyermekfolyamat futása sza-bályosan, az exit vagy az _exit függvény hívásával, fejeződött be.

int WEXITSTATUS(int allapot)

Ha a WIFEXITED igaz (nem nulla), akkor e makró visszatérési értéké-nek alsó nyolc bitje a gyermekfolyamat kilépési kódját (az exit függvény-nek átadott paraméter értékét) tartalmazza.

int WIFSIGNALED(int allapot)

Igaz, ha a gyermek futása valamilyen le nem kezelt jel következtében állt le. int WTERMSIG(int allapot)

Ha a WIFSIGNALED igaz, e makró a gyermekfolyamatot leállító jelzés kódját adja.

int WCOREDUMP(int allapot)

Nem zérus, ha a gyermekfolyamat leállása memóriaképet (core dump) produkált.

int WIFSTOPPED(int allapot)

Akkor szolgáltat igaz értéket, ha a gyermekfolyamatot megállították. int WSTOPSIG(int allapot)

Ha WIFSTOPPED igaz, e makró a gyermekfolyamatot leállító jelzés számát szolgáltatja.

int WIFCONTINUED(int allapot)

Csak GNU/Linux 2.6.10-től használható. Igaz, ha a gyermek újraindult a neki továbbított SIGCONT jelzés hatására.

A GNU/Linux rendszerek is lehetőséget adnak az állapotinformáci-óknak a BSD UNIX-ban ismert módon történő kezelésére. A BSD a sys/wait.h fejállományban található wait uniót használja az állapotinfor-mációk tárolására, de ebben is ugyanolyan módon értelmezendő bitmintá-kat találunk, mint a waitpid által szolgáltatott int értékben. Az unió tagja-inak (w_termsig, w_coredump, w_retcode, w_stopsig) közvetlen eléré-se helyett ajánlatos a fenti makrókat használni.

7.8. Ellenőrző kérdések

• Mi a különbség programok és folyamatok között? • Milyen fejfájlokat kell bekapcsolni a folyamatkezelő függvények, típu-

sok és szimbolikus állandók használatához?

Page 167: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatok

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 167 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 167 ►

• Melyik a legmagasabb szintű, egyúttal ANSI/ISO szabványos módja folyamatok indításának?

• Hogyan azonosítjuk a folyamatokat? (adattípus, függvények) • Mire szolgál a fork függvény? • Mit nevezünk többfázisú programnak? Hogyan hozhatunk ilyet létre? • Miben különböznek egymástól az exec függvénycsalád tagjai? Sorolja

fel, melyik névvégződés mire utal! • Milyen lehetőségei vannak a szülőfolyamatnak, ha meg kívánja várni a

gyermek befejeződését? Mi a létjogosultsága a különféle függvényeknek? • Milyen következménnyel jár, ha a szülő nem olvassa ki befejezett

gyermekeinek állapotinformációit? • A fejezet mely függvényei alkotnak érvénytelenedési pontokat?

7.9. Megoldandó feladatok

Készítsen olyan programot, amely lehetőséget ad a felhasználónak arra, hogy az operációs rendszer segédprogramjait magyar nyelven indíthassa! Pl. pwd helyett aktkonyvtar, cd helyett valtas, ps helyett folyamatok, stb. Ne feledkezzen meg arról, hogy a parancsokat számos parancssori para-méter követheti!

Egy vállalat két, egyszerű szöveges állományban tárolja havi bevételeit és kiadásait. Készítsen olyan programot, amely egy-egy folyamatot indít a bevételek és a kiadások összegzésére, majd a két részeredmény ismereté-ben megállapítja, hogy az adott hónapban mekkora volt a nyere-ség/veszteség!

Page 168: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 168 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 168 ►

8. Folyamatközi kommunikáció

Az előző fejezetben egyebek mellett áttekintettük, hogyan hozhatunk létre folyamatokat, indíthatunk el programokat vagy várakozhatunk azok befe-jeződésére. Egy lényeges kérdésről azonban mindeddig nem esett szó: hogyan kommunikálhatnak egymással a folyamatok?

Kezdetleges megoldásként eddig csak a fájlokon keresztül történő in-formációátadás, a futását befejező gyermekfolyamat kilépési kódja, pa-rancssori paraméterek, esetleg a környezeti változók álltak rendelkezé-sünkre. Bármelyiket próbáljuk is választani, világosan látszanak a megoldás korlátai. Szerencsére a GNU/Linux rendszerek sokkal kifinomultabb esz-közöket is rendelkezésünkre bocsátanak.

A folyamatközi kommunikáció (InterProcess Communication, IPC) lebonyolítására kidolgozott módszerek a következők:

• osztott memória (shared memory), • leképezett memória (mapped memory), • csövek (pipe), • nevesített csövek (named pipe, FIFO, first-in first-out special file) és • foglalatok (socket).

E jegyzet keretein belül csak az utóbbi hárommal foglalkozunk részletesen.

8.1. Osztott és leképezett memória A folyamatok közötti kommunikáció leggyorsabb módja az osztott me-mória használata. A koncepció lényege, hogy a kommunikálni vágyó fo-lyamatok egyike lefoglal egy memóriaterületet, majd a többi folyamat elké-ri ennek a tárterületnek a címét. Ezután bármelyik folyamat írhatja és ol-vashatja a megosztott tárat (de rendelkezhetünk arról is, hogy a terület egyes folyamatok számára csak olvasható legyen). Az információcsere végeztével a tárterület felszabadítható.

A leképezett memória használata nagyban hasonlít az osztott memóri-ánál leírtakra, azaz ebben az esetben is egyszerű memória írások és olvasá-sok teszik lehetővé az adatcserét. A tárterületet azonban hozzárendelik egy fájlhoz. Ezzel a módszerrel lehetővé válik, hogy egy fájl tartalmát memó-riaműveletek segítségével módosítsuk.

Mind az osztott, mind a leképezett memória használata esetén a prog-ramozónak kell gondoskodnia a folyamatok közti szinkronizáció megvaló-

Page 169: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 169 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 169 ►

sításáról. Azonos tárterületek egyidejű írása és olvasása ugyanis könnyen eredményezhet rendellenes működést. A probléma egyik lehetséges meg-oldása: szemaforok (process semaphores, System V semaphores) alkalma-zása, melyek használatát a legtöbb UNIX és GNU/Linux rendszer támogatja.

Az osztott memória kezelésével kapcsolatos problémák és megoldása-ik részletes bemutatása túlmutat ennek a jegyzetnek a keretein. Az érdek-lődőknek a [7] irodalom tanulmányozását ajánljuk. Érdemes továbbá a súgóban (man, info) a következő kulcsszavakra keresni:

• Osztott memória témakör: shmget, shmat, shmctl. • Leképezett memória témakör: mmap, munmap. • Szemaforok témaköre: semget, semctl, semop.

8.2. Csövek A csövek az egymással leszármazotti viszonyban álló (szülő - gyermek) folyamatok közötti kétirányú kapcsolattartásra szolgálnak. A csatorna egy olvasásra és egy írásra szolgáló, tehát két, ellentétes irányú adattovábbítást lehetővé tevő részből áll. A kommunikáció pufferezett, soros, fifo jellegű. Amit a küldő hamarabb továbbított, azt az információt kapja meg az olva-só fél először.

Ezt a kommunikációs formát használja a héjprogram is, ha a felhasz-náló a következőhöz hasonló utasítást ad ki a konzolon:

ps aux | less

Ilyenkor két gyermekfolyamat indul, egyik a ps, másik a less program futtatására. A szülő folyamat összeköti az első folyamat szabvány kime-netét a második szabvány bemenetével.

A csövek kapacitása korlátozott, de szerencsére az operációs rendszer automatikusan szinkronizálja a folyamatok tevékenységét. Ez azt jelenti, hogyha például a küldő fél gyorsabban továbbítja az információt, mint ahogyan a fogadó fél fel képes azokat dolgozni, és a cső megtelik, akkor átmenetileg képes blokkolni a küldő folyamat működését.

Csövek használata esetén be kell kapcsolni az unistd.h fejfájlt. Új cső létrehozásakor az alábbi lépéseket kell követni:

• Meg kell hívni a pipe függvényt, melynek eredményeként két fájlleírót kapunk: egyet az olvasáshoz, és egy másikat íráshoz.

• Új folyamat hozandó létre a fork függvénnyel. A gyermek örökli a szülőtől a fájlleírókat, így az imént létrehozottakat is.

Page 170: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 170 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 170 ►

• Ezt követően a folyamatok már kommunikálhatnak egymással. A konkrét megvalósításhoz használhatóak az alacsonyszintű fájlkezelés-nél megismert rutinok, de a leírók alapján folyamok is létrehozhatóak.

A csövek készítéséhez használt függvény prototípusa a következő: int pipe(int fajlleirok[2]);

A POSIX szabványos rutin a fajlleirok tömb első elemében helyezi el azt a fájlleírót, amellyel olvasni tud a folyamat a csőből, a másodikba pedig azt, amelyikbe írhat. A korábbi fejezetekben ismertetett O_NONBLOCK és FD_CLOEXEC bitek nem lehetnek beállítva (ez az alapértelmezés). Azt az adatot, amit az egyik folyamat a fajlleirok[1] fájlleíró segítségével továb-bít, a fogadó fél a fejlleirok[0] felhasználásával olvashatja el. Nincs meghatá-rozva, mi történik, ha ugyanazt a fájlleírót mind írásra, mind olvasásra próbáljuk használni.

Sikeres hívás esetén a pipe frissítésre jelöli meg az általa visszaadott fájlleírókhoz tartozó st_atime, st_ctime és st_mtime adatokat, visszaté-rési értékül pedig zérust szolgáltat. Ha hiba lépett fel, a visszatérési érték −1, a probléma természetéről pedig az errno globális változó értéke tájé-koztat:

• EMFILE: A folyamat már OPEN_MAX−2-nél több fájlleírót hasz-nál.

• ENFILE: Az egyidejűleg megnyitott fájlok száma elérte a rendszer egészére adott korlátot.

Az alábbi példaprogram (pipe1.c) bemutatja a szülő– és gyermekfolyamat közötti kommunikációt. A szülő folyamat újsor karakterrel lezárt karakter-láncot kér a felhasználótól, amit eljuttat a gyermeknek. A fogadó fél a szabvány kimenetre írja a kapott információt.

/* pipe1.c */ #include <stdio.h> #include <unistd.h> #include <sys/types.h> enum { OLVAS, IR }; void gyermek(FILE* cso) { char k; printf("[Gyermek] A szülő üzenete: "); while((k = fgetc(cso)) != EOF)

Page 171: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 171 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 171 ►

putchar(k); putchar('\n'); } void szulo(FILE* cso) { char k; printf("[Szülő] Adjon meg egy újsor karakterrel " "lezárt\nkarakterláncot, amelyet továbbítani fogunk " "a gyermeknek!\n"); while((k = getchar()) != '\n') fputc(k, cso); fflush(cso); } int main(void) { int alk[2]; pid_t faz; FILE* cso; if(!pipe(alk)) { switch(faz = fork()) { case -1: fprintf(stderr, "Gyermekfolyamat létrehozása " "nem sikerült.\n"); return 2; case 0: close(alk[IR]); if((cso = fdopen(alk[OLVAS], "r")) != NULL) gyermek(cso); close(alk[OLVAS]); break; default: close(alk[OLVAS]); if((cso = fdopen(alk[IR], "w")) != NULL) szulo(cso); close(alk[IR]); waitpid(faz, NULL, 0); break; } } else { fprintf(stderr, "Hiba lépett fel a csövek " "létesítése során.\n"); return 1; } return 0; } Korábban már említettük, hogy az operációs rendszer héjprogramja is gyakran használja a csöveket a párhuzamosan futó alkalmazások szabvány bemeneteinek és kimeneteinek összekapcsolására. Természetesen mi is élhetünk programjainkban ezzel a lehetőséggel. Először azonban ismer-kedjünk meg a dup2 függvénnyel!

int dup2(int fajlleiro1, int fajlleiro2);

Page 172: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 172 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 172 ►

A POSIX szabvány részét képező rutin lezárja a fajlleiro2-vel azonosított, már megnyitott fájlt, majd újra felhasználja a leírót a fajlleiro1 másolataként. Ettől kezdve mindkét fájlleíróval ugyanazon a fájlon dolgozhatunk. Az aktuális fájlpozíció egyezik, és a különféle állapotjelzők is mind ugyanolyan értékűek.

Sikeres hívás esetén a fajlleiro2-t kapjuk vissza, egyébként −1-et. A hiba okáról bővebben az errno változó tájékoztat:

• EBADF: fajlleiro1 nem érvényes, megnyitott fájl leírója. A hibát az is okozhatja, ha fajlleiro2 negatív, illetve ha nem kisebb OPEN_MAX-nál.

• EINTR: A dup2 függvényt jelzés szakította meg.

A pipe2.c mintaprogramban átalakítottuk a [2]-ből ismert forint – euró átváltó programot úgy, hogy 0-tól 3000 Ft-ig, 20-as lépésközzel haladva táblázatosan mutassa az összetartozó értékpárokat. Egy ilyen hosszú lista már természetesen nem fér el a konzolon, ezért a lapokra tördeléshez a less programot használjuk.

/* pipe2.c */ #include <stdio.h> #include <unistd.h> #include <sys/types.h> #define ALSO 0 #define FELSO 3000 #define LEPES 20 #define ARFOLYAM 244.5 enum { OLVAS, IR }; int main(void) { int forint, alk[2]; pid_t faz; FILE* cso; if(!pipe(alk)) { switch(faz = fork()) { case -1: fprintf(stderr, "Gyermekfolyamat létrehozása " "nem sikerült.\n"); return 2; case 0: close(alk[IR]); dup2(alk[OLVAS], STDIN_FILENO); execlp("less", "less", 0); break;

Page 173: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 173 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 173 ►

default: close(alk[OLVAS]); if((cso = fdopen(alk[IR], "w")) != NULL) { fprintf(cso, "Forint - Euró átszámítás\n\n" "%9s|%9s\n---------+---------\n", "Forint", "Euro"); for(forint = ALSO; forint <= FELSO; forint += LEPES) fprintf(cso, "%9d|%9.2f\n", forint, forint / ARFOLYAM); fflush(cso); } close(alk[IR]); waitpid(faz, NULL, 0); break; } } else { fprintf(stderr, "Hiba lépett fel a csövek " "létesítése során.\n"); return 1; } return 0; } Mivel a csöveket nagyon gyakran használják a szabványos folyamok átirá-nyítására, ezért kidolgoztak két POSIX szabványos függvényt, melyek segítségével a fenti problémát sokkal egyszerűbben oldhatjuk meg.

FILE* popen(const char* parancs, const char* mod);

A popen a korábbi példában látottakhoz hasonlóan csövet készít pipe hívással, majd a fork segítségével új folyamatot indít. Ezt követően a héj-programmal futtatja a parancs paraméterben kapott programot.

A mod értéke csak r vagy w lehet. r esetén a szülő olvashatja a gyermek szabvány kimenetére írt adatokat, w esetén pedig a szabványos bemeneté-re küldhet információt. A kommunikáció egyirányú.

A popen folyamot hoz létre a cső fájlleírójához, és ezt adja vissza. Ennek lezárására nem alkalmas azonban a korábban megismert fclose. Helyette a pclose használandó.

A popen által létrehozott cső alapértelmezés szerint teljesen pufferezett. A popen hívása több okból is lehet sikertelen. A visszaadott érték

NULL, ha a pipe vagy fork hívások sikertelenek voltak, vagy ha nem sikerült elegendő memóriát lefoglalni. Az első két esetben az errno válto-zó azokat az értékeket veheti fel, amelyeket az érintett rutinok ismerteté-sénél már bemutattunk. Ha a mod paraméter értéke érvénytelen, az errno EINVAL.

int pclose(FILE* folyam);

Page 174: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 174 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 174 ►

A függvény megvárja a popen-nel létrehozott gyermekfolyamat befejező-dését, és visszaadja annak állapotinformációját. Ezen kívül lezárja a para-méter megnyitott folyamot is.

A pclose −1-gyel tér vissza, ha a gyermek befejeződésére váró wait4 hibát jelez. Ha nem sikerült lekérdezni az állapotinformációt, errno az ECHILD értéket veszi fel.

Korábbi forint – euró átszámító programunk most már sokkal tömöreb-ben is megírható (pipe3.c).

/* pipe3.c */ #include <stdio.h> #include <unistd.h> #define ALSO 0 #define FELSO 3000 #define LEPES 20 #define ARFOLYAM 244.5 int main(void) { int forint; FILE* cso; if((cso = popen("less", "w")) != NULL) { fprintf(cso, "Forint - Euró átszámítás\n\n" "%9s|%9s\n---------+---------\n", "Forint", "Euro"); for(forint = ALSO; forint <= FELSO; forint += LEPES) fprintf(cso, "%9d|%9.2f\n", forint, forint / ARFOLYAM); return pclose(cso); } return 1; }

8.3. Nevesített csövek A csövek használatának egyik legnagyobb korlátja az, hogy csak egymással leszármazotti viszonyban lévő folyamatok közötti kommunikációra alkal-masak. E problémán segítenek a nevesített csövek.

A nevesített csövek egyszerű állományokként látszanak a fájlrendszer-ben. Éppen úgy megnyithatjuk, olvashatjuk, írhatjuk, stb. alacsony vagy magas szintű fájlkezelő függvények segítségével, mint bármely más fájlt. Lényeges különbség azonban, hogy ezek a csövek soha nem tárolnak ada-tokat az adathordozón. A fájlnév csak arra szolgál, hogy a folyamatok hi-

Page 175: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 175 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 175 ►

vatkozhassanak az egyértelműen azonosított csőre, de a kernel segítségével az információcsere közvetlenül a csövet használó folyamatok között zajlik.

Konzolon az mkfifo parancsot kiadva hozhatunk létre nevesített csö-veket. Például az

mkfifo /tmp/cso

hatására új bejegyzés jelenik meg a fájlrendszerben. Érdemes ezt kicsit alaposabban is megvizsgálni az

ls -l /tmp/cso

paranccsal! Az eredmény a következő: prw-r--r-- 1 wajzy users 0 2006-02-25 14:07 /tmp/cso

A legelső karakter mutatja, hogy a bejegyzés nevesített csövet takar. Most már akár több folyamat is írhat a csőbe, vagy olvashat abból, akár egymás-sal párhuzamosan is.

A GNU/Linux rendszereken a nevesített csövek alapértelmezés sze-rint blokkoló módban működnek, de nemblokkoló üzemmódban is hasz-nálhatóak. Utóbbi esetben, ha a folyamat úgy próbál írni, hogy egyetlen folyamat sem olvas az adott csőből, akkor SIGPIPE jelzést kap.

Nevesített csövek programozott létrehozására a POSIX szabványos mkfifo függvény használható, miután bekapcsoltuk a sys/stat.h fejfájlt.

int mkfifo(const char *fajlnev, mode_t mod);

Az mkfifo a fajlnev paraméterben adott elérési úton és fájlnévvel nevesített csövet hoz létre mod jogosultságokkal. E jogokat természetesen korlátoz-zák a folyamatra érvényes beállítások (lásd umask).

Sikeres hívás esetén a visszatérési érték zérus, egyébként −1, és a hiba pontos okát az errno változó segítségével állapíthatjuk meg.

• EACCESS: A fajlnev paraméterben szereplő könyvtárnevek valamelyi-kére nem állították be a keresési jogosultságot.

• EEXIST: A megadott fájlnév már létezik. • ENAMETOOLONG: Vagy a fajlnev paraméter hossza haladja meg a

PATH_MAX korlátot, vagy az elérési útban található nevek valame-lyike hosszabb NAME_MAX-nál.

• ENOENT: Az egyik megnevezett könyvtár nem létezik, vagy közve-tett link nem létező célra hivatkozik.

• ENOSPC: A könyvtárban vagy a fájlrendszerben nincs elegendő hely a cső létrehozásához.

Page 176: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 176 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 176 ►

• ENOTDIR: Az elérési út egyik részeként használt könyvtár valójában nem könyvtár.

• EROFS: Az elérési út csak olvasható fájlrendszerre hivatkozik.

A fifo1.c nevesített csövet hoz létre, majd megnyitja azt írásra. Ezt köve-tően a felhasználó által megadott, újsor karakterrel lezárt karakterláncot beleírja a csőbe. Végül lezárja a fájlt.

A fifo2.c megkísérli megnyitni a fifo1 által létrehozott csövet, és kiolvasni belőle az üzenetet. Ha sikerült, a csövet is törli.

Akkor tudjuk eredményesen kipróbálni a programokat, ha először elindítjuk fifo1-et, majd egy másik konzolon a fifo2-t. Ezután megadhatjuk a kért szöveget fifo1-nek, ami eljuttatja azt fifo2-höz.

A két program egyidejű futtatásához egy második virtuális terminálra is szükségünk lesz. A Windows operációs rendszert futtató gépeken új kon-zol megnyitásához és a Linux rendszerbe való belépéshez indítsunk el valamilyen távoli bejelentkezést lehetővé tevő szoftvert, pl. a

putty.exe

programot! /* fifo1.c */ #include <stdio.h> #include <sys/stat.h> #define CSOFAJL "cso" int main(void) { if(!mkfifo(CSOFAJL, S_IRUSR | S_IWUSR)) { FILE* cso; printf("Cső megnyitása, várakozás az olvasóra...\n"); if(cso = fopen(CSOFAJL, "w")) { int k; printf("Továbbítandó üzenet: "); do { fputc(k = getchar(), cso); } while(k!=EOF && k!='\n'); fclose(cso); } else { fprintf(stderr, "A csövet nem sikerült " "megnyitni írásra.\n"); return 2; } } else {

Page 177: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 177 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 177 ►

fprintf(stderr,"Nem sikerült létrehozni " "a csövet.\n"); return 1; } return 0; } /* fifo2.c */ #include <stdio.h> #define CSOFAJL "cso" int main(void) { FILE* cso; if(cso = fopen(CSOFAJL, "r")) { int k; printf("Várakozás a feladó üzenetére...\n"); while((k = fgetc(cso)) != EOF) putchar(k); fclose(cso); if(remove(CSOFAJL)) { fprintf(stderr, "A csövet nem " "sikerült törölni.\n"); return 2; } } else { fprintf(stderr, "Nem sikerült megnyitni a csövet.\n" "Külön konzolon futtassa először fifo1-et,\n" "majd próbálja újra!\n"); return 1; } return 0; }

8.4. Foglalatok A foglalat folyamatok közötti információcserét lehetővé tevő, magas szin-ten programozható technológia. Előnye, hogy a programozó elől elrejti a – különféleképpen is megvalósítható – kommunikáció részleteit, és azonos felületet teremt minden protokoll használatához. Népszerűségét elsősor-ban annak köszönheti, hogy hálózaton keresztül a különböző gépeken futó folyamatok közötti adatcserét is lehetővé teszi. Foglalatok segítségé-vel történik pl. az internetes böngészők és a webszerverek közötti adatto-vábbítás is.

A kommunikáció típusa (communication style) alapvetően kétféle lehet:

• összeköttetéses, megbízható (stream) és • összeköttetés-mentes, nem megbízható (datagram) kapcsolat.

Page 178: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 178 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 178 ►

Az összeköttetéses eset leginkább a telefonbeszélgetésekre emlékeztet. Miután a két fél között létrejött a kapcsolat, az egyetlen, kétirányú adatát-vitelt lehetővé tevő csatornán folyamatosan beszélhetnek. Ha a kapcsolat-ra többé nincs igény, akkor a felek megszüntetik azt.

A megbízhatóság itt azt jelenti, hogy az alkalmazott protokoll gondos-kodik arról, hogy a csomagokra bontott információ helyes sorrendben, sérülésmentesen jusson el a címzetthez, és mindegyik csomag megérkezzen.

Összeköttetés–mentes esetben a küldő egyszerre csak egyetlen adat-csomagot juttathat el a címzetthez, így a hagyományos postai levelezéshez hasonlítható. Ha további információt szeretne továbbítani, újabb csoma-gokat kell összeállítani, és azok mindegyikét külön–külön megcímezni.

A nem megbízható adattovábbítás során nem zárható ki, hogy egy csomag elveszik vagy többször is kézbesítik. A csomagok a küldés sor-rendjétől eltérő sorrendben is megérkezhetnek.

A második lehetőség hiányosságai ellenére is népszerű, mert rövid üzenetek továbbítása esetén sokkal gyorsabb működést lehet vele elérni.

Attól függően, hogy ugyanazon a számítógépen futó folyamatok kö-zötti kommunikációt szeretnénk megvalósítani, vagy hálózattal összekap-csolt gépek folyamatainak adatcseréjét biztosítani, különböző névterek (namespace) állnak rendelkezésre.

A foglalatok névterei semmiféle kapcsolatban nincsenek a progra-mozási nyelvekkel kapcsolatban megismert névtér fogalommal.

E jegyzet csak a helyi és az internet szabványcsaládjának használatát ismerteti.

A foglalatok kezeléséhez szükséges függvények a POSIX szabvány ré-szét képezik. Használatukhoz be kell kapcsolni a sys/socket.h és sys/types.h fejfájlokat.

Mivel a kommunikációban résztvevő feleknek némiképp eltérő lépése-ket kell végrehajtaniuk, nevezzük azokat a továbbiakban szervernek és kliensnek! Az elvégzendő feladatokat, ezek sorrendjét és időbeli lefutását a kommunikáció típusa határozza meg. A következő ábra ezt igyekszik szemléltetni.

Page 179: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 179 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 179 ►

8.1. ábra. Összeköttetés-mentes kapcsolat létesítésének lépései

A foglalat alkalmazói programinterfész (socket api) és az alacsonyszin-tű fájlkezelés függvényeivel az összeköttetés-mentes kapcsolatok létesítése a következőkben leírt módon történhet.

A szerver oldalon a socket rutinnal kell a foglalatot létrehozni. A socket fájlleírót szolgáltat, mely a későbbi kommunikáció során azonosítja a foglalatot. A bind olyan címmel látja el a foglalatot, melyre a kliens a kapcsolat kiépülésekor hivatkozhat. Ha a kapcsolat kiépült, az alacsony-szintű fájlkezelő függvényekkel írhatunk a fájlleíróval azonosított foglalat-ra, vagy olvashatunk belőle. A kapcsolat jellegéből adódóan csak egyszer hívhatjuk a write vagy read rutinok valamelyikét, amit a close követ. Az alkalmazott kommunikációs protokoll befolyásolja az egyszerre elküldhető vagy fogadható adatok mennyiségét.

A kliens is létrehozza a maga foglalatát a socket-tel. Ha a szerver már készen áll a kapcsolódási kérelem fogadására, meghívhatjuk a connect-et, ami kiépíti az összeköttetést a két fél között. Most a socket által vissza-adott fájlleírót használva write vagy read függvényekkel csomagot juttat-hatunk el a szerverhez, vagy olvashatjuk az általa küldöttet. (Értelemszerű-en, ha a szerver a read-et használja, akkor a kliensnek a write-ot kell, és fordítva.) Az információtovábbítást követően a foglalatot close-zal kell lezárni.

Tekintsük át részletesen az említett függvényeket! int socket(int nevter, int tipus, int protokoll);

A rutin foglalatot hoz létre. A használandó névteret a nevter, a kommu-nikáció típusát a tipus paraméter határozza meg.

Az utolsó paraméter értelmezése az előző kettő értékétől függ, de általában jó választás a zérus.

A nevter lehetséges értékei:

Szerver Kliens

1.) socket

4.) read / write5.) close

1.) socket

4.) write / read5.) close

2.) bind 3.) connect

Page 180: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 180 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 180 ►

• PF_LOCAL, PF_UNIX, PF_FILE: A helyi névteret jelölik. • PF_INET: Az internet névtér. • PF_INET6: A következő generációs IP protokoll (IPv6) használatá-

hoz szükséges; a továbbiakban csak a jelenleg is általánosan elterjedt és használt IPv4-gyel foglalkozunk.

A tipus paraméter lehetséges értékei:

• SOCK_STREAM: Bájtfolyamok megbízható továbbítását jelzi. • SOCK_DGRAM: Egyenként címzendő adatcsomagok használatakor

alkalmazandó. A kapcsolat valószínűleg nem megbízható. • SOCK_RAW: Lehetővé teszi az alacsony szintű hálózati protokollok

és interfészek elérését. Használatát nem részletezzük.

A socket sikeres esetben mind olvasási, mind írási műveletet lehetővé tevő fájlleíróval tér vissza, melyet később az alacsonyszintű fájlkezelő függvények segítségével adatok küldéséhez és fogadásához használhatunk.

A folyamban pozicionáló függvények nem alkalmazhatók foglalatokra! Ha hiba lépett fel, a visszatérési érték −1, a hiba pontos okát ebben az

esetben is az errno változó tartalmazza:

• EPROTONOSUPPORT: A megadott névtérben a kívánt kommu-nikációs típus vagy protokoll nem támogatott.

• EMFILE: A folyamat már túl sok megnyitott fájlleíróval rendelkezik. • ENFILE: Az operációs rendszer tart túl sok fájlleírót nyitva. • EACCESS: A folyamatnak nincs joga a megadott típusú kommuniká-

cióra alkalmas, vagy protokollt használó foglalat létesítéséhez. • ENOBUFS: A rendszernek elfogyott a belső pufferterülete.

Az imént létrehozott foglalathoz csak akkor tudnak más folyamatok csat-lakozni, ha azt megfelelő címmel (address) látjuk el. A cím formája a socket függvény hívásakor alkalmazott névtértől függ. A tárolására hasz-nálatos típus a következő: struct sockaddr { short int sa_family; char sa_data[14]; };

Az sa_family kód, mely azonosítja a névtérnek megfelelő címcsaládot, és meghatározza a további adatok értelmezését. A socket-nél felsorolt névtér konstansok mindegyike rendelkezik AF_ kezdetű párral (a betűk az Address Family-t rövidítik). Pl.: AF_LOCAL, AF_INET.

Page 181: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 181 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 181 ►

Az sa_data (a tényleges cím adat) 14 bájtnyi hossza tulajdonképpen önkényesen megválasztott érték. A különböző protokollok speciális – a sockaddr struktúrához hasonló – adatszerkezeteket használnak, melyek további tagokat, vagy eltérő méretű sa_data tagot tartalmazhatnak.

int bind(int foglalat, struct sockaddr *cim, socklen_t hossz);

A bind függvényt a szerver hívja, hogy hozzárendelje a socket által visz-szaadott fájlleíróval azonosított foglalat-hoz a cim címet. A harmadik para-méter a cim struktúrájának méretét rögzíti.

A visszatérési érték sikeres hívásnál zérus, egyébként pedig −1, és az errno az alábbi értékek valamelyikét veszi fel:

• EBADF: A foglalat paraméter nem érvényes fájlleíró. • ENOTSOCK: A foglalat fájlleíró nem foglalatot azonosít. • EADDRNOTAVAIL: A megadott cim nem használható ezen a gépen. • EADDRINUSE: Más foglalat már használja a kívánt címet. • EINVAL: A foglalat már rendelkezik címmel. • EACCESS: A felhasználónak nincs joga a kívánt cim eléréséhez. (Bi-

zonyos címeket csak a rendszergazda használhat.)

A felsoroltakon kívül más, az alkalmazott névtértől függő hibakódokat is kaphatunk.

Ha a foglalatot elláttuk a megfelelő címmel, akkor már kapcsolódhat-nak hozzá más folyamatok foglalatai. (Természetesen csak azonos névte-ret, kommunikációs típust és protokollt használó foglalatok kapcsolhatók össze.) A szerver foglalatához történő kapcsolódás a connect meghívásá-val lehetséges.

int connect(int foglalat, struct sockaddr *cim, socklen_t hossz);

Miután a kliens is elkészítette a maga foglalatát socket hívással, hozzákap-csolja azt a szerver foglalatához. A connect foglalat paramétere a kliens foglalat leírója. A cim a szerver foglalat címe, s a hossz most is a struktúra mérete.

Alapértelmezés szerint a connect addig vár, míg a szerver nem vála-szol a kapcsolódási kérésre. Természetesen beállítható nem blokkoló üzemmód is, az alacsonyszintű fájlkezelésnél megismert módon.

A sikeres függvényhívás visszatérési értéke itt is zérus, a sikertelené pedig −1. Az errno változó a következő lehet:

Page 182: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 182 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 182 ►

• EBADF: A foglalat paraméter nem érvényes fájlleíró. • ENOTSOCK: A foglalat fájlleíró nem foglalatot azonosít. • EADDRNOTAVAIL: A cim nem áll rendelkezésre a szerveren. • EAFNOSUPPORT: A cim paraméter névteret nem támogatja a foglalat. • EISCONN: A foglalat már hozzá van kapcsolva a szerverhez. • ETIMEDOUT: A kapcsolat létesítéséhez rendelkezésre álló időkorlát

letelt. • ECONNREFUSED: A szerver visszautasította a kapcsolódási ké-

relmet. • ENETUNREACH: A cim-mel hivatkozott hálózat nem érhető el

erről a gépről. • EADDRINUSE: A megadott cim már használatban van. • EINPROGRESS: A foglalat nem blokkoló üzemmódban van, és a

kapcsolat nem hozható létre azonnal. Az összeköttetés kiépültét ilyen-kor a select hívással lehet ellenőrizni. Ha nincs még meg a kapcsolat, az újabb connect hívás EALREADY hibakódot eredményez a foglalat-ra.

• EALREADY: A foglalat nem blokkoló üzemmódban van, és a kap-csolat kiépülésére vár.

A függvény érvénytelenedési pont többszálú programokban. Bár az említett select függvény eredetileg csak a BSD UNIX-ban volt

elérhető, mára sok rendszer átvette, ezért érdemes alaposabban is megis-merni. A rutin blokkolja a folyamatot, míg a paraméterként átadott fájlle-írók valamelyikével meghatározott ki– vagy bemeneten nincs aktivitás, illetve le nem telik az előre beállított időkorlát. Szerver foglalat esetén a bemenet olvasásra kész, ha van függőben egy olyan kapcsolódási kérelem, melyet az accept függvény fogadni képes. A kliens foglalat írásra kész, ha a kapcsolat a két fél között teljesen kiépült.

Mielőtt a select függvénnyel foglalkoznánk, meg kell ismerkednünk néhány új adattípussal, és az azok kezeléséhez nélkülözhetetlen makrókkal.

Az fd_set adattípus fájlleírók ábrázolására szolgáló bittömb. Az int FD_SETSIZE

makró megadja, hogy legfeljebb hány leíró befogadására alkalmas az adott rendszeren az fd_set típus. A

void FD_ZERO(fd_set *halmaz)

üríti a halmazt, azaz kitörli a már beállított leírókat. A void FD_SET(int leiro, fd_set *halmaz)

Page 183: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 183 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 183 ►

beleteszi leiro-t a halmaz-ba. A void FD_CLR(int leiro, fd_set *halmaz);

eltávolítja a leiro-t a halmaz-ból. Az int FD_ISSET(int leiro, fd_set *halmaz);

zérustól különböző (igaz) értékkel tér vissza, ha a leiro tagja a halmaz-nak, egyébként zérust szolgáltat. struct timeval { long int tv_sec; long int tv_usec; };

A sys/time.h fejfájlban deklarált timeval típus időintervallumok tárolásá-ra használatos. A tv_sec mező egész másodperceket tárol, a tv_usec pe-dig a töredék másodperceket mikromásodpercben.

Most már rátérhetünk a select-re! int select(int leirodb, fd_set *olvas, fd_set *ir,

fd_set *kivetel, struct timeval *idokorlat)

A függvény blokkolja a hívó folyamatot, míg az olvas paraméterben adott fájlleírók valamelyike olvasásra kész, az ir leíróinak bármelyike írásra kész állapotba nem kerül (tehát van leolvasható, illetve kiírandó adat). A blok-kolt állapotot megszüntetheti az is, ha a kivetel leírók valamelyikével kivéte-les esemény (exception) történik.

A kivételes esemény alatt nem az objektum-orientált nyelvekből ismerős kivétel fogalmat kell érteni, hanem egy megkülönböztetett állapot meglétét (pl. sürgősen kézbesítendő csomag érkezése).

Az olvas, ir és kivetel paraméterek bármelyikének helyén szerepelhet NULL mutató, ha az adott kategóriában egyetlen leírót sem szeretnénk figyeltetni.

A select a paraméterként kapott halmazokban szereplő leírók közül csak az első leirodb darabbal kapcsolatosan bekövetkező eseményeket fogja érzékelni. A leirodb-t általában FD_SETSIZE-nak szokták választani.

Az idokorlat-tal szabályozható, hogy a select legfeljebb mennyi ideig várakozzék a megadott leírókkal kapcsolatos eseményekre. Ha a paramé-tert NULL mutatónak választjuk, akkor a folyamat blokkolása addig tart, míg valamelyik leíró műveletre kész nem lesz.

Jelzés bármikor megszakíthatja a függvény működését, az időkorlát lejárta előtt is!

Sikeres hívás esetén a select a paraméterként kapott halmazokban csak azokat a leírókat hagyja beállítva, amelyekkel kapcsolatban valamilyen esemény bekövetkezett, és visszaadja ezeknek a leíróknak a darabszámát.

Page 184: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 184 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 184 ►

(A beállított időkorlát letelte zérus értéket eredményez.) Leíró halmazban való megkeresésére az FD_ISSET makró használatos.

Hiba esetén a paraméter halmazok tartalma nem változik, a visszatérési érték pedig −1 lesz. Az errno a következő értékek valamelyikét veszi fel:

• EBADF: A halmazok valamelyikében érvénytelen fájlleíró található. • EINTR: A függvényt jelzés szakította meg. • EINVAL: Az idokorlat paraméter értéke hibás; negatív, vagy túl nagy

érték fordul benne elő.

Most, hogy megismertük az összeköttetés-mentes kommunikáció lebo-nyolításának módját és a szükséges függvényeket, vizsgáljuk meg, hogyan lehet összeköttetéses kapcsolatokat létesíteni!

8.2. ábra. Összeköttetéses kapcsolat kiépítésének lépései

A szerver először foglalatot hoz létre socket hívással. A visszakapott fájlleíró általában csak a kommunikáció kiépítéséhez használatos, ezért szerver foglalatnak (server socket) is szokás nevezni. A bind címmel látja el a foglalatot, hogy a kliensek képesek legyenek hozzákapcsolódni. Ez-után a szerver listen hívással engedélyezi a kliens felől érkező kapcsolat-felépítési kérelmek fogadását. A kérelmekre az accept figyel, mely új fog-lalatot hoz létre a kommunikáció lebonyolítására. Sikeres hívás esetén en-nek fájlleírójával tér vissza. A szerverek ezután legtöbbször fork hívással olyan új folyamatot indítanak a legutóbbi kapcsolat kezelésére, ami a szo-kásos alacsonyszintű fájlkezelési eszközöket (read, write, close) használja. Ezidő alatt a szerver foglalaton újabb kapcsolódási kérelmekre tud figyelni az accept. Kilépés előtt a szerver ezt a fájlleírót is lezárja.

2.) bind

4.) connect

Szerver Kliens

1.) socket 1.) socket

3.) listen4.) accept

4.1.) fork 4.2.) read / write4.3.) close

5.) close

4.2.) write / read4.3.) close

Page 185: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 185 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 185 ►

A kliens pontosan ugyanolyan felépítésű, mint az összeköttetés-mentes esetben. Az egyetlen lényegi eltérés, hogy a felépített kommunikációs csa-tornán több adatcsomag továbbítható, akár váltakozó irányban is. Az in-formáció csomagokra bontását a rendszer automatikusan elvégzi.

int listen(int foglalat, unsigned int n);

A listen engedélyezi a kapcsolatok fogadását a socket-tel létesített foglalat-on, ami így szerver foglalattá válik. A függvény csak összeköttetéses kap-csolatok esetén használható. Az n paraméter a csatlakozási kérelmeket tároló sor méretét határozza meg. Ha a sor megtelik, a csatlakozni vágyó kliens connect függvénye errno=ECONNREFUSED hibával tér visz-sza, míg a szerver nem fogad accept-tel újabb kapcsolatot a sorból.

Sikeres hívás esetén a visszatérési érték zérus, egyébként −1, az errno változó pedig a következő értékek valamelyikét veszi fel:

• EBADF: A foglalat paraméter nem érvényes fájlleíró. • ENOTSOCK: A foglalat fájlleíró nem foglalatot azonosít. • EOPNOTSUPP: A foglalat nem támogatja ezt a műveletet.

int accept(int foglalat, struct sockaddr *cim, socklen_t *hossz);

Az accept arra való, hogy a szerver foglalat-ra érkező kapcsolódási kérel-meket kiszolgálja. Ez abból áll, hogy új, kapcsolt foglalatot hoz létre a kommunikáció lebonyolítására, és visszaadja ennek fájlleíróját. A szerver folyamata blokkolt az első kérelem beérkeztéig. (Ha a szerver foglalatot előzőleg nemblokkoló üzemmódba állítottuk, akkor a select függvénnyel kell várakozni a kapcsolódásra.) Természetesen ezt a függvényt is csak összeköttetéses kapcsolat esetén lehet használni.

A cim és hossz paraméterek a kapcsolatot létesítő kliens foglalatról szol-gáltatnak információt.

Sikertelen hívás esetén az accept −1-el tér vissza, és az errno a követ-kező értékek valamelyike:

• EBADF: A foglalat paraméter nem érvényes fájlleíró. • ENOTSOCK: A foglalat fájlleíró nem foglalatot azonosít. • EOPNOTSUPP: A foglalat nem támogatja ezt a műveletet. • EWOULDBLOCK: A foglalat nemblokkoló módban van, és egyetlen

kapcsolódási kérelem sem érkezett.

Page 186: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 186 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 186 ►

8.4.1. A UNIX névtér A UNIX, vagy helyi névtér azonos számítógépen futó, egymástól függet-len folyamatok közötti adatcsere lebonyolítására is alkalmas. A foglalat címek fájlazonosítók az operációs rendszerben, ami biztosítja a címek egyediségét. Ez a kommunikációs eszköz tehát nagyban hasonlít a nevesí-tett csövekre.

Az alábbi példa helyi foglalat fájl adatait mutatja, melyet az ls -l /tmp/foglalat

parancs segítségével jelenítettünk meg. srwxr-xr-x 1 wajzy users 0 2006-02-26 12:16 /tmp/foglalat

Érdemes megfigyelni a legelső karaktert. Bár egy számítógép fájlrendszerét több másik is igénybe veheti, olvas-

hat róla vagy írhat arra, a helyi névtér használata nem teszi lehetővé a kü-lönböző gépeken futó folyamatok közti kommunikációt. Arról sem szabad megfeledkezni, hogy a foglalatot tartalmazó könyvtárban írási joggal kell rendelkezniük az információcserét bonyolítani szándékozó folyamatoknak, ezért a programozók gyakran választják a /tmp könyvtárat.

A helyi névtér használata esetén be kell kapcsolni a sys/un.h fejfált is. A socket nevter paramétereként a PF_LOCAL szimbolikus állandó alkal-mazandó, bár kompatibilitási okokból elfogadottak a PF_UNIX és PF_FILE értékek is. Az egyetlen használható protokoll a zérus.

A bind cim paramétere sockaddr_un típusú változó. A struktúra szer-kezete a következő: struct sockaddr_un { short int sun_family; char sun_path[108]; };

A foglalat címcsaládját azonosító első tag értékének AF_LOCAL-nak kell lennie.

A fájl teljes elérési útja a karakterláncot lezáró karakterrel együtt sem lehet hosszabb 108 bájtnál! Ha ennél feltétlenül hosszabbat szükséges megadni, akkor az alloca nem POSIX szabványos függvény alkalmazását javasoljuk. Használatát itt nem részletezzük, bővebb információval szol-gálnak a kézikönyvlapok:

man 3 alloca

Helyi névtérben a foglalat cim méretét az int SUN_LEN(struct sockaddr_un* cim);

Page 187: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 187 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 187 ►

makró szolgáltatja. A hosszba a sun_family tag mérete, illetve az elérési út karakterei számítanak bele.

A megismert eszközökkel már el tudunk készíteni két programot (fhomszerver.c és fhomkliens.c), melyek a UNIX névtér használatával kommunikálnak. A kliens az első parancssori paramétereként megadott szöveges üzenetet eljuttatja a szervernek összeköttetés-mentes kapcsolat segítségével. Amikor a foglalatra már nincs szükség, a szerver letörli azt.

A program kipróbálásához először a szervert kell elindítani, azután a felparaméterezett klienst egy másik konzol ablakban. A futtatható állomá-nyoknak ugyanabban a könyvtárban kell lenniük, és írási jogokkal is ren-delkezniük kell. /* fhomszerver.c */ #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #define FOGLALATFAJL "foglalat" #define KORLAT 5 #define MAX 255 int main(void) { int foglalat, hossz; char csomag[MAX]; struct sockaddr_un cim; if((foglalat = socket(PF_LOCAL, SOCK_DGRAM, 0)) == -1) { fprintf(stderr, "Szerver foglalat létrehozása nem " "sikerült.\n"); return 1; } cim.sun_family = AF_LOCAL; strcpy(cim.sun_path, FOGLALATFAJL); if(bind(foglalat, (struct sockaddr*)&cim, SUN_LEN(&cim))) { fprintf(stderr, "Foglalat elnevezése nem " "sikerült.\n"); return 2; } printf("Várakozás a kliens üzenetére...\n"); read(foglalat, csomag, MAX-1); csomag[MAX] = '\0'; printf("Üzenet: %s\n", csomag);

Page 188: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 188 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 188 ►

if(close(foglalat)) { fprintf(stderr, "Foglalat lezárása nem sikerült.\n"); return 3; } if(remove(FOGLALATFAJL)) { fprintf(stderr, "Foglalat törlése nem sikerült.\n"); return 4; } return 0; } /* fhomkliens.c */ #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #define FOGLALATFAJL "foglalat" int main(int argc, char* argv[]) { int foglalat; struct sockaddr_un cim; if(argc != 2) { printf("Használat: fhomkliens üzenet\n"); return 1; } if((foglalat=socket(PF_LOCAL, SOCK_DGRAM, 0))== -1){ fprintf(stderr, "Foglalat létrehozása nem " "sikerült.\n"); return 2; } cim.sun_family = AF_LOCAL; strcpy(cim.sun_path, FOGLALATFAJL); if(connect(foglalat, (struct sockaddr*)&cim, SUN_LEN(&cim))) { fprintf(stderr, "Kapcsolódás nem sikerült.\n"); return 3; } write(foglalat, argv[1], strlen(argv[1])+1); if(close(foglalat)) { fprintf(stderr, "Foglalat lezárása nem sikerült.\n"); return 4; } return 0; }

A következő két fájl (fhoszerver.c és fhokliens.c) összeköttetéses kap-csolaton keresztül kommunikál, és Forint – Euró átszámítást valósítanak meg foglalat segítségével. A szerver gyermekfolyamatokat indít a kliensek kiszolgálására. A kliens először egy egész értéket küld a szervernek, mely azt jelzi, hogy le kell állnia, vagy futnia kell tovább. Az utóbbi esetben egy újabb egész érkezik, mely az átszámítandó Forint összeg. A művelet vé-geztével a szerver visszaküldi az eredményt. A gyermekfolyamat befejező-

Page 189: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 189 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 189 ►

déséről a GNU/Linux SIGCHLD jelzéssel értesíti a szülőt. A jelzéskeze-lő kiolvassa a gyermek állapotinformációját, megszüntetve ezzel a zombi folyamatot.

/* fhoszerver.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/wait.h> #include <unistd.h> #include <errno.h> #define FOGLALATFAJL "/tmp/foglalat" #define KORLAT 5 #define ARFOLYAM 244.5 void gyermekbef(int jelzes) { while(waitpid(WAIT_ANY, NULL, WNOHANG) > 0); return; } int foglalat_sz; void szulobef(int jelzes) { close(foglalat_sz); remove(FOGLALATFAJL); exit(0); } int main(void) { struct sockaddr_un cim_sz, cim_kl; socklen_t cim_klhossz; int foglalat_kl; printf("Forint - Euró átváltó szerver " "program.\nLeállítás: Ctrl+C\n"); if(signal(SIGCHLD, gyermekbef) == SIG_ERR) { fprintf(stderr, "Gyermek jelzéskezelőjének " "beállítása nem sikerült.\n"); return 1; } if(signal(SIGINT, szulobef) == SIG_ERR) { fprintf(stderr, "Szülő jelzéskezelőjének " "beállítása nem sikerült.\n"); return 2; } if((foglalat_sz=socket(PF_LOCAL, SOCK_STREAM, 0))==-1){ fprintf(stderr, "Szerver foglalat létrehozása nem " "sikerült.\n"); return 3; }

Page 190: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 190 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 190 ►

cim_sz.sun_family = AF_LOCAL; strcpy(cim_sz.sun_path, FOGLALATFAJL); if(bind(foglalat_sz, (struct sockaddr*)&cim_sz, SUN_LEN(&cim_sz))) { fprintf(stderr, "Foglalat elnevezése nem " "sikerült.\n"); return 4; } if(listen(foglalat_sz, KORLAT)) { fprintf(stderr, "Nem lehet fogadni a szerver " "foglalaton érkező kéréseket.\n"); return 5; } while(1) { if((foglalat_kl = accept(foglalat_sz, (struct sockaddr*)&cim_kl, &cim_klhossz)) == -1) { fprintf(stderr, "Kliens foglalat létesítése nem " "sikerült.\n"); return 6; }; if(!fork()) { int folytatas; while(read(foglalat_kl, &folytatas, sizeof(folytatas)), folytatas) { int forint; float euro; read(foglalat_kl, &forint, sizeof(forint)); euro = forint / ARFOLYAM; write(foglalat_kl, &euro, sizeof(euro)); } close(foglalat_kl); return(0); } } return 0; } /* fhokliens.c */ #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <unistd.h> #define FOGLALATFAJL "/tmp/foglalat" #define ALSO 0 #define FELSO 300 #define LEPES 20 int main(void) { int foglalat, forint, folytatas = 1; struct sockaddr_un cim; if((foglalat=socket(PF_LOCAL, SOCK_STREAM, 0)) == -1){ fprintf(stderr, "Foglalat létrehozása nem "

Page 191: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 191 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 191 ►

"sikerült.\n"); return 1; } cim.sun_family = AF_LOCAL; strcpy(cim.sun_path, FOGLALATFAJL); if(connect(foglalat, (struct sockaddr*)&cim, SUN_LEN(&cim)) == -1) { fprintf(stderr, "Csatlakozás sikertelen.\n"); return 2; } printf("Forint - Euró átszámítás\n\n" "%9s|%9s\n---------+---------\n", "Forint", "Euro"); for(forint = ALSO; forint <= FELSO; forint+=LEPES) { float euro; write(foglalat, &folytatas, sizeof(folytatas)); write(foglalat, &forint, sizeof(forint)); read(foglalat, &euro, sizeof(euro)); printf("%9d|%9.2f\n", forint, euro); } folytatas = 0; write(foglalat, &folytatas, sizeof(folytatas)); close(foglalat); return 0; }

8.4.2. Az internet névtér Az internet névtér segítségével különböző számítógépeken futó folyama-tok is kommunikálhatnak egymással. Nem meglepő tehát, hogy a socket api-nak ez a leggyakrabban alkalmazott része. A legnépszerűbb internetes szolgáltatások és alkalmazási réteg protokollok (http, ftp, smtp, pop3, stb.) mind foglalatokat használnak. Legtöbbjük összeköttetéses, megbízha-tó kapcsolatot igényel, amit a foglalatok a TCP (Transmission Control Protocoll) protokoll segítségével valósítanak meg.

A ma általánosan használt hálózati réteg protokoll az IPv4, de egyre népszerűbbé válik az IPv6 használata. Ebben a jegyzetben nem foglalko-zunk ez utóbbival!

Ha az internet névtérben az IPv4 protokollt szeretnénk használni, programjainkba be kell kapcsolni a netinet/in.h fejfájlt, s a socket rutin nevter paraméterét pedig PF_INET–nek kell választani.

A bind és connect használatos cim struktúrája ebben a névtérben a következő szerkezetű: struct sockaddr_in { sa_family_t sin_family; struct in_addr sin_addr; unsigned short int port; };

A sin_family struktúratagnak az AF_INET értéket kell tartalmaznia.

Page 192: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 192 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 192 ►

A sin_addr a connect esetében a szerver gép, illetve az azon futó al-kalmazás címe. A bind-nél azonban az INADDR_ANY szimbolikus állandót használják szinte kizárólag, ami azt jelzi, hogy bárki kapcsolódhat a szerverhez. Az interneten minden aktív eszközt (esetünkben a számító-gépet, de ha több hálózati csatolója is van, akkor azok mindegyikét) egye-dileg kell azonosítani. (Előfordulhat, hogy több eszköznek is ugyanaz a címe, de mégsem sérül az egyedi azonosítási lehetősége. Az alhálózati címosztályokkal, azok címzésmódjával terjedelmi okokból itt nem foglal-kozunk.) Ez a cím az IPv4 szabvány szerint 32 bites egész, melyet a köny-nyebb olvashatóság kedvéért 4 darab 8 bites szám formájában, tízes szám-rendszerben, azokat egymástól pontokkal elválasztva, szoktak felírni. Pl. 193.224.128.1 Az ilyen címekkel a számítógépek elegánsan tudnak dol-gozni, a felhasználók számára azonban megjegyezhetetlenek. Ennek a problémának a megoldásaként született meg a DNS (Domain Name System) rendszer. Az IP címekhez könnyen megjegyezhető szöveges azo-nosítókat (nevet) rendelnek, a fenti címhez pl. az rs1.sze.hu-t, de egy gép-nek akár több neve is lehet. A legtöbb szoftver elfogadja a felhasználóktól az IP címet és a domén nevet is. Ez utóbbi nem alkalmazható azonban közvetlenül címzésre, mert először „fel kell oldani a nevet”, azaz elő kell állítani a névnek megfelelő IP címet.

A sin_addr struktúratag maga is struktúra, szerkezete az alábbi: struct in_addr { uint32_t s_addr; };

Mint látható, csak az előjel nélküli 32 bites egészként ábrázolt IP címet tároló tagot tartalmaz.

A karakterláncként leírt IP címformátum és az uint_32t típus közötti konverziók elvégzését könnyítik meg a következőkben tárgyalt függvé-nyek, melyek használatához be kell kapcsolni az arpa/inet.h fejfájlt. A cím tárolására használt változóban a hálózati bájtsorrendnek megfelelően (big endian, azaz a magasabb helyértékű biteket tároló bájtok kerülnek a kisebb memóriacímekre) helyezkednek el a címet alkotó bájtok. (Kivéve a csoportcímzésnél alkalmazott címeket, melyekkel itt nem foglalkozunk!) Ez nem feltétlenül esik egybe számítógépünk bájtsorrendjével. Pl. az x86 architektúrájú gépek is little endian (tehát a kisebb helyértékű biteket táro-ló bájtok találhatóak a kisebb memóriacímeken) üzemmódban dolgoznak.

int inet_aton(const char *karakterlanc, struct in_addr *struktura);

Page 193: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 193 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 193 ►

A karakterlanc formájában adott IP címet átkonvertálja, és kiteszi a struktura címre a rutin. Érvénytelen cím esetén zérust kapunk tőle.

char* inet_ntoa(struct in_addr struktura);

Fordított konverziót hajt végre a struktura–ból a függvény, az átalakított karakterlánc IP címet statikus memóriaterületre teszi, majd visszatér en-nek címével. Az egymást követő hívások felülírják a statikus területet. Ha később is szükség van rá, el kell menteni innét az IP címet.

A domén név feloldásához a netdb.h fejfájlt kell bekapcsolni. A hostent struktúra arra szolgál, hogy domén neveket, a hozzájuk tartozó címeket, és a címek típusát tárolja. struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; #define h_addr h_addr_list[0] }; A h_name tag a gép elsődleges neve. Ne felejtsük el azonban, hogy egy gépnek akár több domén neve, „álneve” (alias) is lehet. Ezeket tárolja a h_aliases mutatótömb, melynek utolsó eleme NULL. A h_addrtype kétféle, az IP címek formátumát leíró értéket vehet fel (AF_INET, AF_INET6). A h_length pedig egy-egy cím bájtban mért hosszát rögzíti. (Egy számítógép ugyanis csatlakozhat több hálózathoz, és így rendelkez-het több címmel.) A h_addr_list szintén NULL zárású mutatótömb, ami a gép címeit tartalmazza. A legutolsó h_addr tag a h_addr_list[0] szino-nimája, létezésének kompatibilitási okai vannak. A címeket itt is hálózati bájtsorrendben tárolják.

struct hostent* gethostbyname(const char *nev);

A függvény kikeresi a nev domén névvel rendelkező gép címét, és visszaad-ja hostent struktúrában.

struct hostent* gethostbyaddr(const char *cim, size_t hossz, int formatum);

A gethostbyaddr megkeresi a cim Internet című gép domén nevét, és visszaadja hostent struktúrában. A cim paraméter hossz bájtos, formátumát pedig a formatum (AF_INET vagy AF_INET6) rögzíti.

A függvények többszöri, egymást követő hívásai mind ugyanattól a memóriacímtől kezdődően fogják tárolni az eredményt. Indokolt lehet tehát az adatokat a hívást követően új területre másolni. Hiba esetén a

Page 194: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 194 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 194 ►

visszatérési érték a NULL mutató, s a részleteket az errno vizsgálatával tudhatjuk meg.

• HOST_NOT_FOUND: A megadott nevű gép nem található. • TRY_AGAIN: Nem sikerült kapcsolatba lépni a névszerverrel. A

később megismételt próbálkozás sikerrel járhat. • NO_RECOVERY: Nem megállapítható hiba lépett fel. • NO_ADDRESS: A keresett név megtalálható a DNS adatbázisban,

de nem tartozik hozzá Internet cím.

Sajnos a fenti két függvény egyike sem használható biztonságosan többszálú programokban. Ilyen esetekben az itt nem részletezett gethostbyname_r és gethostbyaddr_r hívható.

Ha számítógépen futó szolgáltatást kívánunk igénybe venni, akkor nem elég csupán a gépet megcímezni, hiszen egy gép számos szolgáltatást nyújthat egyidejűleg. Egyértelmű kijelölésre a kapuk (port) szolgálnak.

A sockaddr_in struktúra eddig nem ismertetett, port tagjának feladata éppen az igénybe venni kívánt kapu (szolgáltatás) kijelölése.

A kapuk előjel nélküli 16 bites egészek. Az alacsonyabb értékek a szabványos szolgáltatásoknak (pl. telnet, ftp, stb.) vannak fenntartva. A netinet/in.h fejfájl bekapcsolását követően rendelkezésünkre áll az IPPROTO_RESERVED makró, mellyel megállapítható az a legmaga-sabb érték, amit ezek a megkülönböztetett szolgáltatások használhatnak. Új, saját fejlesztésű kiszolgáló programjaink csak az IPPROTO_USERRESERVED értékével megegyező, vagy annál maga-sabb kapuszámot vehetnek igénybe. A programozó feladata olyan kaput választani, amit más szolgáltatás még nem használ. Ebben a tartományban a rendszer soha nem fog automatikusan új kaput használatba venni (pl. egy accept hívást követően).

Korábban már szó esett arról, hogy számítógépünk nem feltétlenül tá-rolja nagyobb méretű változók bájtjait olyan sorrendben, mint ahogyan azt a hálózati protokollok továbbítják. A gép által használt és a hálózati bájt-sorrend közötti konverziók elvégzésére számos függvény használható a netinet/in.h fejfájl bekapcsolását követően.

uint16_t htons(uint16_t geprovid);

A geprovid helyi gépen használatos bájtsorrenddel ábrázolt 16 bites értéket a hálózati bájtsorrendnek megfelelő formátumra alakítja.

uint16_t ntohs(uint16_t halorovid);

Page 195: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 195 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 195 ►

A halorovid 16 bites értéket a hálózati bájtsorrendről a számítógép bájtsor-rendjének megfelelőre változtatja.

uint32_t htonl(uint32_t gephosszu);

Működése abban tér el a htons-től, hogy 32 bites értékkel dolgozik. uint32_t ntohl(uint32_t halohosszu);

Az ntohs 32 bites adattal dolgozó megfelelője.

A pop3.c mintaprogram megállapítja, hogy érkeztek-e egy adott felhasz-nálónak elektronikus levelei, és ha igen, hány darab. A program három parancssori paramétert vár: a POP3 szabvány szerint üzemelő levélszerver domén nevét, a felhasználó azonosítóját és jelszavát.

A kommunikáció összeköttetéses, megbízható csatornán keresztül zaj-lik. A kliens és a szerver felváltva küldi egymásnak az egyszerű, \r\n karakterekkel lezárt szöveges parancsokat és az arra érkező válaszokat. Nem kívánjuk itt részletesen ismertetni a szabvány [RFC1939] minden részletét, a program működésének megértéséhez azonban nélkülözhetet-lenek bizonyos ismeretek.

A POP3 szerverek szabványos kapuja a 110-es. Az erre való kapcso-lódást követően a szerver azonnal üdvözlő szöveget küld. A kliens most már elküldheti a felhasználónevet az USER nev paranccsal. A nev a fel-használó azonosítója azon a gépen, amellyel a kapcsolatot kiépítettük. A szerver minden parancs után állapotinformációt juttat vissza a klienshez, mely elárulja, hogy a kívánt parancsot sikerült-e végrehajtani. Szerencsés esetben +OK parameterek karakterláncot kapunk, egyébként +ERR parameterek a válasz. A parameterek – ha vannak egyáltalán – további részle-teket árulnak el a parancs végrehajtásáról, vagy éppen a kívánt eredményt tartalmazzák. Ezután el kell juttatni a szerverhez a felhasználó jelszavát a PASS jelszo utasítással. Ha a felhasználó azonosítása rendben lezajlott, elküldhetjük a STAT parancsot, ami a levélszekrény állapotát jellemző adatok küldésére utasítja a szervert. Szerencsés esetben a válasz a követke-ző alakú: +OK db meret, ahol db a postaládában lévő levelek számát, meret pedig azok összesített méretét jelöli, bájtban megadva. A kommunikációt QUIT parancs küldésével kell lezárni.

Page 196: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 196 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 196 ►

/* pop3.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <netdb.h> #define POP3PORT 110 #define MAX 512 #define OK "+OK " void hiba(int sorszam, int foglalat) { char* uzenet[] = { "", "Foglalat létesítése sikertelen.", "A szerver címének feloldása nem sikerült.", "Nem sikerült csatlakozni a szerverhez.", "POP3 hiba: hiba a bejelentkezésnél.", "POP3 hiba: nem sikerült üzenetet küldeni a " "szervernek.", "POP3 hiba: hibás felhasználónév vagy jelszó.", "POP3 hiba: nem sikerült lekérdezni a levelek " "számát.", "POP3 hiba: nem sikerült lekapcsolódni a szerverről." }; if(sorszam>5) { char buffer[MAX]; ir(foglalat, strcpy(buffer, "QUIT")); } if(sorszam>1) close(foglalat); fprintf(stderr, "%s\n", uzenet[sorszam]); exit(sorszam); } char* olvas(int foglalat, char* uzenet) { int olvasva; olvasva = read(foglalat, uzenet, MAX); if(olvasva < 1) return NULL; *(uzenet + olvasva) = '\0'; if(strstr(uzenet, OK) == uzenet) return uzenet + sizeof(OK) -1; else return NULL; } int ir(int foglalat, char* uzenet) { strcat(uzenet, "\r\n"); return write(foglalat, uzenet, strlen(uzenet)); }

Page 197: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 197 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 197 ►

int main(int argc, char* argv[]) { int foglalat; struct sockaddr_in cim; char buffer[MAX]; char* valasz; struct hostent* gep; if(argc!=4) { printf("Használat: pop3 gép felhasználónév " "jelszó\n"); return 0; } printf("Levelek számának lekérdezése POP3 " "kiszolgálón\n\n"); if((foglalat = socket(PF_INET, SOCK_STREAM, 0)) == -1) hiba(1, 0); cim.sin_family = AF_INET; if(!(gep = gethostbyname(argv[1]))) hiba(2, foglalat); cim.sin_addr = *((struct in_addr*)gep->h_addr); cim.sin_port = htons(POP3PORT); if(connect(foglalat, (struct sockaddr*)&cim, sizeof(cim)) == -1) hiba(3, foglalat); if(olvas(foglalat, buffer) == NULL) hiba(4, foglalat); if(!ir(foglalat, strcat(strcpy(buffer, "USER "), argv[2]))) hiba(5, foglalat); if(olvas(foglalat, buffer) == NULL) hiba(6, foglalat); if(!ir(foglalat, strcat(strcpy(buffer, "PASS "), argv[3]))) hiba(5, foglalat); if(olvas(foglalat, buffer) == NULL) hiba(6, foglalat); if(!ir(foglalat, strcpy(buffer, "STAT"))) hiba(5, foglalat); if((valasz = olvas(foglalat, buffer)) == NULL) hiba(7, foglalat); else printf("Levelek száma: %d\n", atoi(valasz)); if(!ir(foglalat, strcpy(buffer, "QUIT"))) hiba(5, foglalat); if(olvas(foglalat, buffer) == NULL) hiba(8, foglalat); close(foglalat); return 0; }

Page 198: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Folyamatközi kommunikáció

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 198 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 198 ►

8.5. Ellenőrző kérdések

• A folyamatok közötti kommunikáció lebonyolításának milyen módjait ismerte meg?

• Milyen előnyökkel, hátrányokkal és lehetőségekkel jár az egyes mód-szerek alkalmazása?

• Vázolja fel a szabványos folyamok átirányításának módját! • Milyen előnnyel és hátránnyal jár a popen/pclose függvények haszná-

lata a „hagyományos” csőhasználattal szemben? • Milyen problémát lehet kiküszöbölni, ha egyszerű csövek helyett neve-

sített csöveket használunk? • Milyen típusú kommunikációt lehet megvalósítani foglalatok segítsé-

gével? • Mik a névterek? Milyeneket ismer? Melyik mire szolgál? • Mik az összeköttetéses és az összeköttetés-mentes kapcsolat létesíté-

sének lépései? Mely függvényeket kell használni? • Mit jelent a „szerver foglalat”? • Milyen eszközeink vannak a hálózati és a számítógépünk által használt

bájtsorrend közötti átalakításra? • Milyen függvényeket ismert meg a névfeloldással kapcsolatban?

8.6. Megoldandó feladatok

Készítsen két programot: egy szervert és egy klienst! A szerver a parancs-sori paraméterként adott domén nevű gépnek küldje el a szintén paramé-terként adott elérési úton található fájlt! A kliens mentse le a hálózaton átküldött fájlt a parancssorban megadott könyvtárba! Készítsen programot, mely szövegsorokat olvas a parancssori paraméter-ként kapott azonosítójú fájlból, míg üres sor vagy EOF nem érkezik! A szoftvernek új szálat kell indítania, mely minden egyes sorról megállapítja, hogy abban hány ékezetes magyar betű, ill. számjegy található. Az ered-ményt visszaadja a fő szálnak, mely a fájlkezelésen, az eredmények összesí-tésén kívül az angol ábécé jeleinek számlálásával foglalkozik. Az eredmé-nyeket az alábbi formában kell közölni:

Kategória Darab Számjegy 12 Angol betűk 78 Ékezetes betűk 21

Page 199: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Irodalomjegyzék

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 199 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 199 ►

Irodalomjegyzék

[1] Bartók Nagy János, Laufer Judit: UNIX felhasználói ismeretek. Budapest, 1994, Openinfo Kiadó.

[2] Bauer Péter: PROGRAMOZÁS I–II. (C PROGRAMNYELV). Győr, 2005, UNIVERSITAS–GYŐR Kht.

[3] The GNU C Library, http://oprendszer.elte.hu/glibc-doc/ [4] Pere László: UNIX–GNU/Linux Programozás C nyelven. Pécs, 2003,

Kiskapu Kft. [5] Linux dokumentációk magyarul, http://szabilinux.hu/index.html [6] Prog.hu, http://www.prog.hu/cikkek/222/Egy+korszerubb

+szerver-kliens+megoldas.html [7] Kallós Gábor: Párhuzamos programozás. http://rs1.szif.hu/~kallos/pp/ [8] Advanced Linux Programming, http://www.advancedlinuxprogramming.com/

Page 200: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Tárgymutató

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 200 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 200 ►

Tárgymutató

” ”C” hely, 8 A,Á accept, 185 access, 106 aktuális könyvtár, 79 ARG_MAX, 159 B B/K műveleti mód jelzők, 85 bájt folyam, 54 Bájt író, 55 Bájt olvasó, 55 Bájt orientált, 55 bejelentkezési könyvtár, 138 bind, 181 blkcnt_t típus, 98 BMP, 20 btowc, 23 C canonicalize_file_name, 115 chdir, 139 chmod, 107 chown, 123 clock, 69 clock_t típus, 69 CLOCKS_PER_SEC, 70 close, 88 closedir, 144 connect, 181 ctime, 62 Cs Csoport azonosítója, 102 csövek, 168

D daemon, 155 démon, 155 dev_t típus, 98 difftime, 62 DIR típus, 143 dirent struktúra, 142 dup, 98 dup2, 171 E,É egész szám–karakterlánc, 50 egybájtos karakter, 18 egyezményes idő, 60 elemzési állapot, 19 Elfoglalt blokkok száma, 103 EMFILE, 170 ENFILE, 170 environ, 157 errno, 170 Érvénytelenedési (cancellation)

pont, 82 Eszközazonosító, 102 execl, 158 execle, 158 execlp, 159 execv, 158 execve, 158 execvp, 158 F Fáj elérési mód jelzők, 84 fájl idő adatai, 103 fájl létrehozási maszk, 110 fájlleíró, 80 Fájlméret, 102 fájlnév, 79

Page 201: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Tárgymutató

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 201 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 201 ►

fájlnév komponens, 79 fájlnév transzlációs jelzők, 86 fájlrendszer, 79 Fájltípus és elérési jogosultság,

99 fchdir, 139 fchmod, 109 fchown, 123 fcntl, 160 FD_CLOEXEC, 160, 170 FD_CLR, 183 FD_ISSET, 183 fd_set, 182 FD_SET, 182 FD_SETSIZE, 182 FD_ZERO, 182 fdopen, 161 FIFO, 168 fileno, 80 first-in first-out special file, 168 flock struktúra, 133 foglalat, 168, 177 folyam állapot, 55 folyam állapot átmenet, 55 folyamat, 151 folyamat befejeződése, 162 folyamat megállása, 162 folyamat újraindítása, 162 folyamatazonosító, 153 folyamatközi kommunikáció, 168 fork, 154 formátumkódok, 67 fstat, 105 ftruncate, 126 G getcwd, 138 gethostbyaddr, 193 gethostbyname, 193

getpid, 154 getppid, 154 getumaszk, 111 gid_t típus, 98 GMT, 60 gmtime, 63 GNU C, 78 GNU LIBC, 78 H helyi idő, 60 helyi kategóriák, 8 htonl, 195 htons, 194 HUGE_VAL, 49 I,Í Ideális blokkméret, 103 idő karakterlánc, 60 idő struktúra, 60 inet_aton, 192 inet_ntoa, 193 ino_t típus, 99 inode szám, 102 inode tábla, 79 Interprocess communication,

168 IPC, 168 írás zár, 133 iswalnum, 32 iswalpha, 32 iswcntrl, 32 iswdigit, 32 iswgraph, 32 iswlower, 32 iswprint, 32 iswpunct, 32 iswspace, 32 iswupper, 32

Page 202: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Tárgymutató

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 202 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 202 ►

iswxdigit, 32 J jel(zés), 73 K kapu, 194 kezdeti konverziós állapot, 18 kezdeti váltó állapot, 19 kibővített karakterkészlet, 18 konverziós állapot, 18 könyvtár fájl, 79 Kötetlen (unbound), 55 közvetett hivatkozás, 113 közvetlen hivatkozás, 112 Közvetlen hivatkozások száma,

102 L leképezett memória, 168 link, 112 Linux, 78 listen, 185 localeconv, 15 localtime, 63 longjmp, 72 lseek, 95 lstat, 105 M mapped memory, 168 MB_CUR_MAX, 22 MB_LEN_MAX, 22 mblen, 23 mbrlen, 23 mbrtowc, 24 mbsinit, 25 mbsrtowcs, 25 mbstate_t, 22 mbstowcs, 26 mbtowc, 26

memchr, 38 memcmp, 39 memcpy, 40 memmove, 41 memset, 44 mkdir, 140 mkfifo, 175 mktime, 64 mode_t típus, 83 munkakönyvtár, 79 N named pipe, 168 nem helyi vezérlésátadás, 71 nevesített csövek, 168, 174 nlink_t típus, 99 ntohl, 195 ntohs, 194 Ny nyitás idejű tevékenység jelző, 86 O,Ó O_NONBLOCK, 170 off_t típus, 91 olvasási zár, 133 open, 83 OPEN_MAX, 170 opendir, 143 osztott memória, 168 P pclose, 173 PID, 153 pid_t, 153 pipe, 168, 170 popen, 173 POSIX, 78 Pozícionáló, 55 pread, 91 pwrite, 94

Page 203: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Tárgymutató

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 203 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 203 ►

R raise, 74 read, 89 readdir, 144 readlink, 114 rekordzárolás, 132 remove, 119 rename, 118 rewinddir, 144 ritka (sparse) fájl, 95 RLIMIT_NOFILE, 83 rmdir, 141 S SA_RESTART, 164 seekdir, 145 select, 183 setjmp, 71 setlocale, 8 shared memory, 168 socket, 168, 179 ssize_t típus, 89 st_atime, 170 st_ctime, 170 st_mtime, 170 stat, 103 stat struktúra, 99 strcat, 38 strchr, 38 strcmp, 38 strcoll, 39 strcpy, 40 strcspn, 41 strftime, 66 strlen, 42 strncat, 38 strncmp, 39 strncpy, 40 strpbrk, 42

strrchr, 43 strspn, 44 strstr, 44 strtod, 49 strtol, 50 strtoul, 50 struct lconv, 13 strxfrm, 47 SUN_LEN, 186 symlink, 114 system, 151 Sz széles folyam, 54 Széles író, 55 széles karakter, 20 széles karakter konstans, 21 széles karakterlánc állandó, 21 Széles olvasó, 55 Széles orientált, 55 szerver foglalat, 184 Szokásos fájlnév hibák, 81 szuperblokk, 79 T telldir, 145 TEMP_FAILURE_RETRY,

93 time, 61 time_t, 62 timeval, 183 többájtos karakter, 18 többfázisú program, 157 truncate, 126 Tulajdonos azonosítója, 102 U,Ú UCS, 20 uid_t típus, 99 umask, 110

Page 204: Bauer Péter – Hatwágner F. Miklósszimki.sulinet.hu/dokumentumok/Tananyag/informatika/programozás... · ismeretek tanítása a Web–programozás irányába történő előkészítést

Programozás II. Tárgymutató

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 204 ►

A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 204 ►

Unicode, 20 univerzális karakterkészlet, 20 unlink, 119 UTC, 60 UTF–8, 22 utimbuf struktúra, 129 utime, 129 V valós (lebegőpontos) szám–

karakterlánc, 49 váltó állapot, 19 vfork, 155 W wait, 164 wait unió, 166 WAIT_ANY, 163 WAIT_MYGRP, 163 wait3, 164 wait4, 164 waitpid, 162 wchar_t, 21 WCONTINUED, 163 WCOREDUMP, 166 wcrtomb, 26 wctob, 29 wctomb, 29 wcscat, 38 wcschr, 38 wcscmp, 38 wcscoll, 40 wcscpy, 40 wcscspn, 41 wcsftime, 66 wcslen, 42

wcsncat, 38 wcsncmp, 39 wcsncpy, 40 wcspbrk, 42 wcsrchr, 43 wcsrtombs, 27 wcsspn, 44 wcsstr, 44 wcstod, 49 wcstol, 50 wcstombs, 28 wcstoul, 50 wcsxfrm, 47 WEOF, 23 WEXITSTATUS, 166 WIFCONTINUED, 166 WIFEXITED, 165 WIFSIGNALED, 166 WIFSTOPPED, 166 wint_t, 23 wmemchr, 38 wmemcmp, 39 wmemcpy, 41 wmemmove, 41 wmemset, 44 WNOHANG, 163 write, 91 WSTOPSIG, 166 WTERMSIG, 166 WUNTRACED, 163 Z zárolás, 132 zombi, 164