Tartalmi kivonat
Bauer Péter – Hatwágner F. Miklós PROGRAMOZÁS II. Készült a HEFOP 3.31-P-2004-09-0102/10 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 Programozás II. A dokumentum használata 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 megszokott 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 tartalomjegyzékre, valamint a tárgymutatóra. A ◄ és a ► nyilakkal az előző és a következő 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 megtekinté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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 3 ► Programozás II. A dokumentum használata | Tartalomjegyzék |
Tárgymutató Tartalomjegyzék 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élda16 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 mktime64 4.9 asctime65 4.10
strftime, wcsftime 66 4.11 clock69 4.12 Ellenőrző kérdések: 70 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 4 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Tartalomjegyzék 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ó feladatok77 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ás89 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ása151 7.2 Folyamatok létrehozásának módjai 153 7.3 Folyamatok azonosítása 154 7.4 Folyamatok létrehozása154 7.5 Fájlok futtatása 157 7.6 Folyamatok befejeződése162 7.7 Állapotinformációk
értelmezése 165 7.8 Ellenőrző kérdések 166 7.9 Megoldandó feladatok167 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 Foglalatok177 8.5 Ellenőrző kérdések 198 8.6 Megoldandó feladatok198 Irodalomjegyzék. 199 Tárgymutató . 200 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 5 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Bevezetés 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 megalapozni 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ő felét (7 hét) Ez a jegyzet a szemeszter maradék 8 hetének tananyagát taglalja, 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 eddigiekből Esetleg: merre találhatók további részletek a kérdéses témával kapcsolatban Lexikális ismeretek taglalása. Valamely folyamat pontosabb részletei Egy fogalom precízebb definíciója 0 Valamilyen aránylag könnyedén elkövethető, de nehezen lokalizálható hiba. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 6 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Bevezetés Vissza ◄ 7 ► ◄ 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Helyi információ 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 LOCALEH fejfájlban 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 kezelik. Például: karakterláncok összehasonlítását a kiválasztott helynek megfelelő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áskö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 mondja 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.11 LC ALL Minden hely specifikus viselkedésre (minden kategóriára) hat. 2.12 LC COLLATE Olyan helyeken, ahol a karakterkészlet kódjainak szigorú numerikus sorrendje 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 8 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Helyi információ Vissza ◄ 9 ► nem szabványos stricoll–lal, wcsicoll–lal és az első n karaktert ugyanilyen 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 numerikus 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.13 LC CTYPE Az isdigit, az isxdigit függvények kivételével a karakterosztályozó, valamint 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 hoszszát megállapító rutinok, • a többájtos karaktert/karaktersorozatot széles karakterré/karaktersorozattá alakító és a megfordított konverziót végző függvények, • az adott karaktert kisbetűssé konvertáló tolower, towlower és nagybetűssé konvertáló toupper, towupper. Az itt említett rutinokkal is a következő nagy fejezetben foglalkozunk! 2.14 LC MONETARY Pénzügyi értékek formázását befolyásolja. Lásd a localeconv függvényt később! 2.15 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ó karaktert, a számrendszer alap felismerést stb. rögzíti A sima vagy széles karakterláncokat belső ábrázolás formákba konvertáló rutinokra (atof, atoi, atol, strtod, wcstod, strtol, wcstol, strtoul és wcstoul) van hatással. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 9 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Helyi információ Vissza ◄ 10 ► A decimális pont karaktert, a számrendszer alap karaktert és felismerést stb. befolyásolja a formázott kimenetre (a printf család), valamint a formázott bemenetre (a scanf család). 2.16 LC TIME A dátum és idő értékeket formázó strftime és wcsftime függvények viselkedé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 paramétert adnak meg, akkor a függvény a specifikált hellyel és kategóriával
kapcsolatos, statikus élettartamú pufferben elhelyezett karakterláncra mutató 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áltozatlanok. 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 paraméterrel előírt kategóriára. Az eredeti környezet tulajdonképpen az operációs rendszertől nyert, alapértelmezés szerinti hely A locale mutathat a setlocale korábbi hívásából visszakapott karakterláncra, és természetesen az implementáció definiálta más karakterláncokra. 0 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 helyreá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: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 10 ► Programozás II. A
dokumentum használata | Tartalomjegyzék | Tárgymutató Helyi információ 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értelmezett 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.17 Microsoft Visual C++ Windows–os környezetben A locale paraméter a hely nevét specifikáló karakterláncra mutat. A Microsoft 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), minden 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 ennél későbbi verziókban érvényes, viszont bárhol használható a hosszú A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 11 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Helyi információ 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ővebben foglalkozunk! A következő példák mind az LC ALL kategóriára vonatkoznak. 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 funkcioná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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 12 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Helyi információ Vissza ◄ 13 ► 0 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 automatikusan beállított ”C” helyen az összes char * típusú tag üres karakterlá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 karakter 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: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 13 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató • • • • • • • Helyi információ 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 korlá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 csoportot 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 szimbó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 csoportok 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 csoportokat 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. A
dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 14 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Helyi információ 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 decimális ponttól jobbra formázott pénzügyi mennyiségekben. • char p cs precedes: (LC MONETARY) (1) 1, ha a pénznem szimbó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 szimbó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 mennyisé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 negatí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, A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 15 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Helyi információ Vissza ◄ 16 ► vagy LC ALL, LC NUMERIC vagy LC MONETARY kategóriás setlocale hívás felülírja. 0 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 érvé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ólumot 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ó: "); /* A helyi infó lekérdezése: / printf("A program indulásakor: %s ", setlocale(LC ALL, NULL)); printf("atof("3,14") 4.2 pontossággal: %42f ", atof("3,14")); /* Alapértelmezett hely beállítása: / strcpy(puff, setlocale(LC ALL, "")); printf("Rendszer alapértelmezett hely: %s ", puff); printf("atof("3,14") 4.2 pontossággal: %42f ", atof("3,14")); /* Tizedespont és pénznem karakter: / plconv = localeconv(); printf("A nem pénzügyi decimális pont: %s ", plconv->decimal point); printf("A pénznem szimbólum: %s ", plconv->currency symbol); /* A hely visszaállítása C környezetre: / printf("Minimális ANSI konform C hely: %s ", setlocale(LC ALL, "C")); printf("atof("3,14") 4.2
pontossággal: %42f ", atof("3,14")); } A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 16 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Helyi információ 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ékeket 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! A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 17 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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üggvé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 karaktereket. Vannak aztán
Európában olyan karakterkészletek, melyek az ASCII 0x00 – 0x7F tartományát megtartva a 0x80 – 0xFF intervallumra is meghatá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 karakterké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 tekintettel, 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 kibő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 eseté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- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 18 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek Vissza ◄ 19 ► jának bájt értéke a [0x00, 0x7F] ASCII kódtartományba esik, az EUC kielé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 egybá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 ( ). 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 magá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 állapot 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árombá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, karakter konstans, karakterlánc konstans, vagy #include
direktívabeli fájlazonosí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! A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 19 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek Vissza ◄ 20 ► 3.21 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ájtokat vagy karaktereket karakterláncként kezelő MBCS függvények előjeltelen char
mutatóként reprezentált többájtos karakteres karakterláncokra számítanak paraméterként. 0 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ósolhatatlan eredményre is vezethet A többájtos karakter egy bájtját ezért célszerű 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öbbnyelvű 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 azonos 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ábbiakban csak az Unicode–dal foglalkozunk! A modern számítástechnikában világszerte használt bármilyen karakter, beleértve a technikai szimbólumokat és a speciális publikációs karaktereket, reprezentálható az Unicode specifikációnak megfelelően széles karakterké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 nemzetkö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 Latin–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: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 20 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek Vissza ◄ 21 ► 3.31 wchar t típus Egyetlen széles karakter ebben a típusban ábrázolandó. Az adattípust definiálja az STDDEFH 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 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: {LH, Le, Ll, Ll, Lo, 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ódolá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 dokumentumban 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.32 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ájtsorozat 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í- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 21 ► Programozás II. Többájtos és széles karakterek 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 átalakí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 STDLIBH 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.41 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.42 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}; A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 22 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek
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.43 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.44 WEOF makró A makró wint t típusú visszatérési érték a széles folyam végének, ill. valamilyen hibafeltétel jelzésére Például: #define WEOF (wint t)(0xFFFF) 3.45 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 karakterké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.46 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 kapunk 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.47 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 karakterré 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); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 23 ► Programozás II. A dokumentum használata | Tartalomjegyzék |
Tárgymutató Többájtos és széles karakterek Vissza ◄ 24 ► hívással, ahol az internal az mbrlen 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 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 eredmé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.48 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 karakterlá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 kevesebb bájt érvényes többájtos karaktert alkot, akkor az mbrtowc 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. 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: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 24 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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 eredmé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’ ’ 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.49 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.410 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ételten 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 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. 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 konverzió eredménye nem áll rendelkezésre. A rutin visszatérési értéke: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 25 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek
Vissza ◄ 26 ► • –1: Ha egy mbrtowc hívás ezzel tért vissza a következő többájtos karakter 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.411 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 karakteré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 ( ) találkozik még a hossz kimerülése előtt, a –t L –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.412 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 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.413 wcrtomb size t wcrtomb(char *s, wchar t wc, mbstate t ps); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 26 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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 állapot 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 állapotot 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’ ’, ps)– vel tér vissza, ahol a puff valamilyen belső puffer a függvényben. A függvé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ározatlanul 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 legeneráltságát jelzi. 3.414 wcsrtombs size t wcsrtombs(char *s, const wchar t forras, size t hossz,mbstate t *ps); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 27 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek Vissza ◄ 28 ► A
függvény a *forras címen kezdődő széles karakterláncot az aktuális helynek 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 állapotba hoz, és a benne tárolt értéket más könyvtári függvény nem is változtathatja 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 karakterkó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.415 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 karakterlá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. Általában nem lehet pontosan tudni, hogy hány bájtra lesz majd szükség a A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 28 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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 ) 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 a konverzió során következett be. Ha a forras és az s átfedik egymást, a wcstombs viselkedése kiszámíthatatlan 3.416 wctob int wctob(wint t c); A rutin megvizsgálja, hogy a c ábrázolható–e kezdeti váltó állapotban kezdő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.417 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 aktuá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 sohasem nagyobb MB CUR MAX–nál Ha a wc széles karakteres null karakter (L ), 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 konverzió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 MAXhossz); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 29 ► Programozás II. Többájtos és széles karakterek 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 ", MB LEN MAX, MB CUR MAX); printf(""%ls" konverziója szélesről többájtos " "karakterlánccá: ", WLANC); printf(" A konvertált többájtos
karakterek bájtszáma:" " %4u ", h=wcstombs(pmbhaho, (const wchar t *) pwchaho, MB CUR MAX*hossz)); printf(" A többájtos karakterlánc bájtjainak hex. " "értéke: "); for(i=0; i<=h; ++i) printf("%.2X ", (unsigned char) pmbhaho[i]); printf(" A negyedik többájtos karakter bájt száma:" " %d ", mblen(pmbhaho+3, MB CUR MAX)); printf(" Konverzió vissza többájtosról széles " "karakterlánccá: " ); pwchaho = (wchar t *)realloc(pwchaho, sizeof(wchar t)*h); printf(" A konvertált karakterek száma: %u ", h=mbstowcs(pwchaho, (const char *)pmbhaho, MB CUR MAX*hossz)); printf(" A széles karakterek hex. értéke: "); for(i=0; i<=h; ++i) printf("%.*X ", 2sizeof(wchar t), pwchaho[i]); printf(" "); 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 30 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek Vissza ◄ 31 ► 3.5 Széles karakterek osztályozása A [2] jegyzet „Karaktervizsgáló függvények (makrók)” fejezete tárgyalja 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 rendszerint 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 paraméter típus int, s prototípusaik az isalpha példáján bemutatva a következők: #include <ctype.h> int isalpha(int c); 0 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 értékű SBCS vagy MBCS egybájtos karakter negatív A paraméterként átadott char értékét a fordító signed int–té vagy signed long–gá konvertá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 viszont függetlenek a helytől 3.51 is és isw függvények Rutin Karaktervizsgálati feltétel A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 31 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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 valamelyike 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, melyekre 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 karakterekre. A szabványos (széles) fehér karakterek: a szóköz (L ), a lapdobás (Lf), a soremelés (L ), a kocsi vissza (L ), a horizontális (L ), és a vertikális tab (Lv). • 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); A
dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 32 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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önben 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 szolgáltathat a széles karakteres szóközön (L’ ’) kívül további fehér karakterekre. 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üggvények némelyike nem zérust szolgáltat. Bármely karakterkészlet tartalmazhat olyan további karaktereket, melyekre nem zérussal tér vissza az: • iswpunct (feltéve, hogy e karakterek az
iswalnum–ot zérus visszaadá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 viszszaadá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 formailag 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ásodik paraméter címen. /* LONGE.C példa */ #include <stdio.h> #include <ctype.h> #include <string.h> A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 33 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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(" A longe() függvényt kipróbáló program: " "Befejezés: üres sorral, vagy EOF-al! "); /* 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 ", l); else if(!strcmp(s," ")) break; else printf("A
szám rossz! "); 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! A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 34 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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; A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 35 ► Programozás II. Többájtos és széles karakterek 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(" A wlonge() függvényt kipróbáló program: " "Befejezés: üres sorral, vagy EOF-al! "); /* 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 ", l); else if(!wcscmp(s, L" ")) break; else printf("A szám rossz! "); } return 0; } A TOUPP.C példában nagybetűssé alakítunk egy minden ékezetes kisbetű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. Meglátjuk, mire jutunk a " "szöveg nagybetűs átalakításával. "; char *ptr = string-sizeof(char); /*SetConsoleOutputCP(1250);/ printf("Ez lesz belőle a "C" helyen " "átalakítva: "); while(*++ptr) putchar(toupper(ptr)); setlocale(LC ALL, ""); ptr = string-sizeof(char); printf("Ugyanez az aktuális helyen ("") " "átalakítva: "); 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 36 ► Programozás II. Többájtos és széles karakterek 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 mondottakra, azaz: 0 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 értékű SBCS vagy MBCS egybájtos karakter negatív A paraméterként átadott char értékét a fordító signed int–té vagy signed long–gá konvertá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 mindegyike 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ö- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 37 ► Programozás
II. Többájtos és széles karakterek 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 karakteres wcs és wmem kezdetű függvények prototípusai a WCHARH fejfájlban találhatók 0 Ha a forrás és a célterület átfedi egymást, a teljes forrást garantáltan csak a (w)memmove másolja át megfelelően. 3.61 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 egyesí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.62 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.63 strcmp, wcscmp, strncmp, wcsncmp, memcmp, wmemcmp int strcmp(const char *string1, const char string2); int wcscmp(const wchar t *string1, const
wchar t *string2); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 38 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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 karakterlá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 összehasonlítást nem befolyásolja a helyi információ, míg a másik függvény összehasonlí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 ugyanaz, 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 karaktersorrend 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 akkor 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övidebb karakterlánc a
kisebb. A mem függvények is összehasonlítják a string1 és string2 pufferek legfeljebb 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.64 strcoll, wcscoll int strcoll(const char *string1, const char string2); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 39 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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 aktuá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 összehasonlítás szempontjából is. A megfelelő cmp függvény csak a karakterláncok egyezőségének vizsgálatára való Bizonyos kódlapokra és a megfelelő karakterkészletekre a karakterké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ódlapokban azonban például az a (0x61) karakter megelőzi az ä–t (0xE4) a karakterké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ódszerként ajánlható az eredeti karakterláncok strxfrm–mel konvertált változatainak összehasonlítása strcmp–vel 0 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.65 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); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 40 ► Programozás II. A dokumentum használata | Tartalomjegyzék |
Tárgymutató Többájtos és széles karakterek 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ímmel. 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átlan 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.66 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őszegmensé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 valamelyiké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"; A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 41 ► Programozás II. Többájtos és széles karakterek 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. ", strcspn(string, strCharSet)); } futásának eredménye: ”xyzabc” karakterláncban az első a, b, vagy c indexe 3. 3.67 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.68 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övegben A megtalálási helyektől kezdve visszaírjuk a szöveg részeket besorszá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. "; char *eredm = string; int i=1; /* Számok keresése: / while(eredm = strpbrk(eredm, "0123456789")) printf("%2d: %s ", i++, eredm++); } Az eredmény: 1: 3 hapsi, s 2 fiu 4 nyulat ettek meg. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 42 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek Vissza ◄ 43 ► 2: 2 fiu 4 nyulat ettek meg. 3: 4 nyulat ettek meg. 3.69 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: %s ", c, string); for(i=1; i<5; ++i)printf("%10d", i); printf(" "); for(i=0; i<50; ++i)printf("%1d", i%10); /* Keresés előre: / if(pcel = strchr(string, c)) printf(" Eredmény: Az első %c a(z) %d indexű " "helyen található. ", c, pcel - string); else printf(" Eredmény: Nincs %c a " "karakterláncban. ", c); /* Keresés hátra: / if(pcel = strrchr(string, c)) printf("Eredmény: Az utolsó %c a(z) %d. indexű " "helyen található. ", c, pcel - string); else printf("Eredmény: Nincs %c a karakterláncban. ", 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 43 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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.610 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.611 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.612 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.", A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 44 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek Vissza ◄ 45 ► string2[] = "lusta", *p; printf("A(z) "%s" karakterlánc keresése a következő " "karakterláncban: %s ", string2, string1); for(i=1; i<5; ++i)printf("%10d", i); printf(" "); for(i=0; i<50; ++i)printf("%1d", i%10); if(p = strstr(string1, string2)) printf(" A(z) "%s" megtalálható a(z) %d indexű " "helytől kezdve. ", string2, p - string1); else printf(" A(z) "%s" nincs a karakterláncban. ", 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.613 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 karakterlá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 karakterrel zárja. Az strToken maradék része további szimbólumokra bontható ú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 visszaadott 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. 0 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- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 45 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek Vissza ◄ 46 ► tán különböző karakterláncokra hívni! Tartózkodjunk attól is, hogy a
rutint olyan ciklusból hívjuk, ahol más olyan függvény hívása is bekövetkezhet, mely ugyanezt a rutint idézi meg! Ha a wcstok hívásban az strToken nem NULL mutató, akkor a függvény megkezdi a széles karakterlánc vizsgálatát. Különben annak a „maradé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 mutató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 karakterlá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üggvé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[] = " Szimbólumok karakterlánca, ,, " "és még néhány további szimbike."; char elv[] = " ,.
", *szimb; printf("%s A fenti karakterláncban a szimbólumok a " "következők: ", string); /* Míg vannak szimbólumok a karakterláncban, / szimb = strtok(string, elv); while(szimb){ /* addig megjelentetjük őket, és / A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 46 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek Vissza ◄ 47 ► printf(" %s ", 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.614 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 konvertá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 átkonvertá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 tartalma 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 konvertált karakterláncra meghívva ugyanazt az eredményt kapjuk, mint az eredeti karakterláncokra az strcoll–t (wcscoll–t) alkalmazva. A “C” helyen a karakterkészlet (ASCII) karaktereinek sorrendje ugyanaz, 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 kategó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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 47 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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: "); if((i=strcmp(egy, ket))<0) printf(""%s" kisebb, mint "%s" ", egy, ket); else if(i>0) printf(""%s" nagyobb, mint "%s" ", egy, ket); else printf(""%s" ugyanaz, mint "%s" ", egy, ket); printf("Karakterláncok vizsgálata a "" helyen " "strxfrm konverzióval: "); setlocale(LC COLLATE, ""); strxfrm(kegy, egy, 10); strxfrm(kket, ket, 10); if((i=strcmp(kegy, kket))<0) printf(""%s" kisebb, mint "%s" ", egy, ket); else if(i>0) printf(""%s" nagyobb, mint "%s" ", egy, ket); else printf(""%s" ugyanaz, mint "%s" ", egy, ket); printf("Karakterláncok vizsgálata a
"" helyen " "strcoll-lal: "); if((i=strcoll(egy, ket))<0) printf(""%s" kisebb, mint "%s" ", egy, ket); else if(i>0) printf(""%s" nagyobb, mint "%s" ", egy, ket); else printf(""%s" ugyanaz, mint "%s" ", egy, ket); } Az eredmény a következő: Karakterláncok vizsgálata a C helyen: "bárka" nagyobb, mint "birka" A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 48 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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 WCHARH fejfájlban találhatók Összefoglaló példa a fejezet végén található! 3.71 HUGE VAL makró A végtelen jelzésére a MATH.H fejfájlban definiált szimbolikus állandó 3.72 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ámjegy, akkor utána legalább egynek lennie kell, ill megfordítva A decimális A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 49 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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. 0 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.73 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 csoporttól Zérust akkor is kapunk, ha konverzió sem történik Túl vagy
alulcsordulá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 karakterné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 karakterlá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 elhagynak 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ásodik 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- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 50 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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áltatunk egy karakterláncot Az strtol long int–té alakít egy másik karakterlá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 ", string); printf(" strtod = %.7f ", x); printf("A vizsgálat leállt: %s ", leallitas); string = "-1101012493Ez is leállítja."; l = strtol(string, &leallitas, 10); printf("string = %s ", string); printf(" strtol = %10ld (alap %d) ", l, 10);
printf("A vizsgálat leállt: %s ", leallitas); printf("string = %s ", 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) ", ul, alap); printf("A vizsgálat leállt: %s ", leallitas); } string = "1101012493"; printf(" string = %s ", 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) ", ul, alap); printf("A vizsgálat leállt: %s ", leallitas); } } A kimenet a következő: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 51 ► Programozás II. Többájtos és széles karakterek 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, KIMENET” című nagy fejezetében tárgyaltakra! Az ott említett „Bemeneti műveletek”–nek megvan a széles karakteres párja, melyek a folyam következő széles karaktereit olvassák formázatlanul, 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); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 52 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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 szabványos STDIO.H, ill széles karakteres párjaikhoz a WCHARH 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, A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 53 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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 tekintik. 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 kimenetkor, sem bemenetkor. Pontosítsunk kicsikét! Vannak bájt és széles folyamok. 3.81 Bájt és széles
folyamok A bájt folyam bájtok sorozatának tekinti a fájlt. A programon belül a folyam 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 orientálttá válásakor határozza meg konverziós szabályait, melyek aztán megmaradnak akkor is, ha az LC TYPE kategória később változik A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 54 ► Programozás II. Többájtos és széles karakterek 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ízható 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.82 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 állapota. • 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 55 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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 pozitív, akkor viszont a széles orientáltság volt a kívánalom A függvény viszszaté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. 0 Az fwide azonban semmi esetre sem fogja változtatja a folyam orientációját, ha egyszer már beállították. 0 Írást nem követhet olvasás közbenső pozícionálás nélkül. Az olvasá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 tartalmazza 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! ", FAJL); return(1); } A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 56 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek Vissza ◄ 57 ► /* Orientáció legyen széles: fwide(outin, 1); / for(i=0; i<=MAX; ++i) fwprintf(outin, L"%d ", 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! ", FAJL); return(1); } /* Olvasás bájtonként:
fwide(outin, -1); / while((i=fgetc(outin))!=EOF) printf("%02X", i); printf(" "); 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üggvé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ásró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? A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 57 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek Vissza ◄ 58 ► • Mely rutinokkal lehet (széles) karakterláncok hosszát megállapítani, melyekkel egyesíteni, másolni és összehasonlítani őket? Az összehasonlítást hogy végezhetjük hely–specifikusan, s hogyan lexikografikusan? • Mely függvények alkalmasak karakterláncokban karakterek és karakterlá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 keresni 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ársaiktól? • Milyen részekből állhat az strtod (wcstod) konvertálandó lebegőpontos 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 paramétere, és milyen következményekkel jár ez? • Mely magas szintű B/K függvénynek van meg a széles karakteres párja, 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á- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 58 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Többájtos és széles karakterek 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ó programban 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ó programot), 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űssé (lwr), ill. nagybetűssé (upr) alakítják, s szolgáltatják az eredmény karakterlánc címét Készítsen strset és wcsset függvényeket (és egy őket kipróbáló programot), 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 karakterlá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! A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 59 ► Programozás II. Az idő kezelése 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 Time 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. time double difftime ctime time t char * mktime gmtime localtime struct tm* asctime strftime 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 60 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Az idő kezelése 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 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 0 A rutinok egyikének hívása megváltoztathatja a statikus élettartamú 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 statikus 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 eltelt másodpercek számában, time t típusban szolgáltatja, ha a cél környezet meg tudja határozni ezt az értéket, máskülönben –1–et kapunk A szoká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 elhelyezi a rutin, ha a paraméter nem NULL mutató NULL mutató aktuális paraméter esetén nincs letárolás. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 61 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Az idő kezelése 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. 0 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 "); time(&kezdet); for(i = 0l; i < 1000000000l; i++) ered = 3.63 * 5.27; time(&veg); printf("%6.0f másodpercet igényel ", 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 62 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Az idő kezelése 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. 0 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ő karakterlá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 érté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ő. 0A 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
ÉÉÉÉ.HHNN ÓÓ: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 ", tmptr->tm year+1900, tmptr->tm mon+1, A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 63 ► Programozás II. Az idő kezelése 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 viszszaadott érték. Az aktuális paraméter struktúra tm wday, s tm yday tagjainak 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 szabvá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. 0A gmtime és a localtime rutinok egyazon statikusan allokált puffert 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ó! A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 64 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Az idő kezelése 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 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. 0 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 jeleztet 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 normalizá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=maj40tm yday=0; maj40.tm isdst=1; /*Nyári időszámítás van hatályban.*/ A
dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 65 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Az idő kezelése Vissza ◄ 66 ► printf("2037. május 40 31 óra 82 perc: %s ", asctime(&maj40)); printf("A hét napja: %d Az év napja: %d ", maj40.tm wday, maj40tm yday); setlocale(LC ALL, ""); printf("Az aktuális hely beállítása az idő " "karakterláncon mit sem változtat: "); printf("2037. május 40 31 óra 82 perc: %s ", asctime(&maj40)); printf("Az mktime megadja a time t időt, és " "normalizálja az idő struktúrát: "); printf("time t: %ld ", mktime(&maj40)); printf("2037. május 40 31 óra 82 perc: %s ", asctime(&maj40)); printf("A hét napja: %d Az év napja: %d ", maj40.tm wday, maj40tm 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, A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 66 ► Programozás II. A dokumentum
használata | Tartalomjegyzék | Tárgymutató Az idő kezelése Vissza ◄ 67 ► ha az eredmény nem haladja meg a maxmeretet. Máskülönben zérust kapunk, é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/PM 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ételezve (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ételezve (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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 67 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Az idő kezelése 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 értelmezé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: %ld ", tt); printf("A UNIX idő és dátum: %s", ctime(&tt)); /* Az UTC kijelzése. */ gmt = gmtime(&tt); printf("Az egyezményes idő: %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 A dokumentum
használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 68 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Az idő kezelése Vissza ◄ 69 ► printf formátumspecifikációval levágjuk a hátsó karaktereket. */ printf("12-órás idő: %.8s %s ", asctime(ma) + 11, dedu ); /* Karácsony déli idejének megállapítása: / if(mktime(&kcsony) != (time t)-1) printf("Karácsony %s", asctime(&kcsony)); /* Mi van karácsonytól 10 napra. */ kcsony.tm mday = kcsonytm mday + 10; if((tt = mktime(&kcsony)) != (time t)-1) printf("Karácsony + 10 nap: %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. ", ma); printf(idpuff); setlocale(LC TIME, "");
strftime(idpuff, 128, "Ma %Y. %B %#d %A van a(z) %Z " "időzónában. ", 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.111 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 69 ► Programozás II. A dokumentum használata | Tartalomjegyzék |
Tárgymutató Az idő kezelése Vissza ◄ 70 ► 4.112 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áltatja. 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. 0 A hívó folyamat indulása óta eltelt idő nem szükségképpen egyenlő 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! A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 70 ► 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 ► 4.13 Megoldandó feladatok: Készítsen programot, mely megjelenteti az strftime összes
formátumkódjá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 ismerkedü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ásoknál, vagy a később
ismertetendő jel(zés)kezelőknél hasznos lehet, ha helyreá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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 71 ► 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 setjmp makrós kifejezéseket aztán while, for, do, if és switch utasí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 viszsza a visszatérési érték Ha a vissza zérus, akkor viszont 1 (Nem jó programozói magatartás a vissza értéket zérusnak választani) 0 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 élettartamú 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); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 72 ► 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 ►
if((i = setjmp(cimke)) > 0) i++; if(i < MAX) elso(i); else { putchar( ); 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 alkalmazá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ípusú 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. Harmadik 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 program futása ott fog folytatódni, ahol a jelzés érkezése következtében félbeszakadt A signal visszatérési értéke sikeres beállítást követően a korábbi jelzé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 alulcsordulá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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 73 ► 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 ► • SIGTERM: Befejezési igény küldése a programnak. A jelzések alapértelmezett kezelési módja operációs rendszerenként elté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. Tová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álnak: man 2 signal man 3p signal man 7 signal A SIGINT jelzést általában a
Ctrl+C billentyűkombinációval a felhasználó küldi a futó alkalmazásnak. A Win32 (Windows XP, Windows 95, stb.) alkalmazásokban gyakorlatilag 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ámogatjá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 alkalmazá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++ Libraries/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ő további intézkedések is szükségesek lehetnek, például a lebegőpontos vezérlő 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- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 74 ► 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 ► 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ó (nonreentrant) 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 rendszereken 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 75 ► 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 ► /* 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. "); #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ő! "); abort(); } if(!setjmp(cimke)) { printf("Teszt érvénytelen lebegőpontos " "műveletre: "); printf("Adjon meg két számot! "); scanf("%lf %lf", &s1, &s2); e = s1 / s2; printf(" %4.3g / %43g = %43g ", s1, s2, e); e = s1 * s2; printf(" %4.3g * %4.3g = %43g ", s1, s2, e); } else printf("Lebegőpontos hiba. "); return 0; } 5.3 Ellenőrző kérdések • Mely fejfájlokat kell bekapcsolni, ha nem helyi vezérlésátadást és jelzé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? A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 76 ► 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 ► • 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! A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 77 ► Programozás II. Fájlok és könyvtárak kezelése 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 (pontosabban a GCC), mely magába foglalja az előfeldolgozót, a fordítót és a kapcsoló–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örnyezetü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 aout 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 forras2c , 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 0 Ne feledjük azonban, hogy az aktuális könyvtárbeli végrehajtható fájlt kell futtatnunk, azaz az indítás: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 78 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 79 ► ./aout 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 valamilyen 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ájlokhoz. E társítási információt hivatkozásnak (link), vagy könyvtári bejegyzésnek (directory entry) nevezzük Beszélünk „fájlokról a könyvtárban”, 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 szerkezeté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 komponensbő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 0 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önyvtár (working directory), melyből indulva relatív módon is elérhetők a fájlok é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 A dokumentum használata |
Tartalomjegyzék | Tárgymutató Vissza ◄ 79 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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álja. 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, tulajdonosa 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” fejezeté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 errnoh, az fcntl.h, a sys/typesh és a sys/stath fejfájlok behozatalára is Ezek a függvények az alacsony szintű művelet elvégzéséhez közvetlenül az operációs rendszert idézik meg (kernel rendszerhívások), és biztosí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 kimenet 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őredefiniá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.11 fileno #include <stdio.h> int fileno(FILE *folyam); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 80 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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. ", fileno(stdin));
printf("Az stdout fájlleírója %d. ", fileno(stdout)); printf("Az stderr fájlleírója %d. ", 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 stdioh 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.12 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 szintaxis 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 keresé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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 81 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 82 ► NAME MAX korlátot. A GNU rendszerben nincsenek ilyen korlátozá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ájlja nincs meg. • ENOTDIR: A fájlnévben könyvtár komponensként hivatkozott fájl létezik ugyan, de nem könyvtár. 6.13 É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örlé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 programokban. 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ülendő 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 struktú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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 82 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 unistdh és az fcntl.h fejfájlok 6.21 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.22 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álatának korlátozását végző rutinokkal változtatható 6.23 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ó. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 83 ► Programozás II. Fájlok és könyvtárak kezelése A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 84 ► • ENFILE: Az egész rendszer, vagy talán 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. • ENOENT: A fájl nem létezik, és O CREAT jelzőt nem specifikáltak. • ENOSPC: A könyvtár, vagy a fájlrendszer – mely az új fájlt tartalmazná 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 bebillentetté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ásra vagy írásra, ahogyan a fájl állapot jelzok paraméterben a megengedett műveletek típusa specifikálja. A jelzok az fcntlh fejfájlban definiált szimbolikus konstansok kombinációjából képzett egész kifejezés A fájl állapotjelző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értelmezett érték az elérési módra! Ha jelzok paraméterként több szimbolikus 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 84 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 Hozzá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 megszakí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 tulajdonosá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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 85 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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ékenysé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 jogosultsá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ögtön zérusméretűre vágni, s aztán reménykedni, hogy majd
valamit lehet 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 megadott, már létező fájl tartalmát! A fájlnév transzlációs jelzők befolyásolják, hogy az open hogyan keresse, 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ájlbó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ő megadni, 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 használni • O NONBLOCK: Megakadályozza az open–t, hogy túl sokáig blokkolja a folyamatot a fájl megnyitására várva. A jelző
csak bizonyos fájltípusoknál értelmes Rendszerint olyan eszközöknél, mint például a soros port Ha a fájllal kapcsolatban nincs értelme a jelzőnek, akkor ártalmatlan é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 kapcsoljuk ki e bitet fcntl hívással. • O NOLINK: Ha a fájl közvetett hivatkozás, akkor magát a szimbolikus 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 86 ► Programozás II. Fájlok és könyvtárak kezelése 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 megadható. 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árják. A mod a sys/stath–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üggvé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 openc fájlt és kimenetre az OPEN.OUT–ot, s aztán bezárja őket Az OPENOUT elérési jogosultsá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: "); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 87 ► Programozás II. Fájlok és könyvtárak kezelése 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." " A hiba száma: %d A hiba szövege:%s ", INP, errno, strerror(errno)); else{ printf("Sikerült a(z) %s fájl megnyitása! ", 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. " " A hiba szövege:"); else{ printf("Sikerült a(z) %s fájl megnyitása! ", 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.24 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! A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 88 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató
Fájlok és könyvtárak kezelése 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 ismertetjük. A függvények hívásához be kell hozni az unistdh fejfájlt 6.31 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.32 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 meghatározott, nyitott fájlból. 0 A beolvasás eredménye nem karakterlánc, s nincs lánczáró null karakter 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 viselkedé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 paramé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áltat 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ésre, 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- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 89 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 90 ► lyett sikeres, a kért bájtszámnál kevesebbel visszatérő olvasással is végző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ő lehet 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: ", INP); if((fh = open(INP, O RDONLY )) == -1){ fprintf(stderr, "A(z) %s fájl megnyitása sikertelen." " A hiba száma: %d A hiba szövege:%s ", INP, errno, strerror(errno)); exit(1); } /* Beolvasás: / if((beobajt=read(fh, puffer, nbajt))<=0) perror("Probléma az olvasásnál. A hiba szövege:"); else printf("%u bájt beolvasva a fájlból. ",
beobajt); close(fh); return 0; } Az eredmény: Olvasás a(z) olvas.c fájlból: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 90 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 91 ► 714 bájt beolvasva a fájlból. 6.33 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.34 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 adatblokkot a rutin nem a leiro leírójú fájl aktuális fájlpozíciójától olvassa, hanem 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 beolvasott 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 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 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.35 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. 0 A pufferbeli adat nem feltétlenül karakterlánc, ill. ha az, a null karakter 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 kezdő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ő. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 91 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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ékonyabb, 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ő lehet 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. Precí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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 92 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és
könyvtárak kezelése Vissza ◄ 93 ► EINTR, egyszerűen meg kell ismételni az írást. Ezt a legegyszerűbben a Linux rendszerekben rendelkezésre álló 6.36 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 irto 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: ", OUT); if((fh = open(OUT, O RDWR|O CREAT)) == -1){ fprintf(stderr, "A(z) %s fájl megnyitása sikertelen." " A hiba száma: %d A hiba szövege:%s ", OUT, errno, strerror(errno)); exit(1); } /* Kiírás: / A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 93 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 94 ► if((kiirtbajt=TEMP FAILURE RETRY(write(fh, puffer, sizeof(puffer))))<=0) perror("Probléma az írásnál. A hiba
szövege:"); else printf("%u bájtot írt a fájlba. ", kiirtbajt); close(fh); return 0; } A kimenet: Irás a(z) irt.o fájlba: 40 bájtot írt a fájlba. 6.37 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, hanem 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ájlkezelé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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 94 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 95 ►
6.41 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 stdioh 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 kevesebb helyet foglalhat el, mint amekkorának tűnik. Ez a fájl a 6.42 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éptelen eszköznek (cső, terminál, nyomtató stb.) felel meg A dokumentum
használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 95 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 96 ► 0 Ha hozzá szeretnénk fűzni a fájlhoz, akkor a fájlmutató írást megelő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 lseekc 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: "); 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 ", 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ó " A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 96 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 97 ► "pozícionálás után =%5ld ", 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 ", 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ájlmutató é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); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 97 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 98 ► /* . */ read(fh1, puff, 10); 6.43 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 valamelyiké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álható, 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/typesh és a sys/stath fejfájlok Lássuk előbb a használatos típusokat és a stat struktúrát! 6.51 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.52 dev t típus A fájl adathordozójának eszköz számát ábrázoló, aritmetikai adattípus.
GNU rendszerben: int. 6.53 gid t típus A csoport azonosítóját (group ID) ábrázoló, egész adattípus. GNU rendszerben: unsigned int A csoport további adatai (név, beletartozó felhasználók) a getgrgid és a getgrnam rutinokkal kérdezhetők le A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 98 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 99 ► 6.54 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.55 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.56 uid t típus A felhasználói azonosítót (user ID) ábrázoló, egész adattípus. GNU rendszerben: 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.57 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, adattí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? A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 99 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 kapcsolatba 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 objektumok (ü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/stath 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 100 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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érni 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. 0 Van másik mód is az elérhetőség elemzéséhez. Leutánozva a rendszer 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árulé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 tulajdonosá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 rendszer egy ideig, mintha a program még futna. Ez előnyös volt a feltehetőleg A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 101 ► Programozás II. Fájlok és könyvtárak kezelése 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 program 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 vannak, ha a legutóbbi futás óta nem lépett fel memóriahiány. Bizonyos modern rendszerekben a „sticky” bitnek nincs hasznos értelme végrehajtható fájlokra, így nem könyvtárakra egyáltalán nem billenthető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 értelmet tulajdonítanak a „sticky” bitnek. Ha nem végrehajtható fájlra billentik 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ájlrendszeren 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) esetén a struktúra tag a hivatkozott fájlnév hosszát tárolja A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 102 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 mikró 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ódosí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ú „lyukak”, melyek aktuálisan nem foglalnak helyet a lemezen. Ritka fájl eseté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.58 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útumok arra a fájlra vonatkoznak, ahova a hivatkozás mutat. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 103 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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: stat fájl vagy" " könyvtárnév "); return 1; } /* Az argv[1] attribútum információ lekérdezése: / printf("A(z) %s fájl vizsgálata: ", 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 "); else if(S ISCHR(puff.st mode)) printf("karakteres eszköz. "); else if(S ISBLK(puff.st mode)) printf("blokkos eszköz. "); else if(S ISREG(puff.st mode)) printf("szabályos fájl. "); else if(S ISFIFO(puff.st mode)) printf("cső "); else if(S ISLNK(puff.st mode)) printf("közvetett hivatkozás. "); else if(S ISSOCK(puff.st mode)) printf("foglalat. "); else
printf("típusa ismeretlen. "); printf("Fájlméret: %ld ", puff.st size); printf("Utsó módosítás ideje: %s", A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 104 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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.59 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.510 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 STATOUT 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: "); if((fh=open(OUT, O CREAT|O RDWR|O TRUNC, A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 105 ► Programozás II. Fájlok
és könyvtárak kezelése 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. A hiba száma: %d A hiba " "szövege:%s ", 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. A 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 "); else if(S ISCHR(puff.st mode)) printf("karakteres eszköz. "); else if(S ISBLK(puff.st mode)) printf("blokkos eszköz. "); else if(S ISREG(puff.st mode)) printf("szabályos fájl.
"); else if(S ISFIFO(puff.st mode)) printf("cső "); else if(S ISLNK(puff.st mode)) printf("közvetett hivatkozás. "); else if(S ISSOCK(puff.st mode)) printf("foglalat. "); else printf("típusa ismeretlen. "); printf("Fájlméret: %ld ", puff.st size); printf("Utsó módosítás ideje: %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.511 Elérési jogosultság vizsgálata és módosítása access int access(const char * fajlnev, int mod); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 106 ► Programozás II. Fájlok és könyvtárak kezelése 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 unistdh 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. 0 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 tagjá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: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 107 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 rendszerekben jelentkezik, ahol a sticky bitnek nem könyvtár fájlokra nincs értelmes jelentése ENOENT: A fájl nem létezik. EPERM: E folyamatnak nincs joga e fájl elérési jogosultságainak változtatá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 visszaállítjuk induló állapotát: ", FAJL); /* Létezik-e a fájl? / if((access(FAJL, F OK))!=-1){ printf("A(z) %s fájl létezik. ", 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ó. ", FAJL); A
dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 108 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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ó. " " A hiba száma: %d A hiba szövege:%s ", FAJL, errno, strerror(errno)); if((access(FAJL, W OK))==-1) printf("A(z) %s fájl csak olvasható lett. ", 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ó. A hiba száma: %d A hiba " "szövege:%s ", FAJL, errno, strerror(errno)); else printf("A(z) %s fájl újra írható, olvasható " "stb. ", FAJL); system(parancs); } else printf("A(z) %s fájl nem létezik. ", 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 nincsenek elérési jogosultságai. • EPERM: E folyamatnak nincs joga e fájl elérési jogosultságainak változtatá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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 109 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 110 ► • EROFS: A fájl csak olvasható fájlrendszeren van, tehát semmije sem módosítható. 6.512 Fájl létrehozási maszk (umask) A fájlokat létrehozó függvényeknek (open, mkdir stb.) van mode t típusú 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 folyamat 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 értelmes 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étrehozá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 jogosultságokat a kívántra. Az umask–ot rendszerint csak a shell (parancsértelmező) 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 110 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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étrehozá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("
Csak 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(" Az indulási umask visszaállítása: "); for(seged=0X80000000; seged; seged>>=1) if(regimaszk&seged) putchar(1); else putchar(0); putchar( ); return 0; } Az eredmény kimenet lehet a következő: Az aktuális fájl létrehozási maszk: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 111 ► Programozás II. Fájlok és könyvtárak kezelése 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.513 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 nevet 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 viszont –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 hivatkozást, s csak aztán hajtatható végre a link A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 112 ► Programozás II. A dokumentum használata |
Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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önyvtá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 hivatkozás korlátozások nélkül létesíthető akár könyvtárakra, vagy akár fájlrendszereken át is Közvetett hivatkozás, mi több, olyan névhez is létrehozható, 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 Hasonlóképpen, ha a korábban létező, de most már törölt fájlra mutató közvetett 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 nagyon ritkán lehet csak „kívánatos”. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 113 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 hivatkozá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 karakterek 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. 0 Ha a readlink visszatérési értéke éppen meret, nem lehetünk biztosak benne, hogy elfért–e a teljes név. Ezen például a Biztireadlink függvé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 karakterlá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> A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 114 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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özvetett 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 ., komponenseket, közvetett hivatkozásokat, ill ismételt útelválasztókat (/) A visszakapott 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 115 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 116 ► • ENOTDIR: A fajlnevben legalább egy könyvtár komponens nem létezik. 0 A canonicalize file name nem szabványos, de Linux rendszerekben 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","linksy1c", "linksy2c"}; printf("Hivatkozások készítése a(z)"%s" fájlhoz: ", 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! " " A hiba száma: %d A hiba szövege:%s ", uj[0], fajl, strerror(errno)); else printf("Közvetlen hivatkozás: %s ", 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! " " A hiba száma: %d A hiba szövege:%s ", uj[1], fajl, strerror(errno)); else printf("Közvetett hivatkozás: %s ", 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! " " A hiba száma: %d A hiba szövege:%s ", uj[2], uj[1], strerror(errno)); else printf("Közvetett hivatkozás: %s <- %s ", uj[1], uj[2]); /* A munkakönyvtár uj[0] kezdetű fájljainak kijelzése: */ A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 116 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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], ¶ncs[++hsz], sizeof(parancs)hsz))<0) fprintf(stderr, "A(z) "%s" közvetett hivatkozás " "readlink-je sikertelen! A hiba száma: " "%d A hiba szövege:%s ", uj[2], errno, strerror(errno)); else { parancs[hsz+i]=0; printf(" readlink("%s",.): %s ", uj[2], ¶ncs[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! " " A hiba száma: %d A hiba szövege:%s ", uj[2], errno, strerror(errno)); else { printf("canonicalize file name("%s"): %s ", 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! A hiba száma: %d A hiba
" "szövege:%s ", uj[i], errno, strerror(errno)); else printf(""%s" törölve. ", 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 <- linksy2c 7243 –rwxr--r-- 2 bauer teacher 2508 2005-12-02 linkek.c 7243 –rwxr--r-- 2 bauer teacher 2508 2005-12-02 linkhd.c 4396 lrwxrwxrwx 1 bauer teacher 8 2005-12-02 linksy1.c -> linkekc 4440 lrwxrwxrwx 1 bauer teacher 9 2005-12-02 linksy2.c -> linksy1c A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza 16:49 16:49 16:51 16:51 ◄ 117 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 118 ► 4173 –rwxr--r-- 1 bauer teacher 1283 2005-11-15 15:29 lseek.c readlink("linksy2.c",): linksy1c 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 minden 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 értelmé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
átmozgatására A könyvtárak átnevezhetők, de nem mozgathatók át 0 A függvény egyik paramétere sem lehet globális fájlnév! A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 118 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 119 ► 0 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 elhelyezkedő 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 119 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 120 ► is megszűnik. Ha valamely más folyamat eközben nyitva tartja a fájlt, akkor 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ájlokra úgy dolgozik, mint az unlink, s könyvtárakra, mint az rmdir Prototípusa viszont az stdioh 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önyvtárhoz nincs írásjog, vagy a könyvtár „sticky” bitje bebillentett, és a folyamat 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önyvtárak törlésére, vagy legalább is csak privilegizált felhasználó alkalmazhatja erre a célra. GNU rendszerben könyvtárat nem unlink–kel, hanem 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 szemlélteti A szoftver előbb lemásolja az aktuális könyvtárbeli renamovc–t a szülő mappába. Majd átnevezi rencic–re úgy, hogy át is mozgatja közben a szülő könyvtárba. Ezt követően törli a program a rencic–et, majd viszszamozgatja a szülőbeli renamovc–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> A dokumentum használata | Tartalomjegyzék |
Tárgymutató Vissza ◄ 120 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 121 ► #include <unistd.h> int main(void){ int hl; char parancs[128]; char regi[] = "renamov.c", uj[] = "rencic", 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): ", regi, parancs); if(rename(regi, parancs)){ fprintf(stderr, "A(z) "%s" fájl nem volt " "átnevezhető! A hiba száma: %d A hiba " "szövege:%s ", 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: ", parancs); if(unlink(parancs) == -1) fprintf(stderr, "A(z) "%s" fájl nem törölhető! " " A hiba száma: %d A hiba szövege:%s ", 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ő! A hiba száma: %d A hiba " A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 121 ► Programozás II. Fájlok és könyvtárak kezelése A dokumentum használata | Tartalomjegyzék | Tárgymutató
Vissza ◄ 122 ► "szövege:%s ", parancs, errno, strerror(errno)); return 1;} return 0; } A program kimenete a következő lehet: "renamov.c" átnevezése "/rencic"-re(a): –rwxr--r-- 1 bauer teacher 1776 2005-11-28 17:07 ./renamovc –rwxr--r-- 1 bauer teacher 1776 2005-11-28 17:06 ./rencic "./rencic" törlése: –rwxr--r-- 1 bauer teacher 1776 2005-11-28 17:07 ./renamovc 6.514 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 definiá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 tulajdonosá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 jogosultsá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, tekintet nélkül
jogosultsági bitjeik beállítására. Egyetlen kivétel van: fájlt végrehajtatni a privilegizált felhasználó is csak akkor képes, ha valamelyik végrehajtást engedélyező bitje be van billentve A fájl létrejövetelekor a tulajdonosa a létrehozó folyamat tényleges felhasználói ID–je lesz. A fájl csoportazonosítója az állományt tároló rendszertől függően vagy a folyamat tényleges csoport ID–je, vagy a fájlt tar- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 122 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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énynyel
tudható meg. Az ugyanilyen rutin páros a valódi és tényleges csoportazonosí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 jogosultsá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 valamely 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 megteheti. 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ó. 0 A POSIX CHOWN RESTRICTED makró definiáltsága esetén nem privilegizált folyamatokban a chown rutinnal tényleges, vagy valamely 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 123 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 124 ► A rutin sikeres esetben zérussal
tér vissza. A szolgáltatott –1 hibát jelez, 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észletek 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 tulajdonosá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: " "tulaj fájlnév újtulaj "); return 1; } /* Az argv[1] fájl létezik és
írható? / printf("A(z) %s fájl tulajdonosváltása: ", argv[1]); if(access(argv[1], F OK|W OK) != 0){ fprintf(stderr,"A(z) %s fájl nem létezik, vagy nem " "írható! ", 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! " " A hiba száma: %d A hiba szövege:%s ", argv[2], errno, strerror(errno)); return 1; } ujtul=pwdmut->pw uid; ujcsop=pwdmut->pw gid; A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 124 ► Programozás II. Fájlok és könyvtárak kezelése 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! A hiba száma: %d A hiba " "szövege:%s ", argv[1], errno, strerror(errno)); return 1; }
if(!(pwdmut=getpwuid(puff.st uid))){ fprintf(stderr,"A(z) eredeti felhasználó " "ismeretlen! A hiba száma: %d A hiba " "szövege:%s ", errno, strerror(errno)); return 1; } printf(" %s --> %s ", pwdmut->pw name, argv[2]); if(chown(argv[1], ujtul, ujcsop)){ fprintf(stderr, "A váltás sikertelen! A hiba " "száma: %d A hiba szövege:%s ", errno, strerror(errno)); return 1;} printf("A váltás sikeresen megtörtént. "); 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 privilegizált felhasználónak, vagy a root–nak engedi meg a fájl tulajdonosváltását. 6.515 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étrehozá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 maradéktalanul el is távolítható, azaz mérete zérusra vágható! 0 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 125 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és
könyvtárak kezelése 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 elvesznek. 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 eredetiné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ű A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 126 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 (chsizefel) 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: ", 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. ", 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){ A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 127 ► Programozás II. Fájlok és könyvtárak kezelése A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 128 ► strcpy(¶ncs[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. A hiba száma: %d A hiba " "szövege: %s ", 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. " " A hiba száma: %d A hiba szövege: %s ", 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 –rwxrr-- 1 bauer teacher 1607 2005-12-08 15:57 chsize.c 4392 –rwxrr-- 1 bauer teacher 803 2005-12-08 16:00 chsize.fel 4396 –rwxrr-- 1 bauer teacher 3214 2005-12-08 16:00 chsize.ket 6.516 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 felelnek 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 pedig 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 bejegyzést tartalmazó könyvtár attribútum változtatási és módosítási ideje is megváltozik A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 128 ► Programozás II. Fájlok és könyvtárak kezelése 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 állomá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álatához be kell kapcsolni az utimeh 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 mutatta 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ő tulajdonosnak 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 129 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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árjait 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 ÉÉÉÉ.HHNN 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 ÉÉÉÉ.HHNN 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 ÉÉÉÉ.HHNN ÓÓ: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; A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 130 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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! A hiba(%d): %s. ", fajl, errno, strerror(errno)); exit(1); } printf("%s fájl idő adatai: elérési:%36s", fajl,
idolanc(attr.st atime)); printf(" módosítási:%33s ", idolanc(attr.st mtime)); printf(" attribútum változtatási:%20s ", 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: idos fájlnév " "ÉÉÉÉ.HHNN "); 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ó! ", argv[1]); return 1; } /* Dátum rendben? / if(!datume(argv[2])){ fprintf(stderr, "A %s dátum hibás! ", argv[2]); return 1; } /* Programcím: / n=printf("%s idő adatainak átállítása %s 12:00:00-ra, " "s vissza: ", argv[1], argv[2]); while(n--) putchar(-); putchar( ); /* Kiindulás. */ printf("* A kiindulási
állapot: "); 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: ", argv[2]); /* Átállítás. */ struki.tm sec=strukitm min=strukitm isdst=0; struki.tm hour=12; struki.tm year=atoi(argv[2])-1900; A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 131 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 132 ► struki.tm mon=atoi(argv[2]+5)-1; struki.tm mday=atoi(argv[2]+8); ujido.actime=ujidomodtime=mktime(&struki); if(utime(argv[1], &ujido)){ fprintf(stderr, "Az időváltoztatás nem sikerült! "); return 1; } idokijelzes(argv[1]); printf("* Visszaállítás a kiindulási " "időre: "); /* 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! "); else idokijelzes(argv[1]); return 0; } „idos idos.c 20160123” indítás után egy lehetséges eredmény kimenet a következő: idos.c idő adatainak átállítása 20160123 12:00:00-ra, s vissza: -------------------------------------------------------* A kiindulási állapot: idos.c fájl idő adatai: elérési: 2005.1213 10:41:05 módosítási: 2005.1213 10:40:51 attribútum változtatási: 2005.1213 10:41:09 * Átállítás 2016.0123 12:00:00-ra: idos.c fájl idő adatai: elérési: 2016.0123 12:00:00 módosítási: 2016.0123 12:00:00 attribútum változtatási: 2005.1213 10:41:40 * Visszaállítás a kiindulási időre: idos.c fájl idő adatai: elérési: 2005.1213 10:41:05 módosítási: 2005.1213 10:40:51 attribútum változtatási: 2005.1213 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árolá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 dolgozó 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- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 132 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 folyamatokat abban, hogy írás zárat tegyenek az állomány adott részére. Termé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 megvaló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. 0 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.61 flock struktúra A típus az fcntl függvénnyel fájl zárak leírására használatos. Tagjai: A dokumentum használata | Tartalomjegyzék |
Tárgymutató Vissza ◄ 133 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 feloldá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 speciá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.62 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 fajtá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ípusú, s a zár adatait tartalmazza. Tehát a zároláshoz használatos fcntl prototípusa a következő: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 134 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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ályban 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 azonosí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ületen. 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 lekezelése persze azonos • EBADF: A leiro paraméter érvénytelen. Olvasási zárat kívántak kihelyezni, 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 135 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak
kezelése 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árakozá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: ", FAJL); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 136 ►
Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése Vissza ◄ 137 ► while(--i) putchar(-); putchar( ); /* A fájl megnyitása: / if((leiro=open(FAJL, O RDWR))==-1){ fprintf(stderr, "A(z) %s fájl megnyitása " "sikertelen! A hiba szövege:%s ", 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! ", 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! A hiba" " szövege:"); close(leiro); return 1; } puffer[10]=0; printf("Beolvasva:"%s" ", 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! ",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! ", 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! A hiba " "szövege:"); else { puffer[i]=0; printf("Kiírva :"%s" ", puffer); } /* Az írás zárat nem kell visszavonni, hisz a close törli. */ } } close(leiro); return 0; } A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 137 ► Programozás II. Fájlok és könyvtárak kezelése 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.71 Aktuális, vagy munkakönyvtár Minden folyamat rendelkezik aktuális, vagy munkakönyvtárral, ahová dolgozik, ill. ez az a könyvtár, ahonnét a relatív utak indulnak Bejelentkezéskor a felhasználó saját (home) könyvtára, vagy bejelentkezési könyvtá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 helyezkednek 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 mutató legyen, amikor is az út tárolásához legalább maxhsz bájtot allokál automatikusan a rendszer a malloc segítségével, ha a maxhsz nagyobb zérusná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 hoszszabb maxhsz karakternél. Nagyobb tömböt kell allokálni, s újra lehet próbálkozni! A dokumentum használata | Tartalomjegyzék |
Tárgymutató Vissza ◄ 138 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 normá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: "); /* Az aktuális könyvtár lekérdezése és kijelzése: / if(!(puffer=getcwd(NULL, 0))) A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 139 ► Programozás II. Fájlok és könyvtárak kezelése A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 140
► perror("getcwd hiba! A hiba szövege:"); else { maxhsz=strlen(puffer)+1; printf("%s ", 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: "); if(chdir("./")) perror("chdir hiba! A hiba szövege:"); if(!getcwd(puffer, maxhsz)) perror("getcwd hiba! A hiba szövege:"); else { printf("%s ", 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: "); if(chdir(&puffer[strlen(puffer)+1])) perror("chdir hiba! A hiba szövege:"); if(!getcwd(puffer, maxhsz)) perror("getcwd hiba! A hiba szövege:"); else printf("%s ", 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.72 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 szolgá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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 140 ► Programozás II. A dokumentum használata | Tartalomjegyzék |
Tárgymutató Fájlok és könyvtárak kezelése 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általá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étrehozatalá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érkö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önyvtárhoz nincs írásjog, vagy a könyvtár „sticky” bitje bebillentett, és a folyamat 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örlé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 szimbolikus á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 állományt csak unlink–kel szabad törölni
A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 141 ► Programozás II. Fájlok és könyvtárak kezelése 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, megjelentetjü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: "); if(mkdir("Proba", S IRWXU| S IRGRP| S IXGRP| S IROTH| S IXOTH)) perror("Gond van a "Proba" könyvtár " "létrehozatalával! A hiba szövege:"); else { printf("A "Proba" könyvtár sikeresen létrehozva és"
" tartalma: "); system("ls -lia Proba"); } if(!rmdir("Proba")) printf("A "Proba" könyvtár sikeresen törölve. "); else perror("Probléma van a "Proba" könyvtár " "megszüntetésével! A hiba szövege:"); return 0; } Egy lehetséges kimenet: A "Proba" könyvtár A "Proba" könyvtár összesen 1 7444 drwxr-xr-x 2 4474 drwxr-xr-x 3 A "Proba" könyvtár létrehozása és törlése: sikeresen létrehozva és tartalma: bauer teacher 48 2005-12-16 10:45 . bauer teacher 584 2005-12-16 10:45 . sikeresen törölve. 6.73 Könyvtárak megnyitása, olvasása és bezárása Az itt ismertetett függvények és szimbólumok használatához bekapcsolandó 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. A dokumentum használata |
Tartalomjegyzék | Tárgymutató Vissza ◄ 142 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 rendszerben 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 okból a tag d ino névvel is elérhető • unsigned char d namlen: A d name karakterlánc hossza a lánczáró ’ ’ 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 bejegyzé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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 143 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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. 0 Bizonyos rendszerekben a readdir nem adja vissza a . és a rekordok 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övetkező lehet: • EBADF: A konyvtar paraméter érvénytelen. 0 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 olvashatók
readdir–rel, csak újabb opendir után. A closedir sikeres esetben zérust szolgáltat, s a problémát –1–gyel jelzi, amikor is esetén az errno a következő lehet: • EBADF: A konyvtar paraméter érvénytelen. 6.74 Pozícionálás a könyvtár fájlban rewinddir void rewinddir(DIR *konyvtar); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 144 ► Programozás II. Fájlok és könyvtárak kezelése 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özben 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. 0 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élkül) a parancssori paraméterként megadott könyvtárra Ha paraméter nélkü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 ÉÉÉÉ.HHNN ÓÓ: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. */ A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 145 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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: ", 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á! " " A hiba: "); return 1; } } /* A könyvtár fájl megnyitása: / if(!(k=opendir("."))) perror("A könyvtár nem nyitható meg! A hiba: "); else {
while(r=readdir(k)){ if(stat(r->d name, &s)) printf("%s: ??? ", 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); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 146 ► Programozás II. Fájlok és könyvtárak kezelése 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 ", 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 4474 drwxr-xr-x . 4449 drwxr-xr-. 7269 –rwxr--r-ir.c 7430 -rwxr--r-akonyvtar.c 42 –rwxr-xr-x a.out . tartalomjegyzéke: 2 bauer teacher 592 2005.1221 14:51:56 9 bauer teacher 648 2005.1221 14:42:11 1 bauer teacher 826 2005.1205 14:38:34 1 bauer teacher 1085 2005.1216 10:13:20 1 bauer teacher 9940 2005.1221 14:51:37 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 147 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Fájlok és könyvtárak kezelése 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 rutinnak? • 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érdezni? • Mi a közvetlen hivatkozás? Hogy lehet létrehozni? Honnan lehet megtudni, hogy egy bizonyos fájlra hány darab közvetlen hivatkozás létezik? • Mi a közvetett hivatkozás? Hogyan létesíthető? A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 148 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató • • • • • • • • • • • Fájlok és könyvtárak kezelése 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 tulajdonosa é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üntető függvényeket! Milyen adatszerkezettel és függvényekkel történik a könyvtárak megnyitá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 foglaljunk 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önyvtá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átralevő, még nem kész ágrészt kell létrehozni. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 149 ►
Programozás II. Fájlok és könyvtárak kezelése 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 megadott 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 parancssori paraméter nélkül indítják a programot, akkor ismertesse a képernyő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 bemenet másolása esetén végül közlendő még az eredményfájl mérete! A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 150 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok 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 operá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 rendelkezik Ugyanazt a programot több folyamat is futtathatja egyszerre Mindegyik folyamat saját programmásolattal rendelkezik, s így a programpé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/typesh 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észfeladatok 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értelmezőjének (shell) egy újabb példányát. A /bin/sh általában csak hivatkozás (link), mely az alapértelmezett héjprogramra mutat A rutin a paramé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áltozóban megadott mappákban is keresi. A dokumentum használata |
Tartalomjegyzék | Tárgymutató Vissza ◄ 151 ► Programozás II. Folyamatok 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 értelmezé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ő. 0 Óvatosan kell eljárni a system hívással többszálú programok eseté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: ",MAPPA); switch(allapot = system(parancs)){ case -1: fprintf(stderr,"Héjprogram indítása " "sikertelen. "); exit(EXIT FAILURE); case 0: printf("A parancsot sikeresen végrehajtottuk. "); exit(EXIT SUCCESS); default: fprintf(stderr,"A system() által visszaadott " "érték: %d ",allapot); exit(EXIT FAILURE); } } A program lehetséges kimenete: A /usr mappa tartalma: összesen 98 drwxr-xr-x drwxr-xr-x 2 root root 41576 2005-10-27 15:58 bin 2 root root 72 2005-07-11 16:23 games A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 152 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok Vissza ◄ 153 ► drwxr-xr-x 5 root root 120
2005-07-11 16:08 i586suse-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 folyamatazonosí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 PIDjét is. Folyamatokat fork rendszerhívással lehet indítani. A fork által létrehozott 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 egymá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 folyamat á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 lehető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észleteiben, tekintsük át a folyamatok azonosításának lehetőségeit! 7.21 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 153 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok 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étfé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 valamelyik
tulajdonságát, annak semmiféle következménye nem lesz a gyerek leíróira nézve, és fordítva Ez alól egyedüli kivétel az aktuális fájlpozí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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 154 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok 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 megvaló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 megszorí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 folyamat, 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ítve, 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 munkakö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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 155 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok 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 értelme 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ójelekbő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 ",getpid()); printf("A gyermek PID-je: %d ",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. A hiba oka: "); if(errno == EAGAIN) { fprintf(stderr,"jelenleg nincs elegendő erőforrás " " a folyamat elindításához, de később " "próbálkozzon újra. "); } else if(errno == ENOMEM) { fprintf(stderr,"nincs elegendő memória. "); } } int main(void) { A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 156 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató
Folyamatok Vissza ◄ 157 ► pid t gyermek; printf("Folyamatok indítása a fork() hívásával " "A szülő < jelet, a gyermek > jelet ír a " "kimenetre. "); switch(gyermek = fork()) { case -1: hiba(); exit(EXIT FAILURE); case 0: gyermekf(); exit(EXIT SUCCESS); default: szulof(gyermek); putchar( ); 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.51 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áltozóját a szülő változójának lemásolásával állítják elő. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 157 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok Vissza ◄ 158 ► 7.52 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 betö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 parancssori 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 karakterlá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, hogyha 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. Amenynyiben 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). A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 158 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok Vissza ◄ 159 ► & Ez a függvény különösen az operációs rendszer segédprogramjainak 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 karaktereket 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 parancssori 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 rendelkezésre elegendő memória. • EACCESS: Csak a p végződésű változatoknál jelentkezhet. Amennyiben 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 159 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok 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ó karakterlá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 rendszerekben a gyökérkönyvtárat nem másolják, ha SUID bites program futtatá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 meghatá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-onexit) 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üggvé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 azokat, 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 A dokumentum használata |
Tartalomjegyzék | Tárgymutató Vissza ◄ 160 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok 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 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 ", 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. "); break; case ENOEXEC: fprintf(stderr, "Az indítandó állomány " "formátuma nem megfelelő. "); break; case ENOMEM: fprintf(stderr, "A megadott program " A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 161 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok Vissza ◄ 162 ► "futtatásához nincs elegendő memória. "); break; case ENOENT: fprintf(stderr, "Nem találom a megadott " "állományt. "); break; default: fprintf(stderr, "Ismeretlen
hiba lépett fel a " "futtatás során. Hibakód: %d ", 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! PID: %d ", *(argv+1), getpid()); exit(EXIT SUCCESS); } else { fprintf(stderr, "Hibás paraméterezés! "); 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áltozá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 kapcsolni a sys/typesh és sys/waith fejfájlokat pid t waitpid(pid t pid, int *allapot, int opciok); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 162 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok Vissza ◄ 163 ► A waitpid függvényt a pid folyamatazonosítójú gyermek állapotának leké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 folyamatcsoportban 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 gyermekfolyamatról állapotinformációt kérni, amelynek folyamatcsoportazonosí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álasztottnak 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 szolgál, hogy a wait ne blokkolja a programunk futását; ha van befejezett folyamat, az allapot-tal adott helyen elhelyezi annak állapotát és visszaadja folyamatazonosítóját. Ha nincs, nullával tér vissza Ha a WUNTRACEDet 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 2610-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. 0 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 folyamatazonosítójával tér vissza, amelynek az állapotinformációját szolgáltatta. Ha léteznek gyermekfolyamatok, de még mindegyik fut, és a waitpid-et a WNOHANG kapcsolóval hívtuk meg, zérust ad vissza. A dokumentum
használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 163 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok Vissza ◄ 164 ► Ha a waitpid-et konkrét gyermekfolyamat PID-ével hívják, akkor termé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áltozó é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 errő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 befejező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
állapotvá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.) 0 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 folyamatokat nevezik „zombi” folyamatoknak, utalva élőhalott állapotukra Ezek feleslegesen foglalják a rendszer erőforrásait. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 164 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok 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ő megfelelő ü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. "); if (fork()) { waitpid(WAIT ANY,NULL,0); printf("Borzalmas tragédia eltemetni saját " "gyermekünk. "); } else { printf(" Megszülettem! Én vagyok a gyermek! " " Boldog gyermekévek. " " De jaj, eljött a szörnyű vég. "); } 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 makrók könnyítik meg, melyeket a sys/wait.h fejfájl bekapcsolása után használhatunk int WIFEXITED(int allapot) A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 165 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok Vissza ◄ 166 ► A makró visszatérési értéke nem zérus, ha a gyermekfolyamat futása szabá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énynek á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.610-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 állapotinformá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ó tagjainak (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ípusok és szimbolikus állandók használatához? A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 166 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatok 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 paramé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 nyereség/veszteség! A dokumentum használata |
Tartalomjegyzék | Tárgymutató Vissza ◄ 167 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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 befejező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ő információátadás, a futását befejező gyermekfolyamat kilépési kódja, parancssori 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 eszkö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 memória használata. A koncepció lényege, hogy a kommunikálni vágyó folyamatok 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 olvashatja 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 programozónak kell gondoskodnia a folyamatok közti szinkronizáció megvaló- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 168 ► Programozás II. Folyamatközi kommunikáció 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 megoldása: szemaforok (process semaphores, System V semaphores) alkalmazá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ásaik
részletes bemutatása túlmutat ennek a jegyzetnek a keretein. Az érdeklő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 olvasó fél először. Ezt a kommunikációs formát használja a héjprogram is, ha a felhaszná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 kimeneté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. A dokumentum használata | Tartalomjegyzék | Tárgymutató
Vissza ◄ 169 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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ésné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ábbí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 haszná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 karakterlá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) A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 170 ► Programozás II. Folyamatközi kommunikáció A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 171 ► putchar(k); putchar( ); } void szulo(FILE* cso) { char k; printf("[Szülő] Adjon meg egy újsor karakterrel " "lezárt karakterláncot, amelyet továbbítani fogunk " "a gyermeknek! "); while((k = getchar()) != ) 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. "); 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. "); 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 ismerkedjünk meg a dup2 függvénnyel! int dup2(int fajlleiro1, int fajlleiro2); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 171 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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. "); return 2; case 0: close(alk[IR]); dup2(alk[OLVAS], STDIN FILENO); execlp("less", "less", 0); break; A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 172 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció Vissza ◄ 173 ► default: close(alk[OLVAS]); if((cso = fdopen(alk[IR], "w")) != NULL) { fprintf(cso, "Forint - Euró átszámítás " "%9s|%9s ---------+--------- ", "Forint", "Euro"); for(forint = ALSO; forint <= FELSO; forint += LEPES) fprintf(cso, "%9d|%9.2f ", 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. "); 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éjprogrammal 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ú 0 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áltozó 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); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 173 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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 paramé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örebben 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 " "%9s|%9s ---------+--------- ", "Forint", "Euro"); for(forint = ALSO; forint <= FELSO; forint += LEPES) fprintf(cso, "%9d|%9.2f ", 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 alkalmasak. E problémán segítenek a nevesített csövek A nevesített csövek egyszerű állományokként látszanak a
fájlrendszerben. É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 adatokat az adathordozón. A fájlnév csak arra szolgál, hogy a folyamatok hi- A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 174 ► Programozás II. Folyamatközi kommunikáció 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ással párhuzamosan is. A GNU/Linux rendszereken a nevesített csövek alapértelmezés szerint blokkoló módban működnek, de nemblokkoló üzemmódban is haszná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átozzá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 valamelyiké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 valamelyike hosszabb NAME MAX-nál. • ENOENT: Az egyik megnevezett könyvtár nem létezik, vagy közvetett 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 175 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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övető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 konzol 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. "); if(cso = fopen(CSOFAJL, "w")) { int k; printf("Továbbítandó üzenet: "); do { fputc(k = getchar(), cso); } while(k!=EOF && k!= ); fclose(cso); } else { fprintf(stderr, "A csövet nem sikerült " "megnyitni írásra. "); return 2; } } else { A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 176 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció Vissza ◄ 177 ► fprintf(stderr,"Nem sikerült létrehozni " "a csövet. "); 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. "); while((k = fgetc(cso)) != EOF) putchar(k);
fclose(cso); if(remove(CSOFAJL)) { fprintf(stderr, "A csövet nem " "sikerült törölni. "); return 2; } } else { fprintf(stderr, "Nem sikerült megnyitni a csövet. " "Külön konzolon futtassa először fifo1-et, " "majd próbálja újra! "); return 1; } return 0; } 8.4 Foglalatok A foglalat folyamatok közötti információcserét lehetővé tevő, magas szinten 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ősorban 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 adattová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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 177 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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átvitelt lehetővé tevő csatornán folyamatosan beszélhetnek. Ha a kapcsolatra többé nincs igény, akkor a felek megszüntetik azt A megbízhatóság itt azt jelenti, hogy az alkalmazott protokoll gondoskodik 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 adatcsomagot 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 csomagokat 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 sorrendjé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 összekapcsolt gépek folyamatainak adatcseréjét biztosítani, különböző névterek (namespace) állnak rendelkezésre. 0 A foglalatok névterei semmiféle kapcsolatban nincsenek a programozá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/socketh és sys/types.h fejfájlokat Mivel a kommunikációban résztvevő feleknek némiképp eltérő lépéseket 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 178 ► Programozás II. Folyamatközi kommunikáció A dokumentum használata | Tartalomjegyzék | Tárgymutató Szerver Kliens 1.) socket 2.) bind 1.) socket Vissza ◄ 179 ► 3.) connect 4.) read / write 4.) write / read 5.) close 5.) close 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 alacsonyszintű 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 alacsonyszintű fájlkezelő függvényekkel írhatunk a fájlleíróval azonosított foglalatra, 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 visszaadott fájlleírót használva write vagy read függvényekkel csomagot juttathatunk 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 kommuniká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: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 179 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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. 0 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 kommuniká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 csatlakozni, 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 haszná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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 180 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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 viszszaadott fájlleíróval azonosított foglalat-hoz a cim címet. A harmadik paramé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. (Bizonyos 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ódhatnak hozzá más folyamatok foglalatai. (Természetesen csak azonos névteret, 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ákapcsolja 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álaszol 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: A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 181 ► Programozás II. Folyamatközi kommunikáció 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 ilyenkor 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 kapcsolat kiépülésére vár. 0 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 megismerni. 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) A dokumentum
használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 182 ► Programozás II. Folyamatközi kommunikáció 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 pedig 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 blokkolt állapotot megszüntetheti az is, ha a kivetel leírók valamelyikével kivételes esemény (exception) történik 0 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. 0 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 183 ► Programozás II. Folyamatközi kommunikáció 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ó lebonyolí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! Szerver Kliens 1.) socket 2.) bind 3.) listen 4.) accept 4.1) fork 4.2) read / write 4.3) close 5.) close 1.) socket 4.) connect 4.2) write / read 4.3) close 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. Ezután a szerver listen hívással engedélyezi a kliens
felől érkező kapcsolatfelépítési kérelmek fogadását A kérelmekre az accept figyel, mely új foglalatot hoz létre a kommunikáció lebonyolítására Sikeres hívás esetén ennek 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 szoká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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 184 ► Programozás II. Folyamatközi kommunikáció 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 csatornán több adatcsomag
továbbítható, akár váltakozó irányban is Az informá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 foglalaton, ami így szerver foglalattá válik. A függvény csak összeköttetéses kapcsolatok 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 viszsza, 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érelmeket 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 szolgáltatnak információt. Sikertelen hívás esetén az accept −1-el tér vissza, és az errno a következő é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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 185 ► Programozás II. Folyamatközi kommunikáció A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 186 ► 8.41 A UNIX névtér A UNIX, vagy helyi névtér azonos számítógépen futó, egymástól független 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, olvashat 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ó alkalmazandó, 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 szerkezete 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. 0 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 szolgá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); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 186 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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 fhomkliensc), 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 rendelkezniü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. "); 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. "); return 2; } printf("Várakozás a kliens üzenetére.
"); read(foglalat, csomag, MAX-1); csomag[MAX] = ; printf("Üzenet: %s ", csomag); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 187 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció Vissza ◄ 188 ► if(close(foglalat)) { fprintf(stderr, "Foglalat lezárása nem sikerült. "); return 3; } if(remove(FOGLALATFAJL)) { fprintf(stderr, "Foglalat törlése nem sikerült. "); 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 "); return 1; } if((foglalat=socket(PF LOCAL, SOCK DGRAM, 0))== -1){ fprintf(stderr, "Foglalat létrehozása nem "
"sikerült. "); 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. "); return 3; } write(foglalat, argv[1], strlen(argv[1])+1); if(close(foglalat)) { fprintf(stderr, "Foglalat lezárása nem sikerült. "); return 4; } return 0; } A következő két fájl (fhoszerver.c és fhokliensc) összeköttetéses kapcsolaton 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ő- A dokumentum használata |
Tartalomjegyzék | Tárgymutató Vissza ◄ 188 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció Vissza ◄ 189 ► déséről a GNU/Linux SIGCHLD jelzéssel értesíti a szülőt. A jelzéskezelő 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. Leállítás: Ctrl+C "); if(signal(SIGCHLD, gyermekbef) == SIG ERR) { fprintf(stderr, "Gyermek jelzéskezelőjének " "beállítása nem sikerült. "); return 1; } if(signal(SIGINT, szulobef) == SIG ERR) { fprintf(stderr, "Szülő jelzéskezelőjének " "beállítása nem sikerült. "); return 2; } if((foglalat sz=socket(PF LOCAL, SOCK STREAM, 0))==-1){ fprintf(stderr, "Szerver foglalat létrehozása nem " "sikerült. "); return 3; } A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 189 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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. "); return 4; } if(listen(foglalat sz, KORLAT)) { fprintf(stderr, "Nem lehet fogadni a szerver " "foglalaton érkező kéréseket. "); 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. "); 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 " A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 190 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció Vissza ◄ 191 ► "sikerült. "); 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. "); return 2; } printf("Forint - Euró átszámítás " "%9s|%9s ---------+--------- ", "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 ", forint,
euro); } folytatas = 0; write(foglalat, &folytatas, sizeof(folytatas)); close(foglalat); return 0; } 8.42 Az internet névtér Az internet névtér segítségével különböző számítógépeken futó folyamatok 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ízható 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 foglalkozunk 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 191 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció Vissza ◄ 192 ► A sin addr a connect esetében a szerver gép, illetve az azon futó alkalmazá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) egyedileg 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 foglalkozunk.) Ez a cím az IPv4 szabvány szerint 32 bites egész, melyet a könynyebb olvashatóság kedvéért 4 darab 8 bites szám formájában, tízes számrendszerben, azokat egymástól pontokkal elválasztva, szoktak felírni Pl 193.2241281 Az ilyen címekkel a számítógépek elegánsan tudnak dolgozni, 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 azonosítókat (nevet) rendelnek, a fenti címhez pl az rs1szehu-t, de egy gépnek 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ároló bájtok találhatóak a kisebb memóriacímeken) üzemmódban dolgoznak.
int inet aton(const char *karakterlanc, struct in addr *struktura); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 192 ► Programozás II. Folyamatközi kommunikáció 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 ennek 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 rendelkezhet 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] szinonimá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 visszaadja 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 193 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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. 0 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
legmagasabb é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 magasabb 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ájtsorrend 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); A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 194 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció Vissza ◄ 195 ► A halorovid 16 bites értéket a hálózati bájtsorrendről a számítógép bájtsorrendjé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 felhaszná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 zajlik. A kliens és a szerver
felváltva küldi egymásnak az egyszerű, 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özhetetlenek bizonyos ismeretek. A POP3 szerverek szabványos kapuja a 110-es. Az erre való kapcsoló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 felhaszná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észleteket á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övetkező 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. A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 195 ► Programozás II. Folyamatközi kommunikáció 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 ", uzenet[sorszam]); exit(sorszam); } char* olvas(int foglalat, char uzenet) { int olvasva; olvasva = read(foglalat, uzenet, MAX); if(olvasva < 1) return NULL; *(uzenet + olvasva) = ; if(strstr(uzenet, OK) == uzenet) return uzenet + sizeof(OK)
-1; else return NULL; } int ir(int foglalat, char* uzenet) { strcat(uzenet, " "); return write(foglalat, uzenet, strlen(uzenet)); } A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 196 ► Programozás II. A dokumentum használata | Tartalomjegyzék | Tárgymutató Folyamatközi kommunikáció 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ó "); return 0; } printf("Levelek számának lekérdezése POP3 " "kiszolgáló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 ", atoi(valasz)); if(!ir(foglalat, strcpy(buffer, "QUIT"))) hiba(5, foglalat); if(olvas(foglalat, buffer) == NULL) hiba(8, foglalat); close(foglalat); return 0; } A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 197 ► Programozás II. Folyamatközi kommunikáció 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ódszerek 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 nevesí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 parancssori 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éterké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 eredmé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 Számjegy Angol betűk Ékezetes betűk Darab 12 78 21 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 198 ► Programozás II. A
dokumentum használata | Tartalomjegyzék | Tárgymutató Irodalomjegyzék 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.eltehu/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/indexhtml [6] Prog.hu, http://www.proghu/cikkek/222/Egy+korszerubb +szerver-kliens+megoldas.html [7] Kallós Gábor: Párhuzamos programozás. http://rs1szifhu/~kallos/pp/ [8] Advanced Linux Programming, http://www.advancedlinuxprogrammingcom/ A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 199 ► Programozás II. Tárgymutató A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 200 ► Tárgymutató ” D ”C” hely,
8 daemon, 155 démon, 155 dev t típus, 98 difftime, 62 DIR típus, 143 dirent struktúra, 142 dup, 98 dup2, 171 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 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 200 ► Programozás II. Tárgymutató A dokumentum használata | Tartalomjegyzék | Tárgymutató 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 Vissza ◄ 201 ► 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 201 ► Programozás II. Tárgymutató A dokumentum használata | Tartalomjegyzék | Tárgymutató 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 Vissza ◄ 202 ► 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 202 ► Programozás II. Tárgymutató A dokumentum használata | Tartalomjegyzék | Tárgymutató 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 Vissza ◄ 203 ► 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza ◄ 203 ► Programozás II. Tárgymutató A dokumentum használata | Tartalomjegyzék | Tárgymutató 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 Vissza ◄ 204 ► ◄ 204 ► 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 A dokumentum használata | Tartalomjegyzék | Tárgymutató Vissza