Programov´ an´ı v UNIXu Jan Pechanec verze: 17. prosince 2007 (c) 1999 – 2004 Martin Beran (c) 2005 – 2007 Jan Pechanec
SISAL MFF UK, Malostransk´e n´ am. 25, 118 00 Praha 1
[email protected]
Organizaˇcn´ı vˇeci: • tento pˇredmˇet je 2/1, cviˇcen´ı bude jednou za dva t´ ydny v laboratoˇri UNIX • vˇsechny informace kter´e budete potˇrebovat a materi´ aly k pˇredn´ aˇsce jsou na http://www.devnull.cz/mff, vˇcetnˇe aktu´aln´ı verze tˇechto pozn´amkov´ ych slajd˚ u • je potˇreba se zapsat na cviˇcen´ı na webu • z´ apoˇcet je za z´ apoˇctov´ y program • zkouˇska m´a p´ısemnou a u ´ stn´ı ˇca´st, je nutn´e z´ıskat z´ apoˇcet pˇ red zkouˇ skou, vˇcetnˇe pˇredterm´ın˚ u • zkouˇset se bude to, co bude odpˇredneseno (kromˇe t´emat na vyplnˇen´ı pˇr´ıpadnˇe zbyl´eho ˇcasu). Vˇetˇsina informac´ı je ve slajdech, ale ˇrada d˚ uleˇzit´ ych podrobnost´ı m˚ uˇze chybˇet. • pˇredpoklady: – uˇzivatelsk´ a znalost UNIXu, programov´an´ı v shellu na u ´rovni pˇredn´ aˇsky ´ ,,Uvod do UNIXu a TCP/IP” – znalost jazyka C – znalost z´ akladn´ıch pojm˚ u teorie operaˇcn´ıch syst´em˚ u • tento text je pr˚ ubˇeˇznˇe doplˇ nov´an, ChangeLog zaˇc´ın´a na stranˇe 207.
1
Obsah • u ´ vod, v´ yvoj UNIXu a C, program´ atorsk´e n´ astroje • z´ akladn´ı pojmy a konvence UNIXu a jeho API • pˇr´ıstupov´a pr´ava, perifern´ı zaˇr´ızen´ı, syst´em soubor˚ u • manipulace s procesy, spouˇstˇen´ı program˚ u • sign´aly • synchronizace a komunikace proces˚ u • s´ıt’ov´a komunikace • vl´ akna, synchronizace vl´ aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu
• budeme se zab´ yvat hlavnˇe principy UNIXu a programov´an´ım pro UNIX pouze v jazyce C. • pˇ redn´ aˇ ska je pˇ rev´ aˇ znˇ e o syst´ emov´ ych vol´ an´ıch, tj. rozhran´ım mezi uˇ zivatelsk´ ym prostorem a j´ adrem • pˇri popisu API se budeme drˇzet normy Single UNIX Specification, version 3. Syst´emy podporuj´ıc´ı tuto specifikaci mohou pouˇz´ıvat oznaˇcen´ı UNIX 03. V souˇcasn´e dobˇe (09/2007) jsou to posledn´ı verze syst´em˚ u Solaris, AIX, HP-UX a Mac OS X. • pro konkr´ern´ı pˇr´ıklady budu pouˇz´ıvat vˇetˇsinou syst´emy FreeBSD a Solaris.
2
Obsah • u ´ vod, v´ yvoj UNIXu a C, program´ atorsk´ e n´ astroje • z´ akladn´ı pojmy a konvence UNIXu a jeho API • pˇr´ıstupov´a pr´ava, perifern´ı zaˇr´ızen´ı, syst´em soubor˚ u • manipulace s procesy, spouˇstˇen´ı program˚ u • sign´aly • synchronizace a komunikace proces˚ u • s´ıt’ov´a komunikace • vl´ akna, synchronizace vl´ aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu
Literatura v ˇ ceˇ stinˇ e 1. Skoˇcovsk´ y, L.: Principy a probl´ emy operaˇ cn´ıho syst´ emu UNIX. Science, 1993 2. Skoˇcovsk´ y, Ludˇek: UNIX, POSIX, Plan9. L. Skoˇcovsk´ y, Brno, 1998 3. Jelen, Milan: UNIX V - programov´ an´ı v syst´ emu. Grada, Praha 1993 4. Linux - Dokumentaˇ cn´ı projekt. Computer Press, 1998; http://www.cpress.cz/knihy/linux ˇ e 5. Herout, Pavel: Uˇ cebnice jazyka C. 2 d´ıly. Kopp, Cesk´ Budˇejovice, 2004 (4., respektive 2. pˇrepracovan´e vyd´an´ı)
3
Ohlednˇ e Unixu doporuˇ cuji sp´ıˇ se literaturu v anglick´ em jazyce. 1. vˇsestrann´ y u ´ vod do UNIXu, ale dost struˇcn´ a; Skoˇcovsk´ y je autorem v´ıce ˇcesk´ ych knih o Unixu, ale dnes jsou jiˇz v´ıce nebo m´enˇe zastaral´e 2. pokroˇcilejˇs´ı pohled, ale pˇredpokl´ad´ a pˇredbˇeˇzn´e znalosti, m´ısty tˇeˇzko straviteln´ a 3. programov´an´ı v C pro UNIX System V, pr´ace se soubory a s procesy, System V IPC, nepopisuje napˇr. vl´ akna a s´ıtˇe 4. o Linuxu bylo samozˇrejmˇe v ˇceˇstinˇe vyd´ano mnoho dalˇs´ıch knih 5. vynikaj´ıc´ı knihy o jazyce C
Literatura - design a principy syst´ emu 1. Uresh Vahalia: UNIX Internals: The New Frontiers. Prentice Hall; 1st edition, 1995 2. Bach, Maurice J.: The Design of the UNIX Operating System. Prentice Hall, 1986 3. McKusick, M. K., Neville-Neil, G. V.: The Design and Implementation of the FreeBSD Operating System. Addison-Wesley, 2004 4. McDougall, R.; Mauro, J.: Solaris Internals. Prentice Hall; 2nd edition, 2006. 5. Linux Documentation Project. http://tldp.org/
Tyto knihy se zab´ yvaj´ı stavbou Unixu, pouˇzit´ ymi algoritmy, strukturami apod., nejsou to kniho o programov´an´ı pod t´ımto syst´emem. 1. skvˇel´ a kniha, zab´ yv´ a se obecn´ ymi myˇslenkami UNIXu a porovn´av´ a syst´emy SVR4.2, 4.4BSD, Solarix 2.x a Mach. 12/2005 mˇelo vyj´ıt druh´e, doplnˇen´e vyd´an´ı. Term´ın se vˇsak nˇekolikr´at posunul, a nyn´ı (stav k 12/2007) toto druh´e vyd´an´ı st´ ale jeˇstˇe nen´ı k dispozici a aktu´aln´ı pl´anovan´ y term´ın je 03/2008. Tˇeˇzko ale ˇr´ıci, jestli k tomu jeˇstˇe nˇekdy v˚ ubec dojde. 2. klasick´a kniha o UNIXu, popis struktury a funkc´ı j´adra UNIX System V Rel. 2, ˇca´steˇcnˇe i 3; pˇrestoˇze je to kniha z dneˇsn´ıho pohledu jiˇz zastaral´a, lze ji poˇra´d jednoznaˇcnˇe doporuˇcit protoˇze to je jedna z nejlepˇs´ıch knih, co byla kdy o UNIXu naps´ ana. V roce 1993 vyˇsel ˇcesk´ y pˇreklad, Principy operaˇ cn´ıho syst´ emu UNIX, SAS.
4
3. popis struktury a funkc´ı j´ adra FreeBSD 5.2; tato kniha navazuje na klasickou knihu The Design and Implementation of the 4.4 BSD Operating System od stejn´eho autora (resp. jeden ze ˇctyˇr, uveden´ y jako prvn´ı) 4. nejlepˇs´ı kniha o operaˇcn´ım syst´emu Solaris. Obsahuje podrobn´e informace o tom, jak tento syst´em funguje vˇcetnˇe nejnovˇejˇs´ıch vˇec´ı z verze 10 jako jsou z´ ony, Crypto Framework, DTrace, Least Privilege model a dalˇs´ı. 5. domovsk´ a strana Linux dokumentaˇcn´ıho projektu
Literatura - programov´ an´ı 1. Stevens, W. R., Rago, S. A.: Advanced Programming in UNIX(r) Environment. Addison-Wesley, 2nd edition, 2005. 2. Rochkind, M. J.: Advanced UNIX Programming, Addison-Wesley; 2nd edition, 2004 3. Stevens, W. R., Fenner B., Rudoff, A. M.: UNIX Network Programming, Vol. 1 – The Sockets Networking API. Prentice Hall, 3rd edition, 2004 4. Butenhof, D. R.: Programming with POSIX Threads, Addison-Wesley; 1st edition, 1997 5. Unixov´e specifikace, viz http://www.unix.org 6. manu´ alov´e str´ anky (zejm. sekce 2, 3)
1. pravdˇepodobnˇe nen´ı lepˇs´ı knihy o programov´an´ı pod Unixem (neobsahuje s´ıt’ov´e programov´an´ı, to je v knize 3) 2. aktualizovan´e vyd´an´ı dalˇs´ı z klasick´ ych knih o programov´an´ı pod Unixem. Obsahuje i s´ıt’ov´e programov´an´ı a aˇc samozˇrejmˇe nen´ı tak podrobn´ a jako spojen´ı knih 1 a 3, m˚ uˇze to b´ yt nˇekdy naopak v´ yhodou. Tuto knihu jednoznaˇcnˇe doporuˇcuji, pokud chcete nˇeco kupovat. 3. klasick´a kniha o s´ıt’ov´em programov´an´ı, jedna z nejlepˇs´ıch k tomuto t´ematu; existuje i druh´ y d´ıl UNIX Network Programming, Volume 2: Interprocess Communications, kter´ a se zab´ yv´ a komunikac´ı mezi procesy (roury, POSIX IPC, System V IPC, synchronizace vl´ aken, RPC). 4. velmi dobr´ a a podrobn´ a kniha o programov´an´ı s vl´ akny 5. domovsk´ a str´ anka posledn´ıch specifikac´ı rozhran´ı UNIXu 6. podrobn´ y popis jednotliv´ ych funkc´ı (v Linuxu bˇeˇznˇe ne zcela dostaˇcuj´ıc´ı; manu´ alov´e str´ anky v tomto syst´emu jsou ˇcasto horˇs´ı kvality neˇz u syst´em˚ u ostatn´ıch)
5
. . . a spousta dalˇs´ıch knih, online dokumentac´ı a internetov´ ych zdroj˚ u, posledn´ı dobou vych´ az´ı pomˇernˇe hodnˇe knih o Linuxu, zamˇeˇren´ ych na pouˇz´ıv´ an´ı i programov´an´ı . . . jdˇete na http://www.amazon.com/ a zadejte kl´ıˇcov´e slovo “unix”. Pokud byste z Amazonu nˇeco kupovali, dejte pozor na to, ˇze mnoho knih m´a aktualizovan´ a vyd´an´ı i po nˇekolika m´alo letech, nˇekdy i levnˇejˇs´ı neˇz ta p˚ uvodn´ı, kter´ a jsou vˇsak st´ ale na skladu a v on-line nab´ıdce; tak at’ zbyteˇcnˇe nekoup´ıte starˇs´ı vyd´an´ı neˇz to aktu´aln´ı. Nav´ıc se vyplat´ı zkontrolovat i u pˇr´ısluˇsn´eho vydavatelstv´ı, ˇze nen´ı v brzk´e dobˇe napl´ anov´ano vyd´an´ı nov´e – tato informace nˇekdy na Amazonu je, nˇekdy ne. . . . na Amazonu se m˚ uˇze vyplatit nakoupit knihy z druh´e ruky, protoˇze jsou ˇcasto v´ yraznˇe levnˇejˇs´ı neˇz knihy nov´e. Probl´em je, ˇze vˇetˇsinou nen´ı moˇzn´e je poslat ˇ ale mus´ı v´am je nˇekdo pˇriv´ezt. pˇr´ımo do CR,
Literatura - historie UNIXu • Peter Salus: A Quarter Century of UNIX, Addison-Wesley; 1st edition (1994) • Libes D., Ressler, S.: Life With Unix: A Guide for Everyone, Prentice Hall (1989) • Open Sources: Voices from the Open Source Revolution, kapitola Twenty Years of Berkeley Unix From AT&T-Owned to Freely Redistributable; O’Reilly (1999); on-line na webu . . . mnoho materi´ al˚ u na webu; ˇcasto vˇsak obsahuj´ıc´ı ne zcela pˇresn´e informace
• kapitola o BSD Unixu napsan´a Marshallem Kirk McKusickem je opravdu v´ yborn´ a
6
(Pre)historie UNIXu • 1925 – Bell Telephone Laboratories – v´ yzkum v komunikac´ıch (napˇr. 1947: transistor) v r´ amci AT&T • 1965 – BTL s General Electric a MIT v´ yvoj OS Multics (MULTIplexed Information and Computing System) • 1969 – Bell Labs opouˇst´ı projekt, Ken Thompson p´ıˇse assembler, z´ akladn´ı OS a syst´em soubor˚ u pro PDP-7 • 1970 – Multi-cs ⇒ Uni-cs ⇒ Uni-x • 1971 – UNIX V1, a portov´an na PDP-11 • prosinec 1971 – prvn´ı edice UNIX Programmer’s Manual
• AT&T = American Telephone and Telegraph Company • po odchodu BTL z projektu Multics prodala GE svoji poˇc´ıtaˇcovou divizi firmˇe Honeywell vˇcetnˇe projektu Multics, kter´ y se pak pod jej´ı patronac´ı d´ ale vyv´ıjel (virtu´ aln´ı pamˇet’, multiprocesory, . . . ). Posledn´ı instalace Multicsu fungovala na kanadsk´em Ministerstvu obrany (Canadian Department of National Defence) a syst´em byl napˇr´ıklad jeˇstˇe aktivnˇe pouˇz´ıv´ an pro vojensk´e operace bˇehem v´alky v Persk´em z´ alivu. Definitivn´ı shutdown byl proveden 31. ˇr´ıjna 2000. V´ıce informac´ı na http://www.multicians.org. • pˇred poˇca´tkem pr´ace na v´ yvojov´em prostˇred´ı pro PDP-7 napsal Thompson program Space Travel, kter´ y byl vyvinut na jin´em prostˇred´ı (Honeywell 635) a na p´ asce pˇrenesen na PDP-7. • celkem bylo 10 edic´ı tohoto manu´ alu, koresponduj´ıc´ı deseti verz´ım UNIXu vznikl´ ych v BTL. • UNIX V1 nemˇel pipe() !!! • za povˇsimnut´ı stoj´ı, ˇze UNIX je zhruba o 10 let starˇs´ı neˇz DOS • syst´em Multics mˇel 9 hlavn´ıch c´ıl˚ u, jak pops´ ano v ˇcl´ anku Introduction and Overview of the Multics System z roku 1965. Za nejzaj´ımavˇejˇs´ı c´ıl bych povaˇzoval poˇzadavek na nepˇreruˇsovan´ y bˇeh syst´emu. • Multics byl napsan´ y v jazyce PL/I (Programming Language #1), tedy dˇr´ıve neˇz byl UNIX pˇrepsan´ y do C ! • Multicsu byl v roce 1980 udˇelen jako prvn´ımu syst´emu level B2. Po nˇekolik let to byl jedin´ y syst´em s t´ımto bezpeˇcnost´ım levelem.
7
• GE byla zaloˇzena v roce 1892 slouˇcen´ım dvou spoleˇcnost´ı, z nichˇz jedna byla Edison General Electric Company zaloˇzen´a roku 1879 Thomasem Alvou Edisonem (vyn´ alezce ˇza´rovky, filmov´e kamery, . . . ); v souˇcasn´e dobˇe jej´ı dceˇrinn´e spoleˇcnosti pokr´ yvaj´ı mnoho oblast´ı, vˇcetnˇe dod´ avky jednoho ze dvou typ˚ u motor˚ u pro Airbus 380 nebo bankovnictv´ı. • PDP = Programmed Data Processor. Prvn´ı typ, PDP-1, se prod´ avala za $120.000 v dobˇe, kdy se jin´e poˇc´ıtaˇce prod´ avaly za ceny pˇres mili´ on. To byla tak´e strategie fy DEC - pojem computer tehdy znamenal drahou vˇec, potˇrebuj´ıc´ı s´ al a t´ ym lid´ı, kter´ y se o to vˇsechno bude starat. Proto DEC sv´e maˇsiny nenaz´ yval poˇc´ıtaˇci, ale pravˇe slovem PDPs. • PDP-11 je legend´ arn´ı maˇsina od firmy DEC, postupnˇe vznikaly verze PDP1 az PDP-16, kromˇe PDP-2, PDP-13. Existuj´ı PDP-11 syst´emy, kter´e jeˇstˇe dnes bˇeˇz´ı, a tak´e firmy, kter´e pro nˇe vyr´ abˇej´ı n´ ahradn´ı d´ıly.
Historie UNIXu, pokraˇ cov´ an´ı • u ´ nor 1973 – UNIX V3 obsahoval cc pˇrekladaˇc (jazyk C byl vytvoˇren Dennisem Ritchiem pro potˇreby UNIXu) • ˇr´ıjen 1973 – UNIX byl pˇredstaven veˇrejnosti ˇcl´ankem The UNIX Timesharing System na konferenci ACM • listopad 1973 – UNIX V4 pˇ reps´ an do jazyka C • 1975 – UNIX V6 byl prvn´ı verz´ı UNIXu bˇeˇznˇe k dost´ an´ı mimo BTL • 1979 – UNIX V7, pro mnoh´e “the last true UNIX”, obsahoval uucp, Bourne shell; velikost kernelu byla pouze 40KB !!! • 1979 – UNIX V7 portov´an na 32-bitov´ y VAX-11 • 1980 – Microsoft pˇr´ıch´ az´ı s XENIXem, kter´ y je zaloˇzen´ y na UNIXu V7
• ACM = Association for Computing Machinery, zaloˇzena 1947. • akt pˇ reps´ an´ı UNIXu do jazyka C byl moˇ zn´ a nejv´ yznamnˇ ejˇ s´ım momentem v historii tohoto syst´ emu ⇒ UNIX mohl b´ yt mnohem jednoduˇseji portov´an na jin´e architektury • na verzi 6 je zaloˇzena legend´ arn´ı kniha A commentary on the Unix Operating System, jej´ıˇz autorem je John Lions. • Microsoft neprod´ aval XENIX pˇr´ımo, ale licencoval ho OEM v´ yrobc˚ um (Original Equipment Manufacturer) jako byl Intel, SCO a jin´ı. Jin´e firmy pak XENIX d´ ale portovaly na 286 (Intel) a 386 (SCO, 1987). Na webu je moˇzn´e naj´ıt zaj´ımav´e informace popisuj´ıc´ı tuto dobu a tehdy kladn´ y vztah Microsoftu k UNIXu. 8
• pokud v´as v´ıce zaj´ım´ a historie unixu, pod´ıvejte se na wikipedii na heslo “unix” a skrz odkazy m´ate na dlouho co ˇc´ıst.
Divergence UNIXu • pol. 70. let – uvolˇ nov´an´ı UNIXu na univerzity: pˇredevˇs´ım University of California v Berkeley • 1979 – z UNIX/32V (zm´ınˇen´ y port na VAX) poskytnut´eho do Berkeley se vyv´ıj´ı BSD Unix (Berkeley Software Distribution) verze 3.0; posledn´ı verze 4.4 v roce 1993 • 1982 AT&T, vlastn´ık BTL, m˚ uˇze vstoupit na trh poˇc´ıtaˇc˚ u (zak´ az´ano od roku 1956) a pˇr´ıch´ az´ı s verz´ı System III (1982) aˇz V.4 (1988) – tzv. SVR4 • vznikaj´ı UNIX International, OSF (Open Software Foundation), X/OPEN, . . . • 1991 – Linus Torvalds zah´ajil v´ yvoj OS Linux, verze j´adra 1.0 byla dokonˇcena v r. 1994
• UNIX je univerz´ aln´ı operaˇcn´ı syst´em funguj´ıc´ı na ˇsirok´e ˇsk´ ale poˇc´ıtaˇc˚ u od embedded a handheld syst´em˚ u (Linux), pˇres osobn´ı poˇc´ıtaˇce aˇz po velk´e servery a superpoˇc´ıtaˇce. • UNIX V3 = UNIX verze 3, UNIX V.4 = system 5 release 4 atd., tj. UNIX V3 != SVR3. • UNIX System III tedy nen´ı UNIX V3; v t´eto dobˇe (pozdn´ı 70. l´eta) bylo v BTL nˇekolik skupin, kter´e pˇr´ısp´ıvaly do v´ yvoje UNIXu. Vx verze byly vyv´ıjeny v r´ amci Computer Research Group, dalˇs´ı skupiny byly Unix System Group (USG), Programmer’s WorkBench (PWB). Dalˇs´ı vˇetv´ı UNIXu byl Columbus UNIX t´eˇz v r´ amci BT. Na tˇechto r˚ uzn´ ych verz´ıch je pr´avˇe zaloˇzena verze System III. Z´ ajemce o v´ıce informac´ı odkazuji na web. • UNIX se rozˇstˇepil na dvˇe hlavn´ı vˇetve: AT&T a BSD, jednotliv´ı v´ yrobci pˇrich´ azeli s vlastn´ımi modifikacemi. Jednotliv´ e klony od sebe navz´ ajem pˇ reb´ıraly vlastnosti. • Berkeley univerzita z´ıskala jako jedna z prvn´ıch licenci UNIXu v roce 1974. Bˇehem nˇekolika let studenti (jedn´ım z nich byl Bill Joy, pozdˇejˇs´ı zakladatel firmy Sun Microsystems a autor C-shellu) vytvoˇrili SW bal´ık Berkeley Software Distribution (BSD) a prod´ avali ho v roce 1978 za $50. Tyto poˇca´teˇcn´ı verze BSD obsahovaly pouze SW a utility (prvn´ı verze: Pascal pˇrekladaˇc, editor ex ), ne syst´em ani ˇza´dn´e jeho zmˇeny. To pˇriˇslo aˇz s verz´ı 3BSD. verze 4BSD vznik´a roku 1980 jiˇz jako projekt financovan´ y agenturou DARPA a veden´ y Billem Joyem. Trp´ı probl´emy spojen´ ymi s nedostateˇcn´ ym v´ ykonem a vznik´a tak vyladˇen´ y syst´em 4.1BSD dod´ avan´ y od roku 1981. 9
• 4.1BSD mˇelo b´ yt p˚ uvodnˇe 5BSD, ale pot´e, co AT&T vzneslo n´ amitky, ˇze by si z´ akazn´ıci mohli pl´est 5BSD se syst´emem System V, pˇreˇslo BSD na ˇc´ıslov´an´ı 4.xBSD. Bˇeˇznou vˇec´ı bylo, ˇze neˇz ps´at vlastn´ı k´od, v´ yvoj´aˇri z Berkeley se radˇeji nejdˇr´ıve pod´ıvali kolem, co je jiˇz hotov´e. Tak BSD napˇr´ıklad pˇrevzalo virtu´ aln´ı pamˇet’ z Machu a nebo NFS-compatibiln´ı k´od vyvinut´ y na jedn´e kanadsk´e univerzitˇe. • v´ yrobci hardware dod´ avali varianty UNIXu pro sv´e poˇc´ıtaˇce a komercializace tak jeˇstˇe zhorˇsila situaci co t´ yˇce diverzifikace UNIXu • v 80-t´ ych letech se proto zaˇcaly objevovat snahy o standardizaci. Standard ˇr´ık´ a, jak m´a vypadat syst´em navenek (pro uˇzivatele, program´ atora a spr´ avce), nezab´ yv´ a se implementac´ı. C´ılem je pˇrenositelnost aplikac´ı i uˇzivatel˚ u. vˇsechny syst´emy totiˇz z d´ alky vypadaly jako UNIX, ale z bl´ızka se liˇsily v mnoha d˚ uleˇzit´ ych vlastnostech. System V a BSD se napˇr. liˇsily v pouˇzit´em filesyst´emu, s´ıt’ov´e architektuˇre i v architektuˇre virtu´ aln´ı pamˇeti. • kdyˇz v roce 1987 firmy AT&T a Sun (jehoˇz tehdejˇs´ı SunOS byl zaloˇzen´ y na BSD) spojily svoje u ´ sil´ı na vyvinut´ı jednoho syst´emu, kter´ y by obsahoval to nejlepˇs´ı z obou vˇetv´ı, kromˇe nadˇsen´ ych reakc´ı to vzbudilo i strach u mnoha dalˇs´ıch v´ yrobc˚ u unixov´ ych syst´em˚ u, kteˇr´ı se b´ ali, ˇze by to pro obˇe firmy znamenalo obrovskou komerˇcn´ı v´ yhodu. Vznik´ a proto Open Software Foundation (nezamˇen ˇ ovat za FSF), a zakl´ adaj´ıc´ımi ˇcleny byly mimo jin´e firmy Hewlett-Packard, IBM a Digital. Z toho vzeˇsl´ y syst´em OSF/1 ale nebyl pˇr´ıliˇs u ´ spˇeˇsn´ y, a dod´ aval ho pouze Digital, kter´ y ho pˇrejmenoval na Digital UNIX. Zaj´ımavost´ı je, ˇze syst´em je postaven´ y na mikroj´adru Mach. Po akvizici Digitalu Compaqem byl syst´em pˇrejmenov´an na Tru64 a s t´ımto jm´enem je d´ ale podporov´an firmou Hewlett-Packard, kter´ a se v roce 2002 s Compaqem spojila. Mezit´ım firmy AT&T a Sun kontrovaly zaloˇzen´ım UNIX International. Toto obdob´ı pˇrelomu 80-t´ ych a 90-t´ ych let se naz´ yv´ a Unix Wars – boj o to, co bude “standardn´ım unixem”. • OSF a UI se staly velk´ ymi rivaly, ale velmi rychle se stˇretly s neˇcekan´ ym protivn´ıkem - s firmou Microsoft. • (1992) 386BSD zaloˇzen´e na Networking Release 2 ; Bill Jolitz vytvoˇril 6 chybˇej´ıc´ıch soubor˚ u a dal tak dohromady funkˇcn´ı BSD syst´em pro i386. Tento syst´em se stal z´ akladem syst´em˚ u NetBSD a FreeBSD (a dalˇs´ıch, z tˇechto dvou syst´em˚ u vych´ azej´ıc´ıch). • (1995) 4.4BSD-Lite Release 2, po kter´e n´ asleduje rozpuˇstˇen´ı CSRG, kter´ a skoro 20 let pilotovala v´ yvoj BSD vˇetve. V´ıce jiˇz zm´ınˇen´ a kapitola o BSD Unixu.
10
Souˇ casn´ e UNIXy Hlavn´ı komerˇcn´ı unixov´e syst´emy: • Sun Microsystems: SunOS (nen´ı jiˇz d´ ale vyv´ıjen), Solaris • SGI: IRIX • IBM: AIX • HP: HP-UX • HP (pˇredt´ım Compaq): Tru64 UNIX • SCO: SCO Unix • Novell: UNIXware Open source: • FreeBSD, NetBSD, OpenBSD, OpenSolaris • Linux distribuce
• kdyˇz jsem cca v roce 1998 projel nmapem vˇsechny DNS root servery, abych zjistil na kter´ ych syst´emech bˇeˇz´ı, bylo 80% z nich na SunOS/Solaris. IRIX je zase syst´em, kter´ y po mnoho let ovl´ adal televizn´ı/filmov´ y pr˚ umysl (napˇr. Pixar studio kde na IRIXu vznikly filmy jako A Bug’s Life, Toy Story a dalˇs´ı). A na AIX napˇr´ıklad bˇeˇzel Blue Deep, paraleln´ı superpoˇc´ıtaˇc, kter´ y v roce 1997 porazil v ˇsesti fascinuj´ıc´ıch z´ apasech 3.5 ku 2.5 u ´ˇraduj´ıc´ıho velmistra ˇsachu Garriho Kasparova. Jin´ ymi slovy – kaˇzd´ y syst´em m´a svoje u ´ spˇechy. • jak jiˇz bylo zm´ınˇeno, Tru64 UNIX vych´ az´ı z OSF/1 firmy DEC. Ta pˇredt´ım dod´ avala Ultrix, zaloˇzen´ y na BSD unixu. • OpenSolaris je projekt vznikl´ y v ˇcervnu 2005 a je zaloˇzen na podmnoˇzinˇe zdrojov´ ych text˚ u v´ yvojov´e verze Solarisu (kernel, knihovny, pˇr´ıkazy) k datu uveden´ı. Ofici´ aln´ı distribuce zaloˇzen´a na OpenSolarisu se jmenuje Solarix Express. Distribuce vzeˇsl´e z komunity jsou napˇr´ıklad LiveCD BeleniX, SchilliX a Nexenta. • pozor na to, ˇze Linux je pouze j´adro, ne syst´em, na rozd´ıl tˇreba od FreeBSD. OpenSolaris je nˇeco mezi t´ım, j´adro + drivery, z´ akladn´ı pˇr´ıkazy a knihovny, a v bin´ arn´ı podobˇe to m´a nˇeco pˇres 100MB. V obou pˇr´ıpadech je tedy spr´ avn´e pouˇz´ıvat spojen´ı “Linux (OpenSolaris) distribuce”, kdyˇz se bav´ıme o cel´em syst´emu. • kaˇzd´ a komerˇcn´ı varianta vych´ azela z jednoho ze dvou hlavn´ıch syst´em˚ u – UNIX V nebo BSD, a pˇrid´ avala si sv´e vlastnosti • vznik´a velk´ y poˇcet r˚ uzn´ ych standard˚ u (viz n´ asleduj´ıc´ı strana), skoro jako poˇcet r˚ uzn´ ych verz´ı UNIX˚ u. Nakonec se vˇetˇsina v´ yrobc˚ u shodla na nˇekolika z´ akladn´ıch standardech.
11
• graf zn´ azorˇ nuj´ıc´ı historii Unixu a z´ avislosti mezi syst´emy na 19-ti A4 listech ve form´atu PS/PDF je k nalezen´ı na http://www.levenez.com/unix
12
Standardy UNIXu • SVID (System V Interface Definition) – ,,fialov´ a kniha”, kterou AT&T vydala poprv´e v roce 1985 – dnes ve verzi SVID3 (odpov´ıd´ a SVR4)
• POSIX (Portable Operating System based on UNIX) – s´erie standard˚ u organizace IEEE znaˇcen´ a P1003.xx, postupnˇe je pˇrej´ım´ a vrcholov´ y nadn´ arodn´ı org´ an ISO
• XPG (X/Open Portability Guide) – doporuˇcen´ı konsorcia X/Open, kter´e bylo zaloˇzeno v r. 1984 pˇredn´ımi v´ yrobci platforem typu UNIX
• Single UNIX Specification – standard organizace The Open Group, vznikl´e v roce 1996 slouˇcen´ım X/Open a OSF – dnes Version 3 (UNIX 03), pˇredchoz´ı Version 2 (UNIX 98) – splnˇen´ı je nutnou podm´ınkou pro uˇzit´ı obchodn´ıho n´ azvu UNIX
• AT&T dovolila v´ yrobc˚ um naz´ yvat svoji komerˇcn´ı UNIX variantu “System V” pouze pokud splˇ novala podm´ınky standardu SVID. AT&T tak´e publikovala System V Verification Suite (SVVS), kter´e ovˇeˇrilo, zda dan´ y syst´em odpov´ıd´a standardu. • POSIX (Portable Operating System Interface) je standardizaˇcn´ı snaha organizace IEEE (Institute of Electrical and Electronics Engineers). Prvn´ım dokumentem je IEEE Std POSIX1003.1-1988, dˇr´ıve oznaˇcovan´ y prostˇe jako POSIX, dnes zn´ am´ y sp´ıˇse jako POSIX.1, protoˇze POSIXem se nyn´ı m´ın´ı sada vz´ ajemnˇe souvisej´ıc´ıch standard˚ u. POSIX.1 obsahuje programovac´ı API, tj. pr´ace s procesy, sign´aly, soubory, ˇcasovaˇci atd. S mal´ ymi zmˇenami byl pˇrevzat organizac´ı ISO, a oznaˇcovan´ y jako POSIX1990. Tento standard byl s´ am o sobˇe velk´ yu ´ spˇech, ale st´ ale jeˇstˇe nespojoval t´ abory System V a BSD, protoˇze v sobˇe napˇr´ıklad nezahrnoval BSD sockety nebo IPC (semafory, zpr´ avy, sd´ılen´a pamˇet’) ze System V. Oznaˇcen´ı POSIX vymyslel Richard Stallman, tedy ˇclovˇek, kter´ y v roce 1983 zaloˇzil GNU projekt. Souˇca´st´ı standardu je i “POSIX conformance test suite (PCTS)”, kter´ y je volnˇe k dispozici. • SUSV3 je spoleˇcn´ y standard The Open Group, IEEE (Std. 1003.1, 2003 Edition) a ISO (ISO/IEC 9945-2003). • dnes je asi nejd˚ uleˇzitˇejˇs´ı Single UNIX Specification, kter´e se budeme drˇzet i my. Tato norma je podm´ınkou pro uˇzit´ı n´ azvu UNIX, Je postavena na b´ azi POSIXu. Popis datov´ ych struktur a algoritm˚ u j´adra v tomto materi´ alu bude vˇetˇsinou vych´ azet ze System V Rel. 4.
13
Jazyk C • t´emˇeˇr cel´ y UNIX je napsan´ y v C, pouze nejniˇzˇs´ı strojovˇe z´ avisl´a ˇca´st v assembleru ⇒ pomˇernˇe snadn´ a pˇrenositelnost • navrhl Dennis Ritchie z Bell Laboratories v roce 1972. • n´ asledn´ık jazyka B od Kena Thomsona z Bell Laboratories. • vytvoˇren jako prostˇredek pro pˇrenos OS UNIX na jin´e poˇc´ıtaˇce – siln´ a vazba na UNIX. • varianty jazyka: – p˚ uvodn´ı K&R C – standard ANSI/ISO C • u ´ spˇ ech jazyka C daleko pˇ res´ ahl u ´ spˇ ech samotn´ eho UNIXu
• CPL ⇒ BCPL ⇒ B (Thompson, interpret) ⇒ C • K&R C – jazyk C tak, jak je popsan´ y v klasick´e knize Brian W. Kernighan, Dennis M. Ritchie: The C Programming Language (Prentice-Hall, 1978). • v roce 1983 ANSI (American National Standards Institute) zformoval v´ ybor pro vytvoˇren´ı C standardu. Po dlouh´em a pracn´em procesu byl v roce 1989 standard koneˇcnˇe hotov, a je zn´ am´ y nejˇcastˇeji jako “ANSI C”, pˇr´ıpadnˇe jako C89 (napˇr´ıklad pˇrekladaˇc pro tuto normu se v Sun Studiu jmenuje c89, jelikoˇz i to samotn´e jm´eno programu mus´ı b´ yt podle normy). Druh´e vyd´an´ı K&R knihy (1988) je jiˇz upraven´e pr´avˇe pro nadch´ azej´ıc´ı ANSI C. V roce 1990 bylo ANSI C adoptov´ano organizac´ı ISO jako ISO/IEC 9899:1990; tento C standard tak m˚ uˇze b´ yt nˇekdy oznaˇcov´an i jako C90. • se C standardem se pak nˇejakou dobu neh´ ybalo, aˇz na konci 90-t´ ych let proˇsel dalˇs´ı reviz´ı v r´ amci ISO a vznik´a ISO 9899:1999, ˇcastˇeji oznaˇcovan´ y jako C99. V roce 2000 pak naopak tento standard pˇrevzal ANSI. • rozd´ıly mezi C89 a C99 jsou mimo jin´e zahrnut´ı inline funkc´ı, definic´ı promˇenn´ ych napˇr´ıklad i do for konstrukce, jednoˇra´dkov´ ych koment´ aˇr˚ u pomoc´ı //, nov´ ych funkc´ı jako snprintf() apod.
14
Form´ aty dat • poˇrad´ı bajt˚ u – z´ avis´ı na architektuˇre poˇc´ıtaˇce –
–
big endian: 0x11223344 =
11
22
33
44
addr +
0
1
2
3
little endian: 0x11223344 =
44
33
22
11
addr +
0
1
2
3
• ˇra´dky textov´ ych soubor˚ u konˇc´ı v UNIXu znakem LF (nikoliv CRLF). Vol´ an´ı putc(’\n’) tedy p´ıˇse pouze jeden znak. • big endian – SPARC, MIPS, s´ıt’ov´e poˇrad´ı bajt˚ u • little endian – Intel
• velk´ y pozor na v´ ystupy program˚ u typu hexdump, kter´e defaultnˇe vypisuj´ı soubor ve 2-bajtov´ ych ˇc´ıslech, coˇz sv´ ad´ı vidˇet soubor jinak, neˇz jak je opravdu zapsan´ y na disku; viz pˇr´ıklad (i386, FreeBSD): $ echo -n 1234 > test $ hexdump test 0000000 3231 3433 0000004 je samozˇrejmˇe moˇzn´e pouˇz´ıt jin´ y form´at v´ ystupu: $ hexdump -C test 00000000 31 32 33 34 00000004
|1234|
• UNIX norma pˇr´ıkaz hexdump nem´ a, ale definuje od (octal dump), takˇze zde je jeho hexdumpu ekvivalentn´ı form´at v´ ypisu na SPARCu (Solaris); vˇsimnˇete si zmˇeny oproti v´ ypisu pod FreeBSD! $ od -tx2 test 0000000 3132 3334 0000004
15
Deklarace a definice funkce • K&R – deklarace n´ avratov´ y_typ indentifik´ ator(); – definice n´ avratov´ y_typ indentifik´ ator(par [,par...]); typ par;... { /* tˇ elo funkce */ }
• ANSI – deklarace n´ avratov´ y_typ indentifik´ ator(typ par [,typ par...]); – definice n´ avratov´ y_typ indentifik´ ator(typ par [,typ par...]); { /* tˇ elo funkce */ }
• pouˇz´ıvejte pouze novˇejˇs´ı (ANSI) typ deklarac´ı a vˇzdy deklarujte prototypy funkc´ı, tj. inkludujte hlaviˇckov´e soubory. V´ yjimkou m˚ uˇze asi jen to, pokud budete pracovat s k´odem, kter´ y byl napsan´ y podle K&R. • r˚ uzn´ ymi z´ apisy deklarac´ı se dost´ av´ ame rovnou i k r˚ uzn´ ym styl˚ um psan´ı zdrojov´ ych text˚ u. Nˇekter´e syst´emy to pˇr´ıliˇs neˇreˇs´ı (Linux), jin´e syst´emy maj´ı velmi striktn´ı pravidla pro psan´ı zdrojov´ ych text˚ u (napˇr. Solaris, viz on-line C Style and Coding Standards for SunOS ). Skoro kaˇzd´ y UNIXov´ y syst´em m´a program indent(1), kter´ y v´am pomoc´ı pˇrep´ınaˇc˚ u pˇreform´atuje jak´ ykoli C zdrojov´ y text do poˇzadovan´eho v´ ystupu.
16
C style • vˇec zd´ anlivˇe podˇradn´ a, pˇritom extr´emnˇe d˚ uleˇzit´ a–u ´ prava zdrojov´ ych k´od˚ u programu • mnoho zp˚ usob˚ u jak ano: int main(void) { char c; int i = 0; printf("%d\n", i); return (0); }
• u C stylu je nejd˚ uleˇzitˇejˇs´ı to, aby byl konzistentn´ı. Pokud skupina program´ ator˚ u pracuje na jednom projektu, nen´ı zas aˇz tak d˚ uleˇzit´e, na jak´em stylu se dohodnout (pokud je alespoˇ n trochu rozumn´ y), ale aby se dohodli. Jednotn´ y a dobˇre zvolen´ y styl ˇsetˇr´ı ˇcas a br´an´ı zbyteˇcn´ ym chyb´am.
17
C style (cont.) • mnoho zp˚ usob˚ u jak NE (tzv. assembler styl): int main(void) { int i = 0; char c; printf("%d\n", i); return (0); } • nebo (schizofrenn´ı styl): int main(void) { int i = 0; char c; if (1) printf("%d\n", i);i=2; return (0); }
• pamatujte na to, ˇze dobr´ y styl zdrojov´ ych k´od˚ u je i vizitkou program´ atora. Kdyˇz se v r´ amci pˇrij´ımac´ıch pohovor˚ u odevzd´avaj´ı i uk´azkov´e k´ody, tak hlavn´ı ´ d˚ uvod nen´ı ten, aby se zjistilo, ˇze v´am dan´ y program funguje. . . Uprava samotn´eho zdrojov´eho textu je jedn´ım z kriteri´ı, protoˇze to pˇrenesenˇe m˚ uˇze svˇedˇcit i o dalˇs´ıch skuteˇcnostech – nˇekdo napˇr. bude odhadovat, ˇze pokud p´ıˇsete neˇcist´ y a neupraven´ y k´od, tak jste moˇzn´ a jeˇstˇe nepracovali na nˇeˇcem opravdu sloˇzit´em ˇci nˇeˇcem zahrnuj´ıc´ım spolupr´aci s v´ıce program´ atory, protoˇze v tom pˇr´ıpadˇe je rozumnˇe ˇcist´ y k´od jednou z podm´ınek u ´ spˇechu a jednou ze zkuˇsenost´ı, kter´e z takov´e spolupr´ace vych´ azej´ı. Toto je samozˇrejmˇe zˇca´sti subjektivn´ı n´ azor, ale ˇcist´ ym k´odem nic nezkaz´ıte, minim´alnˇe nekaz´ıte oˇci sv´ ym cviˇc´ıc´ım.
18
Utility cc, c99∗ , gcc† †
pˇrekladaˇc C
CC, g++
pˇrekladaˇc C++
ld
spojovac´ı program (linker)
ldd
pro zjistˇen´ı z´ avislost´ı dynamick´eho objektu
cxref ∗
kˇr´ıˇzov´e odkazy ve zdrojov´ ych textech v C
∗
sccs , rcs,cvs make
∗
spr´ ava verz´ı zdrojov´eho k´odu ˇr´ızen´ı pˇrekladu podle z´ avislost´ı
∗
ar
spr´ ava knihoven objektov´ ych modul˚ u
dbx, gdb†
debuggery
prof, gprof † ∗
UNIX 03
profilery †
GNU
UNIX 03 • standardn´ı pˇr´ıkaz vol´ an´ı kompil´ atoru a linkeru C je c99 (podle ISO normy pro C z roku 1999) • cb (C program beautifier) nen´ı • pro spr´ avu verz´ı je sccs • debuggery a profilery nejsou
19
Konvence pro jm´ ena soubor˚ u *.c
jm´ena zdrojov´ ych soubor˚ u program˚ uvC
*.cc
jm´ena zdrojov´ ych soubor˚ u program˚ u v C++
*.h
jm´ena hlaviˇckov´ ych soubor˚ u (header˚ u)
*.o
pˇreloˇzen´e moduly (object files)
a.out
jm´eno spustiteln´eho souboru (v´ ysledek u ´ spˇeˇsn´e kompilace)
/usr/
koˇren stromu syst´emov´ ych header˚ u
/usr/lib/lib*.a
statick´e knihovny objektov´ ych modul˚ u
/usr/lib/lib*.so
um´ıstˇen´ı dynamick´ ych sd´ılen´ ych knihoven objektov´ ych modul˚ u
statick´ e knihovny – pˇri linkov´an´ı se stanou souˇca´st´ı v´ ysledn´eho spustiteln´eho programu. Dnes se uˇz moc nepouˇz´ıv´ a. sd´ılen´ e knihovny – program obsahuje pouze odkaz na knihovnu, pˇri spuˇstˇen´ı programu se potˇrebn´e knihovny naˇctou do pamˇeti ze soubor˚ u *.so a pˇrilinkuj´ı. • dnes se vˇetˇsinou pouˇz´ıvaj´ı sd´ılen´e knihovny, protoˇze nezab´ıraj´ı tolik diskov´eho prostoru (knihovna je na disku jednou, nen´ı souˇca´st´ı kaˇzd´eho spustiteln´eho souboru) a snadnˇeji se upgraduj´ı (staˇc´ı instalovat novou verzi knihovny, nen´ı tˇreba pˇrelinkovat programy). Posledn´ı verze Solarisu uˇz napˇr´ıklad v˚ ubec neobsahuje libc.a, d´ıky ˇcemuˇz jiˇz program´ ator nem˚ uˇze vytvoˇrit statickou bin´ arku, aniˇz by nemˇel dostateˇcn´e znalosti syst´emu. • nˇekdy se bez statick´ ych knihoven neobejdeme. V nˇekter´ ych situac´ıch nen´ı moˇzn´e pouˇz´ıt knihovny dynamick´e, spustiteln´e soubory jsou takzvan´e standalone binaries a pouˇzit´ı naleznou napˇr´ıklad pˇri bootov´an´ı operaˇcn´ıho syst´emu.
20
Princip pˇ rekladu
util.c msg() { puts(); }
syst´emov´a knihovna
program a.out
main msg ??
main msg
util.o msg puts ??
linker
main() { msg(); }
objektov´e moduly main.o
pˇrekladaˇc
zdrojov´e moduly main.c
msg puts
puts
puts
• u sloˇzitˇejˇs´ıch program˚ u b´ yv´ a zvykem rozdˇelit zdrojov´ y text programu do nˇekolika modul˚ u, kter´e obsahuj´ı pˇr´ıbuzn´e funkce a tyto moduly se pak mohou pˇrekl´ adat zvl´aˇst’ (dokonce kaˇzd´ y modul m˚ uˇze b´ yt v jin´em jazyce a pˇrekl´ ad´ an jin´ ym pˇrekladaˇcem). V´ yhodou je jednak urychlen´ı pˇrekladu (pˇrekl´ adaj´ı se vˇzdy jen moduly zmˇenˇen´e od posledn´ıho pˇrekladu) a jednak flexibilita (nˇekter´e moduly se mohou pouˇz´ıvat v r˚ uzn´ ych programech). Pro ˇr´ızen´ı pˇrekladu se obvykle pouˇz´ıv´ a utilita make. • pˇrekladaˇc jednotliv´e zdrojov´e moduly pˇreloˇz´ı do tvaru tzv. objektov´ych modul˚ u, jeˇz obsahuj´ı k´od programu (vˇcetnˇe vol´an´ı lok´aln´ıch funkc´ı), ale nam´ısto vol´ an´ı extern´ıch funkc´ı obsahuj´ı jen tabulku jejich jmen. • po f´ azi pˇrekladu nastupuje spojovac´ı program (t´eˇz linker editor nebo loader ), kter´ y zkompletuje v´ ysledn´ y program vˇcetnˇe vyˇreˇsen´ı extern´ıch odkaz˚ u mezi moduly a syst´emov´ ymi knihovnami resp. mezi moduly navz´ ajem. • pouˇzit´e statick´e knihovny jsou zkop´ırov´any do spustiteln´eho souboru. Na sd´ılen´e knihovny jsou ve spustiteln´em souboru pouze odkazy a linkuje je runtime linker pˇri kaˇzd´em spuˇstˇen´ı programu. V´ıce viz dynamick´ y linker na stranˇe 31. • pomoc´ı parametr˚ u linkeru lze urˇcit, zda se budou pouˇz´ıvat statick´e nebo dynamick´e knihovny. Zdrojov´ y k´od je v obou pˇr´ıpadech stejn´ y. Existuje i mechanismus (dlopen(), dlsym(). . . ), pomoc´ı kter´eho se za bˇehu programu vybere sd´ılen´a knihovna a daj´ı se volat jej´ı funkce. T´ımto zp˚ usobem m˚ uˇzete tak´e zjistit, zda v syst´emu je pˇr´ıtomna pˇr´ısluˇsn´a funkcionalita a pokud ne, zachovat se podle toho.
21
Pˇ reklad jednoho modulu (preprocesor)
/usr/include/stdio.h int puts(char *s);
main.i int puts(char *s); preprocesor
main.c #include <stdio.h> #include "mydef.h" main() { puts(MSG); }
mydef.h #define MSG "Ahoj" extern int var a;
extern int var a; main() { puts("Ahoj"); }
• preprocesor prov´ad´ı expanzi maker, ˇcten´ı vloˇzen´ ych () soubor˚ u a vynech´ av´ a koment´ aˇre. • v´ ystup preprocesoru lze z´ıskat pomoc´ı cc -E pˇr´ıpadnˇe pˇr´ımo zavol´an´ım cpp, nemus´ı to b´ yt ale vˇzdz tot´eˇz protoˇze nˇekter´e pˇrekladaˇce maj´ı preprocesor integrov´an v sobˇe. Preprocesor m˚ uˇzete samozˇrejmˇe pouˇz´ıvat i pro jin´e projekty, kter´e s pˇrekladem zdrojov´ ych soubor˚ u v jazyce C nemus´ı m´ıt v˚ ubec nic spoleˇcn´eho. • pouˇzit´ı preprocesoru se m˚ uˇze velmi hodit v situaci, kdy potˇrebujete zas´ ahnout do ciz´ıho k´odu, pln´eho podm´ıneˇcn´ ych vkl´ ad´ an´ı r˚ uzn´ ych hlaviˇckov´ ych soubor˚ u a r˚ uzn´ ych definic z´ avisl´ ych na dan´ ych podm´ınk´ ach. Pˇri hled´ an´ı p˚ uvodce chyby v´am pr´avˇe m˚ uˇze hodnˇe pomoci samostatn´eho zpracov´an´ı vstupn´ıho souboru pomoc´ı preprocesuru, kde probl´em jiˇz vˇetˇsinou rozpozn´ate snadno. • cpp v´am dok´ aˇze na standardn´ı chybov´ y v´ ystup zobrazit i cel´ y strom vkl´ adan´ ych soubor˚ u, coˇz opˇet pˇri podm´ıneˇcn´ ych pˇrekladech m˚ uˇze b´ yt velmi uˇziteˇcn´ a vˇec. Staˇc´ı pro to pouˇz´ıt volbu -H a pˇresmˇerovat v´ ystup do /dev/null ˇc´ımˇz dostanete pouze hierarchii vkl´ adan´ ych hlaviˇckov´ ych soubor˚ u.
22
Pˇ reklad jednoho modulu (kompil´ ator) main.s
main.i
.globl main .type main, @function main: pushhl %ebp movl %esp, %ebp pushl $.LC0 call puts addl $4, %esp .L1: leave ret
extern int var a; main() { puts("Ahoj"); }
kompil´ator
int puts(char *s);
• pˇreklad z C do assembleru • v´ ystup t´eto f´ aze pˇrekladu lze z´ıskat pomoc´ı cc -S.
23
Pˇ reklad jednoho modulu (assembler) main.s .globl main .type main, @function main: pushhl %ebp movl %esp, %ebp pushl $.LC0 call puts addl $4, %esp .L1: leave ret
assembler
main.o 457f 0000 0001 0000 00ec 0034
• pˇreklad z assembleru do strojov´eho k´odu • objektov´ y modul je v´ ysledkem pˇr´ıkazu cc -c.
24
464c 0000 0003 0000 0000 0000
0101 0000 0001 0000 0000 0000
0001 0000 0000 0000 0000 0028
Kompil´ ator • vol´ an´ı: cc [options ] soubor ... • nejd˚ uleˇzitˇejˇs´ı pˇrep´ınaˇce: -o soubor jm´eno v´ ysledn´eho souboru -c
pouze pˇreklad (nelinkovat)
-E
pouze preprocesor (nepˇrekl´ adat)
-l
slinkuj s pˇr´ısluˇsnou knihovnou
-Ljm´ eno
pˇridej adres´ aˇr pro hled´ an´ı knihoven z -l
-Olevel
nastaven´ı u ´ rovnˇe optimalizace
-g
pˇreklad s ladic´ımi informacemi
-Djm´ eno
definuj makro pro preprocesor
-Iadres´ aˇ r
um´ıstˇen´ı #include soubor˚ u
• -l/-L jsou pˇrep´ınaˇce linker editoru, tj. kompil´ ator pˇr´ısluˇsn´e informace pˇred´ a, ale jsou pouˇz´ıv´ any tak ˇcasto, ˇze jsou vloˇzeny i do tohoto slajdu. • kompil´ ator a linker maj´ı mnoho dalˇs´ıch pˇrep´ınaˇc˚ u ovlivˇ nuj´ıc´ıch generovan´ y k´od, vypisov´an´ı varovn´ ych hl´aˇsen´ı nebo variantu jazyka (K&R/ANSI). Je tˇreba nastudovat dokumentaci konkr´etn´ıho produktu.
25
Pˇ reddefinovan´ a makra __FILE__, __LINE__, __DATE__, __TIME__, __cplusplus, apod. jsou standardn´ı makra kompil´ atoru C/C++ unix vˇzdy definov´ano v Unixu mips, i386, sparc hardwarov´a architektura linux, sgi, sun, bsd klon operaˇcn´ıho syst´emu _POSIX_SOURCE, _XOPEN_SOURCE pˇreklad podle pˇr´ısluˇsn´e normy pro pˇreklad podle urˇcit´e normy by pˇred prvn´ım #include mˇel b´ yt ˇra´dek s definic´ı n´ asleduj´ıc´ıho makra. Pak naˇctˇete unistd.h. UNIX 98
#define _XOPEN_SOURCE 500
SUSv3
#define _XOPEN_SOURCE 600
POSIX1990
#define _POSIX_SOURCE
• funguje to tak, ˇze pomoc´ı konkr´etn´ıch maker definujete co chcete (napˇr. POSIX SOURCE) a podle nastaven´ı jin´ ych maker (napˇr. POSIX VERSION) pak zjist´ıte, co jste dostali. Mus´ıte ale vˇzdy po nastaven´ı maker nainkludovat unistd.h. Napˇr´ıklad se pokus´ıme pˇreloˇzit program, kter´ y vyˇzaduje SUS3, na syst´emu podporuj´ıc´ım SUS3 (Solaris 10), ale pˇrekladaˇcem, kter´ y podporuje pouze SUS2 (UNIX03 pˇrekladaˇc je c99). $ cat standards.c #define _XOPEN_SOURCE 600 /* you must #include at least one header !!! */ #include <stdio.h> int main(void) { return (0); } $ cc standards.c "/usr//sys/feature_tests.h", line 336: #error: "Compiler or options invalid; UNIX 03 and POSIX.1-2001 applications require the use of c99" cc: acomp failed for standards.c • zdroj maker pro standard tedy m˚ uˇze b´ yt /usr//sys/feature tests.h na Solarisu. Bud’ ho najdete pˇr´ımo na Solaris syst´emu nebo pˇres code browser na www.opensolaris.org. • v dokumentaci konkr´etn´ıho kompil´ atoru je moˇzn´e naj´ıt, kter´ a dalˇs´ı makra se pouˇz´ıvaj´ı. Mnoˇzstv´ı maker je definov´ano tak´e v syst´emov´ ych hlaviˇckov´ ych souborech. 26
• POSIX.1 v sobˇe zahrnuje ANSI C; tedy C89, ne C99 (o C standardech v´ıce na stranˇe 13). • co se t´ yˇce maker k jednotliv´ ym standard˚ um, velmi dobr´ a je kapitola 1.5 v [Rochkind]. Tak´e doporuˇcuji C program c1/suvreq.c k t´eto kapitole, kter´ y je moˇzn´e naj´ıt i na m´ ych str´ ank´ ach v sekci uk´azkov´ ych pˇr´ıklad˚ u. • mal´ y pˇr´ıklad na podm´ınˇen´ y pˇreklad: int main(void) { #ifdef unix printf("yeah\n"); #else printf("grr\n"); #endif return (0); }
Linker • Vol´ an´ı: ld [options ] soubor ... cc [options ] soubor ... • Nejd˚ uleˇzitˇejˇs´ı pˇrep´ınaˇce: -o soubor
jm´eno v´ ysledn´eho souboru (default a.out)
-llib
linkuj s knihovnou liblib.so nebo liblib.a
-Lpath
cesta pro knihovny (-llib )
-shared
vytvoˇrit sd´ılenou knihovnu
-non shared
vytvoˇrit statick´ y program
• linker je program, kter´ y vezme typicky v´ıce objekt˚ u vygenerovan´ ych pˇrekladaˇcem a vytvoˇr´ı z nich bin´ arn´ı program, knihovnu nebo dalˇs´ı objekt vhodn´ y pro dalˇs´ı f´ azi linkov´an´ı. • pozor na to, ˇze na r˚ uzn´ ych syst´emech se nˇekter´e pˇrep´ınaˇce mohou liˇsit, napˇr´ıklad ld na Solarisu nezn´ a pˇrep´ınaˇce -shared a -non shared, je nutn´e pouˇz´ıt jin´e. • u mal´ ych program˚ u (v jednom souboru) lze prov´est pˇreklad a linkov´an´ı jedn´ım pˇr´ıkazem cc. U vˇetˇs´ıch program˚ u skl´adaj´ıc´ıch se z mnoha zdrojov´ ych 27
soubor˚ u a knihoven se obvykle oddˇeluje pˇreklad a linkov´ an´ı a cel´ y proces je ˇr´ızen utilitou make.
ˇ ızen´ı pˇ R´ rekladu a linkov´ an´ı (make) • zdrojov´ e texty main.c #include "util.h" main() { msg(); }
util.h void msg();
util.c #include "util.h" msg() { puts(); }
• soubor Makefile prog : main.o util.o main.c ց cc -o prog main.o util.o main.o main.o : main.c util.h ր ց util.h prog cc -c main.c ց ր util.o : util.c util.h util.o ր cc -c util.c util.c
• z´ avislosti
• program je moˇzn´e tak´e pˇreloˇzit a slinkovat jedn´ım vol´an´ım kompil´ atoru, nebo definovat postup pˇrekladu a linkov´an´ı pomoc´ı shellov´eho skriptu. D˚ uvodem pro pouˇzit´ı make je to, ˇze vyhodnocuje z´ avislosti mezi soubory a po zmˇenˇe ˇ y nˇekter´eho zdrojov´eho souboru pˇrekl´ ad´ a jenom to, co na nˇem z´ avis´ı. Cast´ zp˚ usob pˇrekladu softwaru po aplikov´an´ı zmˇen zp˚ usobem “make clean; make all” je v situaci, kdy cel´ y pˇreklad trv´a minuty (des´ıtky minut, hodiny. . . ), trochu nevhodn´ y – pr´avˇe proto je d˚ uleˇzit´e m´ıt dobˇre napsan´ y Makefile. • ˇra´dek “prog : main.o util.o” definuje, ˇze se m´a nejprve rekurzivnˇe zajistit existence a aktu´alnost soubor˚ u main.o a util.o. Pak se zkontroluje, zda soubor (c´ıl) prog existuje a je aktu´aln´ı (datum posledn´ı modifikace souboru je mladˇs´ı neˇz main.o a util.o). Pokud ano, nedˇel´a se nic. Kdyˇz ne, provede se pˇr´ıkaz na n´ asleduj´ıc´ım ˇra´dku. • make se spouˇst´ı typicky s parametrem urˇcuj´ıc´ı pˇr´ıˇsluˇsn´ y c´ıl (target ); pˇri spuˇstˇen´ı bez parametr˚ u se vezme prvn´ı target. Ten typicky b´ yv´ a all, coˇz vˇetˇsinou podle unixov´e konvence pˇreloˇz´ı vˇse, co se pˇreloˇzit m´a. N´ asleduje pak tˇreba zavol´ an´ı make s parametrem install apod. • make je samozˇrejmˇe univerz´ aln´ı n´ astroj, pouˇziteln´ y i jinde neˇz u pˇreklad˚ u
28
Syntaxe vstupn´ıho souboru (make) • popis z´ avislost´ı c´ıle:
targets : [files ]
• prov´adˇen´e pˇr´ıkazy:
command
• koment´ aˇr:
#comment
• pokraˇcovac´ı ˇra´dek:
line-begin \ line-continuation
• pozor na to, ˇ ze ˇ r´ adek s pˇ r´ıkazem zaˇ c´ın´ a tabul´ atorem, nikoliv mezerami. Kaˇzd´ y pˇr´ıkazov´ y ˇra´dek se prov´ad´ı samostatn´ ym shellem, pokud je potˇreba prov´est v´ıce ˇra´dk˚ u pomoc´ı jednoho shellu, mus´ı se vˇsechny aˇz na posledn´ı ukonˇcit backslashem (shell je dostane jako jeden ˇra´dek). Viz pˇr´ıklad, ve kter´em dva posledn´ı echo pˇr´ıkazy jsou souˇca´st´ı jednoho if pˇr´ıkazu, kter´ y je spuˇstˇen samostatn´ ym shellem: $ cat Makefile # Makefile test all: @echo $$$$ @echo $$$$ @if true; then \ echo $$$$; \ echo $$$$; \ fi $ make 5513 5514 5515 5515 • zdvojen´ım $ se potlaˇc´ı speci´ aln´ı v´ yznam dolaru (viz n´ asleduj´ıc´ı slajd) • znak @ na zaˇca´tku ˇra´dku potlaˇc´ı jeho v´ ypis – make jinak standardnˇe vypisuje nejdˇr´ıve to, co bude vykon´ avat.
29
• znak - na zaˇca´tku ˇra´dku zp˚ usob´ı ignorov´an´ı nenulov´e n´ avratov´e hodnoty; jinak make vˇzdy v takov´e situaci zahl´ as´ı chyby a okamˇzitˇe skonˇc´ı. • test1: false echo "OK" test2: -false echo "OK"
Makra (make) • definice makra: name = string • pokraˇcov´an´ı vkl´ ad´ a mezeru • nedefinovan´ a makra jsou pr´azdn´ a • nez´ aleˇz´ı na poˇrad´ı definic r˚ uzn´ ych maker • definice na pˇr´ıkazov´e ˇra´dce: make target name =string • vyvol´ an´ı makra: $name (pouze jednoznakov´e name ), ${name } nebo $(name ) • syst´emov´e promˇenn´e jsou pˇr´ıstupn´e jako makra
• kdyˇz je stejn´e makro definov´ano v´ıcekr´at, plat´ı posledn´ı definice. • makra nen´ı moˇzn´e definovat rekurzivnˇe. $ cat Makefile M=value1 M=$(M) value2 all: echo $(M) $ make Variable M is recursive. • ˇcasto se pouˇz´ıvaj´ı r˚ uzn´e rozˇs´ıˇren´e verze make (napˇr. GNU, BSD), kter´e um´ı, podm´ınˇen´e sekce v Makefile, redefinice promˇenn´ ych, apod. • napsat Makefile kter´ y bude fungovat najednou pro r˚ uzn´e verze make nemus´ı b´ yt jednoduch´e, proto existuj´ı projekty jako je napˇr. GNU automake. Pro jednoduch´ y podm´ıneˇcn´ y pˇreklad v z´ avislosti na syst´emu a kde se daj´ı oˇcek´ avat 30
r˚ uzn´e verze pˇr´ıkazu make, je moˇzn´e pouˇz´ıt napˇr´ıklad n´ asleduj´ıc´ı k´od, kter´ y mi fungoval na vˇsech v nˇem zm´ınˇen´ ych syst´emech (znak ‘ je zpˇetn´a uvozovka, a ’ je norm´ aln´ı uvozovka): CFLAGS=‘x=\‘uname\‘; \ if [ $${x} = FreeBSD ]; then \ echo ’-Wall’; \ elif [ $${x} = SunOS ]; then \ echo ’-v’; \ elif [ $${x} = Linux ]; then \ echo ’-Wall -g’; \ fi‘ all: @echo "$(CFLAGS)" • v ostatn´ıch situac´ıch je vhodn´e, pˇr´ıpadnˇe nezbytn´e pouˇz´ıt programy typu autoconf nebo automake • make je velmi siln´ y n´ astroj, staˇc´ı se pod´ıvat do syst´emov´ ych Makefile soubor˚ u jak´ehokoli Unixov´eho syst´emu. Typickou spoleˇcnou vlastnost´ı je to, ˇze neexistuje dokumentace jak je dan´ y makefile framework postaven.
Dynamick´ y linker Pˇri pˇrekladu je nutn´e m´ıt vˇsechny potˇrebn´e dynamick´e knihovny, protoˇze se kontroluje dosaˇzitelnost pouˇzit´ ych symbol˚ u, sestaven´ı kompletn´ıho programu se ale provede aˇ z pˇ ri spuˇ stˇ en´ı. To je u ´ kol pro dynamick´ y linker (run-time linker, loader ). • seznam dynamick´ ych knihoven zjist´ı ze sekce .dynamic • syst´em m´a nastaveno nˇekolik cest, kde se automaticky tyto knihovny hledaj´ı • v sekci .dynamic je moˇzn´e dalˇs´ı cesty ke knihovn´am pˇridat pomoc´ı tag˚ u RUNPATH/RPATH • nalezen´e knihovny se pˇripoj´ı do pamˇet’ov´eho procesu pomoc´ı vol´ an´ı mmap() (bude pozdˇeji)
Nasleduj´ıc´ı pˇ r´ıkazy a pˇ r´ıklady se t´ ykaj´ı Solarisu. Pokud to nebude fungovat na jin´ ych syst´emech, tak maj´ı ekvivaletn´ı n´ astroje s podobnou funkcionalitou. • proces spuˇstˇen´ı dynamicky slinkovan´eho programu prob´ıh´a tak, ˇze kernel ve vol´ an´ı exec() namapuje dan´ y program do pamˇeti a zjist´ı, jak´ y dynamick´ y linker se m´a pouˇz´ıt (viz d´ ale). Pˇred t´ım, neˇz j´adro pˇred´ a linkeru kontrolu, linker 31
namapuje do pamˇet’ov´eho prostoru spouˇstˇen´eho procesu. Linker z hlaviˇcky programu zjist´ı, jak´e dynamick´e knihovny se maj´ı d´ ale namapovat, provede to a teprve pak pˇred´ a ˇr´ızen´ı vaˇsemu programu. V´ aˇs program m˚ uˇze za bˇehu d´ ale vyuˇz´ıvat dynamick´ y linker pomoc´ı vol´an´ı typu dlopen() a spol. (k tomu se tak´e dostaneme pozdˇeji). Uvˇedomte si, ˇze dynamick´ y linker zde nepracuje jako samostatn´ y proces, jeho k´od se pouˇz´ıv´ a v r´ amci pamˇet’ov´eho prostoru vaˇseho procesu; v´aˇs program, linker a knihovny dohromady tvoˇr´ı jeden proces. • seznam sekc´ı se zjist´ı pomoc´ı elfdump -c (GNU m´a pˇr´ıkaz readelf). O programov´ ych sekc´ıch bude v´ıce na stranˇe 115. • jak´ y dynamick´ y linker se pouˇzije kernel zjist´ı ze sekce .interp, viz ”elfdump -i” a “ld -I”. To znamen´ a, ˇze si m˚ uˇzete napsat vlastn´ı linker a pomoc´ı -I pro ld ho pak nastavit jako dynamick´ y linker pro v´aˇs program. • dynamick´a sekce se vyp´ıˇse pomoc´ı elfdump -d, dynamick´e knihovny jsou oznaˇcen´e tagem NEEDED • z´ avislosti na dynamick´ ych knihovn´ach je moˇzn´e dobˇre zjistit pomoc´ı pˇr´ıkazu ldd, kter´ y zjist´ı konkr´etn´ı cesty ke knihovn´am. Tento pˇr´ıkaz ˇreˇs´ı z´ avislosti rekurz´ıvnˇe, tj. uvid´ıte tedy i nepˇr´ım´e z´ avislosti - tj. takov´e knihovny, kter´e jsou pouˇzit´e knihovnami, kter´e pˇr´ısluˇsn´ y program pouˇz´ıv´ a pˇr´ımo. Zjistit co je pˇresnˇe z´ avisl´e na ˇcem je moˇzn´e pomoc´ı volby -v. • jak´e knihovny byly pˇri spuˇstˇen´ı nakonec pouˇzity m˚ uˇze b´ yt jin´e neˇz co uk´aˇze pˇr´ıkaz ldd, a to tˇreba d´ıky mechanismu LD PRELOAD, na Solarisu proto existuje pˇr´ıkaz pldd, kter´ y pomoc´ı ˇc´ısla procesu uk´aˇze z´ avislosti konkr´etn´ıho spuˇstˇen´eho programu. • v´ıce viz manu´ alov´a str´ anka pro dynamick´ y linker v Solarisu, ld.so.1, pˇr´ıpadnˇe Linker and Libraries Guide na docs.sun.com. Na FreeBSD se dynamick´ y linker naz´ yv´ a ld-elf.so.1, na Linuxu ld-linux.so.1, na IRIXu rld atd. • dynamick´ y linker se typicky d´ a konfigurovat pomoc´ı nastaven´ı promˇenn´ ych, napˇr´ıklad si zkuste na Solarisu spustit toto: LD_LIBRARY_PATH=/tmp LD_DEBUG=libs,detail date a pro vˇsechny moˇznosti jak debugovat dynamick´ y linker pouˇzijte: LD_DEBUG=help date • jin´ y pˇr´ıklad, kdy linker hled´ a knihovny i jinde neˇz v defaultn´ıch adres´ aˇr´ıch: $ cp /lib/libc.so.1 /tmp $ LD_LIBRARY_PATH=/tmp sleep 100 & [1] 104547 $ pldd 104547 104547: sleep 100 /tmp/libc.so.1 /usr/lib/locale/cs_CZ.ISO8859-2/cs_CZ.ISO8859-2.so.3
32
API vers ABI API – Application Programming Interface • rozhran´ı pouˇzit´e pouze ve zdrojov´em k´odu • rozhran´ı zdroj´ aku v˚ uˇci syst´emu, knihovnˇe ˇci vlastn´ımu k´odu, tj. napˇr. exit(1), printf("hello\n") nebo my function(1, 2) • . . . aby se stejn´ y zdrojov´ y text mohl pˇreloˇzit na vˇsech syst´emech podporuj´ıc´ı dan´e API ABI – Application Binary Interface • low-level rozhran´ı aplikace v˚ uˇci syst´emu, knihovnˇe ˇci jin´e ˇca´sti sama sebe • . . . aby se objektov´ y modul mohl pouˇz´ıt vˇsude tam, kde je podporov´ano stejn´e ABI
• pˇr´ıkladem API je tˇreba API definovan´e normou POSIX.1 • ABI definuje konvenci vol´ an´ı (to jak program pˇred´ a parametry funkci a jak od n´ı pˇrevezme n´ avratovou hodnotu), jak´ a jsou ˇc´ısla syst´emov´ ych vol´an´ı, jak se syst´emov´e vol´ an´ı provede ˇci form´at objektov´eho modulu ˇci pˇrij´ıman´ ych argument˚ u, viz pˇr´ıklad dole. • ABI knihovny pak definuje mimo jin´e mnoˇzinu vol´an´ı kter´ a jsou knihovnou definov´ana, jejich parametry a typy tˇechto parametr˚ u • n´ asledn´ a uk´ azka je pˇr´ıklad na to, kdy v´ yvoj´aˇr zmˇen´ı velikost argument˚ u v bajtech (tj. zmˇen´ı ABI knihovny), a nahrad´ı novou verz´ı tu starou. Vˇsimnˇete si, ˇze dynamick´ y linker toto nezjist´ı; nem´ a totiˇz jak, ˇr´ıd´ı se podle jm´ena knihovny v dynamick´e sekci programu, a to se nezmˇenilo. Uveden´ a zmˇena je sice i zmˇena v API a probl´em by se odstranil, kdybychom main.c znovu pˇreloˇzili se zmˇenˇen´ ym ˇra´dkem deklarace funkce add(). To je ale ˇcasto probl´em (pˇrekl´ adejte cel´ y syst´em jen kv˚ uli tomu), proto je tak d˚ uleˇzit´e dodrˇzovat zpˇetnou kompatibilitu v ABI u knihoven. V´ ysledek n´ asleduj´ıc´ıho pˇrekladu knihovny, programu a jeho spuˇstˇen´ı je jak bychom oˇcek´ avali (pouˇzit cc ze SunStudio, pro gcc pouˇzijte m´ısto -G volbu -shared; novˇejˇs´ı gcc nav´ıc neznaj´ı -R a je m´ısto toho nutn´e pouˇz´ıt -Xlinker -R -Xlinker .: $ cat main.c int my_add(int a, int b); int main(void)
33
{ printf("%d\n", my_add(128, 1)); return (0); } $ cat add.c int my_add(int a, int b) { return (a + b); } $ cc -G -o libadd.so add.c $ cc -L. -ladd -R. main.c $ ./a.out 129 Nyn´ı ale pˇriˇsla dalˇs´ı verze knihovny se stejn´ ym jm´enem, a ve funkci add() nastala zmˇena v typu argument˚ u. Program ale o niˇcem nev´ı, nech´ a se spustit a vr´ at´ı chybnou hodnotu: $ cat add2.c int my_add(char a, char b) { return (a + b); } $ cc -G -o libadd.so add2.c $ ./a.out -127 • zde pak pˇrich´ az´ı ke slovu verzov´an´ı knihoven, tj. je nutn´e “nˇeco” zmˇenit tak, aby po instalaci nov´e knihovny neˇslo program spustit bez jeho rekompilace.
34
Debugger dbx • Vol´ an´ı: dbx [ options ] [ program [ core ] ] • Nejbˇeˇznˇejˇs´ı pˇr´ıkazy: run [arglist ] where print expr set var = expr cont next, step stop condition trace condition command n help [name ] quit
start programu vypiˇs z´ asobn´ık vypiˇs v´ yraz zmˇen ˇ hodnotu promˇenn´e pokraˇcov´an´ı bˇehu programu proved’ ˇra´dku (bez/s vnoˇren´ım do funkce) nastaven´ı breakpointu nastaven´ı tracepointu akce na breakpointu (pˇr´ıkazy n´ asleduj´ı) n´ apovˇeda ukonˇcen´ı debuggeru
• z´ akladn´ı ˇra´dkov´ y symbolick´ y debugger, aby bylo moˇzn´e ho plnˇe vyuˇz´ıt, mus´ı b´ yt program pˇreloˇzen s ladic´ımi informacemi (cc -g). Ladˇen´ y program se startuje z debuggeru pˇr´ıkazem run, nebo se debugger pˇripoj´ı k jiˇz bˇeˇz´ıc´ımu procesu. Pomoc´ı dbx lze analyzovat i havarovan´ y program, kter´ y vygeneroval soubor core. • je moˇzn´e ho naj´ıt napˇr. na Solarisu, na Linuxu a FreeBSD defaultnˇe nen´ı. • pro debugging se zdrojov´ ymi k´ody nestaˇc´ı pouˇz´ıt volbu -g, je z´ aroveˇ n nutn´e m´ıt i zdroj´ aky a objektov´e moduly tam, kde byly pˇri pˇrekladu. To je typicky bˇeˇzn´ a situace, protoˇze lad´ıte na stroji, kde z´ aroveˇ n i vyv´ıj´ıte. Pokud tomu tak nen´ı, je nutn´e si zdroj´ aky a objektov´e moduly zajistit, pokud k nim vede jin´a cesta, lze pouˇz´ıt dbx pˇr´ıkaz pathmap. • gdb-kompatibiln´ı m´od se spust´ı pˇres gdb on. Pokud v´as zaj´ım´ a, jak´ y m´a dbx ekvivalentn´ı pˇr´ıkaz ke konkr´etn´ımu gdb pˇr´ıkazu, pom˚ uˇze v´am help FAQ; hned prvn´ı ot´azka je “A.1 Gdb does <something>; how do I do it in dbx?”
35
GNU debugger gdb • Vol´ an´ı: gdb [ options ] [ program [ core ] ] • Nejbˇeˇznˇejˇs´ı pˇr´ıkazy: run [arglist ] bt print expr set var = expr cont next, step break condition help [name ] quit
start programu vypiˇs z´ asobn´ık vypiˇs v´ yraz zmˇen ˇ hodnotu promˇenn´e pokraˇcov´an´ı bˇehu programu proved’ ˇra´dku (bez/s vnoˇren´ım do funkce) nastaven´ı breakpointu n´ apovˇeda ukonˇcen´ı debuggeru
• GNU obdoba dbx. M´ od kompatibiln´ı s dbx spust´ıte pˇres -dbx. • na r˚ uzn´ ych platform´ ach existuj´ı i debuggery s grafick´ ym rozhran´ım, napˇr. ˇ debugger (Solaris), cvd (IRIX), xxgdb (GNU), ddd (GNU). Casto funguj´ı jako nadstavby nad dbx, gdb. • #include <stdio.h> int main(void) { printf("hello, world\n"); return 0; } $ cc -g main.c $ gdb -q a.out (gdb) break main Breakpoint 1 at 0x8048548: file main.c, line 4. (gdb) run Starting program: /share/home/jp/src/gdb/a.out Breakpoint 1, main () at main.c:4 4 printf("hello, world\n"); (gdb) next hello, world 5 return 0; (gdb) c Continuing. Program exited normally. (gdb) q • debuggery jsou v´ yborn´ ymi pomocn´ıky pokud v´aˇs program konˇc´ı na chyby 36
typu “segmentation error” – tj. kdyˇz zkus´ıte nekorektnˇe pˇristoupit do pamˇeti, napˇr´ıklad tam kde nem´ ate co dˇelat. Kdyˇz pˇri pˇrekladu pouˇzijete option -g, uk´ aˇze v´am pak debugger pˇresnˇe ˇc´ıslo ˇra´dku, kde nastal probl´em. Konkr´etn´ı pˇr´ıklad (proˇc se vlastnˇe tento program chov´a jak se chov´a??? Hint: zkuste pˇreloˇzit na Solarisu pˇrekladaˇcem cc a spustit): $ cat -n main.c 1 int 2 main(void) 3 { 4 char *c = "hey world"; 5 c[0] = ’\0’; 6 return (0); 7 } } $ gcc -g main.c $ ./a.out Bus error (core dumped) $ gdb a.out a.out.core ... Core was generated by ‘a.out’. Program terminated with signal 10, Bus error. ... #0 0x080484e6 in main () at main.c:5 5 c[0] = ’\0’;
Obsah • u ´ vod, v´ yvoj UNIXu a C, program´ atorsk´e n´ astroje • z´ akladn´ı pojmy a konvence UNIXu a jeho API • pˇr´ıstupov´a pr´ava, perifern´ı zaˇr´ızen´ı, syst´em soubor˚ u • manipulace s procesy, spouˇstˇen´ı program˚ u • sign´aly • synchronizace a komunikace proces˚ u • s´ıt’ov´a komunikace • vl´ akna, synchronizace vl´ aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu
37
Standardn´ı hlaviˇ ckov´ e soubory (ANSI C) stdlib.h errno.h stdio.h ctype.h string.h time.h math.h setjmp.h assert.h stdarg.h limits.h signal.h
... ... ... ... ... ... ... ... ... ... ... ...
z´ akladn´ı makra a funkce oˇsetˇren´ı chyb vstup a v´ ystup pr´ace se znaky pr´ace s ˇretˇezci pr´ace s datem a ˇcasem matematick´e funkce dlouh´e skoky ladic´ı funkce pr´ace s promˇenn´ ym poˇctem parametr˚ u implementaˇcnˇe z´ avisl´e konstanty oˇsetˇren´ı sign´al˚ u
• hlaviˇckov´ y soubor (header file) je soubor s deklaracemi funkc´ı (forward declaration), promˇenn´ ych a maker. Z pohledu preprocesoru je to obyˇcejn´ y soubor napsan´ y v jazyce C. • pozor na to, ˇze tyto hlaviˇckov´e soubory nejsou specifick´e pro UNIX. Jsou souˇca´st´ı standardu ANSI C, kter´ y jak jiˇz v´ıme (strana 13), je zahrnut v POSIX.1. • pˇr´ısluˇsn´ y hlaviˇckov´ y soubor pro konkr´etn´ı funkci najdete v manu´ alov´e str´ ance dan´e funkce, toto je zaˇca´tek manu´ alov´e str´ anky na Solarisu pro memcpy(): Standard C Library Functions
memory(3C)
NAME memory, memccpy, memchr, memcmp, memcpy, memmove, memory operations
memset
-
SYNOPSIS #include <string.h> ... ... • jednotliv´ a makra obsaˇzen´ a v tˇechto souborech vˇetˇsinou nejsou vysvˇetlena, v´ yznam jednotliv´ ych maker je ale moˇzn´e si vyhledat v pˇr´ısluˇsn´ ych specifikac´ıch, kter´e jsou on-line. Na nˇekter´ ych syst´emech (Solaris) maj´ı jednotliv´e hlaviˇckov´e soubory svoji vlastn´ı manu´ alovou str´ anku (man stdlib.h). • makro assert() je moˇzn´e z bˇehem kompilace odstranit pomoc´ı makra NDEBUG
38
Standardn´ı hlaviˇ ckov´ e soubory (2) unistd.h
...
nejpouˇz´ıvanˇejˇs´ı syst´emov´a vol´an´ı
sys/types.h
...
datov´e typy pouˇz´ıvan´e v API UNIXu
fcntl.h
...
ˇr´ıd´ıc´ı operace pro soubory
sys/stat.h
...
informace o souborech
dirent.h
...
proch´ azen´ı adres´ aˇr˚ u
sys/wait.h
...
ˇcek´ an´ı na synovsk´e procesy
sys/mman.h
...
mapov´an´ı pamˇeti
curses.h
...
ovl´ ad´ an´ı termin´alu
regex.h
...
pr´ace s regul´ arn´ımi v´ yrazy
• tyto headery uˇz patˇr´ı do UNIXu. • zaj´ımav´e m˚ uˇze b´ yt pod´ıvat se do sys/types.h
39
Standardn´ı hlaviˇ ckov´ e soubory (3) semaphore.h
...
semafory (POSIX)
pthread.h
...
vl´ akna (POSIX threads)
sys/socket.h
...
s´ıt’ov´a komunikace
arpa/inet.h
...
manipulace se s´ıt’ov´ ymi adresami
sys/ipc.h
...
spoleˇcn´e deklarace pro System V IPC
sys/shm.h
...
sd´ılen´a pamˇet’ (System V)
sys/msg.h
...
fronty zpr´ av (System V)
sys/sem.h
...
semafory (System V)
• dokonˇcen´ı nejd˚ uleˇzitˇejˇs´ıch UNIXov´ ych header˚ u. Existuj´ı samozˇrejmˇe jeˇstˇe dalˇs´ı.
40
Funkce main() • pˇri spuˇstˇen´ı programu je pˇred´ ano ˇr´ızen´ı funkci main(). • int main (int argc, char *argv []); – argc . . . poˇcet argument˚ u pˇr´ıkazov´e ˇra´dky – argv . . . pole argument˚ u ∗ podle konvence je argv[0] cesta k programu ∗ posledn´ı prvek je argv[argc] == NULL
– n´ avrat z main() nebo vol´an´ı exit() ukonˇc´ı program – standardn´ı n´ avratov´e hodnoty EXIT_SUCCESS (0) a EXIT_FAILURE (1) ls -l / argc argv
3
argv[0] argv[1] argv[2] argv[3]
"ls" "-l" "/"
• prvn´ı parametr (typu int) ud´ av´ a poˇcet argument˚ u na pˇr´ıkazov´em ˇra´dku (vˇcetnˇe parametru 0 – jm´ena programu) a druh´ y parametr (typu char**) je pole ukazatel˚ u na tyto ˇretˇezce. Za posledn´ım ˇretˇezcem je jeˇstˇe NULL pointer – a to je nˇeco jin´eho neˇz pr´azdn´ y ˇretˇezec. • pˇri spuˇstˇen´ı programu pˇred´ a k´od dynamick´eho linkeru ˇr´ızen´ı funkci main(), viz strana 31. Staticky slinkovan´emu programu se ˇr´ızen´ı pˇred´ a pˇr´ımo. Nepˇr´ıtomnost main() v programu zp˚ usob´ı chybu pˇri pˇrekladu na u ´ rovni linkeru. T´eto funkci se pˇred´ a jm´eno spuˇstˇen´eho programu, argumenty z pˇr´ıkazov´e ˇra´dky a pˇr´ıpadnˇe i promˇenn´e prostˇred´ı. Ukonˇcen´ı t´eto funkce znamen´ a konec programu a n´ avratov´a hodnota se pouˇzije jako k´od ukonˇcen´ı programu pro OS. Jinou moˇznost´ı ukonˇcen´ı programu je pouˇzit´ı funkce exit() nebo _exit(), kterou lze pouˇz´ıt kdykoliv, nejen ve funkci main(). V C lze pouˇz´ıvat obˇe metody ukonˇcen´ı programu. • pˇred´ an´ı promˇenn´ ych prostˇred´ı tˇret´ım parametrem typu char** nen´ı souˇca´st´ı normativn´ı ˇca´sti C standardu, pouze informativn´ı. Pˇrekladaˇce to ale typicky podporuj´ı. Varianta main() s promˇenn´ ymi prostˇred´ı pak vypad´ a takto: int main(int argc, char *argv [], char *envp []); • n´ avratov´ y typ funkce main by mˇel b´ yt vˇzdy int; pˇrekladaˇc si jinak bude stˇeˇzovat. Z t´eto hodnoty se ale pouˇzije pouze nejniˇzˇs´ıch 8 bit˚ u. Pozor na to, ˇze na rozd´ıl od jazyka C n´ avratov´a hodnota 0 m´a v shellu v´ yznam true (´ uspˇech) a nenula v´ yznam false (ne´ uspˇech). • rozd´ıl mezi funkcemi exit() a _exit() je v tom, ˇze exit() pˇred ukonˇcen´ım programu jeˇstˇe zavˇre streamy stdio a vol´a funkce zaregistrovan´e pomoc´ı atexit(). V z´ avislosti na syst´emu to mohou b´ yt i dalˇs´ı akce. Napˇr´ıklad ve FreeBSD je exit() syst´emov´e vol´an´ı a exit() knihovn´ı funkce. V Solarisu jsou obˇe syst´emov´ ymi vol´ an´ımi. 41
Promˇ enn´ e prostˇ red´ı • seznam vˇsech promˇenn´ ych prostˇred´ı (environment variables) se pˇred´ av´ a jako promˇenn´a extern char **environ; • je to pole ukazatel˚ u (ukonˇcen´e NULL) na ˇretˇezce ve tvaru: promˇ enn´ a =hodnota environ environ[0] environ[1] environ[2] environ[3]
"SHELL=/bin/bash" "DISPLAY=:0" "LOGNAME=beran"
• shell pˇred´ av´ a spuˇstˇen´emu programu ty promˇenn´e, kter´e jsou oznaˇceny jako exportovan´e (Bourne-like shellech pˇr´ıkazem export variable ). Po zmˇenˇe obsahu jiˇz jednou exportovan´e promˇenn´e samozˇrejmˇe nen´ı potˇreba promˇennou znovu exportovat. Pˇr´ıkazem env v´am shell vyp´ıˇse aktu´aln´ı promˇenn´e prostˇred´ı, a pˇridat promˇennou do prostˇred´ı spouˇstˇen´eho programu pak staˇc´ı prov´est na pˇr´ıkazov´e ˇra´dce, aniˇz byste museli mˇenit prostˇred´ı vaˇseho shellu: $ date Sun Oct 7 13:13:58 PDT 2007 $ LC_TIME=fr date dimanche 7 octobre 2007 13 h 14 PDT • pˇri nahrazen´ı aktu´aln´ıho obrazu procesu obrazem jin´ ym se pˇred´ av´ a, pokud se neˇrekne jinak, synovsk´ ym proces˚ um cel´e pole environ automaticky. Je moˇzn´e ve vol´ an´ı pˇr´ısluˇsn´e varianty funkce exec pˇredat pole jin´e. • jak´e promˇenn´e prostˇred´ı konkr´etn´ı pˇr´ıkaz pouˇz´ıv´ a (a jak je pouˇz´ıv´ a) by mˇelo b´ yt v manu´ alov´e str´ ance. Typicky v sekci nazvan´e ENVIRONMENT nebo ENVIRONMENT VARIABLES • man napˇr´ıklad pouˇz´ıv´ a PAGER, vipw pak promˇennou EDITOR apod. • pokud je envp tˇret´ım parametrem funkce main, tak je to stejn´ a hodnota co je v ukazatelu environ.
42
Manipulace s promˇ enn´ ymi prostˇ red´ı • je moˇzn´e pˇr´ımo zmˇenit promˇennou environ • char *getenv (const char *name ); – vr´ at´ı hodnotu promˇenn´e name • int putenv (char *string ); – vloˇz´ı string ve tvaru jm´ eno =hodnota do prostˇred´ı (pˇrid´ a novou nebo modifikuje existuj´ıc´ı promˇennou) • zmˇeny se pˇren´ aˇsej´ı do synovsk´ ych proces˚ u • zmˇeny v prostˇred´ı syna samozˇrejmˇe prostˇred´ı otce neovlivn´ı • existuj´ı i funkce setenv() a unsetenv()
• u putenv() se vloˇzen´ y ˇretˇezec stane souˇca´st´ı prostˇred´ı (jeho pozdˇejˇs´ı zmˇena tak zmˇen´ı prostˇred´ı) a nesm´ıte proto pouˇz´ıvat retˇezce v automatick´ ych promˇenn´ ych, toto ˇreˇs´ı setenv(), kter´ y hodnotu promˇenn´e zkop´ıruje • d˚ uleˇzit´e je zapamatovat si, ˇze synovsk´ y proces zdˇed´ı v okamˇziku sv´eho vzniku od rodiˇce vˇsechny promˇenn´e prostˇred´ı, ale jak´ akoliv manipulace s nimi v synovi je lok´aln´ı a do otce se nepˇren´ aˇs´ı. Kaˇzd´ y proces m´a svou kopii promˇenn´ ych, proto ani n´ asledn´ a zmˇena prostˇred´ı otce nezmˇen´ı promˇenn´e jiˇz existuj´ıc´ıho potomka. • dalˇs´ı rozd´ıl mezi putenv a setenv je ten, ˇze v setenv mohu definovat, zda existuj´ıc´ı promˇennou chci nebo nechci pˇrepsat. putenv vˇzdy pˇrepisuje. • int main(void) { printf("%s\n", getenv("USER")); return 0; } $ ./a.out jp • jelikoˇz v´ıte, ˇze promˇenn´a environ je obyˇcejn´e pole ukazatel˚ u na ˇretˇezce, m˚ uˇzete s timto polem pracovat pˇr´ımo. Pozor vˇsak na to, ˇze v takov´em pˇr´ıpadˇe pak jiˇz nesm´ıte pouˇz´ıvat zde uveden´e funkce, jinak se m˚ uˇzete dostat do probl´em˚ u s jejich konkr´etn´ı implementac´ı.
43
Zpracov´ an´ı argument˚ u programu • obvykl´ y z´ apis v shellu: program -pˇ rep´ ınaˇ ce argumenty • pˇrep´ınaˇce tvaru -x nebo -x hodnota , kde x je jedno p´ısmeno nebo ˇc´ıslice, hodnota je libovoln´ y ˇretˇezec • nˇekolik pˇrep´ınaˇc˚ u lze slouˇcit dohromady: ls -lRa • argument ’--’ nebo prvn´ı argument nezaˇc´ınaj´ıc´ı ’-’ ukonˇcuje pˇrep´ınaˇce, n´ asleduj´ıc´ı argumenty nejsou povaˇzov´ any za pˇrep´ınaˇce, i kdyˇz zaˇc´ınaj´ı znakem ’-’. • tento tvar argument˚ u poˇzaduje norma a lze je zpracov´avat automaticky funkc´ı getopt().
• argumenty lze samozˇrejmˇe zpracov´avat vlastn´ı funkc´ı, ale standardn´ı funkce je pohodlnˇejˇs´ı. • argumenty se typicky mohou opakovat, ale to m´a smysl jen v nˇekter´ ych situac´ıch • poˇrad´ı pˇrep´ınaˇc˚ u m˚ uˇze b´ yt d˚ uleˇzit´e a je na aplikaci, aby toto specifikovala • UNIX norma definuje pomoc´ı 13 pravidel velmi pˇresnˇe, jak by mˇely vypadat n´ azvy pˇr´ıkaz˚ u a form´at pˇrep´ınaˇc˚ u. Napˇr´ıklad jm´eno pˇr´ıkazu by mˇelo b´ yt pouze mal´ ymi p´ısmeny, dlouh´e 2–9 znak˚ u, z pˇrenositeln´e znakov´e sady. Pˇrep´ınaˇce bez argument˚ u by mˇelo b´ yt moˇzn´e d´ at do skupiny za jedn´ım znakem ’–’. Atd. • pouˇz´ıvat ˇc´ıslice jako pˇrep´ınaˇce je zastaral´e; je to nˇekde v normˇe UNIX 03, i kdyˇz j´ a to v n´ı nenaˇsel. • pozor na Linux a jeho (ponˇekud zvl´aˇstn´ı a nestandardn´ı) permutov´an´ı argument˚ u (viz dalˇs´ı slidy). • pˇrep´ınaˇc -W by mˇel b´ yt rezervovan´ y pro vendor options, tj. pro nepˇrenositeln´ a rozˇs´ıˇren´ı
44
Zpracov´ an´ı pˇ rep´ınaˇ c˚ u: getopt() int getopt(int argc, char *const argv [], const char *optstring ); extern char *optarg ; extern int optind, opterr, optopt ; • funkce dostane parametry z pˇr´ıkazov´eho ˇra´dku, pˇri kaˇzd´em vol´ an´ı zpracuje a vr´ at´ı dalˇs´ı pˇrep´ınaˇc. Pokud m´a pˇrep´ınaˇc hodnotu, vr´ at´ı ji v optarg. • kdyˇz jsou vyˇcerp´ any vˇsechny pˇrep´ınaˇce, vr´ at´ı -1 a v optind je ˇc´ıslo prvn´ıho nezpracovan´eho argumentu. • moˇzn´e pˇrep´ınaˇce jsou zad´ any v optstring, kdyˇz za znakem pˇrep´ınaˇce n´ asleduje ’:’, m´a pˇrep´ınaˇc povinnou hodnotu. • pˇri chybˇe (nezn´ am´ y pˇrep´ınaˇc, chyb´ı hodnota) vr´ at´ı ’?’, uloˇz´ı znak pˇrep´ınaˇce do optopt a kdyˇz opterr nebylo nastaveno na nulu, vyp´ıˇse chybov´e hl´aˇsen´ı.
• obvykle se nejprve pomoc´ı getopt() naˇctou pˇrep´ınaˇce a pak se vlastn´ımi prostˇredky zpracuj´ı ostatn´ı argumenty (ˇcasto jsou to jm´ena soubor˚ u). • je konvenc´ı, ˇze volby v parametru optstring jsou setˇr´ıdˇen´e.
45
Pˇ r´ıklad pouˇ zit´ı getopt() struct { int a, b; char c[128]; } opts; int opt; char *arg1; while((opt = getopt(argc, argv, "abc:")) != -1) switch(opt) { case ’a’: opts.a = 1; break; case ’b’: opts.b = 1; break; case ’c’: strcpy(opts.c, optarg); break; case ’?’: fprintf(stderr, "usage: %s [-ab] [-c Carg] arg1 arg2 ...\n", basename(argv[0])); break; } arg1 = argv[optind];
• dobr´ ym zvykem je pˇri detekov´an´ı nezn´ am´eho pˇrep´ınaˇce (nebo ˇspatn´eho z´ apisu parametr˚ u programu) vypsat struˇcnou n´ apovˇedu (pˇr´ıpadnˇe s odkazem na podrobnˇejˇs´ı dokumentaci) a ukonˇcit program s chybou, tj. s nenulovou n´ avratovou hodnotou. • pˇr´ıklad tak´e ukazuje nebezpeˇcn´e pouˇzit´ı funkce strcpy() • z pouˇzit´ı funkce getopt() je vidˇet, ˇze je stavov´a. Zpracovat dalˇs´ı pole argument˚ u, pˇr´ıpadnˇe znovuzpracovat p˚ uvodn´ı, je moˇzn´e nastaven´ım extern´ı promˇenn´e optreset na 1. • standardn´ı getopt() zachov´a poˇrad´ı pˇrep´ınaˇc˚ u pˇri zpracov´an´ı • pˇri pouˇzit´ı nedefinovan´eho pˇrep´ınaˇce funkce vyp´ıˇse chybu; to lze potlaˇcit nastaven´ım opterr na 0.
46
Dlouh´ y tvar pˇ rep´ınaˇ c˚ u • poprv´e se objevilo v GNU knihovnˇe libiberty: --jm´ eno nebo --jm´ eno=hodnota • argumenty se permutuj´ı tak, aby pˇrep´ınaˇce byly na zaˇca´tku, napˇr. ls * -l je tot´eˇz jako ls -l *, standardn´ı chov´an´ı lze doc´ılit nastaven´ım promˇenn´e POSIXLY_CORRECT. • zpracov´avaj´ı se funkc´ı getopt long(), kter´ a pouˇz´ıv´ a pole struktur popisuj´ıc´ıch jednotliv´e pˇrep´ınaˇce: struct option { const char *name; /* jm´eno pˇrep´ınaˇce */ int has arg; /* hodnota: ano, ne, volitelnˇ e */ int *flag; /* kdyˇz je NULL, funkce vrac´ı val, jinak vrac´ı 0 a d´ a val do *flag */ int val; /* n´ avratov´a hodnota */ };
verze jak se objevila ve FreeBSD (funkce getopt long() nen´ı standarizovan´ a), m´a n´ asleduj´ıc´ı vlastnosti: • pokud vˇsechny dlouh´e pˇrep´ınaˇce maj´ı nastaveny kr´ atkou variantu ve val, je chov´an´ı getopt long() kompatibiln´ı s getopt() • je moˇzn´e zad´ avat argument k dlouh´emu pˇrep´ınaˇci i s mezerou (napˇr´ıklad --color green) • pokud je nastaven flag, tak getopt long() vrac´ı 0, ˇc´ımˇz se tyto dlouh´e pˇrep´ınaˇce bez kr´ atk´e varianty zpracuj´ı v jedn´e vˇetvi pˇr´ıkazu case • existuje i vol´ an´ı getopt long only(), kter´e povoluje i dlouh´e pˇrep´ınaˇce uvozen´e jednou uvozovkou (-option) • funkci getopt long() je moˇzn´e pouˇz´ıvat dvˇemi zp˚ usoby. Prvn´ı zp˚ usob je, ˇze kaˇzd´ y dlouh´ y pˇrep´ınaˇc m´a koresponduj´ıc´ı kr´ atk´ y – takto lze jednoduˇse pˇridat dlouh´e pˇrep´ınaˇce do existuj´ıc´ıho programu a je kompatibiln´ı s getopt. Druh´ y zp˚ usob umoˇzn ˇ uje m´ıt samostatn´e dlouh´e pˇrep´ınaˇce. V tom pˇr´ıpadˇe funkce vrac´ı vˇzdy 0 (nekompatibilita s getopt) a promˇenn´a *flag se nastav´ı na val. • na konkr´etn´ım pˇr´ıkladu na n´ asleduj´ıc´ı str´ ance je vidˇet, jak to cel´e funguje
47
Dlouh´ e pˇ rep´ınaˇ ce (pokraˇ cov´ an´ı) int getopt long(int argc, char * const argv [], const char *optstring, const struct option *longopts, int *longindex ); • optstring obsahuje jednop´ısmenn´e pˇrep´ınaˇce, longopts obsahuje adresu pole struktur pro dlouh´e pˇrep´ınaˇce (posledn´ı z´ aznam pole obsahuje sam´e nuly) • pokud funkce naraz´ı na dlouh´ y pˇrep´ınaˇc, vrac´ı odpov´ıdaj´ıc´ı val nebo nulu (pokud flag nebyl NULL), jinak je chov´an´ı shodn´e s getopt(). • do *longindex (kdyˇz nen´ı NULL) d´ a nav´ıc -1 nalezen´eho pˇrep´ınaˇce v longopts.
• toto je upraven´ y pˇr´ıklad z manu´ alov´e str´ anky na FreeBSD: #include <stdio.h> #include #include int ch, fd, daggerset, bflag = 0; static struct option longopts[] = { { "buffy", no_argument, { "fluoride", required_argument, { "daggerset", no_argument, { NULL, 0,
NULL, NULL, &daggerset, NULL,
’b’ }, ’f’ }, 1 }, 0 }};
int main(int argc, char **argv) { while ((ch = getopt_long(argc, argv, "bf:", longopts, NULL)) != -1) switch (ch) { case ’b’: bflag = 1; break; case ’f’: if ((fd = open(optarg, O_RDONLY, 0)) == -1) printf("unable to open %s", optarg); break; case 0: if (daggerset) { printf("Buffy will use her dagger to " "apply fluoride to dracula’s teeth\n"); } break; default: printf("usage: ...\n"); } argc -= optind; argv += optind; return 0; }
48
Struktura klasick´ eho OS UNIX uˇzivatelsk´y proces pˇreruˇsen´ı n´avrat
uˇzivatelsk´y reˇzim
rozhran´ı vol´an´ı j´adra reˇzim j´adra
subsyst´em ˇr´ızen´ı soubor˚ u buffery
subsyst´em ˇr´ızen´ı proces˚ u a pamˇeti
znakov´e blokov´e ovladaˇce
IPC pl´anovaˇc pˇridˇelov´an´ı pamˇeti
strojovˇe z´avisl´a vrstva ovl´ad´an´ı hardware hardware
• toto sch´ema je pˇrevzato z [Bach86], viz literatura. Zd˚ urazˇ nuje dva u ´ stˇredn´ı pojmy v modelu syst´emu UNIX – soubory a procesy. V praxi se j´ adro od modelu typicky odliˇsuje, ale zde je d˚ uleˇzit´ a z´ akladn´ı pˇredstava. • UNIX rozliˇsuje dva reˇzimy bˇehu procesoru: uˇzivatelsk´y reˇzim a reˇzim j´ adra. V uˇzivatelsk´em reˇzimu nejsou pˇr´ıstupn´e privilegovan´e instrukce (napˇr. mapov´an´ı pamˇeti, I/O, maskov´an´ı pˇreruˇsen´ı). Tyto dva reˇzimy mus´ı b´ yt podporov´any na hardwarov´e u ´ rovni (procesorem). • procesy bˇeˇz´ı obvykle v uˇzivatelsk´em reˇzimu, do reˇzimu j´adra pˇrech´ az´ı bud’ instrukc´ı synchronn´ıho pˇreruˇsen´ı (trap) pro vol´an´ı sluˇzby j´adra, nebo na z´ akladˇe asynchronn´ıch pˇreruˇsen´ı (hodiny, I/O). D´ ale se v reˇzimu j´adra oˇsetˇruj´ı v´ yjimeˇcn´e stavy procesoru (v´ ypadek str´ anky, naruˇsen´ı ochrany pamˇeti, nezn´ am´ a instrukce apod.). Nˇekter´e speci´ aln´ı akce jsou zajiˇst’ov´any syst´emov´ ymi procesy, kter´e bˇeˇz´ı celou dobu v reˇzimu j´adra. • klasick´e UNIXov´e j´ adro je tvoˇreno monolitick´ ym k´odem. P˚ uvodnˇe bylo potˇreba vygenerovat (tj. pˇreloˇzit ze zdrojov´ ych text˚ u a slinkovat) j´adro pˇri zmˇenˇe nˇekter´eho parametru nebo pˇrid´ an´ı ovladaˇce zaˇr´ızen´ı. V novˇejˇs´ıch implementac´ıch je moˇzno nastavovat parametry j´adra, nˇekdy i za bˇehu, pomoc´ı syst´emov´ ych utilit bez nutnosti rekompilace j´adra. Modern´ı UNIXy umoˇzn ˇ uj´ı rozˇsiˇrovat k´od j´ adra za bˇehu pomoc´ı tzv. modul˚ u j´adra (loadable kernel modules). Napˇr´ıklad syst´em FreeBSD 5.4-RELEASE m´a 392 takov´ ych modul˚ u. • existuj´ı dva zp˚ usoby pr´ace s perif´eriemi: blokov´a (block devices) a znakov´a zaˇr´ızen´ı (character, raw devices). Data z blokov´ ych zaˇr´ızen´ı (napˇr. disky) proch´ azej´ı pˇres vyrovn´ avac´ı pamˇeti (buffers) po bloc´ıch, znakov´a zaˇr´ızen´ı (napˇr. termin´ aly) umoˇzn ˇ uj´ı pracovat s jednotliv´ ymi bajty a nepouˇz´ıvaj´ı vyrovn´ avac´ı pamˇet’. 49
• j´ adro nen´ı samostatn´ y proces, ale je ˇca´st´ı kaˇzd´eho uˇzivatelsk´eho procesu. Kdyˇz j´ adro nˇeco vykon´ av´ a, tak vlastnˇe proces, bˇeˇz´ıc´ı v reˇzimu j´adra, nˇeco prov´ad´ı.
Procesy, vl´ akna, programy • proces je syst´emov´ y objekt charakterizovan´ y sv´ ym kontextem, identifikovan´ y jednoznaˇcn´ ym ˇc´ıslem (process ID, PID); jin´ ymi slovy ,,k´ od a data v pamˇeti” • vl´ akno (thread) je syst´emov´ y objekt, kter´ y existuje uvnitˇr procesu a je charakterizov´an sv´ ym stavem. Vˇsechna vl´ akna jednoho procesu sd´ıl´ı stejn´ y pamˇet’ov´ y prostor kromˇe registr˚ u procesoru a z´ asobn´ıku; ,,linie v´ ypoˇctu”, ,,to, co bˇeˇz´ı” • program ... soubor pˇresnˇe definovan´eho form´atu obsahuj´ıc´ı instrukce, data a sluˇzebn´ı informace nutn´e ke spuˇstˇen´ı; ,,spustiteln´ y soubor na disku” ◦ pamˇ et’ se pˇridˇeluje proces˚ um. ◦ procesory se pˇridˇeluj´ı vl´ akn˚ um. ◦ vl´ akna jednoho procesu mohou bˇeˇzet na r˚ uzn´ ych procesorech.
• kontext je pamˇet’ov´ y prostor procesu, obsah registr˚ u a datov´e struktury j´adra t´ ykaj´ıc´ı se dan´eho procesu • jinak ˇreˇceno – kontext procesu je jeho stav. Kdyˇz syst´em vykon´ av´ a proces, ˇr´ık´ a se, ˇze bˇeˇz´ı v kontextu procesu. J´ adro (klasick´e) obsluhuje pˇreruˇsen´ı v kontextu pˇreruˇsen´eho procesu. • vl´ akna se dostala do UNIXu aˇz pozdˇeji, p˚ uvodnˇe v nˇem existovaly pouze procesy (s jedin´ ym hlavn´ım vl´ aknem). Moˇznost pouˇz´ıt v procesu v´ıce vl´ aken byla zavedena, protoˇze se uk´ azalo, ˇze je vhodn´e m´ıt v´ıce paraleln´ıch lini´ı v´ ypoˇctu nad sd´ılen´ ymi daty. • pamˇet’ov´e prostory proces˚ u jsou navz´ ajem izolovan´e, ale procesy spolu mohou komunikovat (pozdˇeji se dozv´ıme, ˇze mohou i ˇca´steˇcnˇe sd´ılet pamˇet’). • procesy jsou entity na u ´ rovni j´adra, vl´ akna mohou b´ yt ˇca´steˇcnˇe nebo zcela implementov´ana knihovn´ımi funkcemi (tj. vl´ akna nemus´ı j´adro v˚ ubec podporovat). S vl´ akny je spojena menˇs´ı reˇzie neˇz s procesy. • syst´emov´ y proces, kter´ y bˇeˇz´ı na pozad´ı obvykle po celou dobu bˇehu syst´emu a zajiˇst’uje nˇekter´e syst´emov´e sluˇzby (inetd, cron, sendmail. . . ) se naz´ yv´ a d´emon (angl. daemon). Syst´em BSD tedy nem´ a ve znaku ˇcerta, ale d´emona.
50
J´ adro, reˇ zimy, pˇ reruˇ sen´ı (klasick´ y UNIX) • procesy typicky bˇeˇz´ı v uˇzivatelsk´em reˇzimu • syst´emov´e vol´ an´ı zp˚ usob´ı pˇrepnut´ı do reˇzimu j´adra • proces m´a pro kaˇzd´ y reˇzim samostatn´ y z´ asobn´ık • j´ adro je ˇca´st´ı kaˇzd´eho uˇzivatelsk´eho procesu, nen´ı to samostn´ y proces (procesy) • pˇrepnut´ı na jin´ y proces se naz´ yv´ a pˇrepnut´ı kontextu • obsluha pˇreruˇsen´ı se prov´ad´ı v kontextu pˇreruˇsen´eho procesu • klasick´e j´ adro je nepreemptivn´ı
• j´ adro nen´ı oddˇ elen´ a mnoˇ zina proces˚ u, bˇ eˇ z´ıc´ıch paralelnˇ e s uˇ zivatelsk´ ymi procesy, ale je ˇ c´ ast´ı kaˇ zd´ eho uˇ zivatelsk´ eho procesu. • pˇrechod mezi uˇzivatelsk´ ym reˇzimem a reˇzimem j´adra nen´ı pˇrepnut´ı kontextu – proces bˇeˇz´ı poˇra´d v tom sam´em • pˇreruˇsen´ y proces nemusel pˇreruˇsen´ı v˚ ubec zp˚ usobit • v reˇzimu j´ adra m˚ uˇze proces pˇristupovat i k adres´ am j´adra, kter´ a z uˇzivatelsk´eho reˇzimu pˇr´ıstupn´ a nejsou; takt´eˇz m˚ uˇze pˇristupovat k instrukc´ım (napˇr. instrukce manipuluj´ıc´ı se stavov´ ym registrem), jejichˇz vykon´ an´ı v uˇzivatelsk´em reˇzimu vede k chybˇe • pˇreruˇsovac´ı rutina se nem˚ uˇze zablokovat, protoˇze t´ım by zablokovala proces; proces se totiˇz m˚ uˇze zablokovat jen ze sv´e vlastn´ı v˚ ule. Modern´ı Unixy dnes pouˇz´ıvaj´ı interrupt vl´ akna, v jejichˇz kontextu se mohou drivery zablokovat. • to, ˇze klasick´e unixov´e j´ adro je nepreemptivn´ı znamen´ a, ˇze jeden proces nem˚ uˇ ze zablokovat jin´ y proces • pˇri obsluze pˇreruˇsen´ı se m˚ uˇze st´at, ˇze nastane dalˇs´ı pˇreruˇsen´ı. Pokud je jeho priorita vˇetˇs´ı, je procesorem pˇrijmuto. Posloupnost pˇrijmut´ ych pˇreruˇsen´ı je uchov´ana v z´ asobn´ıku kontextov´ych vrstev. • u modern´ıch kernel˚ u je situace ˇ casto velmi rozd´ıln´ a – obsluha pˇ reruˇ sen´ı, preeptivnost kernelu atd.; k nˇ ekter´ ym vˇ ecem se moˇ zn´ a dostaneme pozdˇ eji bˇ ehem semestru
51
Vol´ an´ı sluˇ zeb a komunikace mezi procesy • UNIX proces
proces
proces
rozhran´ı vol´an´ı j´adra j´adro • distribuovan´ y OS uˇzivatelsk´y proces
uˇzivatelsk´y proces
server
server
server
j´adro
j´adro
j´adro
j´adro
j´adro
• pokud unixov´ y proces vyˇzaduje proveden´ı syst´emov´e sluˇzby, pomoc´ı syst´emov´eho vol´ an´ı pˇred´ a ˇr´ızen´ı j´ adru. J´ adro je kus k´ odu sd´ılen´ y vˇsemi procesy (ovˇsem pˇr´ıstupn´ y jen pro ty, kter´e jsou pr´avˇe v reˇzimu j´adra). J´ adro tedy nen´ı samostatn´ y privilegovan´ y proces, ale vˇzdy bˇeˇz´ı v r´ amci nˇekter´eho procesu (toho, kter´ y poˇza´dal j´ adro o sluˇzbu, nebo toho, kter´ y bˇeˇzel v okamˇziku pˇr´ıchodu pˇreruˇsen´ı). • komunikace mezi procesy v UNIXu je ˇreˇsena pomoc´ı syst´emov´ ych vol´an´ı, je tedy zprostˇredkovan´ a j´ adrem. • aby to nebylo tak jednoduch´e, mohou existovat syst´emov´e procesy (oznaˇcovan´e jako kernel threads), kter´e bˇeˇz´ı celou dobu v reˇzimu j´adra. Naprost´a vˇetˇsina syst´emov´ ych proces˚ u vˇsak bˇeˇz´ı v uˇzivatelsk´em reˇzimu a liˇs´ı se jen t´ım, ˇze maj´ı vˇetˇs´ı pˇr´ıstupov´a pr´ava. Pl´anovaˇc proces˚ u pˇrep´ın´a mezi procesy a t´ım umoˇzn ˇ uje bˇeh v´ıce proces˚ u souˇcasnˇe i na jednom procesoru. Na multiprocesorov´ ych poˇc´ıtaˇc´ıch pak funguje skuteˇcn´ y paralelismus proces˚ u a vl´ aken (dokonce se proces m˚ uˇze pˇri pˇrepl´anov´an´ı dostat i na jin´ y procesor). • v distribuovan´em operaˇcn´ım syst´emu m´a j´adro obvykle formu mikroj´adra, tj. zajiˇst’uje pouze nejz´ akladnˇejˇs´ı sluˇzby ˇr´ızen´ı procesoru, pˇridˇelov´an´ı pamˇeti a komunikace mezi procesy. Vyˇsˇs´ı syst´emov´e sluˇzby, kter´e jsou v UNIXu souˇca´st´ı j´ adra (napˇr. pˇr´ıstup k syst´emu soubor˚ u) jsou realizov´any speci´ aln´ımi procesy (servery) bˇeˇz´ıc´ımi v uˇzivatelsk´em reˇzimu procesoru. J´ adro pˇred´ a poˇzadavek uˇzivatelsk´eho procesu pˇr´ısluˇsn´emu serveru, kter´ y m˚ uˇze bˇeˇzet i na jin´em uzlu s´ıtˇe. • dostupn´ ych mikrokernel˚ u je v dneˇsn´ı dobˇe mnoho. M˚ uˇzete zkusit napˇr´ıklad Minix (unix-like v´ yukov´ y syst´em), pˇr´ıpadnˇe syst´em HURD, kter´ y bˇeˇz´ı nad mikroj´ adrem Mach. 52
Syst´ emov´ a vol´ an´ı, funkce • v UNIXu se rozliˇsuj´ı syst´ emov´ a vol´ an´ı a knihovn´ı funkce. Toto rozliˇsen´ı dodrˇzuj´ı i manu´ alov´e str´ anky: sekce 2 obsahuje syst´emov´a vol´ an´ı (syscalls), sekce 3 knihovn´ı funkce (library functions). – knihovn´ı funkce se vykon´ avaj´ı v uˇzivatelsk´em reˇzimu, stejnˇe jako ostatn´ı k´od programu. – syst´emov´a vol´ an´ı maj´ı tak´e tvar vol´an´ı funkce. Pˇr´ısluˇsn´a funkce ale pouze dohodnut´ ym zp˚ usobem zpracuje argumenty vol´ an´ı a pˇred´ a ˇr´ızen´ı j´ adru pomoc´ı instrukce synchronn´ıho pˇreruˇsen´ı. Po n´ avratu z j´adra funkce uprav´ı v´ ysledek a pˇred´ a ho volaj´ıc´ımu. • standardy tyto dvˇe kategorie nerozliˇsuj´ı, protoˇze z hlediska program´ atora je jedno, jestli urˇcitou funkci provede j´adro nebo knihovna.
• zjednoduˇsenˇe lze ˇr´ıci, ˇze syst´emov´e vol´an´ı je funkce, kter´ a jen uprav´ı sv´e argumenty do vhodn´e podoby, pˇrepne reˇzim procesoru a skuteˇcnou pr´aci nech´ a na j´ adru. Nakonec zase uprav´ı v´ ysledek. Knihovn´ı funkce m˚ uˇze a nemus´ı volat j´adro, ale vˇzdy sama dˇel´ a nˇejakou netrivi´ aln´ı ˇcinnost v uˇzivatelsk´em reˇzimu. • v assembleru je moˇzn´e zavolat vol´an´ı j´adra pˇr´ımo • API j´ adra je definovan´e na u ´ rovni vol´an´ı funkc´ı standardn´ı knihovny, nikoliv na u ´ rovni pˇreruˇsen´ı a datov´ ych struktur pouˇz´ıvan´ ych tˇemito funkcemi pro pˇred´ an´ı ˇr´ızen´ı j´ adru. Mechanismus pˇrepnut´ı mezi uˇzivatelsk´ ym reˇzimem a reˇzimem j´ adra se totiˇz m˚ uˇze liˇsit nejen v z´ avislosti na hardwarov´e platformˇe, ale i mezi r˚ uzn´ ymi verzemi syst´emu na stejn´em hardwaru.
53
N´ avratov´ e hodnoty syst´ emov´ ych vol´ an´ı • celoˇc´ıseln´ a n´ avratov´a hodnota (int, pid t, off t, apod.) – >= 0 . . . operace u ´ spˇeˇsnˇe provedena – == -1 . . . chyba • n´ avratov´a hodnota typu ukazatel – != NULL . . . operace u ´ spˇeˇsnˇe provedena – == NULL . . . chyba • po ne´ uspˇeˇsn´em syst´emov´em vol´an´ı je k´od chyby v glob´ aln´ı promˇenn´e extern int errno; • u ´ spˇeˇsn´e vol´ an´ı nemˇen´ı hodnotu v errno! Je tedy tˇreba nejprve otestovat n´ avratovou hodnotu a pak teprve errno. • chybov´e hl´aˇsen´ı podle hodnoty v errno vyp´ıˇse funkce void perror(const char *s ); • textov´ y popis chyby s dan´ ym ˇc´ıslem vr´ at´ı funkce char *strerror(int errnum );
• funkce pro pr´aci s vl´ akny pthread *() nenastavuj´ı errno, ale vrac´ı bud’ nulu (´ uspˇech) nebo pˇr´ımo k´od chyby. • pro nˇekter´ a vol´ an´ı m˚ uˇze m´ıt smysl i n´ avratov´a hodnota -1. Pak je tˇreba nejprve nastavit errno = 0 a po n´ avratu zkontrolovat, zda se errno zmˇenilo. Napˇr. funkce strtol() vrac´ı pˇri chybˇe 0, coˇz je platn´a hodnota i pro spr´ avn´ y v´ ysledek (a −1 je samozˇrejmˇe platn´ y v´ ysledek tak´e). • je tedy vˇzdy nutn´e si pˇreˇc´ıst manu´ alovou str´ anku pro pˇr´ıˇsluˇsn´e vol´an´ı nebo knihovn´ı funkci • pozn.: u ´ spˇeˇsnost funkc´ı ze stdio.h je tˇreba testovat pomoc´ı int ferror(FILE *stream );, protoˇze jinak nelze rozliˇsit mezi chybou a koncem streamu. Vzhledem k tomu, ˇze tyto funkce nepouˇz´ıv´ ame (kromˇe printf/fprintf na stdout/stderr), nemˇeli byste ji potˇrebovat.
54
Obsah • u ´ vod, v´ yvoj UNIXu a C, program´ atorsk´e n´ astroje • z´ akladn´ı pojmy a konvence UNIXu a jeho API • pˇ r´ıstupov´ a pr´ ava, perifern´ı zaˇ r´ızen´ı, syst´ em soubor˚ u • manipulace s procesy, spouˇstˇen´ı program˚ u • sign´aly • synchronizace a komunikace proces˚ u • s´ıt’ov´a komunikace • vl´ akna, synchronizace vl´ aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu
Uˇ zivatel´ e a skupiny beran:x:1205:106:Martin Beran:/home/beran:/bin/bash v´ yznam jednotliv´ ych pol´ı: uˇzivatelsk´e jm´eno, zak´ odovan´e heslo (novˇe v /etc/shadow), ˇc´ıslo uˇzivatele (UID); superuˇzivatel (root) m´a UID 0, ˇc´ıslo prim´ arn´ı skupiny (GID), pln´e jm´eno, domovsk´ y adres´ aˇr, login-shell sisal:*:106:forst,beran v´ yznam jednotliv´ ych pol´ı: jm´eno skupiny, heslo pro pˇrepnut´ı do skupiny, ˇc´ıslo skupiny (GID), seznam ˇclen˚ u skupiny
55
• informace o uˇzivatel´ıch v souborech /etc/passwd, a /etc/group jsou zpracov´av´ any r˚ uzn´ ymi syst´emov´ ymi programy, napˇr. login (pˇrihl´aˇsen´ı do syst´emu na z´ akladˇe uˇzivatelsk´eho jm´ena a hesla) nebo su (zmˇena identity). J´ adro o tˇ echto souborech nic nev´ı, pouˇ z´ıv´ a pouze numerickou identifikaci uˇ zivatele a skupiny. • dnes jiˇz hesla nejsou z bezpeˇcnostn´ıch d˚ uvod˚ u pˇr´ımo v /etc/passwd, ale napˇr´ıklad v /etc/shadow, kter´ y bˇeˇzn´emu uˇzivateli pˇr´ıstupn´ y nen´ı. A napˇr. na FreeBSD se soubor /etc/passwd generuje ze souboru /etc/master.passwd, kter´ y zak´ odovan´ a hesla obsahuje. • existuj´ı i jin´e syst´emy, kter´e (nejen) pro autentizaci /etc/passwd nemus´ı v˚ ubec pouˇz´ıvat, napˇr´ıklad NIS (Network Information Service). • skupina uˇzivatele uveden´ a v /etc/passwd je prim´ arn´ı. Tuto skupinovou identifikaci dostanou napˇr. soubory vytvoˇren´e procesy uˇzivatele. Dalˇs´ı skupiny, ve kter´ ych je uˇzivatel uveden v souboru /etc/group, jsou doplˇ nkov´e (supplementary) a rozˇsiˇruj´ı pˇr´ıstupov´a pr´ava uˇzivatele: skupinov´ y pˇr´ıstup je povolen ke vˇsem objekt˚ um, jejichˇz skupinov´ y vlastn´ık je roven bud’ prim´ arn´ı, nebo jedn´e z doplˇ nkov´ ych skupin. • p˚ uvodnˇe mˇel v UNIXu kaˇzd´ y uˇzivatel vˇzdy aktivn´ı pouze jednu skupinovou identitu. Po nalogov´an´ı byl ve sv´e prim´ arn´ı skupinˇe, pro z´ısk´an´ı pr´av jin´e skupiny bylo tˇreba se do n´ı pˇrepnout pˇr´ıkazem newgrp (skupinov´a obdoba su, ˇr´ıd´ı se obsahem souboru /etc/group), kter´ y spustil nov´ y shell. • v novˇejˇs´ıch UNIXech nen´ı tˇreba pro pˇr´ıstup k soubor˚ um mˇenit prim´ arn´ı skupinovou identitu procesu, pokud uˇzivatel patˇr´ı do potˇrebn´e skupiny. Zmˇena identity je nutn´a, pouze kdyˇz chceme vytv´aˇret soubory s jinou skupinovou identitou neˇz je prim´ arn´ı skupina uˇzivatele. Lok´ alnˇe pro urˇcit´ y adres´ aˇr toho lze dos´ ahnout nastaven´ım skupinov´eho vlastn´ıka adres´ aˇre na poˇzadovanou skupinu a nastaven´ım bitu SGID v pˇr´ıstupov´ ych pr´avech adres´ aˇre – to plat´ı pro syst´emy zaloˇzen´e na System V. U BSD staˇc´ı zmˇenit poˇzadovanou skupinu u adres´ aˇre. • druh´ a poloˇzka v ˇra´dc´ıch /etc/group obsahuje zak´ odovan´e skupinov´e heslo pouˇz´ıvan´e pˇr´ıkazem newgrp, to se jiˇz dnes nepouˇz´ıv´ a. Napˇr´ıklad na FreeBSD je pˇr´ıkaz newgrp pˇr´ıstupn´ y uˇz jen superuˇzivateli (kv˚ uli vol´an´ı setgroups).
56
Name service switch • dneˇsn´ı syst´emy nejsou omezeny na pouˇz´ıv´ an´ı /etc/passwd a /etc/groups • syst´em pouˇz´ıv´ a datab´ aze (passwd, groups, protocols, . . . ) • data datab´ az´ı poch´ azej´ı ze zdroj˚ u (soubory, DNS, NIS, LDAP, ...) • soubor nsswitch.conf definuje jak´e datab´ aze pouˇz´ıvaj´ı jak´e zdroje • knihovn´ı funkce toto samozˇrejmˇe mus´ı explicitnˇe podporovat • je moˇzn´e nˇekter´e zdroje kombinovat, napˇr´ıklad uˇzivatel se nejdˇr´ıve m˚ uˇze hledat v /etc/passwd a pot´e v NISu • poprv´e se objevilo v Solarisu, pot´e pˇrevzato dalˇs´ımi syst´emy
• syst´emy maj´ı typicky manu´ alovou str´ anku nsswitch.conf(5), kde lze nal´ezt podrobnosti v z´ avislosti na konkr´etn´ım operaˇcn´ım syst´emu • zde je ˇca´st skuteˇcn´eho souboru nsswitch.conf ze stroje u-us: passwd: group:
files ldap files ldap
# You must also set up the /etc/resolv.conf file for DNS name # server lookup. See resolv.conf(4). hosts: files dns # Note that IPv4 addresses are searched for in all of the # ipnodes databases before searching the hosts databases. ipnodes: files dns networks: protocols: rpc: ethers:
files files files files
57
Testov´ an´ı pˇ r´ıstupov´ ych pr´ av • uˇzivatel je identifikov´an ˇc´ıslem uˇzivatele (UID) a ˇc´ısly skupin, do kter´ ych patˇr´ı (primary GID, supplementary GIDs). • tuto identifikaci dˇed´ı kaˇzd´ y proces dan´eho uˇzivatele. • soubor S m´a vlastn´ıka (U IDS ) a skupinov´eho vlastn´ıka (GIDS ). • algoritmus testov´an´ı pˇr´ıstupov´ ych pr´av pro proces P (U IDP , GIDP , SU P G) a soubor S(U IDS , GIDS ): Jestliˇze
pak P roces m´a v˚ uˇci Souboru
if(U IDP == 0)
. . . vˇsechna pr´ava
else if(U IDP == U IDS )
. . . pr´ava vlastn´ıka
else if(GIDP == GIDS || GIDS ∈ SU P G)
. . . pr´ava ˇclena skupiny . . . pr´ava ostatn´ıch
else
• procesy superuˇzivatele root mohou mˇenit svoji uˇzivatelskou a skupinovou identitu. Toho vyuˇz´ıv´ a napˇr. proces login, kter´ y bˇeˇz´ı jako root a po zkontrolov´an´ı jm´ena a hesla spust´ı shell s uˇzivatelskou identitou (pomoc´ı vol´an´ı setuid() – viz dalˇs´ı slajdy). • z algoritmu plyne, ˇze pro roota nen´ı relevantn´ı nastaven´ı pr´av (m´ a vˇzdy neomezen´ y pˇr´ıstup). Pokud se shoduje uˇzivatel, nepouˇzij´ı se nikdy pr´ava skupiny nebo ostatn´ıch, i kdyˇz povoluj´ı v´ıce neˇz uˇzivatelsk´ a pr´ava. Podobnˇe pr´ava ostatn´ıch se nepouˇzij´ı, jestliˇze se shoduje skupinov´a identita. Tedy pokud m´a m˚ uj soubor nastaveny pr´ava ---rwxrwx, nemohu ho ˇc´ıst, zapisovat ani spustit (dokud nastaven´ı pr´av nezmˇen´ım). • nˇekter´e syst´emy se odkl´ anˇej´ı od klasick´eho modelu, kdy mnoho proces˚ u bˇeˇzelo pod uˇzivatelem s UID 0 a pˇri bezpeˇcnostn´ı chybˇe v takov´e aplikaci ˇcasto u ´ toˇcn´ık z´ıskal vl´ adu nad cel´ ym syst´emem a zav´adˇej´ı modely jako je least privilege model v Solarisu 10, systrace v OpenBSD, . . .
58
Re´ aln´ e a efektivn´ı UID/GID • u kaˇzd´eho procesu se rozliˇsuje: – re´ aln´ e UID (RUID) – kdo je skuteˇcn´ ym vlastn´ıkem procesu – efektivn´ı UID (EUID) – uˇzivatel, jehoˇz pr´ava proces pouˇz´ıv´ a – uschovan´ e UID (saved SUID) – p˚ uvodn´ı efektivn´ı UID • podobnˇe se rozliˇsuje re´ aln´e, efektivn´ı a uschovan´e GID procesu. • obvykle plat´ı RUID==EUID && RGID==EGID. • prop˚ ujˇ cov´ an´ı pr´ av . . . spuˇstˇen´ı programu s nastaven´ ym SUID (set user ID) bitem zmˇen´ı EUID procesu na UID vlastn´ıka programu, RUID se nezmˇen´ı. • podobnˇe SGID bit ovlivˇ nuje EGID procesu. • pˇri kontrole pˇr´ıstupov´ ych pr´av se pouˇz´ıvaj´ı vˇzdy EUID, EGID a supplementary GIDs.
• bity SUID a SGID se pouˇz´ıvaj´ı u program˚ u, kter´e potˇrebuj´ı vˇetˇs´ı pˇr´ıstupov´a pr´ava, neˇz m´a uˇzivatel, jenˇz je spouˇst´ı. Pˇr´ıkladem je program passwd, kter´ y mus´ı aktualizovat soubory /etc/passwd a /etc/shadow, kde ten prvn´ı nem˚ uˇze bˇeˇzn´ y uˇzivatel mˇenit a druh´ y z nich ani ˇc´ıst. Dalˇs´ı pˇr´ıklad je program su. Ten mus´ı m´ıt pr´avo libovolnˇe zmˇenit uˇzivatelskou a skupinovou identitu, coˇz je privilegium proces˚ u s UID 0. • SUID a SGID programy by mˇely b´ yt peˇclivˇe naprogramov´any, aby dovolily pouze ty operace, pro kter´e jsou urˇceny, a neumoˇznily zneuˇz´ıt jejich privilegia pro neopr´avnˇen´e akce (napˇr. spuˇstˇen´ı rootovsk´eho shellu). Zkuˇsenost ukazuje, ˇze tyto programy jsou jednou z nejˇcastˇejˇs´ıch pˇr´ıˇcin bezpeˇcnost´ıch probl´em˚ u UNIXov´ ych syst´em˚ u. • z´ akladn´ım pravidlem pro SUID programy je: nepiˇ ste je pokud to nen´ı opravdu nezbytn´e. Je to typick´e m´ısto pro generov´an´ı bezpeˇcnostn´ıch chyb protoˇze dobˇre (= bezpeˇcnˇe) napsat sloˇzitˇejˇs´ı SUID program nen´ı jednoduch´e. • toto jsou pravidla pro zmˇeny: – beˇzn´ y uˇzivatel nem˚ uˇze zmˇenit sv´e RUID nebo uschovan´e SUID (vyj´ımka je pˇri vol´ an´ı exec(), viz strana 113) – proces m˚ uˇze vˇzdy zmˇenit sv´e EUID na to z RUID nebo z uschovan´eho UID. Toto zaruˇcuje, ˇze v SUID programu je moˇzn´e libovolnˇe mˇenit EUID mezi t´ım p˚ uvodn´ım kter´ ym proces z´ıskal pr´ava vlastn´ıka a mezi UID skuteˇcn´eho uˇzivatele kter´ y dan´ y proces spustil) – root m˚ uˇze vˇsechno, a kdyˇz zmˇen´ı RUID, tak se z´ aroveˇ n zmˇen´ı i uchovan´e UID – nemˇelo by smysl mˇenit jen jedno z nich kdyˇz kter´ekoli m˚ uˇzete pouˇz´ıt pro nastaven´ı EUID.
59
Identifikace vlastn´ıka procesu • uid t getuid(void) vrac´ı re´ aln´e user ID volaj´ıc´ıho procesu. • uid t geteuid(void) vrac´ı efektivn´ı user ID volaj´ıc´ıho procesu. • gid t getgid(void) vrac´ı re´ aln´e group ID volaj´ıc´ıho procesu. • gid t getegid(void) vrac´ı efektivn´ı group ID volaj´ıc´ıho procesu. • int getgroups(int gidsz, gid t glist []) – do glist d´ a nejv´ yˇse gidsz supplementary group IDs volaj´ıc´ıho procesu a vr´ at´ı poˇcet vˇsech GIDs procesu.
• getgroups(): kdyˇz gidsz == 0, jen vr´ at´ı poˇcet skupin. Kdyˇz 0 < gidsz < #skupin, vr´ at´ı -1. • v UNIXu je mnoho typ˚ u jako uid_t, gid_t, size_t, apod. Vesmˇes jsou to celoˇc´ıseln´e typy, ˇcasto je najdete v /usr//sys/types.h
60
Zmˇ ena vlastn´ıka procesu • int setuid(uid t uid ); – v procesu s EUID == 0 nastav´ı RUID, EUID i saved-SUID na uid (viz tak´e pozn´ amky na stranˇe 59). – pro ostatn´ı procesy nastavuje jen EUID, a uid mus´ı b´ yt bud’ rovn´e RUID nebo uschovan´emu SUID • int setgid(gid t gid ); obdoba setuid(), nastavuje group-IDs procesu. • int setgroups(int ngroups, gid t *gidset ) nastavuje supplementary GIDs procesu, m˚ uˇze b´ yt pouˇzito jen superuˇzivatelsk´ ym procesem.
• co v´ yˇse uveden´e tedy znamen´ a: proces s efektivn´ımi pr´ avy superuˇzivatele m˚ uˇze libovolnˇe mˇenit identitu. Ostatn´ı procesory mohou pouze stˇr´ıdat sv´ a re´ aln´ a a efektivn´ı pr´ava. • program login vyuˇz´ıv´ a vol´ an´ı setuid() • pokud chce process s UID == 0 zmˇenit svou identitu, mus´ı nejprve volat setgid() a setgroups(). Teprve pak lze zavolat setuid(). Pˇri opaˇcn´em poˇrad´ı vol´ an´ı by proces po proveden´ı setuid uˇz nemˇel pr´ava na setgid() a setgroups(). • setgroups() nen´ı uvedeno v UNIX 98 ani UNIX 03. • RUID/EUID jsou uloˇzen´e v z´ aznamu tabulky proces˚ u pro pˇr´ısluˇsn´ y proces a z´ aroveˇ n v tzv. u-area (viz napˇr´ıklad [Bach]). EUID v tabulce proces˚ u se naz´ yv´ a jiˇz zm´ınˇen´e uschovan´e UID, neboli saved UID. Jak jiˇz bylo ˇreˇceno, uschovan´e UID se pouˇz´ıv´ a pro kontrolu, kdyˇz se proces chce vr´ atit k EUID, se kter´ ym byl spuˇstˇen (po t´e, co doˇcasnˇe nastavil sv´e EUID na UID uˇzivatele, kter´ y proces spustil, tj. na RUID). • pokud tedy jako root vytvoˇr´ıte SUID program a v nˇem zavol´ ate setuid() pro jak´eholi UID mimo 0, jiˇz se v programu k EUID==0 nem˚ uˇzete vr´ atit. V tom pˇr´ıpadˇe byste museli pouˇz´ıt vol´an´ı seteuid(), kter´e nastavuje pouze EUID.
61
Syst´ em soubor˚ u • adres´ aˇre tvoˇr´ı strom, spolu se soubory acyklick´ y graf (na jeden soubor m˚ uˇze existovat v´ıce odkaz˚ u). • kaˇzd´ y adres´ aˇr nav´ıc obsahuje odkaz na sebe ’.’ (teˇcka) a na nadˇrazen´ y adres´ aˇr ’..’ (dvˇe teˇcky). • pomoc´ı rozhran´ı syst´emu soubor˚ u se pˇristupuje i k dalˇs´ım entit´ am v syst´emu: – – – – – –
perifern´ı zaˇr´ızen´ı pojmenovan´e roury sokety procesy (/proc) pamˇet’ (/dev/mem, /dev/kmem) pseudosoubory (/dev/tty, /dev/fd/0,. . . )
• z pohledu j´ adra je kaˇzd´ y obyˇcejn´ y soubor pole bajt˚ u. • vˇsechny (i s´ıt’ov´e) disky jsou zapojeny do jednoho stromu.
• root m˚ uˇze v nˇekter´ ych syst´emech strukturu adres´ aˇr˚ u zacyklit, ale t´ım zmate utility pro proch´ azen´ı filesyst´emu; moc se cyklick´e struktury nepouˇz´ıvaj´ı. Symbolick´e linky na adres´ aˇre funguj´ı vˇsude. • pojmenovan´e roury (viz strana 81) lze pouˇz´ıt i mezi procesy, kter´e nejsou pˇr´ıbuzensky spˇr´ıznˇen´e. Jinak funguj´ı stejnˇe jako nepojmenovan´e roury. • zmiˇ novan´e sokety jsou v dom´enˇe UNIX, tj. slouˇz´ı pro komunikaci v r´ amci jednoho syst´emu. Sokety z dom´eny INET, pˇres kter´e prob´ıh´a s´ıt’ov´a komunikace, se v syst´emu soubor˚ u neobjevuj´ı. S´ıt’ov´a komunikace zaˇc´ın´a na stranˇe 155. • debuggery pouˇz´ıvaj´ı pamˇet’ov´e obrazy proces˚ u dostupn´e v /proc. Ve vˇetˇsinˇe unix-like syst´em˚ u obsahuje podstrom /proc u ´ daje o j´adru syst´emu a bˇeˇz´ıc´ıch procesech ve formˇe textov´ ych soubor˚ u. • dneˇsn´ı modern´ı unixy m´ıvaj´ı speci´ aln´ı filesyst´em devfs, jehoˇz obsah odr´aˇz´ı aktu´aln´ı konfiguraci syst´emu co se t´ yˇce pˇripojen´ ych zaˇr´ızen´ı. Tj. napˇr. pˇri pˇripojen´ı USB sticku se v /dev objev´ı pˇr´ısluˇsn´e diskov´e zaˇr´ızen´ı. Po fyzick´em odpojen´ı zaˇr´ızen´ı odkaz z adres´ aˇrov´e struktury opˇet zmiz´ı.
62
Jednotn´ y hierarchick´ y syst´ em soubor˚ u /
etc
dev
usr
home
tty
• svazek (angl. file system) je ˇca´st souborov´eho syst´emu, kterou lze samostatnˇe vytvoˇrit, pˇripojit, zruˇsit... Kaˇzd´ y filesyst´em m˚ uˇze m´ıt jinou vnitˇrn´ı strukturu (s5, ufs, ext2, xfs, atd.) a m˚ uˇze b´ yt uloˇzen na lok´aln´ım disku nebo na jin´em poˇc´ıtaˇci a pˇr´ıstupn´ y po s´ıti (nfs, afs). • po startu j´ adra je pˇripojen´ y jen koˇrenov´ y filesyst´em, dalˇs´ı filesyst´emy se zapojuj´ı do hierarchie na m´ısta adres´ aˇr˚ u pˇr´ıkazem mount. Tento pˇr´ıkaz je moˇzn´e spustit ruˇcnˇe (uˇzivatel root libovolnˇe, ostatn´ı pouze na nˇekter´ ych syst´emech a s omezen´ımi) nebo automaticky bˇehem inicializace syst´emu (ˇr´ıd´ı se obsahem souboru /etc/fstab). Pˇred zastaven´ım syst´emu se filesyst´emy odpojuj´ı pˇr´ıkazem umount. • dalˇs´ı moˇznost je pˇripojen´ı filesyst´emu na ˇza´dost (pˇri prvn´ım pˇr´ıstupu) a jeho odpojen´ı po urˇcit´e dobˇe neˇcinnosti. Tuto funkci zajiˇst’uje d´emon automounter (autofs, automount, amd). • UNIX nem´ a ˇza´dn´e A, B, C, D. . . disky apod.
63
Typick´ a skladba adres´ aˇ r˚ u /bin
...
z´ akladn´ı syst´emov´e pˇr´ıkazy
/dev
...
speci´ aln´ı soubory (zaˇr´ızen´ı, devices)
/etc
...
konfiguraˇcn´ı adres´ aˇr
/lib
...
z´ akladn´ı syst´emov´e knihovny
/tmp
...
veˇrejn´ y adres´ aˇr pro doˇcasn´e soubory
/home
...
koˇren domovsk´ ych adres´ aˇr˚ u
/var/adm
...
administrativn´ı soubory (ne na BSD)
/usr/
...
knihovny header˚ u pro C
/usr/local
...
lok´alnˇe instalovan´ y software
/usr/man
...
manu´ alov´e str´ anky
/var/spool
...
spool (poˇsta, tisk,...)
• v /bin, /lib, /sbin jsou pˇr´ıkazy a knihovny potˇrebn´e pˇri startu syst´emu, kdy je pˇripojen pouze koˇrenov´ y filesyst´em. Ostatn´ı pˇr´ıkazy a knihovny jsou typicky v /usr/bin, /usr/lib a /usr/sbin (/usr b´ yv´ a ˇcasto samostatn´ y filesyst´em, ˇc´ımˇz jeho obsah nen´ı dostupn´ y bˇehem startu syst´emu). • podstrom /usr obsahuje soubory, kter´e se nemˇen´ı pˇri bˇeˇzn´em provozu a nejsou z´ avisl´e na konkr´etn´ım poˇc´ıtaˇci. Proto by mˇel j´ıt sd´ılet read-only. Na sv´e stanici doma ho ale samozˇrejmˇe budete m´ıt read-write. • v podstromu /var jsou data, kter´ a se za provozu mˇen´ı a jsou specifick´a pro kaˇzd´ y poˇc´ıtaˇc. • r˚ uzn´e syst´emy (i instalace jednoho syst´emu) se ˇcasto liˇs´ı. • hier(7) na FreeBSD popisuje adres´ aˇrovou hierarchii tohoto syst´emu, Solaris m´a filesystem(5).
64
Pˇ r´ıstup k perifern´ım zaˇ r´ızen´ım • adres´ aˇr /dev obsahuje speci´ aln´ı soubory zaˇr´ızen´ı. Proces otevˇre speci´ aln´ı soubor syst´emov´ ym vol´an´ım open() a d´ ale komunikuje se zaˇr´ızen´ım pomoc´ı vol´ an´ı read(), write(), ioctl(), apod. • speci´ aln´ı soubory se dˇel´ı na – znakov´ e . . . data se pˇren´ aˇs´ı pˇr´ımo mezi procesem a ovladaˇcem zaˇr´ızen´ı, napˇr. s´eriov´e porty – blokov´ e . . . data proch´ az´ı syst´emovou vyrovn´ avac´ı pamˇet´ı (buffer cache) po bloc´ıch pevnˇe dan´e velikosti, napˇr. disky
• speci´ aln´ı soubor identifikuje zaˇr´ızen´ı dvˇema ˇc´ısly – hlavn´ı (major) ˇ c´ıslo . . . ˇc´ıslo ovladaˇce v j´ adru – vedlejˇ s´ı (minor) ˇ c´ıslo . . . ˇc´ıslo v r´ amci jednoho ovladaˇce
• vyrovn´ avac´ı pamˇeti urychluj´ı perifern´ı operace. Pˇri ˇcten´ı se data hledaj´ı nejprve v bufferu. Teprve kdyˇz nejsou k dispozici, tak se ˇctou z disku. Pˇri pˇr´ıˇst´ım ˇcten´ı stejn´eho bloku jsou data v bufferu. Pˇri z´ apisu se data uloˇz´ı do bufferu. Na disk je syst´em pˇrep´ıˇse pozdˇeji. Lze si vynutit i okamˇzit´ y z´ apis dat na disk. • disky v UNIXu jsou obvykle pˇr´ıstupn´e pˇres znakov´e (pouˇz´ıvan´e pˇri mkfs – vytvoˇren´ı svazku – a fsck – kontrola konzistence) i blokov´e rozhran´ı (pouˇz´ıvan´e pˇri norm´ aln´ım provozu syst´emu soubor˚ u). Nˇekter´e syst´emy (FreeBSD) ale uˇz v /dev v˚ ubec soubory pro blokov´a zaˇr´ızen´ı nemaj´ı, pouze znakov´a. • dˇr´ıve musel administr´ ator syst´emu po zmˇenˇe hardwarov´e konfigurace upravit obsah adres´ aˇre /dev skriptem MAKEDEV nebo ruˇcnˇe. Dnes (Linux, IRIX, FreeBSD, Solaris, . . . ) jiˇz speci´ aln´ı soubory dynamicky vznikaj´ı a zanikaj´ı podle toho, jak j´ adro detekuje pˇrid´ an´ı nebo odebr´ an´ı hardwarov´ ych komponent (viz devfs na stranˇe 62). • okamˇzit´ y z´ apis na disk lze vynutit pˇres O DIRECT command ve vol´an´ı fcntl()
65
Fyzick´ e uloˇ zen´ı syst´ emu soubor˚ u • syst´ em soubor˚ u (svazek, filesystem) lze vytvoˇrit na: – odd´ılu disku (partition) – ˇc´ ast disku, na jednom disku m˚ uˇze b´ yt v´ıce odd´ıl˚ u – logick´ em odd´ılu (logical volume) – takto lze spojit v´ıce odd´ıl˚ u, kter´e mohou b´ yt i na nˇekolika disc´ıch, do jednoho svazku.
• dalˇs´ı moˇznosti: striping, mirroring, RAID /home
/
disk 1
/usr
disk 2
disk 3
• v´ yraz syst´em soubor˚ u se pouˇz´ıv´ a v nˇekolika v´ yznamech: – jeden filesyst´em, tj. to, co vyrob´ı pˇr´ıkaz mkfs – cel´ a hierarchie pˇripojen´ ych svazk˚ u v syst´emu (v´ ystup pˇr´ıkazu mount) – zp˚ usob organizace svazku (tj. typ fs) a tomu odpov´ıdaj´ıc´ı modul j´adra, kter´ y s daty manipuluje (UFS2, Ext3, XFS, ...) • striping je od slova stripe, ne strip; podle toho se tak´e vyslovuje. Znamen´ a, ˇze za sebou n´ asleduj´ıc´ı bloky dat se ukl´ adaj´ı paralelnˇe na r˚ uzn´e disky a t´ım se zvyˇsuje pˇrenosov´a rychlost. • mirroring ukl´ ad´ a kopie dat pro pˇr´ıpad hav´arie prim´ arn´ıho disku. • paritn´ı disky: data se ukl´ adaj´ı na dva disky, na tˇret´ı se ukl´ ad´ a XOR prvn´ıch dvou, po hav´arii libovoln´eho disku jsou vˇsechna data st´ ale ˇciteln´ a. • jednotliv´e u ´ rovnˇe RAID (Redundant Array of Inexpensive Disks) zahrnuj´ı striping, mirroring a vyuˇzit´ı paritn´ıch disk˚ u. • na terminologii je tˇreba d´ at velk´ y pozor. Napˇr. to, co se v DOS svˇetˇe naz´ yv´ a partition, se v BSD naz´ yv´ a slice. Tam jsou pak partitions definov´any v r´ amci jednoho slice a v nich se vytv´aˇrej´ı filesyst´emy. • pokud v´as to zaj´ım´ a nebo se s t´ım setk´ av´ ate v praxi, doporuˇcuji vaˇs´ı pozornosti ZFS, coˇz je filesyst´em a manaˇzer logick´ ych odd´ıl˚ u v jednom. Ze Solarisu se jiˇz dostal do FreeBSD od verze 7.0 a tak´e do Mac OS X Leopard (kter´ y byl ofici´ alnˇe vyd´an v ˇr´ıjnu 2007). M˚ uˇzete okamˇzitˇe zapomenout na jednotliv´e disky, co je d˚ uleˇzit´e je pouze celkov´a diskov´a kapacita v syst´emu.
66
Organizace syst´ emu soubor˚ u s5 blok ˇc. zav´adˇec´ı blok (boot block) 0 superblok 1 2 oblast i-uzl˚ u (i-nodes)
0
9 12
... oblast datov´ych blok˚ u
• p˚ uvodn´ı UNIXov´ y syst´em soubor˚ u standardnˇe pouˇz´ıvan´ y do verze System V Release 3; v BSD se prim´ arnˇe pouˇz´ıval do verze 4.1 • vlastnosti: – bloky d´elky 512, 1024 nebo 2048 bajt˚ u – jedin´ y (neduplikovan´ y) superblok – datov´ y prostor pevnˇe rozdˇelen´ y na oblast i-uzl˚ u a oblast datov´ych blok˚ u – pˇri velikosti bloku 1024 bajt˚ u byla teoretick´a velikost filesyst´emu pˇres 16 GB • boot block – pro uloˇzen´ı zavadˇeˇce OSu • superblok – z´ akladn´ı informace o svazku: poˇcet blok˚ u pro i-uzly, poˇcet blok˚ u svazku, seznam voln´ ych blok˚ u (pokraˇcuje ve voln´ ych bloc´ıch), seznam voln´ ych i-uzl˚ u (po vyˇcerp´ an´ı se prohled´ av´ a tabulka i-uzl˚ u), z´ amky pro seznamy voln´ ych blok˚ u a i-uzl˚ u, pˇr´ıznak modifikace (pro kontrolu korektn´ıho odpojen´ı svazku), ˇcas posledn´ı aktualizace, informace o zaˇr´ızen´ı • i-uzel – typ souboru, pˇr´ıstupov´a pr´ava, vlastn´ık, skupina, ˇcasy posledn´ıho pˇr´ıstupu, modifikace dat a modifikace i-uzlu (ˇcas vytvoˇren´ı souboru nen´ı uloˇzen), poˇcet odkaz˚ u na soubor, velikost souboru, 10 odkaz˚ u na datov´e bloky a 3 odkazy na nepˇr´ım´e bloky • maxim´ aln´ı velikost souboru: 2113674 blok˚ u, tj. pˇribliˇznˇe 1 GB pˇri pouˇzit´ı blok˚ u velikosti 512 B • jm´ena soubor˚ u – max. 14 znak˚ u (14 + 2 = 16, tedy mocnina dvou a tedy bezprobl´emov´e uloˇzn´ı adres´ aˇrov´ ych poloˇzek do blok˚ u) 67
• pˇri pouˇzit´ı tohoto filesyst´emu byla v´ ykonnost disk˚ u vyuˇzita jen cca na 2% a rychlost ˇcten´ı byla v ˇra´du jen nˇekolika des´ıtek kilobajt˚ u za sekundu (!!!) • pro srovn´ an´ı – MS-DOS 2.0 z roku 1983 podporoval pouze FAT12, poˇc´ıtaj´ıc´ı s maxim´ aln´ı velikost´ı filesyst´emu 16 MB. Velikost svazku do 2 GB byla umoˇznˇena aˇz ve verzi 4.0 (1988); tato verze z´ aroveˇ n zavedla diskov´e vyrovn´ avac´ı pamˇeti, tedy to, co UNIX m´a od sv´eho vzniku v roce 1970. . .
Navigace v adres´ aˇ rov´ e struktuˇ re /etc/passwd
i-nodes i-node 2
data
i-node 37
i-node 71
. ..
2 2
. ..
37 2
etc
37
passwd
71
root:x:0:...
• kdyˇz cesta zaˇc´ın´a znakem ’/’, zaˇc´ın´a navigace v koˇrenov´em adres´ aˇri, jinak zaˇcne v pracovn´ım adres´ aˇri procesu. • koˇrenov´ y adres´ aˇr m´a typicky ˇc´ıslo 2. 0 je pro oznaˇcen´ı pr´azdn´eho uzlu a 1 byla dˇr´ıve pouˇz´ıvan´ a pro soubor, do kter´eho se vkl´ adaly vadn´e bloky, aby je syst´em uˇz d´ ale nepouˇz´ıval. • cesta ve kter´e je v´ıce lom´ıtek za sebou je st´ale platn´a, tj. ///a///b///c je ekvivaletn´ı /a/b/c.
68
Linky hard link /var password
origin´al /etc 20
passwd
... i-nodes
0
data
symbolick´y link /usr 20
... 20
...
passwd
31
... ...
root:x:0:...
31 ../etc/passwd
Hard linky lze vytv´aˇret pouze v r´ amci jednoho (logick´eho) filesyst´emu.
hardlink • odkaz na stejn´ y i-uzel • vlastnˇe druh´e jm´eno souboru • nen´ı rozd´ıl mezi origin´alem a hardlinkem • lze vytv´aˇret jen v r´ amci filesyst´emu • nelze vytv´aˇret pro adres´ aˇre symbolick´ y link (symlink, softlink) • pouze odkaz na skuteˇcnou cestu k souboru jin´eho typu (ls -l ho oznaˇcuje ’l’), tj. symbolick´ y link je typem odliˇsn´ y od bˇeˇzn´eho souboru a jeho data obsahuj´ı obyˇcejn´ y ˇretˇezec – jm´eno cesty, at’ jiˇz relativn´ı nebo absolutn´ı • odliˇsn´e chov´an´ı pro origin´al a link (napˇr. pˇri maz´ an´ı) • pozor na relativn´ı a absolutn´ı cesty pˇri pˇresouv´an´ı symbolick´eho linku • m˚ uˇze ukazovat i na adres´ aˇr nebo na neexistuj´ıc´ı soubor Nejjednoˇsˇs´ı zp˚ usob jak si ovˇeˇrit, zda dan´e dva linky ukazuj´ı na stejn´ y soubor na disku je pouˇz´ıt -i pˇrep´ınaˇc pˇr´ıkazu ls. $ ls -i /etc/passwd 172789 /etc/passwd
69
Vylepˇ sen´ı syst´ emu soubor˚ u • c´ıl: sn´ıˇzen´ı fragmentace soubor˚ u, omezen´ı pohybu hlav disku um´ıstˇen´ım i-uzl˚ u a datov´ ych blok˚ u bl´ıˇz k sobˇe • UFS (Unix File System), p˚ uvodnˇe Berkeley FFS (Fast File System) • ˇclenˇen´ı na skupiny cylindr˚ u, kaˇzd´ a skupina obsahuje – kopii superbloku – ˇr´ıdic´ı blok skupiny – tabulku i-uzl˚ u – bitmapy voln´ ych i-uzl˚ u a datov´ ych blok˚ u – datov´e bloky • bloky velikosti 4 aˇz 8 kB, fragmenty blok˚ u • jm´ena dlouh´ a 255 znak˚ u
• superblok v kaˇzd´e cylinder skupinˇe posunut tak, aby superbloky nebyly na stejn´e plotnˇe • dalˇs´ı typy filesyst´em˚ u: UFS2, Ext3, ReiserFS, XFS, ZFS aj. • v http://www.devnull.cz/mff/pvu/common/docs/filesystems.ps je porovn´ an´ı osmi r˚ uzn´ ych filesyst´em˚ u podle r˚ uzn´ ych implementaˇcn´ıch krit´eri´ı; nezahrnuje v sobˇe ale v´ yvoj posledn´ıch let. • UFS byl st´ ale 32-bitov´ y, coˇz se odr´aˇzelo na maxim´ aln´ı d´elce souboru i na maxim´ aln´ı velikosti filesyst´emu • ˇzurn´ alov´an´ı (XFS, Ext3, ReiserFS) – snaha o zmenˇsen´ı nebezpeˇc´ı ztr´ aty dat v pˇr´ıpadˇe hav´arie, urychlen´ı zotaven´ı po hav´arii • ZFS – modern´ı 128-bitov´ y filesyst´em vyvinut´ y Sunem, nyn´ı open-source, v Solarisu 10, ted’ jiˇz tak´e v Mac OS X 10.5 a FreeBSD 7.
70
V´ yvoj ve spr´ avˇ e adres´ aˇ rov´ ych poloˇ zek • maxim´ aln´ı d´elka jm´ena souboru 14 znak˚ u nebyla dostaˇcuj´ıc´ı • FFS – d´elka aˇz 255; kaˇzd´ a poloˇzka z´ aroveˇ n obsahuje i jej´ı d´elku • nov´e filesyst´emy pouˇz´ıvaj´ı pro vnitˇrn´ı strukturu adres´ aˇr˚ u r˚ uzn´e varianty B-strom˚ u – v´ yraznˇe zrychluje pr´aci s adres´ aˇri obsahuj´ıc´ı velk´e mnoˇzstv´ı soubor˚ u – XFS, JFS, ReiserFS, . . . • UFS2 zav´ad´ı zpˇetnˇe kompatibiln´ı tzv. dirhash pro zrychlen´ı pˇr´ıstupu k adres´ aˇr˚ um s velk´ ym poˇctem soubor˚ u
• dirhash pracuje tak, ˇze pˇri prvn´ım pˇreˇcten´ı adres´ aˇre se vytvoˇr´ı v pamˇeti hash struktura, n´ asledn´e pˇr´ıstupy do adres´ aˇre jsou pak srovnateln´e se syst´emy pouˇz´ıvaj´ıc´ı B-stromy. T´ımto zp˚ usobem je dosaˇzeno zlepˇsen´ı bez toho, aby se mˇenila struktura filesyst´emu na disku. Nelze to ale takto dˇelat do nekoneˇcna, bude zaj´ımav´e jestli FreeBSD v budoucnu pˇrejde na ZFS jako jejich hlavn´ı filesyst´em. • mal´e soubory se ˇcasto ukl´ adaj´ı v i-nodech, ˇc´ımˇz se uˇsetˇr´ı dalˇs´ı pˇr´ıstupy na disk
71
Virtu´ aln´ı syst´ em soubor˚ u (Virtual File System) struct struct struct struct struct file file file file file *file f vnode f vnode f vnode f vnode VNODE
VNODE
VNODE
VNODE
VNODE
v data INODE
v data INODE
v data INODE
v data INODE
v data INODE s realvp
ufs vnodeops ufs file system
s5 vnodeops s5 file system
spec vnodeops spec file system
• FFS uveden´ y ve 4.2BSD byl historicky druh´ y unixov´ y filesyst´em. Nˇekteˇr´ı dodavatel´e unixov´ ych syst´em˚ u ho zaˇcali preferovat vzhledem k jeho lepˇs´ımu v´ ykonu a nov´ ym moˇznostem, jin´ y z˚ ust´ avali d´ ale u s5fs z d˚ uvodu zpˇetn´e kompatibility. To d´ ale prohlubovalo probl´em jiˇz tak nedostateˇcn´e interoperability mezi r˚ uzn´ ymi unixov´ ymi syst´emy. Nˇekter´ ym aplikac´ım nav´ıc plnˇe nevyhovoval ani jeden z tˇechto filesyst´em˚ u. Postupnˇe se tak´e objevovala potˇreba pracovat s ne-unixov´ ymi filesyst´emy, napˇr. FAT. A s rostouc´ı popularitou poˇc´ıtaˇcov´ ych s´ıt´ı se zvyˇsovala popt´avka po sd´ılen´ı soubor˚ u mezi jednotliv´ ymi syst´emy, coˇz mˇelo za n´ asledek vznik distribuovan´ ych filesyst´em˚ u – napˇr. NFS (Network File System). • FFS zaveden´ y do syst´emu s VFS se zaˇcal naz´ yvat UFS. • vzhledem k v´ yˇse popsan´e situaci bylo jen ot´azkou ˇcasu, kdy dojde k fundament´ aln´ım zmˇen´ am v infrastruktuˇre syst´emu soubor˚ u, aby souˇcasnˇe podporoval v´ıce typ˚ u filesyst´em˚ u. Vzniklo nˇekolik r˚ uzn´ ych implementac´ı od r˚ uzn´ ych v´ yrobc˚ u, aˇz se nakonec de facto standardem stala VFS/vnode architektura od firmy Sun Microsystems. V dneˇsn´ı dobˇe prakticky vˇsechny unixov´e a unixlike syst´emy podporuj´ı VFS, i kdyˇz ˇcasto se vz´ ajemnˇe nekompatibiln´ımi u ´ pravami. VFS se poprv´e objevilo v roce 1985 v Solarisu 2.0; brzy bylo pˇrevzato BSD – FFS s podporou VFS je pr´avˇe UFS. • hlavn´ı myˇslenka: kaˇzd´emu otevˇren´emu souboru v syst´emu pˇr´ısluˇs´ı struktura file (to by vlastnˇe byl jeden slot v n´ ami jiˇz zn´ am´e syst´emov´e tabulce otevˇren´ ych soubor˚ u). Ta ukazuje na vnode (virtual node). Vnode obsahuje ˇca´st nez´ avislou na konkr´etn´ım syst´emu soubor˚ u a ˇca´st z´ avislou, coˇz m˚ uˇze b´ yt napˇr´ıklad struktura inode. Ta je specifick´a pro kaˇzd´ y typ souborov´eho syst´emu. Kaˇ zd´ y typ filesyst´ emu implementuje pevnˇ e danou
72
sadu funkc´ı pro jednotliv´ e operace nad soubory, na kterou se odkazuj´ı vˇsechny virtu´ aln´ı uzly odpov´ıdaj´ıc´ı dan´emu typu filesyst´emu. Tato sada funkc´ı tedy definuje vnode interface. Kdyˇz tedy zavol´ate napˇr´ıklad open(), j´ adro zavol´ a pˇr´ısluˇsnou implementaci v z´ avislosti na typu filesyst´emu (napˇr. z modulu ext2fs). Implementaˇcnˇe z´ avisl´a ˇca´st struktury vnode je pˇr´ıstupn´ a pouze z funkc´ı pˇr´ısluˇsn´eho typu filesyst´emu; j´adro do n´ı tedy ,,nevid´ı” pˇr´ımo. Jak uvid´ıte na dalˇs´ım slajdu, existuje jeˇstˇe jedna sada funkc´ı, kter´ a se t´ yka pr´ace s filesyst´emy jako takov´ ymi. Ta pak definuje VFS interface. Tyto dvˇe sady spoleˇcnˇe tvoˇr´ı vnode/VFS rozhran´ı, kter´emu se bˇeˇznˇe ˇr´ık´ a jen VFS. • (nebudu zkouˇset) – u speci´ aln´ıch soubor˚ u je situace sloˇzitˇejˇs´ı, v SVR4 struktura file ukazuje na snode (shadow-special-vnode), kter´ y definuje operace se zaˇr´ızen´ım (pomoc´ı souborov´eho syst´emu spec) a prostˇrednictv´ım ukazatele aln´ y vnode pro operace se speci´ aln´ım souborem; s realvp se odkazuje na re´ ten je potˇreba napˇr´ıklad pro kontrolu pr´av pˇr´ıstupu. Kaˇzd´emu zaˇr´ızen´ı m˚ uˇze odpov´ıdat v´ıce speci´ aln´ıch soubor˚ u, a tedy v´ıce snodes a pˇr´ısluˇsn´ ych re´aln´ ych vnodes. Vˇsechny takov´e snodes pro jedno zaˇr´ızen´ı maj´ı ukazatel s commonvp na jeden spoleˇcn´ y snode (toto nen´ı na obr´ azku zachyceno). Pˇri otevˇren´ı speci´ aln´ıho souboru se hled´ a v hash tabulce snodes otevˇren´ ych zaˇr´ızen´ı poloˇzka odpov´ıdaj´ıc´ı speci´ aln´ımu souboru (podle major a minor ˇc´ısla zaˇr´ızen´ı). Kdyˇz snode nen´ı nalezen, vytvoˇr´ı se nov´ y. Tento snode se pak pouˇz´ıv´ a pˇri operac´ıch se zaˇr´ızen´ım. V´ıce viz napˇr´ıklad [Vahalia].
Hierarchie souborov´ ych syst´ em˚ u struct file f vnode
vfs mount list rootvfs VFS
v vfsp VNODE
VFS
vfs next vfs data
vfs data
root vnode super block
root vnode super block
vfs op
v op
vfs next
INODE
vnodeops vfs mountedhere
vfs op
vfs vnodecovered v vfsp
vfsops
VNODE
vfsops
v op INODE
vfssw[]
vsw vfsops
mount point in rootvfs
vnodeops
• struktura vfs obsahuje implementaˇcnˇe nez´avisl´e informace o filesyst´emu, nez´ avisl´e na konkr´ent´ım typu filesyst´emu (podobnˇe jako vnode funguje pro soubory). Tato struktura nereprezentuje konkr´etn´ı typ filesyst´emu, ale filesyst´em pˇrimontovan´ y do hierarchie soubor˚ u. V tomto v´azan´em seznamu tedy m˚ uˇze b´ yt v´ıce struktur stejn´eho typu souborov´eho syst´emu. 73
• rootvfs – odkaz na root file system • vfsops – tabulka funkc´ı pro konkr´etn´ı typ syst´emu soubor˚ u • vfssw[] – pole odkaz˚ u na tabulky vfsops pro vˇsechny syst´emem podporovan´e typy filesyst´em˚ u, z tabulky se vyb´ır´ a pˇri pˇripojov´an´ı svazku podle typu filesyst´emu zadan´eho pˇri vol´ an´ı mount() • v vfsp – odkaz z vnode na filesyst´em (strukturu vfs), na kter´em leˇz´ı soubor reprezentovan´ y pomoc´ı vnode y reprezentuje mount point (ad• v vfsmountedhere – pouze ve vnode, kter´ res´ aˇr, na kter´em je pˇripojen koˇren jin´eho filesyst´emu); odkazuje na strukturu vfs reprezentuj´ıc´ı pˇripojen´ y filesyst´em • v vnodecovered – odkaz na vnode adres´ aˇre, na kter´em je filesyst´em pˇripojen
Otevˇ ren´ e soubory z pohledu j´ adra I. user file descriptor table file table : count 1 process A WRONLY Z Z Z Z ZZ ~ count 2 3 RDONLY process B - count 1 RDWR
inode table @ @ @ R count 1 @
/etc/group
Q Q QQ s
count 3
: /etc/passwd
• toto je nejjednoduˇsˇs´ı pohled na tabulky v j´adˇre kter´e se t´ ykaj´ı soubor˚ u, je to pˇrevzato z [Bach]; dnes to je o nˇeco sloˇzitˇejˇs´ı, myˇslenka je ale poˇra´d stejn´ a. Realitˇe v´ıce se podobaj´ıc´ı obr´ azek je na pˇr´ıˇst´ım slajdu. • kaˇzd´ y proces m´a svoji tabulku souborov´ ych deskriptor˚ u (user file descriptor table) • z t´eto tabulky je odkazovn´ ano na syst´emovou tabulku otevˇren´ ych soubor˚ u syst´emu (file table; ano, tato tabulka je pouze jedna). Zde je m´od otevˇren´ı souboru a tak´e aktu´ aln´ı pozice v souboru. • z tabulky otevˇren´ ych soubor˚ u je odkazov´ano do tabulky naˇcten´ ych inod˚ uv pamˇeti (dnes jsou to tzv. vnodes – virtual nodes, ale to n´ am nyn´ı m˚ uˇze b´ yt u ´ plnˇe jedno) 74
• tabulka otevˇren´ ych soubor˚ u syst´emu, kter´ a vlastnˇe vytv´aˇr´ı o jednu u ´ roveˇ n odkaz˚ u nav´ıc, je zde proto, aby r˚ uzn´e procesy mohly sd´ılet stejnou aktu´aln´ı pozici v souboru. • pˇri otevˇren´ı souboru pomoc´ı vol´an´ı open() se vˇzdy alokuje nov´ y slot v tabulce deskriptor˚ u a tak´e v syst´emov´e tabulce otevˇren´ ych soubor˚ u (to je d˚ uleˇzit´e!). Sd´ılen´ı se pak v r´ amci jednoho procesu dos´ ahne duplikac´ı deskriptor˚ u, kdy v´ıce deskriptor˚ u sd´ıl´ı stejn´ y slot v tabulce otevˇren´ ych soubor˚ u syst´emu nebo v pˇr´ıpadˇe r˚ uzn´ ych proces˚ u pak pomoc´ı vytvoˇren´ı nov´eho procesu pomoc´ı vol´ an´ı fork(), viz strana 119.
Otevˇ ren´ e soubory z pohledu j´ adra II. struct proc uf next
uf next
struct ufchunk
struct ufchunk
struct ufchunk
... 2 1 0
... 2 1 0
... 2 1 0
u proc
u first
struct user p cred uf ofile[]
struct cred
f cred
f next
struct file
struct file
struct file
struct file
struct file
VNODE
VNODE
VNODE
VNODE
f prev
VNODE
struct file *file
f vnode
• struktury proc a user vytv´aˇr´ı j´adro pro kaˇzd´ y proces a drˇz´ı v nich sluˇzebn´ı informace o procesu. • struktura ufchunk obsahuje NFPCHUNK (obvykle 24) deskriptor˚ u soubor˚ u, po zaplnˇen´ı se alokuje dalˇs´ı ufchunk. • struktura file (otevˇren´ı souboru) obsahuje m´od souboru (otevˇren pro ˇcten´ı, z´ apis, atd.), poˇcet deskriptor˚ u, kter´e se na ni odkazuj´ı, ukazatel na vnode a pozici v souboru. Jedno otevˇren´ı souboru m˚ uˇze b´ yt sd´ıleno v´ıce deskriptory, jestliˇze byl p˚ uvodn´ı deskriptor zkop´ırov´an, napˇr. vol´an´ım fork() nebo dup(). • struktura cred obsahuje uˇzivatelskou a skupinovou identitu procesu, kter´ y otevˇrel soubor. • jeden vnode odpov´ıdaj´ıc´ı jednomu souboru m˚ uˇze b´ yt sd´ılen nˇekolika strukturami file, pokud byl dan´ y soubor v´ıcekr´at otevˇren. • ne vˇsechny vnodes jsou asociov´any s tabulkou otevˇren´ ych soubor˚ u. Napˇr. pˇri spuˇstˇen´ı programu je potˇreba pˇristupovat do spustiteln´eho souboru a proto se alokuje vnode. 75
Oprava konzistence souborov´ eho syst´ emu • pokud nen´ı filesyst´em pˇred zastaven´ım syst´emu korektnˇe odpojen, mohou b´ yt data v nekonzistentn´ım stavu. • ke kontrole a opravˇe svazku slouˇz´ı pˇr´ıkaz fsck. Postupnˇe testuje moˇzn´e nekonzistence: – v´ıcen´ asobn´e odkazy na stejn´ y blok – odkazy na bloky mimo rozsah datov´e oblasti syst´emu soubor˚ u – ˇspatn´ y poˇcet odkaz˚ u na i-uzly – nespr´ avn´ a velikost soubor˚ u a adres´ aˇr˚ u – neplatn´ y form´at i-uzl˚ u – bloky kter´e nejsou obsazen´e ani voln´e – chybn´ y obsah adres´ aˇr˚ u – neplatn´ y obsah superbloku • operace fsck je ˇcasovˇe n´ aroˇcn´ a. • ˇzurn´ alov´e syst´emy soubor˚ u (napˇr. XFS v IRIXu, Ext3 v Linuxu) nepotˇrebuj´ı fsck.
• data se pˇrepisuj´ı na disky z vyrovn´avac´ıch pamˇet´ı se zpoˇzdˇen´ım. Uloˇzen´ı vˇsech vyrovn´ avac´ıch pamˇet´ı lze vynutit syst´emov´ ym vol´an´ım sync(). Periodicky vyrovn´ avac´ı pamˇeti ukl´ ad´ a zvl´aˇstn´ı syst´emov´ y proces (d´emon). • zde je uk´ azka fsck na odpojen´ y filesyst´em: toor@shewolf:~# fsck /dev/ad0a ** /dev/ad0a ** Last Mounted on /mnt/flashcard ** Phase 1 - Check Blocks and Sizes ** Phase 2 - Check Pathnames ** Phase 3 - Check Connectivity ** Phase 4 - Check Reference Counts ** Phase 5 - Check Cyl groups 24 files, 8848 used, 12951 free (7 frags, 1618 blocks, 0.0% fragmentation)
76
Dalˇ s´ı zp˚ usoby zajiˇ stˇ en´ı konzistence filesyst´ emu • tradiˇcn´ı UFS – synchronn´ı z´ apis metadat – aplikace vytv´aˇrej´ıc´ı nov´ y soubor ˇcek´ a na inicializaci inode na disku; tyto operace pracuj´ı rychlost´ı disku a ne rychlost´ı CPU – asynchronn´ı z´ apis ale ˇcastˇeji zp˚ usob´ı nekontistenci metadat • ˇreˇsen´ı probl´em˚ u s nekonzistenc´ı metadat na disku: – journalling – skupina na sobˇe z´ avisl´ ych operac´ı se nejdˇr´ıve atomicky uloˇz´ı do ˇzurn´ alu; pˇri probl´emech se pak ˇzurn´ al m˚ uˇze “pˇrehr´at” – bloky metadat se nejdˇr´ıve zap´ıˇs´ı do non-volatile pamˇeti – soft-updates – sleduje z´ avislosti mezi ukazately na diskov´e struktury a zapisuje data na disk metodou write-back tak, ˇze data na disku jsou vˇzdy konzistentn´ı – ZFS je nov´ y filesyst´em v Solarisu, kter´ y pouˇz´ıv´ a copy-on-write
• filesystem metadata = inodes, directories, free block maps • ext2 dokonce defaultnˇe pouˇz´ıv´ a asynchronn´ı z´ apis metadat a je pˇri pouˇz´ıt´ı synchronn´ıho z´ apisu v´ yraznˇe pomalejˇs´ı neˇz UFS • z´ avisl´e operace jsou napˇr´ıklad smaz´ an´ı poloˇzky z adres´ aˇre a smaz´ an´ı diskov´eho inode. Pokud by se stalo, ˇze se nejdˇr´ıve smaˇze diskov´ y inode a pak teprve poloˇzka v adres´ aˇri, pˇri v´ ypadku mezi tˇemito dvˇemi operacemi vnik´ a nekonzistence – link ukazuje na diskov´ y soubor, kter´ y neexistuje. Nen´ı probl´em se tomuto vyhnout pˇri synchronn´ım z´ apisu metadat (v´ıme kdy a co zapisujeme, urˇcujeme tedy poˇrad´ı z´ apisu), ale pˇri metodˇe write-back je jiˇz nutn´e ˇreˇsit z´ avislosti jednotliv´ ych blok˚ u na sebe, protoˇze pˇri klasick´e synchronizaci vyrovn´ avac´ıch pamˇet´ı na disk j´adro nezaj´ım´ a, kter´ y blok se zap´ıˇse dˇr´ıv a kter´ y pozdˇeji. • ˇcasto jsou bloky na sobˇe z´ avisl´e ve smyˇcce. Soft updates dok´ aˇze takovou smyˇcku rozb´ıt t´ım, ˇze provede roll-back a po z´ apisu pak roll-forward • v´ ykon soft updates je srovnateln´ y v´ ykonu UFS filesyst´emu s asynchronn´ım z´ apisem metadat • teoreticky soft updates zaruˇcuj´ı, ˇze po rebootu nen´ı potˇreba pouˇz´ıt fsck, tj. ˇze filesyst´em bude v takov´em stavu, ˇze je moˇzn´e nabootovat. Je vˇsak nutn´e pouˇz´ıt tzv. background fsck pro opravy nez´avaˇzn´ ych chyb – to je povaˇzov´ano st´ ale za velkou nev´ yhodu soft updates zvl´aˇstˇe s t´ım, jak rostou velikosti bˇeˇznˇe pouˇz´ıvan´ ych disk˚ u. Takovou chybou, kter´ a nebr´ an´ı nabootov´an´ı, ale je nutn´e ji odstranit je napˇr´ıklad blok, kter´ y je oznaˇcen jako pouˇzit´ y, ale ˇza´dn´ y soubor ho nepouˇz´ıv´ a.
77
• soft updates nejsou vˇzdy doporuˇcov´any pro root filesyst´em. Probl´em je to, ˇze ztr´ ata metadat na root filesyst´emu (viz 30-ti sekundov´a perioda z´ apisu) m˚ uˇze b´ yt v´ yraznˇe vˇetˇs´ı hrozbou zde neˇz na /usr, /home atd. Dalˇs´ı nev´ yhodou m˚ uˇze b´ yt i to, ˇze u soft updates mi smaz´ an´ı velk´eho souboru hned neuvoln´ı m´ısto. • pˇr´ıklad: na bezpeˇcnou operaci rename potˇrebuju pˇri synchronn´ım z´ apisu metadat 4 z´ apisy – zv´ yˇsen´ı poˇctu odkaz˚ u v inode, vytvoˇren´ı nov´e adres´ aˇrov´e poloˇzky, smaz´ an´ı star´e, a opˇetn´e sn´ıˇzen´ı poˇctu odkaz˚ u v inode. Kdykoli by syst´em spadnul, nenastane nebezpeˇcn´ a situace. Napˇr´ıklad 2 odkazy z adres´ aˇr˚ u na inode s referenˇcn´ım poˇctem 1 je probl´em, protoˇze po zruˇsen´ı jednoho odkazu to bude vypadat, ˇze soubor je st´ale na disku, kdyˇz uˇz byls jeho data d´ avno smaz´ ana. Nen´ı teˇzk´e si asi pˇredstavit, co by to mohlo znamenat v pˇr´ıpadˇe, ˇze soubor obsahoval opravdu d˚ uleˇzit´ a data – napˇr´ıklad z´ alohu. Opaˇcn´ a situace, tj. jeden odkaz na inode s referenc´ı 2 sice tak´e nen´ı spr´ avn´ a situace, ale neohroˇzuje to moˇznost filesyst´em namontovat a norm´alnˇe pouˇz´ıvat. V nejhorˇs´ım se stane, ˇze soubor vypad´ a ˇze jiˇz na disku nen´ı a pˇritom st´ale existuje. U soft updates operace rename vytvoˇr´ı kruˇznici, protoˇze nejdˇr´ıve je potˇreba zapsat zv´ yˇsen´ı poˇctu referenc´ı, pak adres´ aˇrov´e bloky a pot´e sn´ıˇzen´ı referenc´ı. A protoˇze zv´ yˇsen´ı/sn´ıˇzen´ı se t´ yk´ a stejn´eho inode, tak je pˇri z´ apisu tˇreba udˇelat roll-back na (ˇreknˇeme) 2, zapsat inode na disk, zapsat bloky adres´ ar˚ u a pak roll-forward na poˇcet referenc´ı 1. Pˇri t´eto akci je nad inodem drˇzen z´ amek, takˇze nikdo nenaˇcte starˇs´ı data. Je jednoduch´e uk´azat, ˇze nelze zapsat ˇza´dn´ y z tˇech tˇr´ı blok˚ u v kruˇznici tak, jak to je na konci operace rename, ˇze je opravdu nutn´ y ten roll-back – mohli bychom uvaˇzovat, ˇze inode se vlastnˇe nezmˇenil a nen´ı tˇreba ˇreˇsit zda se m˚ uˇze/nem˚ uˇze zapsat; z´ apis nov´eho adres´ aˇrov´eho odkazu bez zv´ yˇsen´ı poˇctu odkaz˚ u v inodu by n´ as totiˇz mohl dostat pˇresnˇe do situace, kter´ a je pops´ ana o p´ ar ˇra´dk˚ u v´ yˇse.
Pˇ r´ıstupov´ a pr´ ava nejvyˇsˇs´ı bit
vlastn´ık (u) skupina (g) ostatn´ı (o) z }| { z }| { z }| {
4 2
suid sgid sticky
1
r w x
• SGID pro soubor bez pr´ava spuˇstˇen´ı pro skupinu v System V: kontrola z´ amk˚ u pˇri kaˇzd´em pˇr´ıstupu (mandatory locking) • sticky bit pro adres´ aˇre: pr´avo mazat a pˇrejmenov´avat soubory maj´ı jen vlastn´ıci soubor˚ u • SGID pro adres´ aˇr: nov´e soubory budou m´ıt stejnou skupinu jako adres´ aˇr (System V; u BSD syst´em˚ u to funguje jinak, viz pozn´ amky)
78
• SGID pro adres´ aˇre u BSD syst´em˚ u zp˚ usob´ı, ˇze soubory a podadres´ aˇre vytvoˇren´e v tomto adres´ aˇri budou m´ıt stejn´eho majitele jako je majitel dan´eho adres´ aˇre. Nutn´ ym pˇredpokladem je d´ ale to, ˇze dan´ y UFS filesyst´em mus´ı b´ yt namontov´an s suiddir pˇr´ıznakem a v j´adru je option SUIDDIR (a to nen´ı default). Nav´ıc to nefunguje pro roota. Tato moˇznost existuje kv˚ uli Sambˇe a Nettalku. • sticky bit pro adres´ aˇre: pˇrejmenovat nebo smazat soubor m˚ uˇze jen jeho vlastn´ık (v nˇekter´ ych implementac´ıch staˇc´ı i pr´avo z´ apisu do souboru), nestaˇc´ı pr´avo z´ apisu do adres´ aˇre. Toto nastaven´ı se pouˇz´ıv´ a pro veˇrejn´e adres´ aˇre (napˇr. /tmp). • p˚ uvodnˇe mˇel sticky bit v´ yznam i pro spustiteln´e soubory: program s nastaven´ ym sticky bitem z˚ ustal po ukonˇcen´ı v pamˇeti a jeho opˇetovn´e spuˇstˇen´ı bylo rychlejˇs´ı. Dnes se sticky bit v tomto v´ yznamu uˇz nepouˇz´ıv´ a. • nˇekter´e filesyst´emy (XFS, AFS, UFS2, ZFS) maj´ı tzv. access control lists (ACL´s), kter´e dovoluj´ı jemnˇejˇs´ı pˇridˇelov´an´ı pr´av jednotliv´ ym uˇzivatel˚ um a skupin´ am.
API pro soubory • pˇred pouˇzit´ım mus´ı proces kaˇzd´ y soubor nejprve otevˇr´ıt vol´an´ım open() nebo creat(). • otevˇren´e soubory jsou dostupn´e pˇres deskriptory soubor˚ u (file descriptors), ˇc´ıslovan´e od 0, v´ıce deskriptor˚ u m˚ uˇze sd´ılet jedno otevˇ ren´ı souboru (m´ od ˇcten´ı/z´ apis, ukazov´atko pozice) • standardn´ı deskriptory: – 0 . . . standardn´ı vstup (jen pro ˇcten´ı) – 1 . . . standardn´ı v´ ystup (jen pro z´ apis) – 2 . . . chybov´ y v´ ystup (jen pro z´ apis) • ˇcten´ı a z´ apis z/do souboru: read(), write() • zmˇena pozice: lseek(), zavˇren´ı: close(), informace: stat(), ˇr´ıdic´ı funkce: fcntl(), pr´ava: chmod(), . . .
• kaˇzd´ a funkce, kter´ a alokuje deskriptory (nejen open() a creat(), ale napˇr. i pipe(), dup(), socket()) alokuje vˇzdy voln´e deskriptory s nejniˇzˇs´ım ˇc´ıslem. • proces dˇed´ı otevˇren´e soubory od rodiˇce, tyto soubory nemus´ı znovu otv´ırat. Obvykle (ale ne vˇzdy) proces dostane otevˇren´e alespoˇ n deskriptory 0, 1 a 2. • funkce ze souboru stdio.h (napˇr. fopen(), fprintf(), fscanf()) a odkazy na soubory pomoc´ı ukazatele na FILE jsou definov´any ve standardn´ı knihovnˇe a pro svoj´ı ˇcinnost pouˇz´ıvaj´ı vol´an´ı j´adra (napˇr. open(), write(), read()). My se nebudeme knihovnou stdio zab´ yvat. 79
Otevˇ ren´ı souboru: open() int open(const char *path, int oflag, ... ); • otevˇre soubor dan´ y jm´enem (cestou) path, vr´ at´ı ˇc´ıslo jeho deskriptoru (pouˇzije prvn´ı voln´e), oflag je OR-kombinace pˇr´ıznak˚ u – O RDONLY/O WRONLY/O RDWR . . . otevˇr´ıt pouze pro ˇcten´ı / pouze pro z´ apis / pro ˇcten´ı i z´ apis – O APPEND . . . pˇripojov´an´ı na konec – O CREAT . . . vytvoˇrit, kdyˇz neexistuje – O EXCL . . . chyba, kdyˇz existuje (pouˇzit´ı s O CREATE) avo z´ apisu nutn´e) – O TRUNC . . . zruˇsit pˇredchoz´ı obsah (pr´ – ... • pˇri O CREAT definuje tˇret´ı parametr mode pˇr´ıstupov´a pr´ava
• pˇri pouˇzit´ı O CREAT se mode jeˇstˇe modifikuje podle umask. • mode nem´ a defaultn´ı hodnotu, tj. vezme se to, co je na z´ asobn´ıku i kdyˇz tento parametr nen´ı pˇr´ıtomen! Pˇr´ıznaky i m´od jsou uloˇzeny v syst´emov´e tabulce otevˇren´ ych soubor˚ u. • pokud se znovu pouˇzije dˇr´ıve vyuˇz´ıvan´ a poloˇzka v tabulce deskriptor˚ u nebo v tabulce otevˇren´ ych soubor˚ u, vˇse potˇrebn´e se vynuluje (pozice v souboru, flagy deskriptoru, . . . ) • existuj´ı jeˇstˇe dalˇs´ı nastaviteln´e pˇr´ıznaky: – O SYNC (O DSYNC, O RSYNC – nen´ı na BSD) . . . operace se souborem skonˇc´ı aˇz po fyzick´em uloˇzen´ı dat (synchronized I/O) – O NOCTTY . . . pˇri otv´ır´ an´ı termin´alu procesem, kter´ y nem´ a ˇr´ıdic´ı termin´ al, se tento termin´ al nestane ˇr´ıd´ıc´ım termin´alem procesu – O NONBLOCK . . . pokud nelze ˇcten´ı nebo z´ apis prov´est okamˇzitˇe, vol´an´ı read()/write() skonˇc´ı s chybou m´ısto zablokov´an´ı procesu • v pˇr´ıstupov´ ych pr´avech se vynuluj´ı ty bity, kter´e jsou nastaven´e pomoc´ı umask(). • pro ˇcten´ı a z´ apis nelze pouˇz´ıt O RDONLY | O WRONLY, protoˇze implementace pouˇzily 0 pro read-only flag. Norma proto definuje, ˇze aplikace mus´ı pouˇz´ıt pr´avˇe jeden z tˇechto tˇr´ı flag˚ u. • je moˇzn´e otevˇr´ıt a z´ aroveˇ n vytvoˇrit soubor pro z´ apis tak, ˇze jeho m´od z´ apis nedovoluje. Pˇri pˇr´ıˇst´ım otevˇren´ı souboru se ale jiˇz tento m´od uplatn´ı pˇri kontrole pˇr´ıstupu a pro z´ apis ho otevˇr´ıt nep˚ ujde. Pro O TRUNC tak´e plat´ı, ˇze mus´ıte m´ıt pro dan´ y soubor pr´avo z´ apisu. 80
Vytvoˇ ren´ı souboru int creat(const char *path, mode t mode ); • open() s pˇr´ıznakem O CREAT vytvoˇr´ı soubor, pokud jeˇstˇe neexistuje. V zadan´e hodnotˇe pˇr´ıstupov´ ych pr´av se vynuluj´ı bity, kter´e byly nastaveny pomoc´ı funkce mode t umask(mode t cmask ); • funkce je ekvivalentn´ı vol´ an´ı open(path, O WRONLY|O CREAT|O TRUNC, mode); int mknod(const char *path, mode t mode, dev t dev ); • vytvoˇr´ı speci´ aln´ı soubor zaˇr´ızen´ı. int mkfifo(const char *path, mode t mode ); • vytvoˇr´ı pojmenovanou rouru.
• vol´ an´ı vrac´ı nejniˇzˇs´ı deskriptor, kter´ y v dan´e chv´ıli nebyl otevˇren´ y pro proces • open() dok´ aˇze otevˇr´ıt regul´ arn´ı soubor, zaˇr´ızen´ı i pojmenovanou rouru, ale vytvoˇrit dok´ aˇze jen regul´ arn´ı soubor; pro ostatn´ı typy soubor˚ u je nutn´e pouˇz´ıt speci´ aln´ı vol´ an´ı. • test, zda soubor existuje, a jeho pˇr´ıpadn´e vytvoˇren´ı je atomick´a operace. Toho se vyuˇz´ıv´ a pˇri pouˇz´ıv´ an´ı lock soubor˚ u. • speci´ aln´ı soubory m˚ uˇze vytv´aˇret pouze root, protoˇze se pomoc´ı nich definuj´ı pˇr´ıstupov´a pr´ava k perifern´ım zaˇr´ızen´ım. • hodnoty pro mode je moˇzn´e nal´ezt vˇetˇsinou v manu´ alov´e str´ ankce pro chmod
81
ˇ Cten´ ı a z´ apis soubor˚ u: read(), write() ssize t read(int fildes, void *buf, size t nbyte ); • z otevˇren´eho souboru s ˇc´ıslem deskriptoru fildes pˇreˇcte od aktu´aln´ı pozice max. nbyte bajt˚ u dat a uloˇz´ı je od adresy buf. • vrac´ı poˇcet skuteˇcnˇe pˇreˇcten´ ych bajt˚ u (<= nbyte), 0 znamen´ a konec souboru. ssize t write(int fildes, const void *buf, size t nbyte ); – do otevˇren´eho souboru s ˇc´ıslem deskriptoru fildes zap´ıˇse na aktu´aln´ı pozici max. nbyte bajt˚ u dat uloˇzen´ ych od adresy buf. – vrac´ı velikost skuteˇcnˇe zapsan´ ych dat (<= nbyte).
• pro UNIX je kaˇzd´ y soubor posloupnost bajt˚ u bez dalˇs´ı vnitˇrn´ı struktury. • chov´an´ı read() a write() z´ avis´ı na typu souboru (regul´ arn´ı, zaˇr´ızen´ı, roura, soket) a na tom, zda je soubor v blokuj´ıc´ım nebo neblokuj´ıc´ım m´odu (flag O NONBLOCK pˇri otevˇren´ı souboru). • vol´ an´ı read() vr´ at´ı nenulov´ y poˇcet bajt˚ u menˇs´ı neˇz nbyte, pokud v souboru zb´ yv´ a m´enˇe neˇz nbyte bajt˚ u, nebo vol´an´ı bylo pˇreruˇseno sign´alem, nebo soubor je roura, zaˇr´ızen´ı ˇci soket a aktu´alnˇe je k dispozici m´enˇe neˇz nbyte bajt˚ u. Pˇri neexistenci dat se blokuj´ıc´ı read() zablokuje, dokud se nˇejak´ a data neobjev´ı, neblokuj´ıc´ı read() vr´ at´ı -1 a nastav´ı errno na EAGAIN. • vol´ an´ı write() vr´ at´ı nenulov´ y poˇcet bajt˚ u menˇs´ı neˇz nbyte, jestliˇze se do souboru nevejde v´ıc dat (napˇr. zaplnˇen´ y disk), z´ apis je pˇreruˇsen sign´alem nebo je nastaveno O_NONBLOCK a do roury, soketu nebo zaˇr´ızen´ı se vejde pouze ˇca´st zapisovan´ ych dat. Bez O_NONBLOCK se ˇcek´ a, dokud se nepodaˇr´ı zapsat vˇse. Pokud nelze aktu´alnˇe zapsat nic, blokuj´ıc´ı write() se zablokuje, dokud nen´ı moˇzn´e zapisovat, neblokuj´ıc´ı write() vr´ at´ı -1 a nastav´ı errno na EAGAIN. • d˚ uleˇ zit´ e v´ yjimky vzhledem k rour´ am jsou uvedeny na stranˇe 85. • kdyˇz read() nebo write() vr´ at´ı m´enˇe neˇz nbyte z d˚ uvodu chyby, opakovan´e vol´ an´ı t´eˇze funkce vr´ at´ı -1 a nastav´ı k´od chyby v errno. • pˇreruˇsen´ı read(), write() sign´alem dˇr´ıv, neˇz se podaˇr´ı pˇreˇc´ıst, resp. zapsat aspoˇ n jeden bajt, zp˚ usob´ı n´ avrat s hodnotou -1 a nastaven´ı errno na EINTR. • pˇr´ıznak otevˇren´ı souboru O_APPEND zajist´ı atomick´ y z´ apis na konec souboru (pouze na lok´aln´ım disku), tj. kaˇ zd´ y z´ apis se provede na konec souboru.
82
Uzavˇ ren´ı souboru: close() int close(int fildes ); • uvoln´ı deskriptor fildes, pokud to byl posledn´ı deskriptor, kter´ y odkazoval na otevˇren´ı souboru, zavˇre soubor a uvoln´ı z´ aznam o otevˇren´ı souboru. • kdyˇz je poˇcet odkaz˚ u na soubor 0, j´adro uvoln´ı data souboru. Tedy i po zruˇsen´ı vˇsech odkaz˚ u (jmen) mohou se souborem pracovat procesy, kter´e ho maj´ı otevˇren´ y. Soubor se smaˇze, aˇz kdyˇz ho zavˇre posledn´ı proces. • kdyˇz se zavˇre posledn´ı deskriptor roury, vˇsechna zb´ yvaj´ıc´ı data v rouˇre se zruˇs´ı. • pˇri skonˇcen´ı procesu se automaticky provede close() na vˇsechny deskriptory.
• pokud proces potˇrebuje doˇcasn´ y soubor, m˚ uˇze ho vytvoˇrit, ihned smazat a pak s n´ım pracovat pˇres existuj´ıc´ı deskriptor (tento deskriptor lze pˇredat synovsk´ ym proces˚ um). Kdyˇz je takov´ y soubor zavˇren vˇsemi procesy, j´adro smaˇze jeho data z disku. • i operace close() m˚ uˇze selhat. Napˇr. nˇekter´e filesyst´emy zapisuj´ı data na disk aˇz v okamˇziku zavˇren´ı souboru, kdyˇz z´ apis skonˇc´ı chybou, close() vr´ at´ı -1. • otev´ır´ an´ı soubor˚ u a nezav´ır´ an´ı je pˇri ukonˇcen´ı pr´ace s nimi vede k alokovan´e pamˇeti, kterou jiˇz nem´ ame jak uvolnit (ztratili jsme na ni odkaz) – situace kde takto pamˇet’ “ztr´ac´ıme” se naz´ yv´ a memory leak
83
Pˇ r´ıklad: kop´ırov´ an´ı soubor˚ u #include #include int main(int argc, char *argv[]) { char buf[4096]; int inf, outf, ilen; inf = open(argv[1], O RDONLY); outf = creat(argv[2], 0666); while ((ilen = read(inf, buf, 4096)) > 0) write(outf, buf, ilen); close(inf); close(outf); exit(0); }
• je neefektivn´ı ˇc´ıst a zapisovat soubor po jednotliv´ ych bajtech, lepˇs´ı je najednou zpracov´avat rozumnˇe velk´e bloky. • pokud potˇrebujete pracovat s mal´ ymi ˇca´stmi/buffery, je lepˇs´ı pouˇz´ıt stream orintovan´e knihovn´ı funkce fopen(), fread(), . . . kter´e data vnitˇrnˇe bufferuj´ı
84
Pr´ ace s pojmenovanou rourou • nemus´ı b´ yt moˇzn´e vytvoˇrit FIFO na s´ıt’ov´em filesyst´emu (NFS, AFS) • je nutn´e zn´ at s´emantiku otev´ır´ an´ı pojmenovan´e roury: – otevˇren´ı roury pouze pro ˇcten´ı se zablokuje do t´e doby, dokud se neobjev´ı zapisovatel (pokud jiˇz neexistuje) – otevˇren´ı roury pouze pro z´ apis se zablokuje do t´e doby, dokud se neobjev´ı ˇcten´ aˇr (pokud jiˇz neexistuje) – toto chov´an´ı je moˇzn´e ovlivnit flagem O NONBLOCK • s´emantika ˇcten´ı a z´ apisu je jeˇstˇe o nˇeco sloˇzitˇejˇs´ı, vˇenujte velkou pozornost pozn´ amk´ am pod t´ımto slajdem – a je to stejn´e jako u nepojmenovan´e roury (strana 118)
• je moˇzn´e otevˇr´ıt rouru pro z´ apis i ˇcten´ı stejn´ ym procesem najednou, aniˇz by pˇredt´ım existoval ˇcten´ aˇr nebo zapisovatel • pokud rouru nem´ a ˇza´dn´ y proces otevˇrenou pro z´ apis, pro okamˇzit´e vr´ acen´ı deskriptoru pouze pro ˇcten´ı je nutn´e otevˇ r´ıt rouru s flagem O NONBLOCK, proces se jinak na rouˇre zablokuje ˇcek´ an´ım na zapisovatele. Pozor ale na to, ˇze pokus o ˇ cten´ı z roury bez zapisovatele okamˇzitˇe vr´ at´ı 0 jako indikaci konce souboru; proces se nezablokuje ˇ cek´ an´ım na zapisovatele. Pˇri z´ apisu do roury bez ˇcten´ aˇre (tj. zapisovatel otevˇrel rouru jeˇstˇe v dobˇe, kdy ˇcten´ aˇr existoval) poˇsle kernel procesu sign´al EPIPE (“broken pipe”). aˇre • v pˇr´ıpadˇe otev´ır´ an´ı pouze pro z´ apis s O NONBLOCK bez existuj´ıc´ıho ˇcten´ se vr´ at´ı chyba a errno se nastav´ı na ENXIO. Tato asymetrie je snahou, aby v rouˇre nebyla data, kter´ a nebudou v kr´ atk´e dobˇe pˇreˇctena – syst´em nem´ a zp˚ usob, jak uschov´avat data v rouˇre bez ˇcasov´eho omezen´ı. Bez O NONBLOCK flagu se proces zablokuje. • z uveden´eho tedy vypl´ yv´ a, ˇze pokud chcete vytvoˇrit proces, kter´ y ˇcek´ a na pojmenovan´e rouˇre a vykon´ av´ a poˇzadavky, mus´ıte ji otevˇr´ıt s flagem O RDWR i kdyˇz do roury nehodl´ate zapisovat; jinak po prvn´ım zablokov´an´ı v open() pˇri ˇcek´ an´ı na otevˇren´ı roury zapisovatelem by dalˇs´ı vol´an´ı read() pro akceptov´an´ı dalˇs´ıho poˇzadavku typicky vr´ atilo 0, pokud by n´ ahodou roura nebyla mezit´ım pro z´ apis otevˇrena dalˇs´ım procesem. • z´ apis maxim´ aln´ı velikosti PIPE BUF (limits.h) je zaruˇcen´ y jako atomick´ y. Napˇr. v OpenSolarisu to je to 5120 bajt˚ u, ve FreeBSD 5.4 je to 512 bajt˚ u. Pokud zapisujete v´ıce, m˚ uˇze write() prov´est ˇca´steˇcn´ y z´ apis (a vr´ at´ı tak menˇs´ı ˇc´ıslo neˇz velikost dat kter´ a se mˇela zapsat). • roura nem´ a pozici v souboru, z´ apis tak vˇzdy pˇrid´ av´ a na konec. 85
• stejnˇe se vzhledem ke ˇcten´ı a z´ apisu chov´a i nepojmenovan´ a roura, viz strana 118.
Nastaven´ı pozice: lseek() off t lseek(int fildes, off t offset, int whence ); • nastav´ı pozici pro ˇcten´ı a z´ apis v otevˇren´em souboru dan´em ˇc´ıslem deskriptoru fildes na hodnotu offset. • podle hodnoty whence se offset poˇc´ıt´a: – SEEK SET . . . od zaˇca´tku souboru – SEEK CUR . . . od aktu´aln´ı pozice – SEEK END . . . od konce souboru • vrac´ı v´ yslednou pozici poˇc´ıtanou od zaˇca´tku souboru. at´ı aktu´aln´ı pozici. • lseek(fildes, 0, SEEK CUR) pouze vr´
• lze se pˇresunout i na pozici za koncem souboru. Pokud se pak provede z´ apis, soubor se prodlouˇz´ı a v pˇreskoˇcen´e ˇca´sti budou sam´e nuly (samotn´e lseek() nestaˇc´ı). Nˇekter´e filesyst´emy takov´e bloky cel´ ych nul pro u ´ sporu m´ısta neukl´ adaj´ı. • velikost souboru je moˇzn´e zjistit pomoc´ı lseek(fildes, 0, SEEK END). • nejˇcastˇejˇs´ı operace s lseek jsou tˇ ri: nastaven´ı konkr´etn´ı pozice od zaˇca´tku souboru, nastaven´ı pozice na konec souboru a zjiˇstˇen´ı aktu´aln´ı pozice v souboru (0 spoleˇcnˇe se SEEK CUR) • pˇri pouˇzit´ı lseek se ˇza´dn´e I/O neprovede, ˇza´dn´ y pˇr´ıkaz se nepoˇsle na ˇradiˇc disku • lseek nemus´ı slouˇzit jen pro operace read a write, ale tak´e pro n´ aslednou operaci lseek • ukl´ ad´ an´ı nul m˚ uˇ ze v´ est k probl´ em˚ um se z´ alohami
86
Zmˇ ena velikosti: truncate() int truncate(const char *path, off t length ); int ftruncate(int fildes, off t length ); • zmˇen´ı d´elku souboru zadan´eho cestou nebo ˇc´ıslem deskriptoru na poˇzadovanou hodnotu. • pˇri zkr´acen´ı souboru zruˇs´ı nadbyteˇcn´ a data. • standard ponech´ av´ a nespecifikovan´e, jestli funguje prodlouˇzen´ı souboru (s vyplnˇen´ım pˇridan´eho u ´ seku nulami). Proto je lepˇs´ı k prodlouˇzen´ı souboru pouˇz´ıt char buf = ’\0’; lseek(fildes, length-1, SEEK_SET); write(fildes, buf, 1);
• zruˇsit veˇsker´ y obsah souboru pˇri otevˇren´ı se d´ a pˇr´ıznakem O TRUNC ve funkci open(). • podrobnosti je tˇreba hledat v manu´ alov´e str´ ance, napˇr. ve FreeBSD v sekci BUGS nalezneme toto: Use of truncate() to extend a file is not portable.
87
Duplikace deskriptoru: dup(), dup2() int dup(int fildes ); • duplikuje deskriptor fildes na prvn´ı voln´ y deskriptor, vr´ at´ı nov´ y deskriptor, kter´ y odkazuje na stejn´e otevˇren´ı souboru. • ekvivalent fcntl(fildes, F DUPFD, 0); int dup2(int fildes, int fildes2 ); • duplikuje deskriptor fildes na deskriptor fildes2. • ekvivalent close(fildes2); fcntl(fildes, F DUPFD, fildes2);
• prvn´ı voln´ y deskriptor se pouˇzije i u otev´ır´ an´ı a vytv´aˇren´ı soubor˚ u, viz strany 80 a 81. • duplikovan´ y a p˚ uvodn´ı deskriptor sd´ıl´ı stejn´e otevˇren´ı souboru a tedy i aktu´ aln´ı pozici a m´od ˇcten´ı/z´ apis. • ekvivalent pro dup2 nen´ı zcela ekvivalentn´ı, napˇr. pokud je fildes rovn´ y fildes2. V´ıce viz norma.
88
Pˇ r´ıklad: implementace shellov´ eho pˇ resmˇ erov´ an´ı • $ program < in > out 2>> err close(0); open("in", O_RDONLY); close(1); open("out", O_WRONLY | O_CREAT | O_TRUNC, 0666); close(2); open("err", O_WRONLY | O_CREAT | O_APPEND, 0666); • $ program > out 2>&1 close(1); open("out", O_WRONLY | O_CREAT | O_TRUNC, 0666); close(2); dup(1);
• dalˇs´ı pˇr´ıklad pouˇzit´ı dup() uvid´ıme, aˇz se budeme zab´ yvat rourami. • je potˇreba si d´ at pozor na stav deskriptor˚ u. Druh´ y pˇr´ıklad nebude fungovat, kdyˇz bude deskriptor 0 uzavˇren, protoˇze open() vr´ at´ı deskriptor 0 (prvn´ı voln´ y) a dup() vr´ at´ı chybu (pokus o duplikaci uzavˇren´eho deskriptoru). Moˇzn´e ˇreˇsen´ı: close(1); if((fd = open("out", O WRONLY | O CREAT | O TRUNC, 0666)) == 0) dup(0); close(2); dup(1); if(fd == 0) close(0); nebo fd = open("out", O WRONLY | O CREAT | O TRUNC, 0666); if(fd != 1) { dup2(fd, 1); close(fd); } dup2(1, 2);
89
ˇ ıdic´ı funkce soubor˚ R´ u a zaˇ r´ızen´ı: fcntl(), ioctl() int fcntl(int fildes, int cmd, ...); • slouˇz´ı pro duplikaci deskriptor˚ u, nastavov´an´ı z´ amk˚ u, testov´an´ı a nastavov´an´ı r˚ uzn´ ych pˇr´ıznak˚ u souboru. pˇr´ıklad: zavˇren´ı standardn´ıho vstupu pˇri spuˇstˇen´ı programu (vol´ an´ı typu exec) fcntl(0, F SETFD, FD CLOEXEC); int ioctl(int fildes, int request, ... ); • rozhran´ı pro ˇr´ıdic´ı funkce perifern´ıch zaˇr´ızen´ı • pouˇz´ıv´ a se jako univerz´ aln´ı rozhran´ı pro ovl´ ad´ an´ı zaˇr´ızen´ı, kaˇzd´e zaˇr´ızen´ı definuje mnoˇzinu pˇr´ıkaz˚ u, kter´ ym rozum´ı.
• moˇzn´e hodnoty cmd ve funkci fcntl(): – F DUPFD . . . duplikace deskriptoru – F GETFD . . . zjiˇstˇen´ı pˇr´ıznak˚ u deskriptoru (FD CLOEXEC – uzavˇren´ı pˇri exec). FD CLOEXEC je jedin´ y flag pro deskriptory, definovan´ y v normˇe UNIX 03. u deskriptoru – F SETFD . . . nastaven´ı pˇr´ıznak˚ – F GETFL . . . zjiˇstˇen´ı m´odu ˇcten´ı/z´ apis a pˇr´ıznak˚ u otevˇren´ı souboru (jako u open()) u otevˇren´ı souboru (O APPEND, O DSYNC, – F SETFL . . . nastaven´ı pˇr´ıznak˚ O NONBLOCK, O RSYNC, O SYNC). Nemohu nastavit pˇr´ıznaky pro ro/rw a ani pˇr´ıznaky pro vytvoˇren´ı, zkr´acen´ı nebo exkluzivn´ı pˇr´ıstup k souboru. – F GETLK, F SETLK, F SETLKW . . . nastavov´an´ı z´ amk˚ u • je d˚ uleˇzit´e si uvˇedomit, ˇze jsou dva druhy pˇr´ıznak˚ u – pˇr´ıznak(y) pro souborov´ y deskriptor a pˇr´ıznaky pro otevˇren´ y soubor – tj. pˇr´ıznaky jsou uloˇzen´e ve dvou r˚ uzn´ ych tabulk´ ach. • perifern´ı zaˇr´ızen´ı podporuj´ı ˇcten´ı a z´ apis dat pomoc´ı read(), write() a mapov´an´ı dat do pamˇeti (mmap()), veˇsker´e dalˇs´ı operace se zaˇr´ızen´ım (napˇr. nastaven´ı parametr˚ u, zamˇcen´ı nebo eject) se dˇelaj´ı funkc´ı ioctl(). • pˇri nastavov´an´ı pˇr´ıznak˚ u nejdˇr´ıv vˇzdy zjistˇete, jak´e byly pˇredt´ım. I kdyˇz jste si jisti, ˇze v dan´e chv´ıli jsou nulov´e a je tedy moˇzn´e prov´est fcntl(fd, uˇzete vˇedˇet, co se m˚ uˇze zmˇenit (napˇr´ıklad o p´ar ˇra´dk˚ u v´ yˇse O APPEND), nem˚ nˇejak´ y flag pˇrid´ ate, aniˇz byste vˇedˇeli, ˇze jste ovlivnˇeni k´odem dole). Tedy vˇzdy pouˇzijte napˇr´ıklad toto:
90
flags = fcntl(fd, F_GETFL); if (fcntl(fd, F_SETFL, flags | O_APPEND) == -1) ... . . . a podobnˇe pro odebr´ an´ı flagu – je ˇspatn´e ˇreˇsen´ı nastavit hodnot˚ u flag˚ u na nulu, m´ısto toho je tˇreba pouˇz´ıt bitov´eho jedniˇckov´eho doplˇ nku pˇr´ısluˇsn´eho flagu
Informace o souboru: stat() int stat(const char *path, struct stat *buf ); int fstat(int fildes, struct stat *buf ); • pro soubor zadan´ y cestou, resp. ˇc´ıslem deskriptoru, vr´ at´ı strukturu obsahuj´ıc´ı informace o souboru, napˇr.: – st ino . . . ˇc´ıslo i-uzlu – st dev . . . ˇc´ıslo zaˇr´ızen´ı obsahuj´ıc´ıho soubor – st uid, st gid . . . vlastn´ık a skupina souboru – st mode . . . typ a pˇr´ıstupov´a pr´ava – st size, st blksize, st blocks . . . velikost souboru v bajtech, velikost bloku a poˇcet blok˚ u – st atime, st mtime, st ctime . . . ˇcasy posledn´ıho pˇr´ıstupu, modifikace souboru a modifikace i-uzlu – st nlink . . . poˇcet odkaz˚ u na soubor
• metadata jsou informace o souboru – tedy m´od, ˇcasy pˇr´ıstupu, d´elka, vlastn´ık a skupina atd. Nepatˇr´ı mezi nˇe skuteˇcn´ a data souboru, a ani jm´eno, kter´e nen´ı uloˇzeno v r´ amci dan´eho souboru, ale v adres´ aˇri ˇci v adres´ aˇr´ıch. • metadata je moˇzn´e pˇreˇc´ıst, i kdyˇz proces nem´ a pr´ava pro ˇcten´ı obsahu souboru. • touto funkc´ı nez´ısk´ am flagy deskriptoru ani flagy z pole tabulky otevˇren´ ych soubor˚ u v syst´emu, zde jde o informace ohlednˇe souboru uloˇzen´eho na pamˇet’ov´em m´ediu. • c time nen´ı ˇcas vytvoˇren´ı soubor (creation time), ale ˇcas zmˇeny (change time) • norma nespecifikuje poˇrad´ı poloˇzek ve struktuˇre ani nezakazuje pˇridat dalˇs´ı
91
Informace o souboru (2) • pro typ souboru jsou v <sys/stat.h> definov´any konstanty S_IFMT (maska pro typ), S_IFBLK (blokov´ y speci´ aln´ı), S_IFCHR (znakov´ y speci´ aln´ı), S_IFIFO (FIFO), S_IFREG (obyˇcejn´ y), S_IFDIR (adres´ aˇr), S_IFLNK (symlink). • typ lze testovat pomoc´ı maker S_ISBLK(m), S_ISCHR(m), S_ISFIFO(m), S_ISREG(m), S_ISDIR(m), S_ISLNK(m). • konstanty pro pˇr´ıstupov´a pr´ava: S_IRUSR (ˇcten´ı pro vlastn´ıka), S_IWGRP (z´apis pro skupinu), atd. int lstat(const char *path, struct stat *buf ); • kdyˇz je zkouman´ y soubor symlink, stat() vr´ at´ı informace o souboru, na kter´ y ukazuje. Tato funkce vrac´ı informace o symlinku.
• typ a pr´ava souboru jsou uloˇzena spoleˇcnˇe v st_mode, proto existuj´ı zmiˇ novan´ a makra. u, kter´e jsou vˇenovan´e typu souboru, makra pro • S IFMT specifikuje tu ˇca´st bit˚ jednotliv´e typy pak nejsou masky, ale hodnoty, takˇze test na typ souboru je nutn´e udˇelat takto: (st mode & S IFMT == S IFREG). Vˇsechna makra jsou v normˇe, takˇze jejich pouˇz´ıv´ an´ım zaruˇc´ıme pˇrenositeln´ y k´od.
92
Nastaven´ı ˇ cas˚ u souboru int utime(const char *path, const struct utimbuf *times ); • nastav´ı ˇcas posledn´ı modifikace souboru a ˇcas posledn´ıho pˇr´ıstupu k souboru. • nelze zmˇenit ˇcas posledn´ı modifikace i-uzlu. • volaj´ıc´ı proces mus´ı m´ıt pr´avo z´ apisu pro soubor.
• tuto funkci pouˇz´ıvaj´ı hlavnˇe kop´ırovac´ı a archivaˇcn´ı programy, aby zajistily stejn´e ˇcasy kopie a origin´alu. • shellov´e rozhran´ı pro funkci utime() pˇredstavuje pˇr´ıkaz touch.
93
Test pˇ r´ıstupov´ ych pr´ av: access() int access(const char *path, int amode ); • otestuje, zda volaj´ıc´ı proces m´a k souboru path pr´ava dan´a OR-kombinac´ı konstant v amode: – R_OK . . . test pr´ava na ˇcten´ı – W_OK . . . test pr´ava na z´ apis – X_OK . . . test pr´ava na spuˇstˇen´ı – F_OK . . . test existence souboru • na rozd´ıl od stat(), v´ ysledek z´ avis´ı na RUID a RGID procesu • toto vol´ an´ı se ned´a pouˇz´ıt bezpeˇcnˇe, proto ho nikdy nepouˇz´ıvejte procesu
• vol´ an´ı access() aplikuje mechanismus testov´an´ı pˇr´ıstupov´ ych pr´av k zadan´emu souboru pro volaj´ıc´ı proces a vr´ at´ı v´ ysledek. • funkce access() vol´ an´ı byla pro setuid proces, aby si mohl ovˇeˇrit, zda uˇzivatel bˇeˇz´ıc´ı dan´ y setuid proces by mˇel za norm´aln´ıch okolnost´ı k pˇr´ısluˇsn´emu souboru pˇr´ıstup. Z toho vypl´ yv´ a, ˇze toto vol´an´ı je security hole – mezi testem a n´ aslednou akc´ı se soubor m˚ uˇze zmˇenit, coˇz proces nem˚ uˇze nikdy oˇsetˇrit. ˇ sen´ım je vr´ Reˇ atit se zp´ atky k re´aln´ ym UID/GID a pˇr´ıstup vyzkouˇset.
94
Nastaven´ı pˇ r´ıstupov´ ych pr´ av int chmod(const char *path, mode t mode ); • zmˇen´ı pˇr´ıstupov´a pr´ava souboru path na hodnotu mode. • tuto sluˇzbu m˚ uˇze volat pouze vlastn´ık souboru nebo superuˇzivatel (root). int chown(const char *path, uid t owner, gid t group ); • zmˇen´ı vlastn´ıka a skupinu souboru path. Hodnota -1 znamen´ a zachovat vlastn´ıka, resp. skupinu. • mˇenit vlastn´ıka m˚ uˇze jen superuˇzivatel, aby uˇzivatel´e nemohli obch´ azet nastaven´e quoty t´ım, ˇze sv´e soubory pˇredaj´ı nˇekomu jin´emu. • bˇeˇzn´ y uˇzivatel m˚ uˇze mˇenit skupinu sv´ ych soubor˚ u a mus´ı pˇritom patˇrit do c´ılov´e skupiny.
• parametr mode zde samozˇrejmˇe neobsahuje typ souboru, jako tomu je napˇr´ıklad u vol´ an´ı stat(). Hodnoty mode viz chmod(2). • pokud nejsem vlastn´ık, nemohu celkem logicky zmˇenit m´od ani u souboru s nastaven´ ym pˇr´ıstupem rw-rw-rw• v nˇekter´ ych implementac´ıch m˚ uˇze vlastn´ık souboru pˇredat vlastnictv´ı nˇekomu jin´emu, napˇr. v IRIXu je chov´an´ı chown() nastaviteln´e jako parametr j´adra. • nen´ı bˇeˇzn´e volat funkci chmod() z uˇzivatelsk´ ych aplikac´ı, na to se pouˇz´ıvaj´ı flagy u vol´ an´ı open(), hlavn´ı pouˇzit´ı je v aplikaci chmod(1)
95
Manipulace se jm´ eny soubor˚ u int link(const char *path1, const char *path2 ); • vytvoˇr´ı nov´ y odkaz (poloˇzku adres´ aˇre) path2 na soubor path1. Funguje pouze v r´ amci jednoho svazku. int unlink(const char *path ); • zruˇs´ı odkaz na soubor. Po zruˇsen´ı posledn´ıho odkazu na soubor a uzavˇren´ı souboru vˇsemi procesy je soubor smaz´ an. int rename(const char *old, const char *new ); • zmˇen´ı jm´eno souboru (pˇresnˇe odkazu na soubor) z old na new. Funguje pouze v r´ amci jednoho svazku.
• vol´ an´ı link() vytv´aˇr´ı hardlinky, tj. zobrazen´ı ze jm´ena souboru na ˇc´ıslo iˇ ısla i-uzl˚ uzlu. C´ u jsou jednoznaˇcn´ a pouze v r´ amci svazku, proto pro linky mezi filesyst´emy je nutn´e pouˇz´ıt symlinky. • parametr path2 nesm´ı existovat, tedy nelze takto pˇrejmenov´avat • unlink() nefunguje na adres´ aˇre • shellov´ y pˇr´ıkaz mv pouˇz´ıv´ a rename pro pˇresuny v r´ amci jednoho svazku. Pˇresun souboru mezi filesyst´emy vyˇzaduje nejprve soubor zkop´ırovat a pak smazat origin´al vol´ an´ım unlink. • rename() funguje nad symlinky, ne nad soubory, na kter´e symlink ukazuje
96
Symbolick´ e linky int symlink(const char *path1, const char *path2 ); • vytvoˇr´ı symbolick´ y link path2 → path1. • c´ıl symbolick´eho linku m˚ uˇze b´ yt i na jin´em svazku, popˇr´ıpadˇe nemus´ı v˚ ubec existovat. int readlink(const char *path, char *buf, size t bufsize ); • do buf d´ a max. bufsize znak˚ u z cesty, na kterou ukazuje symlink path. • vr´ at´ı poˇcet znak˚ u uloˇzen´ ych do buf. • obsah buf nen´ı zakonˇcen nulou (znakem ’\0’)!
• shellov´ y pˇr´ıkaz ln vol´ a symlink() nebo link(), podle toho, jestli je pouˇzit pˇrep´ınaˇc -l nebo ne. • smaz´ an´ı hardlinku nesmaˇze soubor, pokud na nˇej vede jeˇstˇe jin´ y hardlink. Naopak soubor (poloˇzku adres´ aˇre i data) je moˇzn´e smazat, i kdyˇz na nˇej ukazuj´ı nˇejak´e symlinky. • readlink() je pouˇziteln´ y v situaci, pokud chci smazat soubor, na kter´ y dan´ y symlink ukazuje • bufsize se typicky d´ av´ a o 1 menˇs´ı neˇz velikost bufferu, to pro ukonˇcen´ı znakem ’\0’
97
Manipulace s adres´ aˇ ri int mkdir(const char *path, mode t mode ); • vytvoˇr´ı nov´ y pr´azdn´ y adres´ aˇr, (bude obsahovat pouze poloˇzky ’.’ a ’..’). int rmdir(const char *path ); • smaˇze adres´ aˇr path. Adres´ aˇr mus´ı b´ yt pr´azdn´ y. DIR *opendir(const char *dirname ); struct dirent *readdir(DIR *dirp ); int closedir(DIR *dirp ); • slouˇz´ı k sekvenˇcn´ımu proch´ azen´ı adres´ aˇr˚ u. • struktura dirent obsahuje poloˇzky – d_ino . . . ˇc´ıslo i-uzlu – d_name . . . jm´eno souboru
• poloˇzky adres´ aˇre nejsou nijak uspoˇra´d´ any, readdir() je m˚ uˇze vracet v libovoln´em poˇrad´ı. • v nˇekter´ ych implementac´ıch (napˇr. FreeBSD) lze adres´ aˇr otevˇr´ıt pro ˇcten´ı (ne pro z´ apis) jako norm´ aln´ı soubor a ˇc´ıst ho pomoc´ı read(), ale je tˇreba zn´ at jeho vnitˇrn´ı organizaci. Proto je readdir() pro zpracov´an´ı obsahu adres´ aˇre lepˇs´ı neˇz read(), kter´ y vrac´ı raw data adres´ aˇre. Kromˇe toho, norma nevyˇzaduje, aby adres´ aˇr bylo moˇzn´e ˇc´ıst funkc´ı read(), Linux to napˇr´ıklad nedovol´ı. • readdir() je stavov´a funkce. Pro vr´ acen´ı se na zaˇca´tek je moˇzn´e pouˇz´ıt funkci rewinddir(). S vl´ akny pak pouˇz´ıvat readdir r(), protoˇze struktura dirent je statick´a. y adres´ aˇr je mount point, • d ino nen´ı moc uˇziteˇcn´e, protoˇze v pˇr´ıpadˇe, kdy dan´ tak ukazuje na adres´ aˇr, na kter´ y je dalˇs´ı filesyst´em namontov´an, ne na koˇren namontovan´eho filesyst´emu • rmdir nefunguje na nepr´ azn´ y adres´ aˇr, je moˇzn´e pouˇz´ıt napˇr´ıklad toto: system("rm -r xxx")
98
Pˇ r´ıklad: proch´ azen´ı adres´ aˇ re int main(int argc, char *argv[]) { int i; DIR *d; struct dirent *de; for(i = 1; i < argc; i++) { d = opendir(argv[i]); while(de = readdir(d)) printf("%s\n", de->d_name); closedir(d); } exit(0); }
• pˇr´ıkaz ls je zaloˇzen na takov´eto smyˇcce, nav´ıc prov´ad´ı napˇr. tˇr´ıdˇen´ı jmen soubor˚ u a zjiˇst’ov´an´ı dalˇs´ıch informac´ı pomoc´ı stat(). • konec adres´ aˇre se pozn´ a tak, ˇze readdir() vr´ at´ı NULL. To vˇsak vr´ at´ı i v pˇr´ıpadˇe, pokud nastala chyba. V takov´em pˇr´ıpadˇe je k´od chyby v promˇenn´e errno, v pˇr´ıpadˇe prvn´ım je errno nezmˇenˇena. Proto by errno mˇelo b´ yt vˇzdy nastaven´e na nulu pˇred vol´an´ım readdir(). V pˇr´ıkladu tomu tak nen´ı, protoˇze z d˚ uvodu m´alo m´ısta nekontrolujeme errno v˚ ubec.
99
Aktu´ aln´ı adres´ aˇ r procesu • kaˇzd´ y proces m´a sv˚ uj aktu´aln´ı (pracovn´ı) adres´ aˇr, v˚ uˇci kter´emu jsou ud´ av´ any relativn´ı cesty k soubor˚ um. Poˇca´teˇcn´ı nastaven´ı pracovn´ıho adres´ aˇre se dˇed´ı od otce pˇri vzniku procesu. int chdir(const char *path ); int fchdir(int fildes ); • nastav´ı nov´ y pracovn´ı adres´ aˇr procesu. char *getcwd(char *buf, size t size ); • uloˇz´ı absolutn´ı cestu k aktu´aln´ımu adres´ aˇri do pole buf, jeho d´elka (size) mus´ı b´ yt aspoˇ n o 1 vˇetˇs´ı neˇz d´elka cesty.
• funkci fchdir() se pˇred´ av´ a deskriptor z´ıskan´ y vol´an´ım open() na adres´ aˇr. • funkce getcwd() m˚ uˇze vr´ atit jinou cestu neˇz tu, po kter´e jsme se do aktu´ aln´ıho adres´ aˇre dostali, jesliˇze ˇca´st cesty v chdir() byl symlink nebo doˇslo k pˇresunu (pˇrejmenov´an´ı) nˇekter´eho adres´ aˇre na cestˇe od koˇrene. U souˇcasn´ ych ˚ u by se to ale uˇz st´ at nemˇelo.
100
Obsah • u ´ vod, v´ yvoj UNIXu a C, program´ atorsk´e n´ astroje • z´ akladn´ı pojmy a konvence UNIXu a jeho API • pˇr´ıstupov´a pr´ava, perifern´ı zaˇr´ızen´ı, syst´em soubor˚ u • manipulace s procesy, spouˇ stˇ en´ı program˚ u • sign´aly • synchronizace a komunikace proces˚ u • s´ıt’ov´a komunikace • vl´ akna, synchronizace vl´ aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu
101
Pamˇ et’ procesu v uˇ zivatelsk´ em reˇ zimu text data bss
nelze adresovat uˇziv. programem
{
z´asobn´ık oblast user
adresovateln´e
uˇziv. programem
• text . . . k´od programu • data . . . inicializovan´e promˇenn´e • sekce text a data jsou uloˇzeny ve spustiteln´em souboru • bss . . . neinicializovan´e promˇenn´e (bss poch´ az´ı z assembleru IBM 7090 a znamen´ a ,,block started by symbol”). Za bˇehu programu tvoˇr´ı sekce data a bss dohromady datov´ y segment procesu. Velikost datov´eho segmentu lze mˇenit pomoc´ı syst´emov´ ych vol´an´ı brk() a sbrk(). • (uˇzivatelsk´y) z´ asobn´ık . . . automatick´e promˇenn´e, parametry funkc´ı, n´ avratov´e adresy. Kaˇzd´ y proces m´a dva z´ asobn´ıky, jeden pro uˇzivatelsk´ y reˇzim a jeden pro reˇzim j´ adra. Uˇzivatelsk´ y z´ asobn´ık procesu automaticky roste podle potˇreby (neplat´ı, pokud se pouˇz´ıvaj´ı vl´ akna). • oblast user (u-area) . . . obsahuje informace o procesu pouˇz´ıvan´e j´adrem, kter´e nejsou potˇrebn´e, kdyˇz je proces odloˇzen na disku (poˇcet otevˇren´ ych soubor˚ u, nastaven´ı oˇsetˇren´ı sign´al˚ u, poˇcet segment˚ u sd´ılen´e pamˇeti, argumenty programu, promˇenn´e prostˇred´ı, aktu´aln´ı adres´ aˇr, atd.). Tato oblast je pˇr´ıstupn´ a pouze pro j´ adro, kter´e vˇzdy vid´ı pr´avˇe jednu u-oblast patˇr´ıc´ı pr´avˇe bˇeˇz´ıc´ımu procesu. Dalˇs´ı informace o procesu, kter´e j´adro m˚ uˇze potˇrebovat i pro jin´ y neˇz pr´avˇe bˇeˇz´ıc´ı proces, nebo i kdyˇz je proces odloˇzen, jsou ve struktuˇre proc. Struktury proc pro vˇsechny procesy jsou st´ale rezidentn´ı v pamˇeti a viditeln´e v reˇzimu j´ adra.
102
Pamˇ et’ procesu v reˇ zimu j´ adra text j´adra data a bss j´adra (tabulky, promˇenn´e, apod.) struktura user beˇz´ıc´ıho procesu
extern struct user u; z´asobn´ık j´adra
• proces se dostane do reˇzimu j´ adra bud’ pˇr´ıchodem pˇreruˇsen´ı vyvolan´eho procesorem (v´ ypadek str´ anky, nezn´ am´ a instrukce,...), ˇcasovaˇcem (v pravideln´ ych intervalech je potˇreba aktivovat pl´anovaˇc proces˚ u), perifern´ım zaˇr´ızen´ım, nebo instrukc´ı synchronn´ıho pˇreruˇsen´ı (standardn´ı knihovna takto pˇred´ av´ a ˇr´ızen´ı j´ adru, aby obslouˇzilo syst´emov´e vol´ an´ı). • v pamˇeti je pouze jedna kopie k´odu a dat j´adra, sd´ılen´a vˇsemi procesy. K´od j´adra je vˇzdy cel´ y rezidentn´ı v pamˇeti, nen´ı odkl´ ad´ an na disk. • text j´ adra . . . k´od j´ adra operaˇcn´ıho syst´emu, zaveden´ y pˇri startu syst´emu a rezidentn´ı v pamˇeti po celou dobu bˇehu syst´emu. Nˇekter´e implementace umoˇzn ˇ uj´ı pˇrid´ avat funkˇcn´ı moduly do j´adra za bˇehu (napˇr. pˇri pˇrid´ an´ı nov´eho zaˇr´ızen´ı se do j´ adra dynamicky pˇrid´ a nov´ y ovladaˇc), nen´ı proto tˇreba kv˚ uli kaˇzd´e zmˇenˇe regenerovat j´ adro a restartovat syst´em. • data a bss j´ adra . . . datov´e struktury pouˇz´ıvan´e j´adrem, souˇca´st´ı je i u-oblast pr´avˇe bˇeˇz´ıc´ıho procesu. • z´ asobn´ık j´ adra . . . samostatn´ y pro kaˇzd´ y proces, je pr´azdn´ y, jestliˇze je proces v uˇzivatelsk´em reˇzimu (a tedy pouˇz´ıv´ a uˇzivatelsk´ y z´ asobn´ık).
103
Pamˇ et’ov´ e segmenty procesu a segs struct as
read only shared
s as s prev
text
s next
read/write private
data
read/write private
z´asobn´ık
read/write shared
sd´ılen´a pamˇet’
struct proc
• kaˇzd´ y proces m´a tˇri z´ akladn´ı segmenty (pamˇet’ov´e segmenty, nemluv´ıme o hardwarov´ ych segmentech): – text – data – z´ asobn´ık • d´ ale lze do adresov´eho prostoru pˇripojit segmenty sd´ılen´e pamˇeti (shmat()) nebo soubory (mmap()). • text je sd´ılen vˇsemi procesy, kter´e prov´ad´ı stejn´ y k´ od. Datov´ y segment a z´ asobn´ık jsou priv´ atn´ı pro kaˇzd´ y proces. • z´ akladn´ım rysem t´eto architektury je tzv. memory object, coˇz je abstrakce mapov´an´ı mezi kusem pamˇeti a m´ıstem, kde jsou data norm´ alnˇe uloˇzena (tzv. backing store nebo data object ). Takov´e m´ısto uloˇzen´ı m˚ uˇze b´ yt napˇr´ıklad swap nebo soubor. Adresov´ y prostor procesu je pak mnoˇzina mapov´an´ı na r˚ uzn´e datov´e objekty. Existuje i anonymn´ı objekt, kter´ y nem´ a m´ısto trval´eho uloˇzen´ı (pouˇz´ıv´ a se napˇr´ıklad pro z´ asobn´ık). Fyzick´a pamˇet’ pak slouˇz´ı jako cache pro data tˇechto namapovan´ ych datov´ ych objekt˚ u. • tato zde velmi hrubˇe popsan´a architektura se naz´ yv´ a VM (od Virtual Memory), a objevila se v SunOS 4.0. Na t´eto architektuˇre je zaloˇzena architektura viru´ aln´ı pamˇeti v SVR4. V´ıce informac´ı viz [Vahalia], p˚ uvodn´ı ˇcl´anek z roku 1987 pˇredstavuj´ıc´ı tuto architekturu: Gingell, R. A., Moran J. P., Shannon, W. A. – Virtual Memory Architecture in SunOS nebo pˇredn´ aˇska o operaˇcn´ıch syst´emech na MFF ve ˇctvrt´em (?) roˇcn´ıku.
104
Virtu´ aln´ı pamˇ et’ pamˇet’ procesu 1 000 111
111 000 000 111 000 111 000 111 000 111 000 111 000 111
re´aln´a pamˇet’
pamˇet’ procesu 2
111 000 000 111 000 111 000 111 000 111
11 00 0 1 0 1 00 11 0 1 0 1 00 11 0 1 001010 11
1 0 0 1 0 1 0 1 0 1 0 1 0 1
pamˇet’ procesu 3
1 0 0 1 0 1 0 1 0 1 0 1 0 1
pamˇet’ procesu 4
111111 000000 000000 111111 000000 111111 000000 111111 11 00 00 11 00 11 00 11
1 0 0 1 0 1 0 1 0 1 0 1 0 1
• kaˇzd´ y proces vid´ı sv˚ uj adresov´ y prostor jako souvisl´ y interval (virtu´aln´ıch) adres od nuly po nˇejakou maxim´ aln´ı hodnotu. Pˇr´ıstupn´e jsou pouze ty adresy, na kter´ ych je namapov´an nˇekter´ y segment procesu (to je pr´avˇe to mapov´an´ı, o kter´em se mluv´ı na pˇredchoz´ım slajdu). • j´adro d´ ale rozdˇeluje pamˇet’ procesu na str´ anky. Kaˇzd´ a str´ anka m´a sv´e um´ıstˇen´ı v r´ amci fyzick´e pamˇeti. Toto um´ıstˇen´ı je d´ ano str´ ankovac´ımi tabulkami j´adra a str´ anky mohou b´ yt v r´ amc´ıch libovolnˇe prom´ıch´ any v˚ uˇci jejich poˇrad´ı ve virtu´ aln´ı adresov´em prostoru. • pokud nen´ı str´ anka pr´avˇe pouˇz´ıv´ ana, m˚ uˇze b´ yt tak´e odloˇzena na disk. • pamˇet’ov´ y manager j´ adra zajiˇst’uje mapov´an´ı mezi virtu´ aln´ımi adresami pouˇz´ıvan´ ymi k´odem uˇzivatelsk´ ych proces˚ u i j´adra na fyzick´e adresy a naˇcten´ı odloˇzen´ ych str´ anek z disku pˇri v´ ypadku str´ anky.
105
Implementace virtu´ aln´ı pamˇ eti • procesy v UNIXu pouˇz´ıvaj´ı k pˇr´ıstupu do pamˇeti virtu´ aln´ı adresy, kter´e na fyzick´e adresy pˇrev´ad´ı hardware ve spolupr´aci s j´ adrem syst´emu. • pˇri nedostatku voln´e pamˇeti se odkl´ adaj´ı nepouˇz´ıvan´e u ´ seky pamˇeti do odkl´ adac´ı oblasti (swap) na disk. • pˇred verz´ı SVR2 se procesem swapper (nyn´ı sched) odkl´ adaly cel´e procesy. • od verze SVR2 se pouˇz´ıv´ a str´ ankov´an´ı na ˇza´dost (demand paging) a copy-on-write. Str´ anky se alokuj´ı aˇz pˇri prvn´ım pouˇzit´ı a priv´ atn´ı str´ anky se kop´ıruj´ı pˇri prvn´ı modifikaci. Uvolˇ nov´an´ı a odkl´ ad´ an´ı jednotliv´ ych str´ anek prov´ad´ı proces pageout, odkl´ ad´ an´ı cel´ ych proces˚ u nastupuje aˇz pˇri kritick´em nedostatku pamˇeti.
pˇ reklad adres: pˇr´ıstup na neplatnou adresu nebo pokus o z´ apis do pamˇeti pouze pro ˇcten´ı vyvol´ a sign´al SIGSEGV. swap: odkl´ adac´ı prostor se vytv´aˇr´ı na samostatn´em odd´ılu disku, od SVR4 m˚ uˇze b´ yt i v souboru. swapper: proces swapper se snaˇz´ı odloˇzit na disk nˇejak´ y proces, kter´ y nen´ı zamˇcen v pamˇeti, a na uvolnˇen´e m´ısto zav´est dˇr´ıve odloˇzen´ y proces. demand paging: pˇri ˇza´dosti procesu o pamˇet’ se pouze uprav´ı tabulka str´ anek. Prvn´ı instrukce adresuj´ıc´ı obsah str´ anky vyvol´a v´ yjimku. J´ adro ji oˇsetˇr´ı t´ım, ˇze alokuje str´ anku. copy-on-write: v´ıce proces˚ u m˚ uˇze sd´ılet zapisovatelnou fyzickou str´ anku, kter´ a je ale logicky priv´ atn´ı pro kaˇzd´ y proces (tato situace nastane napˇr. po vytvoˇren´ı procesu vol´ an´ım fork()). Dokud procesy z pamˇeti pouze ˇctou, pˇristupuj´ı ke sd´ılen´e str´ ance. Pokud se proces pokus´ı obsah str´ anky zmˇenit, vyvol´a v´ yjimku. J´ adro zkop´ıruje str´ anku, pˇridˇel´ı procesu kopii, kter´ a uˇz je priv´ atn´ı a proces ji m˚ uˇze d´ ale libovolnˇe mˇenit. Ostatn´ı procesy pouˇz´ıvaj´ı st´ale nezmˇenˇenou p˚ uvodn´ı str´ anku. str´ anky k odloˇzen´ı se hledaj´ı algoritmem NRU (not recently used): kaˇzd´ a str´ anka m´a pˇr´ıznaky referenced a modified, na zaˇca´tku vynulovan´e. Pˇri prvn´ım pˇr´ıstupu se nastav´ı referenced, pˇri zmˇenˇe modified. Oba pˇr´ıznaky se periodicky nuluj´ı. Pˇrednostnˇe se uvolˇ nuj´ı str´ anky, kter´e nejsou modifikovan´e ani pouˇzit´e. Str´ anky k´odu programu a mapovan´ ych soubor˚ u se neukl´ adaj´ı do odkl´ adac´ıho prostoru, ale obnovuj´ı se z pˇr´ısluˇsn´eho souboru.
106
Stavy procesu bˇeˇz´ı v uˇziv. reˇzimu
vznik
*
pˇreruˇsen´ı
n´avrat
bˇeˇz´ı v reˇzimu j´adra pˇridˇelen´ı procesoru
z´anik
†
wait
exit kill
m´atoha (zombie)
preempce pˇripraven v pamˇeti odloˇzen´ı
zaveden´ı
pˇripraven na disku
probuzen´ı probuzen´ı
usp´an´ı sp´ı v pamˇeti odloˇzen´ı sp´ı na disku
• po ukonˇcen´ı procesu vol´ an´ım exit() nebo v reakci na sign´al pˇrech´ az´ı proces do stavu m´atoha (zombie), protoˇze j´adro si mus´ı pamatovat k ˇc´ıslu procesu jeho n´ avratovou hodnotu. Cel´a pamˇet’ procesu je uvolnˇena, zb´ yv´ a pouze struktura proc. Proces lze definitivnˇe zruˇsit, aˇz kdyˇz se jeho rodiˇc zept´ a na n´ avratovou hodnotu vol´ an´ım typu wait(). • v dneˇsn´ıch UNIXech se obvykle do odkl´ adac´ı oblasti na disku (swap area) neodkl´ adaj´ı cel´e procesy, ale jednotliv´e str´ anky pamˇeti. • proces je usp´ an, kdyˇz o to s´ am poˇza´d´ a, napˇr. zaˇcne ˇcekat na dokonˇcen´ı perifern´ı operace. Preempce je naopak nedobrovoln´e odebr´ an´ı procesoru pl´anovaˇcem.
107
Pl´ anov´ an´ı proces˚ u • preemptivn´ı pl´ anov´ an´ı – jestliˇze se proces nevzd´a procesoru (neusp´ı se ˇcek´ an´ım na nˇejakou ud´ alost), je mu odebr´ an procesor po uplynut´ı ˇcasov´eho kvanta. • procesy jsou zaˇrazeny do front podle priority, procesor je pˇridˇelen vˇzdy prvn´ımu pˇripraven´emu procesu z fronty, kter´ a m´a nejvyˇsˇs´ı prioritu. • v SVR4 byly zavedeny prioritn´ı tˇr´ıdy a podpora proces˚ u re´aln´eho ˇcasu (real-time) s garantovanou maxim´ aln´ı dobou odezvy. • na rozd´ıl od pˇredchoz´ıch verz´ı znamen´ a v SVR4 vyˇsˇs´ı ˇc´ıslo vyˇsˇs´ı prioritu.
• z´ akladem preemptivn´ıho pl´anov´an´ı jsou pravideln´a pˇreruˇsen´ı od ˇcasovaˇce, kter´ a odeberou procesor bˇeˇz´ıc´ımu procesu a pˇredaj´ı ˇr´ızen´ı j´adru (aktivuje se pl´anovaˇc proces˚ u). • jin´a varianta je nepreemptivn´ı (kooperativn´ı) pl´anov´an´ı, kdy proces bˇeˇz´ı, dokud se s´ am nevzd´ a procesoru, tj. dokud nezavol´a takovou syst´emovou funkci, kter´ a pˇrepne kontext na jin´ y proces. Nev´ yhodou kooperativn´ıho pl´anov´an´ı je, ˇze jeden proces m˚ uˇze st´ ale blokovat procesor a ostatn´ı procesy se nikdy nedostanou na ˇradu. • UNIX pouˇz´ıv´ a pouze preemptivn´ı pl´anov´an´ı pro uˇzivatelsk´e procesy. • tradiˇcn´ı UNIXov´e j´ adro funguje kooperativn´ım zp˚ usobem, tj. proces bˇeˇz´ıc´ı v reˇzimu j´ adra nen´ı pˇrepl´anov´an, dokud se s´ am nevzd´a procesoru. J´ adra modern´ıch UNIX˚ u jsou jiˇz preemtivn´ı – je to hlavnˇe kv˚ uli real-time syst´em˚ um; tam je potˇreba m´ıt moˇznost bˇeˇz´ıc´ı proces zbavit procesoru okamˇzitˇe, neˇcekat na to, aˇz se vr´ at´ı z reˇzimu j´ adra nebo se s´ am usp´ı. • pˇri preemptivn´ım pl´anov´an´ı m˚ uˇze b´ yt proces kdykoliv pˇreruˇsen a ˇr´ızen´ı pˇred´ ano jin´emu procesu. Proces si proto nikdy nem˚ uˇze b´ yt jist´ y, ˇze urˇcitou operaci (v´ıce neˇz jednu intstrukci, kromˇe syst´emov´ ych vol´an´ı se zaruˇcenou atomiˇcnost´ı) provede atomicky, bez ovlivnˇen´ı ostatn´ımi procesy. Pokud je tˇreba zajistit atomiˇcnost nˇejak´e akce, mus´ı se procesy navz´ ajem synchronizovat. Pˇri kooperativn´ım pl´anov´an´ı probl´em synchronizace odpad´a (atomick´a posloupnost operac´ı se zajist´ı t´ım, ˇze se proces bˇehem n´ı nevzd´a procesoru).
108
Prioritn´ı tˇ r´ıdy • syst´ emov´ a – priorita 60 aˇz 99 – rezervov´ana pro syst´emov´e procesy (pageout, sched, . . . ) – pevn´ a priorita • real-time – priorita 100 aˇz 159 – pevn´ a priorita – pro kaˇzdou hodnotu priority definov´ano ˇcasov´e kvantum • sd´ılen´ı ˇ casu (time-shared) – priorita 0 aˇz 59 – promˇenn´a dvousloˇzkov´a priorita, pevn´ a uˇzivatelsk´ aa promˇenn´a syst´emov´a ˇca´st – pokud proces hodnˇe vyuˇz´ıv´ a procesor, je mu sniˇzov´ana priorita (a zvˇetˇsov´ano ˇcasov´e kvantum)
• syst´emov´a tˇr´ıda je pouˇz´ıv´ ana pouze j´adrem, uˇzivatelsk´ y proces bˇeˇz´ıc´ı v reˇzimu j´ adra si ponech´ av´ a svou pl´anovac´ı charakteristiku. • procesy ve tˇr´ıdˇe re´ aln´eho ˇcasu maj´ı nejvyˇsˇs´ı prioritu, proto mus´ı b´ yt spr´ avnˇe nakonfigurov´any, aby nezablokovaly zbytek syst´emu. • jestliˇze je proces ve tˇr´ıdˇe sd´ılen´ı ˇcasu usp´an a ˇcek´ a na nˇejakou ud´ alost, je mu doˇcasnˇe pˇriˇrazena syst´emov´a priorita. Po probuzen´ı se takov´ y proces dostane na procesor dˇr´ıve, neˇz ostatn´ı procesy, kter´e nesp´ı. • pevn´ a ˇca´st priority procesu ve tˇr´ıdˇe sd´ılen´ı ˇcasu se d´ a nastavit pomoc´ı int setpriority(int which, id t who, int nice ); nebo int nice(int 1 );
109
Skupiny proces˚ u, ˇ r´ızen´ı termin´ al˚ u • kaˇzd´ y proces patˇr´ı do skupiny proces˚ u, tzv. process group • kaˇzd´ a skupina m˚ uˇze m´ıt vedouc´ı proces, tzv. group leader • kaˇzd´ y proces m˚ uˇze m´ıt ˇr´ıd´ıc´ı termin´al (je to obvykle login termin´ al), tzv. controlling terminal • speci´ aln´ı soubor /dev/tty je asociov´an s ˇr´ıd´ıc´ım termin´alem kaˇzd´eho procesu • kaˇzd´ y termin´ al je asociov´an se skupinou proces˚ u, tato skupina se naz´ yv´ a ˇr´ıd´ıc´ı skupina (controlling group) • kontrola job˚ u (job control ) je mechanizmus, jak pozastavovat a probouzet skupiny proces˚ u a ˇr´ıdit jejich pˇr´ıstup k termin´al˚ um • session (relace) je kolekce skupin proces˚ u vytvoˇren´ a pro u ´ˇcely ˇr´ızen´ı job˚ u
• kdyˇz se uˇzivatel pˇrihl´ as´ı do syst´emu, je vytvoˇren´ a nov´a relace, kter´ a se skl´ad´ a z jedn´e skupiny proces˚ u, ve kter´e je jeden proces – ten kter´ y vykon´ av´ a uˇzivatel˚ uv shell. Tento proces je z´ aroveˇ n vedouc´ı t´eto jedin´e skupiny proces˚ u a tak´e je vedouc´ı relace. V pˇr´ıpadˇe, ˇze job control je povolen, kaˇzd´ y pˇr´ıkaz nebo kolona pˇr´ıkaz˚ u vytvoˇr´ı novou skupinu proces˚ u, jeden z proces˚ u v kaˇzd´e skupinˇe se vˇzdy stane vedouc´ım procesem dan´e skupiny. Jedna ze skupin m˚ uˇze bˇeˇzet na popˇred´ı, ostatn´ı bˇeˇz´ı na pozad´ı. Sign´ aly kter´e jsou generovan´e z kl´ avesnice (tj. stiskem kombinace kl´ aves, nemysl´ı se t´ım spuˇstˇen´ı pˇr´ıkazu kill!) jsou zasl´ any pouze skupinˇe, kter´ a bˇeˇz´ı na popˇred´ı. • pokud job control nen´ı zapnut, znamen´ a spustˇen´ı pˇr´ıkazu na pozad´ı pouze to, ˇze shell neˇcek´ a na jeho ukonˇcen´ı. Existuje pouze jedna skupina proces˚ u, sign´aly z kl´ avesnice se pos´ılaj´ı vˇsem proces˚ um beˇz´ıc´ım na popˇred´ı i na pozad´ı. Nelze pˇresouvat procesy z pozad´ı na popˇred´ı a naopak. • kdyˇz proces, kter´ y m´a kontroln´ı termin´al, otevˇre soubor /dev/tty, tak se asociuje se sv´ ym kontroln´ım termin´alem. Tj. pokud dva r˚ uzn´e procesy z r˚ uzn´ ych relac´ı otevˇrou tento soubor, pˇristupuj´ı oba k r˚ uzn´ ym termin´al˚ um.
110
Identifikace procesu pid t getpid(void); • vrac´ı process ID volaj´ıc´ıho procesu. pid t getpgrp(void); • vrac´ı ID skupiny proces˚ u, do kter´e patˇr´ı volaj´ıc´ı proces. pid t getppid(void); • vrac´ı process ID rodiˇce. pid t getsid(pid t pid ); • vrac´ı group ID vedouc´ıho procesu session (sezen´ı, termin´alov´e relace) pro proces pid (0 znamen´ a pro volaj´ıc´ı proces)
skupiny proces˚ u umoˇzn ˇ uj´ı pos´ılat sign´aly najednou cel´e skupinˇe. session (relace, sezen´ı) je kolekce proces˚ u vytvoˇren´ a pro u ´ˇcely ˇr´ızen´ı prac´ı (job control ). Procesy sezen´ı sd´ılej´ı jeden ˇr´ıd´ıc´ı termin´ al. Session zahrnuje jednu nebo v´ıce skupin proces˚ u. Max. jedna skupina v r´ amci sezen´ı bˇeˇz´ı na popˇred´ı (foreground process group) a m´a pˇr´ıstup k ˇr´ıdic´ımu termin´alu pro vstup i v´ ystup, ostatn´ı bˇeˇz´ı na pozad´ı (background process groups) a maj´ı k ˇr´ıdic´ımu termin´ alu pˇr´ıstup volitelnˇe jen pro v´ ystup nebo v˚ ubec (nepovolen´ a operace s termin´ alem pozastav´ı proces). Proces, kter´ y jeˇstˇe nen´ı vedouc´ım skupiny proces˚ u, se m˚ uˇze st´ at vedouc´ım sezen´ı a z´ aroveˇ n skupiny proces˚ u vol´an´ım setsid(). Jestliˇze proces uˇz je vedouc´ım skupiny, setsid() selˇze, pak je tˇreba prov´est fork() a setsid() zavolat v synovsk´em procesu. Takov´ y proces nem´ a ˇr´ıdic´ı termin´ al, m˚ uˇze ho z´ıskat otevˇren´ım termin´alu, kter´ y jeˇstˇe nen´ı ˇr´ıdic´ım termin´ alem sezen´ı, kdyˇz pˇri open() neuvede pˇr´ıznak O NOCTTY, nebo jin´ ym implementaˇcnˇe z´ avisl´ ym zp˚ usobem. rodiˇ covsk´ y proces: Kaˇzd´ y proces (kromˇe swapperu, pid == 0) m´a rodiˇce, tj. proces, kter´ y ho stvoˇril vol´ an´ım fork(). Jestliˇze rodiˇc skonˇc´ı dˇr´ıve neˇz d´ıtˇe, adoptivn´ım rodiˇcem se st´ av´ a proces init s pid == 1, kter´ y se tak´e postar´ a o uklizen´ı zombie po skonˇcen´ı procesu.
111
Vytvoˇ ren´ı procesu: fork() getpid() == 1234 switch(pid = case -1: case 0: default: } rodiˇc (pokraˇcuje)
fork()) { /* Chyba */ /* D´ ıtˇ e */ /* Rodiˇ c */
d´ıtˇe (nov´y proces)
getpid()==1234, pid==2345 switch(pid = fork()) { case -1: /* Chyba */ case 0: /* D´ ıtˇ e */ default: /* Rodiˇ c */ }
getpid()==2345, pid==0 switch(pid = case -1: case 0: default: }
fork()) { /* Chyba */ /* D´ ıtˇ e */ /* Rodiˇ c */
• d´ıtˇe je t´emˇeˇr pˇresnou kopi´ı rodiˇce, liˇs´ı se PID, d´ ale parent PID a nˇekter´e dalˇs´ı podrobnosti (´ uˇctovac´ı ˇcasy se nastav´ı na 0, nedˇed´ı se nastaven´ı alarm() a z´ amky soubor˚ u). A pokud mˇel rodiˇc v´ıce vl´ aken, m´a syn pouze to, kter´e zavolalo fork(); o tom v´ıce pozdˇeji aˇz se dostaneme k vl´ akn˚ um. Tyto informace by mˇely b´ yt v manu´ alov´e str´ ance textttfork(2). • pro urychlen´ı a menˇs´ı spotˇrebu pamˇeti se adresov´ y prostor nekop´ıruje, ale pouˇz´ıv´ a se mechanismus copy-on-write. • je logick´e, ˇze otec dostane jako n´ avratovou hodnotu vol´ an´ı fork PID syna a syn 0; syn si m˚ uˇze velmi jednoduˇse sv˚ uj vlastn´ı PID zjistit. Otec by ale jiˇz nemˇel moˇznost jednoduˇse zjistit, jak´ y je PID syna kter´ y byl pr´avˇe vytvoˇren, nav´ıc v situaci, kdy jiˇz vytvoˇril dˇr´ıve syny jin´e.
112
Spuˇ stˇ en´ı programu: exec extern char **environ; int execl(const char *path, const char *arg0, ... ); • spust´ı program definovan´ y celou cestou path, dalˇs´ı argumenty se pak pˇredaj´ı programu v parametrech argc a argv funkce main(). Seznam argument˚ u je ukonˇcen pomoc´ı (char *)0, tj. NULL. arg0 by mˇel obsahovat jm´eno programu (tj. ne celou cestu) • u ´ spˇeˇsn´e vol´ an´ı execl() se nikdy nevr´at´ı, protoˇze spuˇstˇen´ y program zcela nahrad´ı dosavadn´ı adresov´ y prostor procesu. • program dˇed´ı promˇenn´e prostˇred´ı, tj. obsah environ. • handlery sign´al˚ u se nahrad´ı implicitn´ı obsluhou. • zavˇrou se deskriptory soubor˚ u, kter´e maj´ı nastaven´ y pˇr´ıznak FD CLOEXEC (implicitnˇe nen´ı nastaven).
• v path mus´ı b´ yt cel´ a (absolutn´ı nebo relativn´ı) cesta ke spustiteln´emu souboru. Obsah promˇenn´e prostˇred´ı PATH se pouˇz´ıv´ a jen pˇri vol´an´ıch execlp() a execvp(), kdyˇz argument path neobsahuje znak ’/’. • nˇekdy se pouˇz´ıv´ a argv[0] r˚ uzn´e od jm´ena spustiteln´eho souboru. Napˇr. login vloˇz´ı na zaˇca´tek jm´ena spouˇstˇen´eho shellu znak ’-’. Shell podle toho pozn´ a, ˇze m´a fungovat jako login-shell, tj. spustit /etc/profile. • exec() nepˇred´ a kontrolu naˇcten´emu programu v pamˇeti pˇr´ımo, ale pˇres dynamick´ y linker (t´eˇz naz´ yvan´ y loader ), pot´e co ho (= toho loader a) namapuje do adresov´eho prostoru procesu. Loader n´ aslednˇe namapuje potˇrebn´e dynamick´e objekty a teprve pot´e pˇred´ a kontrolu aplikaci. Viz tak´e strana 31. Pro jednoduch´e ovˇeˇren´ı staˇc´ı vytvoˇrit program obsahuj´ıc´ı pouze tˇreba vol´an´ı open(). Tento program pak spust’te pomoc´ı truss(1) takto: truss ./a.out. Uvid´ıte, kter´ a vol´ an´ı se pouˇzij´ı jeˇstˇe pˇredt´ım, neˇz se zavol´a open(). • exec() nezmˇen´ı hodnoty RUID a RGID. A pokud je to program s nastaven´ ym SUID bitem, tak se EUID a uschovan´e UID nastav´ı na UID majitele spustiteln´eho souboru. • dneˇsn´ı unixov´e syst´emy um´ı spouˇstˇet i skripty, kter´e zaˇc´ınaj´ı ˇra´dkem #!/interpreter path /interpreter name [args]
113
Varianty sluˇ zby exec int execv(const char *path, char *const argv []); • obdoba execl(), ale argumenty jsou v poli argv, jehoˇz posledn´ı prvek je (char *)0. int execle(const char *path, const char *arg0, ... , char *const envp []); • obdoba execl(), ale m´ısto environ se pouˇzije envp. int execve(const char *path, char *const argv [], char *const envp []); • obdoba execv(), ale m´ısto environ se pouˇzije envp. int execlp(const char *file, const char *arg0, ...); int execvp(const char *file, char *const argv []); • obdoby execl() a execv(), ale pro hled´ an´ı spustiteln´eho souboru se pouˇzije promˇenn´a PATH.
• l = list, v = vector, e = environment, p = PATH. • kromˇe execlp() a execvp() je nutn´e vˇzdy zad´avat celou cestu. • vˇsechny varianty kromˇe execle() a execve() pˇred´ avaj´ı spouˇstˇen´emu programu sv´e aktu´aln´ı prostˇred´ı, tj. obsah pole environ. • z nˇejak´ ych historick´ ych d˚ uvod˚ u neexistuje vol´an´ı s p a e dohromady.
114
Form´ at spustiteln´ eho souboru • Common Object File Format (COFF) – starˇs´ı System V • Extensible Linking Format (ELF) – nov´ y v SVR4 • ˇcasto se mluv´ı o a.out form´atu, protoˇze tak se jmenuje (pokud nen´ı pouˇzit pˇrep´ınaˇc -o) v´ ystup linkeru. • Form´at ELF:
hlaviˇcka souboru tabulka programov´ ych hlaviˇcek sekce 1 .. . sekce N tabulka hlaviˇcek sekc´ı
• hlaviˇcka souboru (ELF header ) obsahuje z´ akladn´ı informace o souboru. • tabulka programov´ ych hlaviˇcek (program header table) je pˇr´ıtomna pouze u soubor˚ u obsahuj´ıc´ıch spustiteln´e programy. Obsahuje informaci o rozvrˇzen´ı virtu´ aln´ı pamˇeti procesu. • sekce obsahuj´ı instrukce, data, tabulku symbol˚ u, relokaˇcn´ı data, apod. • tabulka hlaviˇcek sekc´ı (section header table) obsahuje sluˇzebn´ı informace pro linker.
115
Ukonˇ cen´ı procesu void exit(int status ); • ukonˇc´ı proces s n´ avratov´ ym k´odem status. • nikdy se nevr´ at´ı na instrukci n´ asleduj´ıc´ı za vol´an´ım. pid t wait(int *stat loc ); • poˇck´ a, aˇz skonˇc´ı nˇekter´ y synovsk´ y proces, vr´ at´ı jeho PID a do stat_loc uloˇz´ı n´ avratov´ y k´od, kter´ y lze d´ ale testovat: – WIFEXITED(stat val) . . . proces volal exit() – WEXITSTATUS(stat val) . . . argument exit() – WIFSIGNALED(stat val) . . . proces dostal sign´al – WTERMSIG(stat val) . . . ˇc´ıslo sign´alu – WIFSTOPPED(stat val) . . . proces pozastaven – WSTOPSIG(stat val) . . . ˇc´ıslo sign´alu pid t waitpid(pid t pid, int *stat loc, int opts ); • ˇcek´ an´ı na jeden proces.
• _exit() . . . jako exit(), ale neprov´ad´ı se flush stdio stream˚ u a nevolaj´ı se funkce nastaven´e pomoc´ı atexit() • ve standardu je jeˇstˇe WIFCONTINUED(stat val) . . . pokraˇcov´an´ı po zastaven´ı • opts ve waitpid() – OR-kombinace: – WCONTINUED . . . vr´ at´ı status procesu, kter´ y nebyl testov´an od pokraˇcov´an´ı procesu po zastaven´ı – WNOHANG . . . neˇcek´ a, pokud nen´ı status okamˇzitˇe k dispozici – WUNTRACED . . . vr´ at´ı status zastaven´eho procesu, kter´ y nebyl testov´an po jeho zastaven´ı • pid ve waitpid(): – == -1 . . . libovoln´e d´ıtˇe – > 0 . . . jedno d´ıtˇe – == 0 . . . d´ıtˇe ve stejn´e skupinˇe proces˚ u jako volaj´ıc´ı proces – < -1 . . . d´ıtˇe ve skupinˇe abs(pid) • rodiˇc by mˇel vˇzdy na sv´e dˇeti zavolat wait() nebo waitpid(), protoˇze jinak se v syst´emu hromad´ı zombie (ukonˇcen´e procesy, kter´e pouze ˇcekaj´ı, aˇz si rodiˇc pˇreˇcte jejich n´ avratovou hodnotu).
116
Pˇ r´ıklad: spuˇ stˇ en´ı programu a ˇ cek´ an´ı int status; pid = fork() rodiˇc if(pid > 0)
wait(&status);
d´ıtˇe else execl("/bin/ls", "/bin/ls", "/", NULL); /bin/ls int main(int argc, char *argv[]) { ... exit(result); }
• toto je klasick´ y zp˚ usob jak spustit nˇejak´ y program a po jeho skonˇcen´ı pokraˇcovat. Rodiˇc nemus´ı jen ˇcekat na ukonˇcen´ı potomka, ale m˚ uˇze vykon´ avat d´ al sv˚ uj k´od. • pozor na to, ˇze aˇckoli to na obr´ azku tak vypad´ a, n´ avratov´a hodnota je pouze souˇca´st toho, co dostanete z vol´an´ı wait(). Na jej´ı z´ısk´an´ı je potˇreba pouˇz´ıt makra z pˇredchoz´ıho slajdu.
117
Roura: pipe() int pipe(int fildes [2]); • vytvoˇr´ı rouru a dva deskriptory – fildes[0] . . . ˇcten´ı z roury – fildes[1] . . . z´ apis do roury • roura zajiˇst’uje synchronizaci ˇcten´ı a z´ apisu: – zapisuj´ıc´ı proces se zablokuje, kdyˇz je roura pln´a, – ˇctouc´ı proces se zablokuje, kdyˇz je roura pr´azdn´ a. • ˇctouc´ı proces pˇreˇcte konec souboru (tj. read() vr´ at´ı 0), pokud jsou uzavˇreny vˇsechny kopie fildes[1]. • pojmenovan´ a roura (vytvoˇren´ a vol´an´ım mkfifo()) funguje stejnˇe, ale m´a pˇridˇelen´e jm´eno v syst´emu soubor˚ u a mohou ji tedy pouˇz´ıvat libovoln´e procesy.
• nepojmenovanou rouru vytv´aˇr´ı jeden proces a m˚ uˇze ji pˇredat pouze sv´ ym potomk˚ um (pomoc´ı deskriptor˚ u zdˇedˇen´ ych pˇri fork()). Toto omezen´ı se d´ a obej´ıt pomoc´ı pˇred´ an´ı otevˇren´eho deskriptoru pˇres unix-domain socket. • jestliˇze funkce write() zap´ıˇse do roury nejv´ yˇse PIPE BUF (syst´emov´a konstanta) bajt˚ u, je zaruˇceno, ˇze z´ apis bude atomick´ y, tj. tato data nebudou proloˇzena daty zapisovan´ ymi souˇcasnˇe jin´ ymi procesy. • v normˇe UNIX03 nen´ı specifikov´ano, zda fildes[0] je tak´e otevˇren´ y pro z´ apis a zda fildes[1] je t´eˇz otevˇren´ y pro ˇcten´ı. Pozor na to, ˇze napˇr´ıklad FreeBSD a Solaris maj´ı roury obousmˇern´e, ale Linux (kdyˇz jsem se naposledy d´ıval) ne. Je proto velmi vhodn´e poˇc´ıtat pouze s jednosmˇern´ ymi rourami. • d˚ uleˇ zit´ e: pro ˇcten´ı/z´ apis z/do roury plat´ı stejn´e podm´ınky jako pro pojmenovanou rouru, viz strana 85. To tak´e znamen´ a, ˇze jedin´ y zp˚ usob, jak ˇcten´ aˇrovi “poslat” end-of-file je, ˇze vˇsichni zapisovatel´e zavˇrou pˇr´ısluˇsn´ y deskriptor pro z´ apis.
118
Pˇ r´ıklad: roura mezi dvˇ ema procesy shell: ls / | more
int pd[2]; pipe(pd); switch(fork()) {
producent (d´ıtˇe)
konzument (rodiˇc) default:
case 0: close(1);
close(0);
dup(pd[1]);
dup(pd[0]);
close(pd[0]);
close(pd[0]);
close(pd[1]);
close(pd[1]);
execl("/bin/ls", "/bin/ls",
execl("/bin/more", "/bin/more", NULL);
"/", NULL);
pd[1]
pd[0]
/bin/ls
/bin/more
• zavˇren´ı z´ apisov´eho deskriptoru pd[1] (ozn. ⊲) v procesu konzumenta je nutn´e, protoˇze jinak se na rouˇre nikdy nedetekuje konec souboru. • ˇctec´ı deskriptor v procesu producenta pd[0] je tak´e vhodn´e zav´ırat (ozn. ⊲), protoˇze kdyˇz konzument pˇredˇcasnˇe skonˇc´ı, dostane producent sign´al SIGPIPE. Kbyby deskriptor v producentovi nebyl zavˇren, producent se nedozv´ı, ˇze konzument skonˇcil, a po naplnˇen´ı bufferu roury v j´adru se zablokuje. • pokud nem´ ame jistotu, ˇze pˇred vol´an´ım pipe() byl otevˇren deskriptor 0, mus´ıme v producentovi pouˇz´ıt dup2(pd[1], 1), protoˇze dup(pd[1]) by mohl vr´ atit deskriptor 0 m´ısto poˇzadovan´eho 1. Tak´e je tˇreba testovat, zda neplat´ı pd[1] == 1, abychom si nechtˇenˇe nezavˇreli rouru. Podobnˇe je tˇreba otestovat pd[0] == 0 v konzumentovi. • je lepˇs´ı vytv´aˇret rouru od syna k otci, protoˇze typicky nejdˇr´ıv skonˇc´ı proces zapisuj´ıc´ı do roury, ˇctouc´ı proces pˇreˇcte zbyl´ a data, zpracuje je, nˇeco vyp´ıˇse a teprve pak skonˇc´ı. shell ˇcek´ a na otce, takˇze kdyˇz smˇeˇruje roura od otce k synovi, otec skonˇc´ı, shell vyp´ıˇse prompt, ale pak jeˇstˇe syn vyp´ıˇse v´ ystup. Moˇzn´ ym ˇreˇsen´ım je ˇcek´ an´ı otce na skonˇcen´ı syna, jenˇze to se ned´a zajistit, pokud otec provede exec. • p˚ uvodn´ı Bourne shell stav´ı rouru tak, ˇze posledn´ı proces v rouˇre vytvoˇr´ı pˇredposledn´ı jako sv´eho syna, ten vytvoˇr´ı pˇredchoz´ı proces a tak se postupuje aˇz k zaˇca´tku roury. • v shellu bash jsou vˇsechny procesy v rouˇre pˇr´ımo potomky shellu (shell vol´a fork() tolikr´ at, jak dlouh´ a je roura). Shell pˇred vyps´ an´ım promptu ˇcek´ a, aˇz vˇsechny procesy roury skonˇc´ı. 119
Sd´ılen´ a pamˇ et’ – u ´ vod • pajpy a soubory jako metody meziprocesov´e komunikace vyˇzaduj´ı syst´emov´a vol´ an´ı • v´ yhoda: procesy nemohou poˇskodit adresov´ y prostor jin´eho procesu • nev´ yhoda: velk´a reˇzie pro syst´emov´a vol´an´ı, typicky read, write • sd´ılen´a pamˇet’ je namapov´an´ı ˇca´sti pamˇeti do adresov´eho prostoru v´ıce proces˚ u • odstranˇen´ı nev´ yhody, ztr´ ata dosavadn´ı v´ yhody • synchronizace pˇr´ıstupu do sd´ılen´e pamˇeti – System V semafory – POSIX semafory bez nutnosti syst´emov´eho vol´an´ı v bˇeˇzn´em pˇr´ıpadˇe
• mapov´an´ı soubor˚ u do pamˇeti je jednou z implementac´ı sd´ılen´e pamˇeti. Pro popis u ´ seku sd´ılen´e pamˇeti pouˇz´ıv´ a soubor. • takto implementovan´ a sd´ılen´a pamˇet’ je tedy pravidelnˇe zapisov´ana na disk • pro sd´ılen´ı bez reˇzie z´ apisu zmˇenˇen´ ych dat na disk je moˇzn´e pouˇz´ıt memory based filesyst´em, napˇr´ıklad tmpfs (Solaris, NetBSD – zde byl tmpfs naps´ an v roce 2005 jako souˇca´st Summer of Code sponzorovan´eho firmou Google, FreeBSD m´a podobnou vlastnost pod n´ azvem memory disk ). Jako tzv. backing store pro pamˇet’ov´e str´ anky patˇr´ıc´ı tˇemto filsyst´em˚ um je obecnˇe moˇzn´e pouˇz´ıt swap oblast na disku.
120
Mapov´ an´ı soubor˚ u do pamˇ eti (1) void *mmap(void *addr, size t len, int prot, int flags, int fildes, off t off ); • do pamˇet’ov´eho prostoru procesu od adresy addr (0 . . . adresu pˇridˇel´ı j´ adro) namapuje u ´ sek d´elky len zaˇc´ınaj´ıc´ı na pozici off souboru reprezentovan´eho deskriptorem fildes. • vrac´ı adresu namapovan´eho u ´ seku nebo MAP FAILED. • v prot je OR-kombinace PROT READ (lze ˇc´ıst), PROT WRITE (lze zapisovat), PROT EXEC (lze spouˇstˇet), nebo PROT NONE (nelze k dat˚ um pˇristupovat). atn´ı • ve flags je OR-kombinace MAP PRIVATE (zmˇeny jsou priv´ pro proces, neukl´ adaj´ı se do souboru), MAP SHARED (zmˇeny se ukl´ adaj´ı do souboru), MAP FIXED (j´ adro nezmˇen´ı addr).
• mapov´an´ı soubor˚ u do pamˇeti je alternativou ke zpracov´ an´ı soubor˚ u pomoc´ı read(), write(), lseek(). Po namapov´an´ı lze se souborem pracovat jako s datovou strukturou v pamˇeti. Soubor se nekop´ıruje cel´ y do pamˇeti, alokuj´ı se pouze str´ anky na kter´e se pˇristupuje. Pokud je potˇreba str´ anku uvolnit, a obsah se ukl´ ad´ a zpˇet do souboru (pˇri MAP SHARED) nebo do swapu – pouˇz´ıv´ se mechanismus copy-on-write (pˇri MAP PRIVATE). • pˇri pouˇzit´ı MAP PRIVATE vid´ım na Solarisu vˇsechny zmˇeny proveden´e jin´ ymi procesy, kter´e namapovaly sd´ılenˇe, aˇz do t´e doby, kdy do str´ anky zap´ıˇsu – v tom okamˇziku se vytvoˇr´ı kopie str´ anky a dalˇs´ı takov´e zmˇeny jiˇz nevid´ım. Na FreeBSD tyto zmˇeny nevid´ım ani pˇred z´ apisem. Viz specifikace: ,,It is unspecified whether modifications to the underlying object done after the MAP PRIVATE mapping is established are visible through the MAP PRIVATE mapping.” • hodnota off+len m˚ uˇze pˇrekraˇcovat aktu´aln´ı velikost souboru, za konec souboru ale nelze zapisovat a soubor tak prodlouˇzit - proces by obdrˇzel sign´al SIGSEGV nebo SIGBUS. Sign´ al dostanu stejnˇe tak v situaci, kdy do readonly namapovan´eho segmentu zkus´ım zapsat (je to logick´e, pˇriˇrazen´ı nem´ a n´ avratovou hodnotu kterou byste mohli otestovat). • mapuje se vˇzdy po cel´ ych str´ ank´ ach, hodnoty off (a pˇri MAP FIXED i addr) mus´ı b´ yt spr´ avnˇe zarovnan´e. Posledn´ı str´ anka je za koncem souboru doplnˇena nulami a tento u ´ sek se nikdy nepˇrepisuje do souboru. • pˇr´ıstup do namapovan´eho u ´ seku, ale za posledn´ı existuj´ıc´ı str´ anku namapovan´eho objektu, zp˚ usob´ı sign´al SIGBUS nebo SIGSEGV. • namapov´an´ı souboru nahrad´ı pˇr´ıpadn´e pˇredchoz´ı mapov´an´ı str´ anek v rozsahu addr aˇz addr+len. 121
• existuj´ıc´ı rozˇs´ıˇren´ı (nejsou souˇca´st´ı UNIX03): – pˇr´ıznak MAP ANON ve FreeBSD a Solarisu – vytvoˇren´ı anonymn´ıho segmentu bez vazby na soubor, deskriptor mus´ı b´ yt -1. Mapuje se tak anonymn´ı objekt, kter´ y jak v´ıme m´a m´ısto fyzick´eho uloˇzen´ı na swapu (tedy nen´ı trval´e). Linux m´a podobnou funkcionalitu pˇres MAP ANONYMOUS. y ob– v IRIXu lze pomoc´ı MAP AUTOGROW automaticky zvˇetˇsit namapovan´ jekt pˇri pˇr´ıstupu za jeho st´avaj´ıc´ı konec.
Mapov´ an´ı soubor˚ u do pamˇ eti (2) int msync(void *addr, size t len, int flags ); • zap´ıˇse zmˇenˇen´e str´ anky v u ´ seku len bajt˚ u od adresy addr do souboru. Hodnota flags je OR-kombinace – MS ASYNC . . . asynchronn´ı z´ apis – MS SYNC . . . synchronn´ı z´ apis a data, kter´ a se liˇs´ı od – MS INVALIDATE . . . zruˇsit namapovan´ obsahu souboru int munmap(void *addr, size t len ); • zap´ıˇse zmˇeny, zruˇs´ı mapov´an´ı v d´elce len od adresy addr. int mprotect(void *addr, size t len, int prot ); • zmˇen´ı pˇr´ıstupov´a pr´ava k namapovan´emu u ´ seku souboru. Hodnoty prot jsou stejn´e jako u mmap().
• uloˇzen´ı zmˇen do souboru na disk je zaruˇcen´e aˇz po proveden´ı msync() nebo munmap(), ale ostatn´ı procesy, kter´e maj´ı soubor namapov´an, vid´ı zmˇeny hned. • mapov´an´ı pamˇeti a nastavov´an´ı pˇr´ıstupov´ ych pr´av pouˇz´ıv´ a napˇr. knihovna Electric Fence, kter´ a slouˇz´ı pro ladˇen´ı chyb pˇri pr´aci s dynamickou pamˇet´ı.
122
Pˇ r´ıklad: mapov´ an´ı soubor˚ u do pamˇ eti int main(int argc, char *argv[]) { int fd, fsz; char *addr, *p1, *p2, c; fd = open(argv[1], O RDWR); fsz = lseek(fd, 0, SEEK END); p1 = addr = mmap(0, fsz, PROT READ|PROT WRITE, MAP SHARED, fd, 0); p2 = p1 + fsz - 1; while(p1
Tento program otoˇc´ı poˇrad´ı znak˚ u v souboru (zap´ıˇse soubor od konce k zaˇca´tku).
123
Dynamick´ y pˇ r´ıstup ke knihovn´ am void *dlopen(const char *file, int mode ); • zpˇr´ıstupn´ı knihovnu v souboru file, vr´ at´ı handle nebo NULL. • v mode je OR-kombinace RTLD NOW (okamˇzit´e relokace), RTLD LAZY (odloˇzen´e relokace), RTLD GLOBAL (symboly budou glob´ alnˇe dostupn´e), RTLD LOCAL (nebudou glob´ alnˇe dostupn´e). void *dlsym(void *handle, const char *name ); • vr´ at´ı adresu symbolu zadan´eho jm´ena z knihovny. int dlclose(void *handle ); • ukonˇc´ı pˇr´ıstup ke knihovnˇe. char *dlerror(void); • vr´ at´ı textov´ y popis chyby pˇri pr´aci s knihovnami.
• pomoc´ı tˇechto funkc´ı lze implementovat dynamicky nahr´ avan´e plug-in moduly naˇc´ıtan´e aplikac´ı podle potˇreby (napˇr. podle obsahu jej´ıho konfiguraˇcn´ıho souboru). • dynamick´ ym naˇc´ıt´ an´ım knihoven se tak´e d´ a vyˇreˇsit situace, kdy potˇrebujeme vyuˇz´ıt nˇekolik knihoven, kter´e definuj´ı symbol se stejn´ ym jm´enem. Jedna knihovna se pˇr´ımo pˇrilinkuje k programu, k ostatn´ım se pˇristupuje pomoc´ı dlopen(). • soubor mus´ı b´ yt ve spr´ avn´em form´atu (sd´ılen´a knihovna .so ve form´atu ELF ), napˇr´ıklad u gcc to znamen´ a pouˇz´ıt pˇrep´ınaˇc -shared. • konstanty pro mode: u) jsou provedeny – RTLD NOW . . . vˇsechny relokace (vyˇreˇsen´ı vˇsech odkaz˚ okamˇzitˇe po nataˇzen´ı knihovny, aplikace m´a jistotu, ˇze jsou vˇsechny symboly pˇr´ıstupn´e – RTLD LAZY . . . relokace mohou b´ yt odloˇzeny aˇz do chv´ıle pouˇzit´ı symbolu yt pouˇzity pˇri zpracov´an´ı – RTLD GLOBAL . . . symboly z knihovny mohou b´ relokac´ı v ostatn´ıch knihovn´ach a jsou dostupn´e pomoc´ı dlopen(0, RTLD GLOBAL) • speci´ aln´ı handle RTLD NEXT hled´ a symbol pouze v knihovn´ach nahran´ ych po knihovnˇe, ve kter´e je vol´ an´ı dlsym(). Hod´ı se pro pˇredefinov´an´ı existuj´ıc´ıch funkc´ı, pokud v redefinovan´e funkci potˇrebujeme volat p˚ uvodn´ı. Knihovna s novou funkc´ı se nahr´ av´ a jako prvn´ı (napˇr. pomoc´ı promˇenn´e LD PRELOAD), adresu p˚ uvodn´ı funkce z´ısk´ a vol´an´ım dlsym(RTLD NEXT, fn name ).
124
• vˇsechny tyto funkce jsou souˇca´st´ı dynamick´eho linkeru, kter´ y m´a kaˇzd´ a dynamicky slinkovan´ a aplikace namapovan´ y ve sv´em adresov´em prostoru. Viz tak´e strany 31 a 113.
Pˇ r´ıklad: zpˇ r´ıstupnˇ en´ı knihovny void *handle; double y, x = 1.3; double (*fun)(double); char *libname = "libm.so", *fn name = "sin"; if ((handle = dlopen(libname, RTLD NOW)) != NULL) { fprintf(stderr, "%s\n", dlerror()); exit(1); } fun = dlsym(handle, fn name); if (err = dlerror()) { fprintf(stderr, "%s\n", err); exit(1); } y = fun(x); dlclose(handle);
• zde se vol´ a funkce sin() z matematick´e knihovny libm.so. • funkce dlsym() vr´ at´ı adresu symbolu dan´eho jm´ena, ale vˇzdy jako ukazatel na void, neprob´ıh´a ˇza´dn´a typov´a kontrola ani nen´ı k dispozici ˇza´dn´a informace o typu symbolu. Ten, kdo tuto adresu pouˇz´ıv´ a, mus´ı zajistit jej´ı spr´ avn´e pˇretypov´an´ı. • pˇri pouˇzit´ı knihoven v C++ je tˇreba si uvˇedomit, ˇze C++ pouˇz´ıv´ a name mangling, tj. do jm´ena funkce (metody) je zak´ odov´ano pˇr´ıpadn´e jm´eno tˇr´ıdy nebo namespace a typy parametr˚ u.
125
Obsah • u ´ vod, v´ yvoj UNIXu a C, program´ atorsk´e n´ astroje • z´ akladn´ı pojmy a konvence UNIXu a jeho API • pˇr´ıstupov´a pr´ava, perifern´ı zaˇr´ızen´ı, syst´em soubor˚ u • manipulace s procesy, spouˇstˇen´ı program˚ u • sign´ aly • synchronizace a komunikace proces˚ u • s´ıt’ov´a komunikace • vl´ akna, synchronizace vl´ aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu
Sign´ aly • informuj´ı proces o v´ yskytu urˇcit´e ud´ alosti. • na uˇzivatelsk´e u ´ rovni zpˇr´ıstupˇ nuj´ı mechanismy pˇreruˇsen´ı. • kategorie sign´al˚ u: – chybov´ e ud´ alosti generovan´e bˇeˇz´ıc´ım procesem, napˇr. pokus o pˇr´ıstup mimo pˇridˇelenou oblast pamˇeti (SIGSEGV) – asynchronn´ı ud´ alosti vznikaj´ıc´ı mimo proces, napˇr. sign´al od jin´eho procesu, vyprˇsen´ı nastaven´eho ˇcasu (SIGALRM), odpojen´ı termin´ alu (SIGHUP), stisk Ctrl-C (SIGINT) • nejjednoduˇsˇs´ı mechanismus pro komunikaci mezi procesy – nesou pouze informaci o tom, ˇze nastala nˇejak´ a ud´ alost. • zpracov´avaj´ı se asynchronnˇe – pˇr´ıchod sign´alu pˇreruˇs´ı bˇeh procesu a vyvol´ a handler.
126
• se sign´alem nen´ı sv´ az´ana ˇza´dn´a jin´a informace neˇz ˇc´ıslo sign´alu. • po n´ avratu z handleru (pokud k nˇemu dojde) proces pokraˇcuje od m´ısta pˇreruˇsen´ı. • historicky sign´aly vznikly jako mechanismus pro ,,n´ asiln´e” ukonˇcen´ı procesu. Z toho vyplynul i n´ azev funkce kill() pro posl´ an´ı sign´alu.
Posl´ an´ı sign´ alu int kill(pid t pid, int sig ); • poˇsle sign´al s ˇc´ıslem sig procesu (nebo skupinˇe proces˚ u) podle hodnoty pid: – > 0 . . . procesu s ˇc´ıslem pid – == 0 . . . vˇsem proces˚ um ve stejn´e skupinˇe – == -1 . . . vˇsem proces˚ um, kromˇe syst´emov´ ych – < -1 . . . proces˚ um ve skupinˇe abs(pid) • sig == 0 znamen´ a, ˇze se pouze zkontroluje opr´ avnˇen´ı poslat sign´al, ale ˇza´dn´ y sign´al se nepoˇsle. • pr´avo procesu poslat sign´al jin´emu procesu z´ avis´ı na UID obou proces˚ u.
• proces s EUID == 0 m˚ uˇze poslat sign´al libovoln´emu procesu. • ostatn´ı procesy: – Linux, Solaris: RUID nebo EUID procesu, kter´ y poslal sign´ al, se mus´ı shodovat s re´ aln´ ym UID nebo saved set-user-ID c´ılov´eho procesu. – FreeBSD: mus´ı se shodovat EUID obou proces˚ u. – IRIX: RUID nebo EUID procesu, kter´ y poslal sign´al, se mus´ı shodovat s re´ aln´ ym nebo efektivn´ım UID nebo saved set-user-ID c´ılov´eho procesu.
127
Oˇ setˇ ren´ı sign´ al˚ u • pokud proces neˇrekne jinak, provede se v z´ avislosti na konkr´etn´ım sign´alu implicitn´ı akce, tj. bud’: – ukonˇcen´ı procesu (exit) – ukonˇcen´ı procesu plus coredump (core) – ignorov´an´ı sign´alu (ignore) – pozastaven´ı procesu (stop) – pokraˇcov´an´ı pozastaven´eho procesu (continue) • proces tak´e m˚ uˇze nastavit ignorov´an´ı sign´alu • nebo sign´al oˇsetˇren´ı uˇzivatelsky definovanou funkc´ı (handler), po n´ avratu z handleru proces pokraˇcuje od m´ısta pˇreruˇsen´ı sign´aly SIGKILL a SIGSTOP vˇzdy vyvolaj´ı implicitn´ı akci (zruˇsen´ı, resp. pozastaven´ı).
• vytvoˇren´ı core dumpu znamen´ a uloˇzen´ı kompletn´ıho obsahu pamˇeti procesu do souboru, typicky se jm´enem core • vˇetˇsina sign´al˚ u implicitnˇe ukonˇc´ı proces, nˇekter´e nav´ıc vytvoˇr´ı jiˇz zmiˇ novan´ y core dump, kter´ y je moˇzn´e n´ aslednˇe pouˇz´ıt pro ladic´ı u ´ˇcely.
128
Pˇ rehled sign´ al˚ u (1) sign´aly je moˇzn´e logicky rozdˇelit do nˇekolika skupin. . . detekovan´ e chyby: SIGBUS
pˇr´ıstup k nedef. ˇca´sti pamˇet’ov´eho objektu (core)
SIGFPE
chyba aritmetiky v pohybliv´e ˇca´rce (core)
SIGILL
nepovolen´ a instrukce (core)
SIGPIPE
z´ apis do roury, kterou nikdo neˇcte (exit)
SIGSEGV
pouˇzit´ı nepovolen´e adresy v pamˇeti (core)
SIGSYS
chybn´e syst´emov´e vol´an´ı (core)
SIGXCPU
pˇrekroˇcen´ı ˇcasov´eho limitu CPU (core)
SIGXFSZ
pˇrekroˇcen´ı limitu velikosti souboru (core)
• generov´an´ı tˇechto sign´al˚ u vych´ az´ı z chyb programu • pro sign´aly BUS, FPE, ILL a SEGV nen´ı normou pˇresnˇe definov´ana pˇr´ıˇcina, ale obvykle jsou to chyby detekovan´e hardwarem • pro tyto ˇctyˇri sign´aly tak´e plat´ı tato speci´ aln´ı pravidla (podrobnosti viz kapitola 2.4 Signal Concepts v normˇe UNIX03): – pokud byly nastaven´e jako ignorovan´e vol´an´ım sigaction, je chov´an´ı programu po t´e, co je mu takov´ y sign´al posl´ an, normou nedefinov´ano – n´ avratov´a hodnova handleru je nedefinov´ana – n´ asledek situace, kdy jeden z tˇechto sign´alu je maskov´an v okamˇziku jeho vygenerovan´ı je nedefinovan´ y • jin´ ymi slovy – pokud je hardwarem detekovan´ a chyba re´aln´ a (sign´ al nen´ı posl´ an pˇres kill), v´aˇs program se pˇres tuto chybu nemus´ı v˚ ubec dostat. Nen´ı bezpeˇcn´e chybu ignorovat, pokraˇcovat v bˇehu po n´ avratu z handleru nebo odd´ alit ˇreˇsen´ı pomoc´ı zamaskov´an´ı. Pokud m´ate pro tyto sign´aly handler, je potˇ reba poˇ reˇ sit danou situaci jak uzn´ ate za vhodn´ e a pak ukonˇ cit program. • pozn´ amka: pokud je nˇeco normou nedefinov´ano (undefined ), obecnˇe to znamen´ a, ˇze se neoˇcek´ av´ a, ˇze by program´ ator potˇreboval zn´ at pˇresn´e chov´an´ı v takov´e situaci. Pokud je to potˇreba, pravdˇepodobnˇe je ve vaˇsem programu nˇeco ˇspatnˇe. Jako vˇzdy, urˇcite by se naˇsly vyj´ımky potvrzuj´ıc´ı pravidlo.
129
Pˇ rehled sign´ al˚ u (2) generovan´ e uˇ zivatelem nebo aplikac´ı: SIGABRT
ukonˇcen´ı procesu (core)
SIGHUP
odpojen´ı termin´ alu (exit)
SIGINT
stisk speci´ aln´ı kl´ avesy Ctrl-C (exit)
SIGKILL
zruˇsen´ı procesu (exit, nelze oˇ setˇ rit ani ignorovat)
SIGQUIT
stisk speci´ aln´ı kl´ avesy Ctrl-\ (core)
SIGTERM
zruˇsen´ı procesu (exit)
SIGUSR1
uˇzivatelsky definovan´ y sign´al 1 (exit)
SIGUSR2
uˇzivatelsky definovan´ y sign´al 2 (exit)
• SIGINT a SIGQUIT jsou obvykle generov´any z termin´alu (Ctrl-C a Ctrl-\) a lze je pˇredefinovat pˇr´ıkazem stty nebo pomoc´ı funkce tcsetattr(). • SIGTERM je defaultn´ı sign´al pro pˇr´ıkaz kill • SIGUSR1 a SIGUSR2 nejsou pouˇzity ˇza´dn´ ym syst´emov´ ym vol´an´ım a jsou plnˇe k dispozici uˇzivateli • SIGALRM a souvisej´ıc´ı funkce alarm() se pouˇz´ıvaj´ı pro odmˇeˇrov´an´ı ˇcasov´ ych interval˚ u v uˇzivatelsk´em procesu (napˇr. pˇri implementaci timeout˚ u). • sign´al SIGHUP se ˇcasto pouˇz´ıv´ a jako zp˚ usob, jak ozn´amit bˇeˇz´ıc´ımu d´emonu, ˇze se zmˇenil jeho konfiguraˇcn´ı soubor a m´a si ho proto znovu naˇc´ıst. • zaj´ımavost: FreeBSD 5.3 obsahuje chybu, kter´ a dovol´ı za jist´ ych okolnost´ı zachytit sign´al SIGKILL.
130
Pˇ rehled sign´ al˚ u (3) job control: SIGCHLD
zmˇena stavu synovsk´eho procesu (ignore)
SIGCONT
pokraˇcov´an´ı pozastaven´eho procesu (continue)
SIGSTOP
pozastaven´ı (stop, nelze oˇ setˇ rit ani ignorovat)
SIGTSTP
pozastaven´ı z termin´alu Ctrl-Z (stop)
SIGTTIN
ˇcten´ı z termin´ alu procesem na pozad´ı (stop)
SIGTTOU
z´ apis na termin´ al procesem na pozad´ı (stop)
• plat´ı, ˇze nikdy nen´ı povoleno v´ıce proces˚ um najednou ˇc´ıst z kontroln´ıho termin´ alu, ale v´ıce proces˚ u najednou m˚ uˇze na termin´ al zapisovat.
Pˇ rehled sign´ al˚ u (4) ˇ casovaˇ ce: SIGALRM
pl´anovan´e ˇcasov´e pˇreruˇsen´ı (exit)
SIGPROF
vyprˇsen´ı profiluj´ıc´ıho ˇcasovaˇce (exit)
SIGVTALRM
vyprˇsen´ı virtu´ aln´ıho ˇcasovaˇce (exit)
r˚ uzn´ e: SIGPOLL
testovateln´ a ud´ alost (exit)
SIGTRAP
ladic´ı pˇreruˇsen´ı (core)
SIGURG
urgentn´ı ud´ alost na soketu (ignore)
131
Nastaven´ı obsluhy sign´ al˚ u int sigaction(int sig, const struct sigaction *act, struct sigaction *oact ); • nastav´ı obsluhu sign´alu sig podle act a vr´ at´ı pˇredchoz´ı nastaven´ı v oact. • obsah struktury sigaction: – void (*sa handler )(int) . . . SIG DFL, SIG IGN, nebo adresa handleru – sigset t sa mask . . . sign´aly blokovan´e v handleru, nav´ıc je blokov´an sign´al sig – int sa flags . . . SA RESETHAND (pˇri vstupu do handleru nastavit SIG DFL), SA RESTART (restartovat pˇreruˇsen´ a syst´emov´a vol´ an´ı), SA NODEFER (neblokovat sign´al sig bˇehem obsluhy)
• kdyˇz je act == NULL, pouze se zjist´ı nastaven´ı obsluhy, nemˇen´ı se. Jestliˇze n´ as pˇredchoz´ı nastaven´ı nezaj´ım´ a, lze pouˇz´ıt oact == NULL. • pokud nen´ı nastaveno SA RESTART, syst´emov´a vol´an´ı aktivn´ı v bodˇe pˇr´ıchodu sign´alu skonˇc´ı s chybou EINTR. Restartov´an´ı nemus´ı fungovat pro vˇsechna syst´emov´a vol´ an´ı, napˇr. na FreeBSD je select() pˇreruˇsen sign´alem vˇzdy, i kdyˇz je nastaveno SA RESTART (pozn: nemus´ı b´ yt pravda u souˇcasn´ ych verz´ı, nezkouˇsel jsem to na nich). • pozor na probl´em vz´ ajemn´eho vylouˇcen´ı mezi procesem a handlerem, popˇr. mezi handlery pro r˚ uzn´e sign´aly. Jestliˇze je nastaveno SA NODEFER, mˇel by b´ yt handler reentrantn´ı. • v sign´alov´em handleru by se mˇely pouˇz´ıvat pouze funkce, kter´e jsou pro takov´e pouˇzit´ı bezpeˇcn´e. Mus´ı bud’ b´ yt reentrantn´ı, nebo je nutn´e zajistit, aby nepˇriˇsel sign´al v nevhodnou dobu (napˇr. uvnitˇr funkce pˇrijde sign´al, v jehoˇz handleru se vol´ a stejn´ a funkce). Bezpeˇcn´e funkce ze syst´emov´e knihovny jsou vyjmenov´any v manu´ alov´e str´ ance k funkci sigaction() (v SUSv3 jsou v kapitole System Interfaces: General Information, Signal Concepts). • funkce sigaction() je obecnˇejˇs´ı neˇz starˇs´ı funkce signal() a sigset(). • pro v´ yskok z handleru sign´alu jinam neˇz na m´ısto vzniku sign´alu se daj´ı pouˇz´ıt funkce sigsetjmp() a siglongjmp(). Pozor na to, ˇze v tomto pˇr´ıpadˇe si mus´ıme b´ yt jisti, ˇze v okamˇziku pˇr´ıchodu sign´alu nen´ı program uvnitˇr nereentrantn´ı funkce. V´ yskokem z handleru do hlavn´ıho programu nen´ı vykon´ av´ an´ı takov´e funkce ukonˇceno a mohou nastat stejn´e probl´emy jako pˇri vol´ an´ı nereentrantn´ı funkce pˇr´ımo z handleru.
132
Pˇ r´ıklad: ˇ casovˇ e omezen´ y vstup #define BUFSZ 4096 void handler(int sig) { fprintf(stderr," !!! TIMEOUT !!! \n"); } int main() { char buf[BUFSZ]; struct sigaction act; int sz; act.sa handler = handler; sigemptyset(&act.sa mask); act.sa flags = 0; sigaction(SIGALRM, &act, NULL); alarm(5); sz = read(0, buf, BUFSZ); if(sz > 0) write(1, buf, sz); exit(0); }
• lze pouˇz´ıvat i ˇcasovaˇce s jemnˇejˇs´ım rozliˇsen´ım neˇz 1 s. Nastavuj´ı a testuj´ı se funkcemi setitimer() a getitimer(). Pˇri vyprˇsen´ı pos´ılaj´ı sign´aly procesu, kter´ y ˇcasovaˇce nastavil: aln´ y ˇcas, pos´ıl´ a SIGALRM – ITIMER REAL . . . mˇeˇr´ı re´ – ITIMER VIRTUAL . . . mˇeˇr´ı virtu´ aln´ı ˇcas (pouze ˇcas, kdy proces bˇeˇz´ı), pos´ıl´ a SIGVTALRM aln´ı ˇcas a ˇcas, kdy syst´em bˇeˇz´ı na konto – ITIMER PROF . . . mˇeˇr´ı virtu´ procesu, pos´ıl´ a SIGPROF • pozn: v pˇr´ıkladu je drobn´ y probl´em: funkce fprintf() nen´ı bezpeˇcn´ a pro pouˇzit´ı v handleru sign´alu.
133
Blokov´ an´ı sign´ al˚ u • blokovan´e sign´aly budou procesu doruˇceny a zpracov´any aˇz po odblokov´an´ı. int sigprocmask(int how, const sigset t *set, sigset t *oset ); • nastav´ı masku blokovan´ ych sign´al˚ u a vr´ at´ı starou masku. an´ı sign´al˚ u co se maj´ı blokovat, pro • how – SIG BLOCK pro pˇrid´ odebr´ an´ı SIG UNBLOCK, pro kompletn´ı zmˇenu masky SIG SETMASK • pro manipulaci s maskou sign´al˚ u slouˇz´ı funkce: sigaddset(), sigdelset(), sigemptyset(), sigfillset(), sigismember() int sigpending(sigset t *set ); • vr´ at´ı ˇcekaj´ıc´ı zablokovan´e sign´aly.
• je rozd´ıl mezi ignorov´an´ım a blokov´an´ım sign´alu. Ignorovan´ y sign´al j´adro zahod´ı a proces ho nedostane, blokovan´ y sign´al proces dostane po jeho odblokov´an´ı. • z´ avis´ı na implementaci, zda pˇri v´ıcen´ asobn´em doruˇcen´ı stejn´eho sign´alu procesu, kter´ y m´a tento sign´al zablokovan´ y, bude sign´al po odblokov´an´ı oˇsetˇren jednou nebo v´ıcekr´at.
134
Pˇ r´ıklad: blokov´ an´ı sign´ al˚ u sigset t sigs, osigs; structure sigaction sa; sigfillset(&sigs); sigprocmask(SIG BLOCK, &sigs, &osigs); switch(cpid = fork()) { case -1: /* Chyba */ sigprocmask(SIG SETMASK, &osigs, NULL); ... case 0: /* Synovsk´ y proces */ sa.sa handler = h cld; sigemptyset(&sa.sa mask); sa.sa flags = 0; sigaction(SIGINT, &sa, NULL); sigprocmask(SIG SETMASK, &osigs, NULL); ... default: /* Rodiˇ covsk´ y proces */ sigprocmask(SIG SETMASK, &osigs, NULL); ... }
• blokov´an´ı je vhodn´e pouˇz´ıt tam, kde oˇsetˇren´ı pˇreruˇsen´ı uprostˇred posloupnosti operac´ı by bylo pˇr´ıliˇs sloˇzit´e, nebo kde korektn´ı oˇsetˇren´ı nen´ı jinak moˇzn´e. • pˇr.: proces vytv´aˇr´ı potomky pomoc´ı fork() a je potˇreba, aby potomci mˇeli jin´ y handler sign´al˚ u neˇz rodiˇcovsk´ y proces. • v uveden´em pˇr´ıkladˇe by bez blokov´an´ı sign´al˚ u mohl synovsk´ y proces dostat sign´al dˇr´ıv, neˇz stihne zmˇenit handler. • dalˇs´ı pˇr´ıklad je proces, kter´ y pˇri vyprˇsen´ı timeoutu pˇreruˇs´ı prov´adˇenou posloupnost operac´ı vol´ an´ım siglongjmp() zevnitˇr handleru sign´alu. Je potˇreba zablokovat sign´al SIGALRM bˇehem prov´adˇen´ı atomick´ ych podposloupnost´ı (tj. takov´ ych, kter´e se mus´ı prov´est bud’ cel´e, nebo v˚ ubec ne).
135
ˇ an´ı na sign´ Cek´ al int pause(void); • pozastav´ı volaj´ıc´ı proces do pˇr´ıchodu sign´alu. Vol´ an´ı se vr´ at´ı po n´ avratu z handleru. int sigsuspend(const sigset t *sigmask ); • jako pause(), ale nav´ıc po dobu ˇcek´ an´ı masku blokovan´ ych sign´al˚ u zmˇen´ı na sigmask. int sigwait(const sigset t *set, int *sig ); • ˇcek´ a na pˇr´ıchod sign´alu z mnoˇziny set (tyto sign´aly mus´ı b´ yt pˇredt´ım zablokovan´e), ˇc´ıslo sign´alu vr´ at´ı v sig. • nevol´ a se handler sign´alu (to ale nen´ı v normˇe jednoznaˇcnˇe definov´ano).
• pomoc´ı tˇechto funkc´ı a blokov´an´ı sign´al˚ u se implementuje synchronn´ı obsluha sign´al˚ u. Proces nejprve zablokuje sign´aly, kter´e ho zaj´ımaj´ı, a pak na nˇe ve vhodn´ ych chv´ıl´ıch bud’ ˇcek´ a, nebo jen testuje (pomoc´ı sigpending()), zda sign´al pˇriˇsel, a pokud ne, pokraˇcuje d´ al.
136
Obsah • u ´ vod, v´ yvoj UNIXu a C, program´ atorsk´e n´ astroje • z´ akladn´ı pojmy a konvence UNIXu a jeho API • pˇr´ıstupov´a pr´ava, perifern´ı zaˇr´ızen´ı, syst´em soubor˚ u • manipulace s procesy, spouˇstˇen´ı program˚ u • sign´aly • synchronizace a komunikace proces˚ u • s´ıt’ov´a komunikace • vl´ akna, synchronizace vl´ aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu
Probl´ em: konflikt pˇ ri sd´ılen´ı dat • m´ame strukturu struct { int a, b; } shared ; • for( ; ; ) { /* neatomick´ a operace */ a = shared.a; b = shared.b; if (a != b) printf("NEKONZISTENTN´ I STAV"); /* neatomick´ a operace */ shared.a = val; shared.b = val; } • jestliˇze tento cyklus spust´ıme ve dvou r˚ uzn´ ych procesech (nebo vl´ aknech), kter´e obˇe sd´ılej´ı stejnou strukturu shared a maj´ı r˚ uzn´e hodnoty val, bude doch´ azet ke konflikt˚ um. • pˇr´ıˇcina: operace na zv´ yraznˇen´ ych ˇra´dc´ıch nejsou atomick´e.
137
• ani operace, kterou lze v C zapsat jedn´ım pˇr´ıkazem, nemus´ı b´ yt atomick´a. Pˇr.: na RISCov´ ych procesorech se pˇr´ıkaz a++ typicky pˇreloˇz´ı jako sekvence: load reg,[a] inc reg store [a],reg a to z toho d˚ uvodu, ˇze na t´eto architektuˇre nelze inkrementovat ˇc´ıslo pˇr´ımo v pamˇeti. Pro tyto pˇr´ıpady m´a napˇr´ıklad Solaris sadu funkc´ı atomic add(3c), jejichˇz pouˇzit´ı je mnohem rychlejˇs´ı neˇz klasick´e zamykac´ı mechanismy. V´ıce viz strana 201. • obecnˇe obdobn´ y probl´em nast´ av´ a, kdyˇz v´ıce proces˚ u sd´ıl´ı nˇejak´ y syst´emov´ y zdroj.
Sc´ en´ aˇ r konfliktu Procesy A(val==1) a B(val==2)
a
b
1.
poˇca´teˇcn´ı stav struktury
?
?
2.
proces A zap´ıˇse do poloˇzky a
1
?
3.
proces B zap´ıˇse do poloˇzky a
2
?
4.
proces B zap´ıˇse do poloˇzky b
2
2
5.
proces A zap´ıˇse do poloˇzky b
2
1
6.
struktura je v nekonzistentn´ım stavu a jeden z proces˚ u to zjist´ı.
• dalˇs´ı moˇznost: 1. struktura je v konzistentn´ım stavu, napˇr. (1, 1) 2. proces B zap´ıˇse 2 do poloˇzky a 3. proces A pˇreˇcte hodnotu struktury (2, 1) dˇr´ıve, neˇz proces B stihne zapsat poloˇzku b
138
ˇ sen´ı: vz´ Reˇ ajemn´ e vylouˇ cen´ı proces˚ u • je potˇreba zajistit atomicitu operac´ı nad strukturou, tzn. jeden proces prov´ad´ı modifikaci a dokud neuvede strukturu do konzistentn´ıho stavu, druh´ y proces s n´ı nem˚ uˇze manipulovat. Procesy A(val==1) a B(val==2)
a
b
poˇca´teˇcn´ı stav struktury
?
?
proces A zap´ıˇse do poloˇzky a
1
?
proces B mus´ı ˇcekat
1
?
proces A zap´ıˇse do poloˇzky b
1
1
proces B zap´ıˇse do poloˇzky a
2
1
6.
proces B zap´ıˇse do poloˇzky b
2
2
7.
Struktura je v konzistentn´ım stavu.
1. 2. 3. 4. 5.
• je tˇreba zajistit vz´ ajemn´e vylouˇcen´ı i pˇri ˇcten´ı, aby ˇctouc´ı proces nepˇreˇcetl nekonzistentn´ı obsah struktury uprostˇred zmˇen. Pˇri z´ apisu je nutn´e vylouˇcit vˇsechny ostatn´ı procesy, ale pˇri ˇcten´ı staˇc´ı vylouˇcit jen z´ apis, souˇcasn´e ˇcten´ı v´ıce procesy nevad´ı. • kritick´ a sekce – kus k´odu, kter´ y by mˇel prov´adˇet pouze jeden proces (vl´akno), jinak m˚ uˇze doj´ıt k nekonzistenc´ım (ˇspatnˇe pospojovan´ y v´azan´ y seznam, neodpov´ıdaj´ıc´ı si indexy v datab´ azi, . . . ). Kritick´a sekce by mˇela b´ yt co nejkratˇs´ı, aby ostatn´ı procesy (vl´akna) ˇza´daj´ıc´ı o vstup do t´eto sekce ˇcekaly co nejkratˇs´ı moˇznou dobu.
139
Probl´ em: konflikt zapisovatel˚ uaˇ cten´ aˇ r˚ u • nˇekolik bˇeˇz´ıc´ıch proces˚ u zapisuje protokol o sv´e ˇcinnosti do spoleˇcn´eho log-souboru. Nov´ y z´ aznam je pˇripojen vˇzdy na konec souboru. • pokud z´ apis z´ aznamu nen´ı proveden atomickou operac´ı, m˚ uˇze doj´ıt k prom´ıch´ an´ı v´ıce souˇcasnˇe zapisovan´ ych z´ aznam˚ u. • zapisovat sm´ı vˇzdy pouze jeden proces. • dalˇs´ı procesy ˇctou data z log-souboru. • pˇri pˇreˇcten´ı pr´avˇe zapisovan´eho z´ aznamu obdrˇz´ıme nespr´avn´ a (ne´ upln´ a) data. • bˇehem operace z´ apisu ze souboru nelze ˇc´ıst. Kdyˇz nikdo nezapisuje, m˚ uˇze v´ıce proces˚ u ˇc´ıst souˇcasnˇe.
na lok´aln´ım disku lze pro synchronizaci zapisovatel˚ u pouˇz´ıt ˇreˇsen´ı pomoc´ı pˇr´ıznaku O APPEND, kter´e ale nemus´ı fungovat napˇr. na vzd´ alen´em disku pˇr´ıstupn´em pˇres NFS nebo v pˇr´ıpadˇe, ˇze z´ apis jedn´e logovac´ı zpr´ avy je proveden v´ıce neˇz jedn´ım vol´an´ım write(). Nav´ıc to neˇreˇs´ı synchronizaci ˇcten´ aˇr˚ u – lze ˇc´ıst i v pr˚ ubˇehu z´ apisu.
140
ˇ sen´ı: zamyk´ Reˇ an´ı soubor˚ u • zapisuj´ıc´ı proces zamkne soubor pro z´ apis. Ostatn´ı procesy (zapisovatel´e i ˇcten´ aˇri) se souborem nemohou pracovat a mus´ı ˇcekat na odemˇcen´ı z´ amku. • ˇctouc´ı proces zamkne soubor pro ˇcten´ı. Zapisovatel´e mus´ı ˇcekat na odemˇcen´ı z´ amku, ale ostatn´ı ˇcten´ aˇri mohou tak´e zamknout soubor pro ˇcten´ı a ˇc´ıst data. • v jednom okamˇziku m˚ uˇze b´ yt na souboru aktivn´ı nejv´ yˇse jeden z´ amek pro z´ apis nebo libovolnˇe mnoho z´ amk˚ u pro ˇcten´ı, ale ne oba typy z´ amk˚ u souˇcasnˇe. • z d˚ uvodu efektivity by kaˇzd´ y proces mˇel drˇzet z´ amek co nejkratˇs´ı dobu a pokud moˇzno nezamykat cel´ y soubor, ale jen u ´ sek, se kter´ ym pracuje. Preferovan´e je pasivn´ı ˇcek´ an´ı, aktivn´ı ˇcek´ an´ı je vhodn´e jen na velmi kr´ atkou dobu.
dva zp˚ usoby ˇcek´ an´ı: aktivn´ı (busy waiting) – proces v cyklu testuje podm´ınku, na kterou ˇcek´ a, dokud nen´ı splnˇena pasivn´ı – proces se zaregistruje v j´ adru jako ˇcekaj´ıc´ı na podm´ınku a pak se usp´ı, j´adro ho probud´ı, kdyˇz dojde ke splnˇen´ı podm´ınky
141
Synchronizaˇ cn´ı mechanismy • teoretick´e ˇreˇsen´ı – algoritmy vz´ ajemn´eho vylouˇcen´ı (Dekker 1965, Peterson 1981) • z´ akaz pˇreruˇsen´ı (na 1 CPU stroji), speci´ aln´ı instrukce (test-and-set ) • lock-soubory • n´ astroje poskytovan´e operaˇcn´ım syst´emem – semafory (souˇca´st System V IPC) – z´ amky pro soubory (fcntl(), flock()) – synchronizace vl´ aken: mutexy (ohraniˇcuj´ı kritick´e sekce, pouze jedno vl´ akno m˚ uˇze drˇzet mutex), podm´ınkov´ e promˇ enn´ e (zablokuj´ı vl´ akno, dokud jin´e vl´ akno nesignalizuje splnˇen´ı podm´ınky), read-write z´ amky (sd´ılen´e a exkluzivn´ı z´ amky, podobnˇe jako pro soubory)
• obˇe ˇreˇsen´ı potˇrebovala k dosaˇzen´ı poˇzadavan´eho v´ ysledku pouze sd´ılenou pamˇ et’, tj. nˇekolik promˇenn´ ych sd´ılen´ ych obˇema procesy • Dekkerovo ˇreˇsen´ı se ud´ av´ a jako prvn´ı ˇreˇsen´ı probl´emu vz´ ajemn´eho vylouˇcen´ı dvou proces˚ u, aniˇz by bylo nutn´e aplikovat naivn´ı algoritmus striktn´ıho stˇr´ıd´ an´ı, tj. pokud druh´ y proces nevykazoval z´ ajem o vstup do kritick´e sekce, mohl tam prvn´ı (a naopak) proces vstoupit tolikr´ at za sebou, kolikr´at chtˇel. Dekkerovo ˇreˇsen´ı nen´ı v˚ ubec trivi´aln´ı, porovnejte s o 16 let mladˇs´ım ˇreˇsen´ım Petersonov´ ym, napˇr´ıklad na en.wikipedia.org (hledejte “Dekker’s algorithm”, “Peterson’s algorithm”) • my se nebudeme zab´ yvat teoretick´ ymi algoritmy vz´ ajemn´eho vylouˇcen´ı ani nebudeme popisovat hardwarov´e mechanismy pouˇz´ıvan´e j´adrem. Zamˇeˇr´ıme se pouze na pouˇzit´ı lock soubor˚ u (kter´e vyuˇz´ıvaj´ı atomiˇcnosti nˇekter´ ych souborov´ ych operac´ı) a speci´ aln´ıch synchronizaˇcn´ıch n´ astroj˚ u poskytovan´ ych j´adrem. • podm´ınkov´e promˇenn´e = conditional variables
142
Lock-soubory • pro kaˇzd´ y sd´ılen´ y zdroj existuje dohodnut´e jm´eno souboru. Zamˇcen´ı zdroje se provede vytvoˇren´ım souboru, odemˇcen´ı smaz´ an´ım souboru. Kaˇzd´ y proces mus´ı otestovat, zda soubor existuje, a pokud ano, tak poˇckat. void lock(char *lockfile) { while( (fd = open(lockfile, O RDWR|O CREAT|O EXCL, 0600)) == -1) ˇ ame ve smyˇcce na odemˇcen´ı */ ; /* Cek´ close(fd); } void unlock(char *lockfile) { unlink(lockfile); }
• kl´ıˇcem k u ´ spˇechu je samozˇrejmˇe pouˇzit´ı flagu O EXCL • pˇri hav´arii procesu nedojde ke zruˇsen´ı pˇr´ıpadn´ ych z´ amk˚ u a ostatn´ı procesy by ˇcekaly vˇeˇcnˇe. Proto je vhodn´e si do lock-souboru poznamenat PID procesu, kter´ y z´ amek vytvoˇril. Proces, kter´ y ˇcek´ a na odemˇcen´ı, m˚ uˇze testovat, zda proces, kter´emu z´ amek patˇr´ı, st´ale bˇeˇz´ı. Kdyˇz ne, lze z´ amek zruˇsit a znovu zkusit vytvoˇrit. User level pˇr´ıkaz kter´ y toto um´ı a dovoluje pouˇz´ıvat lock soubory z shellov´ ych skript˚ u je napˇr´ıklad shlock(1) (na FreeBSD v /usr/ports/sysutils/shlock), teoreticky by vˇsak mohl zp˚ usobit situaci z n´ asleduj´ıc´ıho odstavce. • POZOR: jestliˇze v´ıce proces˚ u najednou zjist´ı, ˇze proces drˇz´ıc´ı z´ amek uˇz neexistuje, m˚ uˇze doj´ıt k n´ asleduj´ıc´ı chybˇe: Jeden proces smaˇze z´ amek a znovu ho vytvoˇr´ı se sv´ ym PID. Dalˇs´ı proces udˇel´a tot´eˇz, protoˇze operace pˇreˇcten´ı obsahu souboru a jeho n´ asledn´eho smaz´ an´ı nen´ı atomick´a. Ted’ si ale oba procesy mysl´ı, ˇze z´ıskaly z´ amek! • probl´em: funkce lock() obsahuje aktivn´ı ˇcek´ an´ı na uvolnˇen´ı z´ amku. Lze ˇreˇsit napˇr. tak, ˇze proces, kter´ y z´ısk´ a z´ amek, otevˇre pro z´ apis pojmenovanou rouru. ˇ Cekaj´ ıc´ı procesy se usp´ı t´ım, ˇze zkus´ı ˇc´ıst z roury. Souˇca´st´ı unlock() bude i zavˇren´ı roury a t´ım uvolnˇen´ı ˇcekaj´ıc´ıch proces˚ u.
143
Zamyk´ an´ı soubor˚ u: fcntl() int fcntl(int fildes, int cmd, ...); • k nastaven´ı z´ amk˚ u pro soubor fildes se pouˇz´ıv´ a cmd: amku z tˇret´ıho argumentu a – F GETLK . . . vezme popis z´ nahrad´ı ho popisem existuj´ıc´ıho z´ amku, kter´ y s n´ım koliduje – F SETLK . . . nastav´ı nebo zruˇs´ı z´ amek popsan´ y tˇret´ım argumentem, pokud nelze z´ amek nastavit, ihned vrac´ı −1 – F SETLKW . . . jako F SETLK, ale usp´ı proces, dokud nen´ı moˇzn´e nastavit z´ amek • tˇret´ı argument obsahuje popis z´ amku a je typu struct flock *
• zamyk´ an´ı soubor˚ u sd´ılen´ ych pˇres NFS zajiˇst’uje d´emon lockd. • z´ amky jsou obecnˇe dvou typ˚ u: advisory locks – pro spr´ avnou funkci mus´ı vˇsechny procesy pracuj´ıc´ı se z´ amˇcen´ ymi soubory kontrolovat z´ amky pˇred ˇcten´ım nebo z´ apisem souboru; jsou v´ıce pouˇz´ıvan´e mandatory locks – jestliˇze je na souboru z´ amek, budou ˇctec´ı a z´ apisov´e operace se souborem automaticky zablokov´any, tj. z´ amek se uplatn´ı i v procesech, kter´e ho explicitnˇe nekontroluj´ı – nedoporuˇcuj´ı se, ne vˇzdy funguj´ı (napˇr. lockd implementuje pouze advisory locking) – pro urˇcit´ y soubor se zapne nastaven´ım bitu SGID a zruˇsen´ım pr´ava spuˇstˇen´ı pro skupinu (tj. nastaven´ı, kter´e jinak nem´ a smysl - m´ıt set UID executable bit na souboru, kter´ y nen´ı spustiteln´ y). Syst´em, kter´ y mandatory locking implementuje, je napˇr´ıklad Solaris nebo Linux, FreeBSD tuto vlastnost naopak nepodporuje.
144
Zamyk´ an´ı soubor˚ u: struct flock • l type . . . typ z´ amku y z´ amek (pro ˇcten´ı), v´ıce proces˚ u – F RDLCK . . . sd´ılen´ – F WRLCK . . . exkluzivn´ı z´ amek (pro z´ apis), jeden proces – F UNLCK . . . odemˇcen´ı • l whence . . . jako u lseek(), tj. SEEK SET, SEEK CUR, SEEK END ´ seku vzhledem k l whence • l start . . . zaˇca´tek zamykan´eho u • l len . . . d´elka u ´ seku, 0 znamen´ a do konce souboru amek, pouˇz´ıv´ a se jen pro • l pid . . . PID procesu drˇz´ıc´ıho z´ avratu F GETLK pˇri n´
• soubory se daj´ı zamykat po ˇca´stech a d´ a se zjistit, kter´ y proces drˇz´ı z´ amek. Pˇri ukonˇcen´ı procesu se automaticky uvoln´ı vˇsechny jeho z´ amky. • pokud pˇri pouˇzit´ı F GETLK nen´ı pˇr´ısluˇsn´a ˇca´st souboru zamˇcen´a, je struktura flock vr´ acena bez zmˇeny kromˇe prvn´ı poloˇzky, kter´ a je nastavena na F UNLCK. • zamyk´ an´ı pˇres fcntl i lockf m´a jednu d˚ uleˇzitou vlastnost, kterou v´ ystiˇznˇe popisuje napˇr´ıklad manu´ alov´a str´ anka pro fcntl v syst´emu FreeBSD: This interface follows the completely stupid semantics of System V and IEEE Std 1003.1-1988 (“POSIX.1”) that require that all locks associated with a file for a given process are removed when any file descriptor for that file is closed by that process. This semantic means that applications must be aware of any files that a subroutine library may access. For example if an application for updating the password file locks the password file database while making the update, and then calls getpwnam(3) to retrieve a record, the lock will be lost because getpwnam(3) opens, reads, and closes the password database.
145
Deadlock • m´ame dva sd´ılen´e zdroje res1 a res2 chr´anˇen´e z´ amky lck1 a lck2. Procesy p1 a p2 chtˇej´ı kaˇzd´ y v´ yluˇcn´ y pˇr´ıstup k obˇema zdroj˚ um. p2
p1 lock(lck1); /* OK */ ˇ a na p2 */ lock(lck2); /* Cek´ use(res1, res2); unlock(lck2); unlock(lck1);
lock(lck2); /* OK */
ˇ a na p1 */ lock(lck1); /* Cek´ Deadlock use(res1, res2); unlock(lck2); unlock(lck1);
• pozor na poˇ rad´ı zamyk´ an´ı!
• obecnˇe deadlock vznikne, jestliˇze proces ˇcek´ a na ud´ alost, kter´ a nem˚ uˇze nastat. Zde napˇr. na sebe dva procesy ˇcekaj´ı navz´ ajem, aˇz ten druh´ y uvoln´ı z´ amek, ale k tomu nikdy nedojde. Dalˇs´ı moˇznost´ı je deadlock jednoho procesu, kter´ y ˇcte z roury a pˇredt´ım zapomnˇel uzavˇr´ıt z´ apisov´ y konec roury. Jestliˇze rouru nem´ a uˇz nikdo dalˇs´ı otevˇrenou, pokus o ˇcten´ı ze zablokuje, protoˇze nejsou zavˇreny vˇsechny kopie z´ apisov´eho deskriptoru a tedy nenastane konec souboru na rouˇre, ale ˇctouc´ı proces sv˚ uj z´ apisov´ y deskriptor nem˚ uˇze zavˇr´ıt, protoˇze ˇcek´ a. • fcntl() prov´ad´ı kontrolu a pˇri v´ yskytu deadlocku vr´ at´ı chybu EDEADLK. • je vhodn´e se deadlocku snaˇzit vyvarovat spr´ avn´ ym naprogramov´an´ım a nespol´ehat se na kontrolu syst´emu.
146
System V IPC • IPC je zkratka pro Inter-Process Communication • komunikace mezi procesy v r´ amci jednoho syst´emu, tj. ’ nezahrnuje s´ıt ovou komunikaci • semafory . . . pouˇzit´ı pro synchronizaci proces˚ u • sd´ılen´ a pamˇ et’ . . . pˇred´ av´ an´ı dat mezi procesy, pˇrin´ aˇs´ı podobn´e probl´emy jako sd´ılen´ı soubor˚ u, k ˇreˇsen´ı lze pouˇz´ıt semafory • fronty zpr´ av . . . spojuj´ı komunikaci (zpr´ ava nese data) se synchronizac´ı (ˇcek´ an´ı procesu na pˇr´ıchod zpr´ avy) • prostˇredky IPC maj´ı podobnˇe jako soubory definovan´ a pˇr´ıstupov´a pr´ava (pro ˇcten´ı a z´ apis) pro vlastn´ıka, skupinu a ostatn´ı.
• prostˇredky IPC existuj´ı i pot´e, kdy skonˇc´ı proces, kter´ y je vytvoˇril. O jejich zruˇsen´ı je nutno explicitnˇe poˇza´dat (ze shellu lze zjistit seznam IPC prostˇredk˚ u pˇr´ıkazem ipcs a smazat je pˇr´ıkazem ipcrm). Stav a obsah existuj´ıc´ıch prostˇredk˚ u IPC z˚ ust´ av´ a v platnosti, i kdyˇz s nimi pr´avˇe nepracuje ˇza´dn´ y proces (napˇr. data ve sd´ılen´e pamˇeti z˚ ust´ avaj´ı, i kdyˇz ji nem´ a ˇza´dn´ y proces pˇripojenou). • dalˇ s´ı dva prostˇ redky System V IPC, kter´ e se bˇ eˇ znˇ e v unixov´ ych syst´ emech vyskytuj´ı a jsou souˇ c´ ast´ı normy, tj. sd´ılen´ a pamˇ et’ a zas´ıl´ an´ı zpr´ av, nebudeme prob´ırat. Pro sd´ılenou pamˇ et’ je moˇ zn´ e pouˇ z´ıt jiˇ z probran´ e vol´ an´ı mmap, m´ısto zas´ıl´ an´ı zpr´ av je moˇ zn´ e pouˇ z´ıt sockety (budou v nˇ ekter´ e z pˇ r´ıˇ st´ıch pˇ redn´ aˇ sek).
147
Semafory • zavedl je E. Dijkstra • semafor s je datov´a struktura obsahuj´ıc´ı – cel´e nez´ aporn´e ˇc´ıslo i (voln´ a kapacita) – frontu proces˚ u q, kter´e ˇcekaj´ı na uvolnˇen´ı • operace nad semaforem: init(s, n) vypr´ azdnit s.q; s.i = n P(s) if(s.i > 0) s.i-- else uspat volaj´ ıc´ ı proces a zaˇ radit do s.q V(s) if(s.q pr´ azdn´ a) s.i++ else odstranit jeden proces z s.q a probudit ho
• P je z holandsk´eho ,,proberen te verlagen” – zkus dekrementovat, V pak ze slova ,,verhogen” – inkrementovat. • operace P(s) a V(s) lze zobecnit: hodnotu semaforu je moˇzn´e mˇenit o libovolnou hodnotu n . . . P(s, n), V(s, n). • Allen B. Downey: The Little Book of Semaphores, Second Edition, on-line na http://greenteapress.com/semaphores/ • bin´ arn´ı semafor m´a pouze hodnotu 0 nebo 1
148
Vz´ ajemn´ e vylouˇ cen´ı pomoc´ı semafor˚ u • jeden proces inicializuje semafor sem s; init(s, 1); • kritick´a sekce se dopln´ı o operace nad semaforem ... P(s); kritick´ a sekce; V(s); ...
inicializace semaforu na hodnotu n dovol´ı vstoupit do kritick´e sekce n proces˚ um. Zde semafor funguje jako z´ amek, vˇzdy ho odemyk´ a (zvyˇsuje hodnotu) stejn´ y proces, kter´ y ho zamknul (sn´ıˇzil hodnotu).
149
API pro semafory int semget(key t key, int nsems, int semflg ); • vr´ at´ı identifik´ ator pole obsahuj´ıc´ıho nsems semafor˚ u asociovan´ y s kl´ıˇcem key (kl´ıˇc IPC PRIVATE . . . priv´ atn´ı semafory, pˇri kaˇzd´em pouˇzit´ı vr´ at´ı jin´ y identifik´ator). semflg je OR-kombinace pˇr´ıstupov´ ych pr´av a konstant IPC CREAT (vytvoˇrit, pokud neexistuje), IPC EXCL (chyba, pokud existuje). int semctl(int semid, int semnum, int cmd, ...); • ˇr´ıdic´ı funkce, voliteln´ y ˇctvrt´ y parametr arg je typu union semun. int semop(int semid, struct sembuf *sops, size t nsops ); • zobecnˇen´e operace P a V.
• nejvˇetˇs´ı zaj´ımavost na System V implementaci semafor˚ u je skuteˇcnost, ˇze dan´ y syscall neoperuje nad jedn´ım semaforem, ale nad polem semafor˚ u, atomicky. Vˇetˇsinou vˇsak budete potˇrebovat pouze jeden semafor, tj. pole o jednom prvku. • dalˇs´ı d˚ uleˇzit´ a informace je, ˇze System V semafory jsou bohuˇzel znaˇcnˇe sloˇzit´e • pˇr´ıstupov´a pr´ava jsou jen pro ˇcten´ı a z´ apis. • podobn´e sch´ema API funkc´ı (funkce na vytvoˇren´ı, ˇr´ızen´ı a operace) dodrˇzuj´ı i ostatn´ı System V IPC mechanismy. • jakmile je jednou pole semafor˚ u jedn´ım procesem vytvoˇreno, mohou i ostatn´ı procesy pouˇz´ıt semctl() a semop(), aniˇz by pˇredt´ım volaly semget(). To plat´ı i pro semafory vytvoˇren´e s kl´ıˇcem IPC PRIVATE, pro kter´e nelze volat semget(), protoˇze by se t´ım vytvoˇrilo nov´e pole semafor˚ u.
150
API pro semafory: semctl() • semnum . . . ˇc´ıslo semaforu v poli • moˇzn´e hodnoty cmd: – GETVAL . . . vr´ at´ı hodnotu semaforu – SETVAL . . . nastav´ı semafor na hodnotu arg.val – GETPID . . . PID procesu, kter´ y provedl posledn´ı operaci – GETNCNT . . . poˇcet proces˚ u ˇcekaj´ıc´ıch na vˇetˇs´ı hodnotu – GETZCNT . . . poˇcet proces˚ u ˇcekaj´ıc´ıch na nulu – GETALL . . . uloˇz´ı hodnoty vˇsech semafor˚ u do pole arg.array – SETALL . . . nastav´ı vˇsechny semafory podle arg.array a poˇcet semafor˚ u, pˇr´ıstupov´a – IPC STAT . . . do arg.buf d´ pr´ava a ˇcasy posledn´ıch semctl() a semop() – IPC SET . . . nastav´ı pˇr´ıstupov´a pr´ava u – IPC RMID . . . zruˇs´ı pole semafor˚
vol´an´ı semctl(semid, semnum, SETVAL, arg) odpov´ıd´a obecn´e semaforov´e inicializaˇcn´ı operaci init(s, n).
151
API pro semafory: semop() • operace se prov´ad´ı atomicky (tj. bud’ se povede pro vˇsechny semafory, nebo pro ˇza´dn´ y) na nsops semaforech podle pole sops struktur struct sembuf, kter´ a obsahuje: – sem num . . . ˇc´ıslo semaforu – sem op . . . operace ∗ P(sem num, abs(sem op)) pro sem op < 0 ∗ V(sem num, sem op) pro sem op > 0 ∗ ˇcek´ an´ı na nulovou hodnotu semaforu pro sem op == 0 – sem flg . . . OR-kombinace ∗ IPC NOWAIT . . . kdyˇz nelze operaci hned prov´est, neˇcek´ aa vr´ at´ı chybu atit operace se ∗ SEM UNDO . . . pˇri ukonˇcen´ı procesu vr´ semaforem
• atomiˇcnost zajist´ı, ˇze nedojde k deadlocku v n´ asleduj´ıc´ı situaci: dva procesy A a B budou pouˇz´ıvat dva semafory k ˇr´ızen´ı pˇr´ıstupu (zamyk´ an´ı) ke dvˇema syst´emov´ ym zdroj˚ um. Proces A je bude zamykat v poˇrad´ı (0, 1) a proces B v poˇrad´ı (1, 0). Ve chv´ıli, kdy proces A zamkne semafor 0 a B zamkne 1, dojde k deadlocku, protoˇze ani jeden proces nem˚ uˇze pokraˇcovat (potˇreboval by zamknout druh´ y semafor). Pˇri pouˇzit´ı atomick´e operace zamˇcen´ı obou semafor˚ u najednou bude u ´ spˇeˇsn´ y vˇzdy pr´avˇe jeden proces, kter´ y z´ısk´a oba semafory, druh´ y bude ˇcekat. u (pou• SEM UNDO zajist´ı, ˇze pˇri ukonˇcen´ı procesu dojde k odemˇcen´ı semafor˚ ˇzit´ ych jako z´ amky), kter´e tento proces mˇel zamˇcen´e.
152
Vytv´ aˇ ren´ı prostˇ redk˚ u IPC • jeden proces prostˇredek vytvoˇr´ı, ostatn´ı se k nˇemu pˇripoj´ı. • po skonˇcen´ı pouˇz´ıv´ an´ı je tˇreba prostˇredek IPC zruˇsit. • funkce semget(), shmget() a msgget() maj´ı jako prvn´ı parametr kl´ıˇc identifikuj´ıc´ı prostˇredek IPC. Skupina proces˚ u, kter´ a chce komunikovat, se mus´ı domluvit na spoleˇcn´em kl´ıˇci. R˚ uzn´e skupiny komunikuj´ıc´ıch proces˚ u by mˇely m´ıt r˚ uzn´e kl´ıˇce. key t ftok(const char *path, int id ); • vr´ at´ı kl´ıˇc odvozen´ y ze zadan´eho jm´ena souboru path a ˇc´ısla id. Pro stejn´e id a libovolnou cestu odkazuj´ıc´ı na stejn´ y soubor vr´ at´ı stejn´ y kl´ıˇc. Pro r˚ uzn´a id nebo r˚ uzn´e soubory na stejn´em svazku vr´ at´ı r˚ uzn´e kl´ıˇce.
Pozn´ amky k ftok(): • Z id se pouˇzije jen nejniˇzˇs´ıch 8 bit˚ u. • Nen´ı specifikov´ano, zda bude stejn´ y kl´ıˇc vr´ acen i po smaz´ an´ı a znovuvytvoˇren´ı souboru. • R˚ uzn´e kl´ıˇce pro r˚ uzn´e soubory nejsou vˇzdy zaruˇcen´e. Napˇr. na Linuxu se kl´ıˇc z´ısk´ a kombinac´ı 16 bit˚ u ˇc´ısla i-uzlu, 8 bit˚ u id a 8 bit˚ u vedlejˇs´ıho ˇc´ısla zaˇr´ızen´ı. Stejn´ y kl´ıˇc pro r˚ uzn´e soubory je vr´ acen, pokud se ˇc´ısla i-uzl˚ u shoduj´ı na spodn´ıch 16 bitech.
153
Dalˇ s´ı prostˇ redky IPC • POSIX a UNIX98 definuj´ı jeˇstˇe dalˇs´ı prostˇredky komunikace mezi procesy: – sign´ aly . . . pro uˇzivatelsk´e u ´ˇcely lze vyuˇz´ıt sign´aly SIGUSR1 a SIGUSR2 – POSIXov´ a sd´ılen´ a pamˇ et’ pˇr´ıstupn´ a pomoc´ı shm open() a mmap() – POSIXov´ e semafory . . . sem open(), sem post(), sem wait(), . . . – POSIXov´ e fronty zpr´ av . . . mq open(), mq send(), mq receive(), . . . • Z BSD poch´ az´ı sokety (sockets) umoˇzn ˇ uj´ıc´ı komunikaci v dom´en´ ach AF UNIX (komunikace v r´ amci jednoho poˇc´ıtaˇce) a AF INET (komunikace na jednom poˇc´ıtaˇci nebo po s´ıti).
• POSIXov´e IPC pouˇz´ıv´ a pro pojmenov´an´ı jednotliv´ ych IPC objekt˚ u ˇretˇezce m´ısto numerick´ ych identifik´ ator˚ u, proto do znaˇcn´e m´ıry odpadaj´ı probl´emy s identifikac´ı zn´ am´e ze System V IPC (kde se ˇreˇs´ı napˇr. funkc´ı ftok()). • sokety se z BSD rozˇs´ıˇrily i do ostatn´ıch UNIXov´ ych syst´em˚ u a dostaly se i do normy UNIX 98.
154
Obsah • u ´ vod, v´ yvoj UNIXu a C, program´ atorsk´e n´ astroje • z´ akladn´ı pojmy a konvence UNIXu a jeho API • pˇr´ıstupov´a pr´ava, perifern´ı zaˇr´ızen´ı, syst´em soubor˚ u • manipulace s procesy, spouˇstˇen´ı program˚ u • sign´aly • synchronizace a komunikace proces˚ u • s´ıt’ov´ a komunikace • vl´ akna, synchronizace vl´ aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu
S´ıt’ov´ a komunikace UUCP (UNIX-to-UNIX Copy Program) – prvn´ı aplikace pro komunikaci UNIXov´ ych syst´em˚ u propojen´ ych pˇr´ımo nebo pˇres modemy, souˇca´st Version 7 UNIX (1978) sokety (sockets) – zavedeny ve 4.1aBSD (1982); soket je jeden konec obousmˇern´eho komunikaˇcn´ıho kan´ alu vytvoˇren´eho mezi dvˇema procesy bud’ lok´alnˇe na jednom poˇc´ıtaˇci, nebo s vyuˇzit´ım s´ıt’ov´eho spojen´ı TLI (Transport Layer Interface) – SVR3 (1987); knihovna zajiˇst’uj´ıc´ı s´ıt’ovou komunikaci na u ´ rovni 4. vrstvy referenˇcn´ıho modelu ISO OSI RPC (Remote Procedure Call) – SunOS (1984); protokol pro pˇr´ıstup ke sluˇzb´ am na vzd´ alen´em stroji, data pˇren´ aˇsena ve tvaru XDR (External Data Representation)
155
• ISO (International Standards Organization) OSI (Open Systems Interconnect) – vrstvy (layers): 1. 2. 3. 4. 5. 6. 7.
fyzick´a (physical) linkov´a (data link) s´ıt’ov´a (network) transportn´ı (transport) relaˇcn´ı (session) prezentaˇcn´ı (presentation) aplikaˇcn´ı (application)
• UUCP je tvoˇreno aplikaˇcn´ımi programy, nevyˇzaduje ˇza´dnou podporu v j´adru. Implementace soket˚ u a TLI jsou souˇca´st´ı j´adra. TLI je ve verzi SVR4 implementov´ano s vyuˇzit´ım mechanismu STREAMS. RPC existuje jako knihovna linkovan´ a k aplikac´ım, kter´ a vyuˇz´ıv´ a sokety (funguje nad protokoly TCP a UDP). RPC bylo vyvinuto jako komunikaˇcn´ı protokol pro NFS (Networked File System). • existuje v´ıce (vz´ ajemnˇe nekompatibiln´ıch) implementac´ı RPC • komunikaˇcn´ı kan´ al je specifikov´an adresami dvou soket˚ u. • sokety pro komunikaci pouze v r´ amci jednoho poˇc´ıtaˇce jsou v dom´enˇe AF UNIX a jejich jm´ena jsou jm´ena speci´ aln´ıch soubor˚ u, kter´e reprezentuj´ı sokety v syst´emu soubor˚ u. • sokety AF UNIX jsou nˇeco jin´eho neˇz lok´aln´ı TCP/IP komunikace pˇres loopback rozhran´ı localhost (127.0.0.1).
TCP/IP • protokoly – IP (Internet Protocol) – pˇr´ıstupn´ y jen pro uˇzivatele root – TCP (Transmission Control Protocol) – streamov´ y, spojovan´ y, spolehliv´ y – UDP (User Datagram Protocol) – datagramov´ y, nespojovan´ y, nespolehliv´ y • IP adresa – 4 bajty, definuje s´ıt’ov´e rozhran´ı, nikoliv poˇc´ıtaˇc • port – 2 bajty, rozliˇsen´ı v r´ amci 1 IP adresy, porty s ˇc´ıslem menˇs´ım neˇz 1024 jsou rezervovan´e (jejich pouˇzit´ı vyˇzaduje pr´ava uˇzivatele root) • DNS (Domain Name System) – pˇrevod mezi symbolick´ ymi jm´eny a numerick´ ymi IP adresami
156
• UNIX pouˇz´ıv´ a pro s´ıt’ovou komunikaci nejˇcastˇeji rodinu protokol˚ u TCP/IP. Pro u ´ˇcely programov´an´ı aplikac´ı n´ as budou zaj´ımat pˇredevˇs´ım protokoly TCP (spojovan´ a spolehliv´ a komunikace) a UDP (nespojovan´ a nespolehliv´ a komunikace). V obou protokolech je jeden konec komunikaˇcn´ıho kan´ alu (odpov´ıd´a soketu v dom´enˇe AF INET) identifikov´an IP adresou s´ıt’ov´eho rozhran´ı a ˇc´ıslem portu (pomoc´ı port˚ u se rozliˇsuj´ı s´ıt’ov´e sluˇzby bˇeˇz´ıc´ı na jednom poˇc´ıtaˇci). • IP – protokol 3. vrstvy, zajiˇst’uje pˇrenos paket˚ u (datagram˚ u) mezi rozhran´ımi identifikovan´ ymi IP adresou; je nespolehliv´ y (nezaruˇcuje doruˇcen´ı dat). RFC 791. Ned´ılnou souˇca´st´ı IP je Internet Control Message Protocol (ICMP), RFC 792. • UDP – jednoduch´ a nadstavba nad IP, pˇrid´ av´ a ˇc´ısla port˚ u, z˚ ust´ av´ a nespolehliv´ y a datagramovˇe orientovan´ y • TCP – vytv´aˇr´ı obousmˇern´e spojen´ı mezi dvˇema body (IP+port), poskytuje tok dat (stream) bez rozdˇelen´ı na zpr´ avy, zajiˇst’uje ˇr´ızen´ı toku dat a spolehliv´e doruˇcov´an´ı • DNS – hierarchicky organizovan´ a datab´ aze, jej´ı struktura nemus´ı m´ıt nic spoleˇcn´eho se strukturou IP adres
Spojovan´ e sluˇ zby (TCP), sekvenˇ cn´ı obsluha server
s´ıt’
klient
fd = socket()
fd = socket() bind(fd) listen(fd) fd2 = accept(fd)
connect(fd)
read(fd2); write(fd2)
write(fd);read(fd)
close(fd2)
close(fd)
• server vytvoˇr´ı jedno spojen´ı, teprve po jeho ukonˇcen´ı akceptuje dalˇs´ıho klienta. • syst´emov´a vol´ an´ı: – socket() – vytvoˇr´ı soket, vr´ at´ı jeho desktiptor
157
– bind() – definuje adresu soketu (IP adresu a ˇc´ıslo portu), mus´ı to b´ yt bud’ adresa jednoho ze s´ıt’ov´ ych rozhran´ı poˇc´ıtaˇce, na kter´em je vytvoˇren soket (pak bude soket pˇrij´ımat ˇza´dosti klient˚ u pouze pˇres toto rozhran´ı), nebo je to speci´ aln´ı hodnota ,,libovoln´ a adresa” (pak soket pˇrij´ım´ a poˇzadavky prostˇrednictv´ım vˇsech s´ıt’ov´ ych rozhran´ı) – listen() – ozn´ am´ı j´ adru, ˇze soket bude pˇrij´ımat poˇzadavky klient˚ u – accept() – usp´ı proces, dokud nebude k dispozici nˇejak´ a ˇza´dost klienta o spojen´ı, vytvoˇr´ı spojen´ı a vr´ at´ı nov´ y deskriptor, pˇres kter´ y bude prob´ıhat dalˇs´ı komunikace s klientem, p˚ uvodn´ı deskriptor lze pouˇz´ıt k nov´emu vol´ an´ı accept() pro obslouˇzen´ı dalˇs´ıho klienta – close() – ukonˇc´ı komunikaci – connect() – ˇza´dost klienta o nav´az´an´ı spojen´ı, IP adresa a ˇc´ıslo portu serveru se zad´ avaj´ı jako parametry, komunikace prob´ıh´ a pˇres deskriptor fd (na rozd´ıl od accept() nevytv´ aˇr´ı nov´ y deskriptor) • klient nemus´ı volat bind(), v takov´em pˇr´ıpadˇe mu j´adro pˇridˇel´ı nˇekter´ y voln´ y port. Existuj´ı sluˇzby (napˇr. rsh), kter´e vyˇzaduj´ı, aby se klient spojoval z privilegovan´eho portu. Takov´ y klient pak mus´ı prov´est bind() (a nav´ıc bˇeˇzet s rootovsk´ ymi pr´avy alespoˇ n do okamˇziku proveden´ı bind()).
Spojovan´ e sluˇ zby (TCP), paraleln´ı obsluha server
s´ıt’
klient
fd = socket()
fd = socket() bind(fd) listen(fd) fd2 = accept(fd) fork()
connect(fd)
syn close(fd2) while(waitpid( read(fd2) -1, stat, write(fd2) WNOHANG)>0) ; exit(0)
write(fd);read(fd) close(fd)
• server akceptuje spojen´ı od klienta a na vlastn´ı komunikaci vytvoˇr´ı nov´ y proces, kter´ y po uzavˇren´ı spojen´ı s klientem skonˇc´ı. Rodiˇcovsk´ y proces m˚ uˇze mezit´ım akceptovat dalˇs´ı klienty a spouˇstˇet pro nˇe obsluˇzn´e procesy. Souˇcasnˇe je tedy obsluhov´ano v´ıce klient˚ u. • po proveden´ı fork(), ale pˇred zah´ajen´ım obsluhy spojen´ı, m˚ uˇze synovsk´ y proces prov´est exec – takto funguje napˇr. inetd. 158
• vol´ an´ı waitpid() v cyklu odstraˇ nuje ze syst´emu zombie. Jinou moˇznost´ı je vyuˇzit´ı sign´alu SIGCHLD, jehoˇz explicitn´ı ignorov´an´ı zabr´ an´ı vzniku ˇziv´ ych mrtv´ ych, popˇr. lze instalovat handler, v nˇemˇz se vol´a wait().
Spojovan´ e sluˇ zby, paraleln´ı accept() server
s´ıt’
fd = socket() bind(fd) listen(fd) fork()
...
klient
fd = socket()
fd2=accept(fd) read(fd2) write(fd2) close(fd2)
connect(fd) write(fd);read(fd) close(fd)
• po bind() a listen() se vytvoˇr´ı nˇekolik synovsk´ ych proces˚ u a kaˇzd´ y v cyklu vol´ a accept() a obsluhuje klienty. J´ adro vybere (nedeterministicky) pro kaˇzd´ y poˇzadavek jeden proces, v nˇemˇz accept() nav´aˇze spojen´ı. • jednotliv´e procesy serveru mezi sebou mohou komunikovat, aby v pˇr´ıpadˇe, ˇze souˇcasn´ ych poˇzadavk˚ u je v´ıce neˇz serverov´ ych proces˚ u, se tato skuteˇcnost zjistila a hlavn´ı server mohl dynamicky vytvoˇrit dalˇs´ı serverov´ y proces. • takto funguje napˇr. webov´ y server Apache. • vˇsechny tˇri uveden´e zp˚ usoby ˇcinnosti serveru funguj´ı se stejn´ ym klientem – ˇcinnost klienta nez´ avis´ı na variantˇe serveru.
159
Datagramov´ e sluˇ zby (UDP) server
s´ıt’
klient
fd = socket()
fd = socket()
bind(fd) recvfrom(fd) sendto(fd)
sendto(fd) recvfrom(fd) close(fd)
• z pohledu volan´ ych s´ıt’ov´ ych funkc´ı je funkce serveru a klienta shodn´ a. Klient je zde ten, kdo poˇsle prvn´ı datagram. • stejnˇe jako v pˇr´ıpadˇe TCP, klient nemus´ı dˇelat bind(), jestliˇze mu nez´aleˇz´ı na tom, jak´e ˇc´ıslo portu bude pouˇz´ıvat. Server zjist´ı jeho port z obsahu adresn´ı ˇca´sti prvn´ıho datagramu. • v´ yhodou nespojovan´e sluˇzby je menˇs´ı reˇzie a to, ˇze pˇres jeden soket lze komunikovat s v´ıce procesy (pˇri spojovan´e komunikaci je spojen´ı vˇzdy nav´az´ano s jedn´ım procesem). • pro UDP je moˇzn´e volat connect(). T´ım se nenav´aˇze spojen´ı, ale soket se nastav´ı tak, ˇze m˚ uˇze nad´ale komunikovat pouze s adresou a portem specifikovan´ ymi ve vol´ an´ı connect(). M´ısto sendto() a recvfrom() se pak pouˇz´ıvaj´ı funkce send() a recv().
160
Vytvoˇ ren´ı soketu: socket() int socket(int domain, int type, int protocol ); • vytvoˇr´ı soket a vr´ at´ı jeho deskriptor. • domain: – AF UNIX . . . lok´aln´ı komunikace, adresa je jm´eno souboru – AF INET . . . s´ıt’ov´a komunikace, adresa je dvojice (IP adresa, port) • type: – SOCK STREAM . . . spojovan´ a spolehliv´ a sluˇzba, poskytuje obousmˇern´ y sekvenˇcn´ı proud dat – SOCK DGRAM . . . nespojovan´ a nespolehliv´ a sluˇzba, pˇrenos datagram˚ u • protocol: 0 (default pro dan´ y type) nebo platn´e ˇc´ıslo protokolu (napˇr. 6 = TCP, 17 = UDP)
• sokety jsou pˇr´ıstupn´e pˇres deskriptory soubor˚ u. Po nav´az´an´ı spojen´ı je (spojovan´ a) komunikace pˇres soket podobn´a komunikaci pomoc´ı roury. • v nˇekter´ ych implementac´ıch se rozliˇsuj´ı konstanty zaˇc´ınaj´ıc´ı PF_ (protocol family, napˇr. PF_INET, PF_UNIX) pouˇz´ıvan´e v socket() a konstanty AF_ (address family, napˇr. AF_INET, AF_UNIX) pouˇz´ıvan´e pˇri zad´av´ an´ı adres soket˚ u. Hodnoty odpov´ıdaj´ıc´ıch konstant AF_ a PF_ jsou typicky stejn´e. • existuj´ı dalˇs´ı typy soket˚ u pro pˇr´ıstup k IP, ICMP, nebo k informac´ım z routovac´ı tabulky.
161
Pojmenov´ an´ı soketu: bind() int bind(int socket, const struct sockaddr *address, socklen t address len ); • pˇriˇrad´ı soketu zadan´emu deskriptorem socket adresu address o velikosti address len bajt˚ u. • struct sockaddr: – sa family t sa family . . . dom´ena – char sa data [] . . . adresa • Pro AF INET se pouˇz´ıv´ a struct sockaddr in: – sa family t sin family . . . dom´ena (AF INET) – in port t sin port . . . ˇc´ıslo portu (16 bit˚ u) u) – struct in addr sin addr . . . IP adresa (32 bit˚ – unsigned char sin zero [8] . . . v´ yplˇ n
• vol´ an´ı bind() pˇriˇrazuje soketu lok´ aln´ı adresu, tj. zdrojovou adresu odes´ılan´ ych dat a c´ılovou adresu pˇrij´ıman´ ych dat. Vzd´alen´ a adresa (adresa druh´eho konce komunikaˇcn´ıho kan´ alu) se nastavuje pomoc´ı connect(). • jeden soket lze spojit se vˇsemi lok´aln´ımi adresami nastaven´ım sin_addr na INADDR_ANY. • nelze spojit v´ıce soket˚ u s jednou dvojic´ı (adresa, port). • vol´ an´ı bind() lze vynechat, j´ adro pak soketu (v pˇr´ıpadˇe TCP, UDP) pˇriˇrad´ı adresu INADDR_ANY a nˇekter´ y voln´ y port. Obvykle bind() vol´a pouze server, protoˇze klienti oˇcek´ avaj´ı, ˇze bude poslouchat na pevn´em portu. Naopak klient pevn´ y port nepotˇrebuje, server se port klienta dozv´ı pˇri nav´az´an´ı spojen´ı nebo z prvn´ıho pˇrijat´eho datagramu. • adresa i port mus´ı b´ yt do struktury uloˇ zeny v s´ıt’ov´ em poˇ rad´ı bajt˚ u. Poˇrad´ı bajt˚ u bylo vysvˇetleno na stranˇe 15, dalˇs´ı informace pak budou na stranˇe 171. • v dom´enˇe AF_UNIX se pouˇz´ıv´ a adresov´a struktura struct sockaddr_un: – sa family t sun family . . . dom´ena – char sun path [] . . . jm´eno soketu – d´elka jm´ena nen´ı ve standardu definov´ana, z´ avis´ı na implementaci. Obvykl´e hodnoty jsou mezi 92 a 108.
162
ˇ an´ı na spojen´ı: listen() Cek´ int listen(int socket, int backlog ); • oznaˇc´ı soket zadan´ y desktriptorem socket jako akceptuj´ıc´ı spojen´ı. • maxim´ alnˇe backlog ˇza´dost´ı o spojen´ı m˚ uˇze najednou ˇcekat ve frontˇe na obslouˇzen´ı (implementace m˚ uˇze hodnotu backlog ˇ adosti, kter´e se zmˇenit, pokud nen´ı v podporovan´em rozsahu). Z´ nevejdou do fronty, jsou odm´ıtnuty (tj. vol´an´ı connect() skonˇc´ı s chybou). • soket ˇcek´ a na spojen´ı na adrese, kter´ a mu byla dˇr´ıve pˇriˇrazena vol´ an´ım bind(). Pro dom´enu AF INET staˇc´ı zadat ˇc´ıslo portu a IP adresu INADDR ANY, kter´ a znamen´ a libovolnou adresu.
• hodnota INADDR ANY se pouˇz´ıv´ a nejˇcastˇeji. Konkr´etn´ı IP adresa serveru se zad´ av´ a tehdy, jestliˇze je potˇreba rozliˇsit, pˇres kter´e s´ıt’ov´e rozhran´ı pˇriˇsel poˇzadavek na spojen´ı (pro kaˇzd´e rozhran´ı m´ame jeden soket). Tuto moˇznost vyuˇz´ıvaj´ı web servery, kter´e podle IP adresy rozliˇsuj´ı virtu´ aln´ı servery . Obvykle se na takov´em serveru jednomu fyzick´emu rozhran´ı pˇriˇrazuje nˇekolik IP adres (IP aliasing). Novˇejˇs´ı rozliˇsen´ı virtu´ aln´ıch server˚ u podle HTTP hlaviˇcky ,,Host:” uˇz nepotˇrebuje IP aliasy.
163
Akceptov´ an´ı spojen´ı: accept() int accept(int socket, struct sockaddr *address, socklen t *address len ); • vytvoˇr´ı spojen´ı mezi lok´aln´ım soketem socket (kter´ y dˇr´ıve zavolal listen()) a vzd´ alen´ ym soketem, kter´ y ˇza´dal o spojen´ı pomoc´ı connect(). Vr´ at´ı deskriptor (nov´ y soket), kter´ y lze pouˇz´ıvat pro komunikaci se vzd´ alen´ ym procesem. P˚ uvodn´ı deskriptor socket umoˇzn ˇ uje pˇrij´ımat dalˇs´ı spojen´ı pomoc´ı accept(). • v address vr´ at´ı adresu vzd´ alen´eho soketu. avratu • address len je velikost struktury pro uloˇzen´ı adresy, po n´ obsahuje skuteˇcnou d´elku adresy. • podobnˇe jako bind() i accept() pouˇz´ıv´ a pro adresy v dom´enˇe AF INET strukturu sockaddr in.
• vytvoˇren´ı druh´eho deskriptoru pro komunikaci umoˇzn ˇ uje na tom p˚ uvodn´ım ihned znovu volat accept(). • jestliˇze se v´ıce klient˚ u ze stejn´eho poˇc´ıtaˇce najednou pˇripoj´ı k jednomu serveru (tj. na jednu serverovou IP adresu a jeden port), jsou jednotliv´ a spojen´ı rozliˇsena jen ˇc´ıslem portu na klientsk´e stranˇe. • address m˚ uˇze b´ yt zad´ ana jako NULL, ˇc´ımˇz oznamujeme, ˇze n´ as konkr´etn´ı adresa naˇseho nov´eho soketu nezaj´ım´ a. V tomto pˇr´ıpadˇe by i address len mˇelo b´ yt 0. • socket vr´ acen´ y z vol´ an´ı accept() si m˚ uˇzeme pˇredstavit tak, ˇze reprezentuje ten konec spojen´ı, na kter´em poslouch´ a a z kter´eho ˇcte druh´ a strana.
164
Nav´ az´ an´ı spojen´ı: connect() int connect(int sock, struct sockaddr *address, socklen t address len); • nav´aˇze spojen´ı lok´aln´ıho soketu sock se vzd´ alen´ ym procesem, kter´ y pomoc´ı listen() a accept() ˇcek´ a na spojen´ı na adrese address (o d´elce address len). • jestliˇze pro soket sock nebyla definov´ana adresa vol´an´ım bind(), je mu pˇriˇrazena nˇejak´ a nepouˇzit´ a adresa dle zvolen´e rodiny protokol˚ u. • pokud se spojen´ı nepovede, je soket v nedefinovan´em stavu. Pˇred nov´ ym pokusem o spojen´ı by aplikace mˇela zavˇr´ıt deskriptor sock a vytvoˇrit nov´ y soket.
• po u ´ spˇeˇsn´em nav´az´an´ı spojen´ı mohou server i klient pro komunikaci pouˇz´ıvat bˇeˇzn´ a souborov´a vol´ an´ı write() a read(), nebo funkce send(), recv(), sendmsg(), recvmsg(). Chov´an´ı funkc´ı pro z´ apis a ˇcten´ı dat je podobn´e jako write() a read() pro roury. • i pro nespojovan´e sluˇzby (UDP) se d´ a volat connect(), t´ım se nenav´aˇze spojen´ı, ale pouze se omez´ı adresa protistrany, se kterou m˚ uˇze soket komunikovat. V tomto pˇr´ıpadˇe mohou connect() volat obˇe strany komunikace. • pokud je socket nastaven jako neblokuj´ıc´ı, viz strana 170, connect() se nezablokuje ˇcek´ an´ım na spojen´ı. M´ısto toho vr´ at´ı -1 s errno nastaven´e na EINPROGRESS (= “nelze vytvoˇrit spojen´ı okamˇzitˇe”), a ˇza´dost o spojen´ı se uloˇz´ı do syst´emov´e fronty pro n´ asledn´e vyˇr´ızen´ı. Do t´e doby, neˇz je spojen´ı pˇripraveno, vol´ an´ı connect() vrac´ı -1, nyn´ı ale s chybou EALREADY. Nen´ı ale takto vhodn´e testovat pˇripravenost spojen´ı, protoˇze pokud connect() skonˇc´ı s chybou, dalˇs´ı vol´ an´ı connect() provede nov´ y pokus o spojen´ı a jsme opˇet tam, kde jsme byli. . . Je moˇzn´e ale pouˇz´ıt select() nebo poll() pro ˇcek´ an´ı na z´ apis (ne ˇcten´ı) do socketu, viz strany 176, 178. Zda spojen´ı probˇehlo u ´ spˇeˇsnˇe se pak dozv´ıte z funkce getsockopt() s opt name nastaven´e na SO ERROR, viz strana 170.
165
Posl´ an´ı zpr´ avy: sendto() ssize t sendto(int socket, void *msg, size t len, int flags, struct sockaddr *addr, socklen t addr len ); • prostˇrednictv´ım soketu socket poˇsle zpr´ avu msg o d´elce len na adresu addr (o d´elce addr len). • parametr flags m˚ uˇze obsahovat pˇr´ıznaky: – MSG EOB . . . ukonˇcen´ı z´ aznamu (pokud je podporov´ano protokolem) an´ı urgentn´ıch (out-of-band) dat, jejichˇz – MSG OOB . . . posl´ v´ yznam je z´ avisl´ y na protokolu
• pouˇz´ıv´ a se hlavnˇe pro sokety typu SOCK DGRAM, protoˇze v t´eto situaci m´ame pouze socket reprezentuj´ıc´ı naˇsi stranu spojen´ı; viz pozn´amka u slajdu k accept(). Mus´ıme proto specifikovat adresu protistrany. Pro datagramovou sluˇzbu se nav´ıc data berou jako nedˇeliteln´ a, tj. bud’ se akceptuj´ı cel´ a, nebo se vol´ an´ı zablokuje – neexistuje ˇca´steˇcn´ y z´ apis. Stejnˇe jako je tomu u souborov´ ych deskriptor˚ u, je i zde moˇzn´e socket nastavit jako neblokuj´ıc´ı, viz strana 170. • m´ısto sendto() se d´ a pouˇz´ıt obecnˇejˇs´ı funkce sendmsg(). • pro sokety, na kter´e bylo vol´ ano connect(), se m´ısto sendto() m˚ uˇze pouˇz´ıt send(). • u ´ spˇeˇsn´ y n´ avrat z libovoln´e funkce zapisuj´ıc´ı data do soketu neznamen´ a u ´ spˇ eˇ sn´ e doruˇ cen´ı zpr´ avy protistranˇ e, ale pouze uloˇzen´ı dat do lok´aln´ıho bufferu odes´ılan´ ych dat. • pokud pouˇzijete sendto() na streamovanou sluˇzbu, je to moˇzn´e, ale adresa se ignoruje. Jedin´ y d˚ uvod proˇc nepouˇz´ıt pˇr´ımo write() by tak byla moˇznost pouˇz´ıt flagy. V tom pˇr´ıpadˇe je ale jednoduˇsˇs´ı pouˇz´ıt send().
166
Pˇ rijet´ı zpr´ avy: recvfrom() ssize t recvfrom(int sock, void *buf, size t len, int flg, struct sockaddr *address, socklen t *address len ); • pˇrijme zpr´ avu ze soketu sock, uloˇz´ı ji do bufferu buf o velikosti len, do address d´ a adresu odes´ılatele zpr´ avy, do address len d´elku adresy. Vr´ at´ı d´elku zpr´ avy. Kdyˇz je zpr´ ava delˇs´ı neˇz len, nadbyteˇcn´ a data se zahod´ı (SOCK STREAM nedˇel´ı data na zpr´ avy, data se nezahazuj´ı). • ve flg mohou b´ yt pˇr´ıznaky: ava se bere jako nepˇreˇcten´ a, dalˇs´ı – MSG PEEK . . . zpr´ recvfrom() ji vr´ at´ı znovu – MSG OOB . . . pˇreˇcte urgentn´ı (out-of-band) data a, dokud nen´ı naˇcten pln´ y objem dat, tj. – MSG WAITALL . . . ˇcek´ len bajt˚ u
• pouˇz´ıv´ a se hlavnˇe pro sokety typu SOCK DGRAM. V takov´e situaci opˇet ˇcek´ a na celou zpr´ avu (tj. nevr´ at´ı v´am p˚ ulku datagramu); opˇet je moˇzn´e nastavit socket jako neblokuj´ıc´ı. • m´ısto recvfrom() se d´ a pouˇz´ıt obecnˇejˇs´ı funkce recvmsg(). • pro sokety, na kter´e bylo vol´ ano connect(), se m´ısto funkce recvfrom() vol´a recv(). yt inicializovan´ a velikost´ı bufferu. • address len mus´ı b´ • po u ´ speˇsn´em n´ avratu z recvfrom() je moˇzn´e address a address len beze zmˇeny pouˇz´ıt pro n´ asledn´e vol´an´ı sendto(). • stejnˇe jako sendto(), je i recvfrom() moˇzn´e pouˇz´ıt pro spojovanou sluˇzbu. Z´ıskat adresu protistrany je ale asi jednoduˇsˇs´ı pˇres vol´an´ı getsockbyname(), viz strana 170.
167
Uzavˇ ren´ı soketu: close() int close(int fildes); • zruˇs´ı deskriptor, pˇri zruˇsen´ı posledn´ıho deskriptoru soketu zavˇre soket. • pro SOCK STREAM soket z´ aleˇz´ı na nastaven´ı pˇr´ıznaku SO LINGER (default je l onoff == 0, mˇen´ı se funkc´ı setsockopt()). – l onoff == 0 . . . vol´ an´ı close() se vr´ at´ı, ale j´adro se snaˇz´ı d´ al pˇren´est zbyl´ a data – l onoff == 1 && l linger != 0 . . . j´adro se snaˇz´ı pˇren´est zbyl´ a data do vyprˇsen´ı timeoutu l linger, kdyˇz se to nepovede, close() vr´ at´ı chybu, jinak vr´ at´ı OK po pˇrenesen´ı dat. – l onoff == 1 && l linger == 0 . . . provede se reset spojen´ı
• po uzavˇren´ı m˚ uˇze TCP soket z˚ ustat po nˇejakou dobu v pˇrechodn´em stavu (kter´ y je definov´an protokolem TCP pˇri ukonˇcov´an´ı spojen´ı). Neˇz je soket zcela zruˇsen, nelze pouˇz´ıt jin´ y soket na stejn´em portu (pokud toto nebylo povoleno nastaven´ım pˇr´ıznaku SO REUSEADDR pomoc´ı funkce setsockopt(), viz strana 170). • pˇri pouˇzit´ı SO_REUSEADDR se d´ a po uzavˇren´ı poslouchaj´ıc´ıho serverov´eho soketu znovu spustit server – volat socket(), bind(), listen() a accept() na stejn´e adrese a portu – i kdyˇz jeˇstˇe dob´ıhaj´ı spojen´ı vytvoˇren´ a pˇredchoz´ı instanc´ı serveru: int opt = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, opt, sizeof(opt)); • reset spojen´ı je abnorm´ aln´ı ukonˇcen´ı spojen´ı. V TCP se pouˇzije paket s nastaven´ ym pˇr´ıznakem RST. Na druh´e stranˇe se norm´aln´ı ukonˇcen´ı spojen´ı projev´ı jako konec souboru (pˇri ˇcten´ı), reset zp˚ usob´ı chybu ECONNRESET.
168
Uzavˇ ren´ı soketu: shutdown() int shutdown(int socket, int how ); • Uzavˇre soket (ale neruˇs´ı deskriptor) podle hodnoty how: – SHUT RD . . . pro ˇcten´ı – SHUT WR . . . pro z´ apis – SHUT RDWR . . . pro ˇcten´ı i z´ apis
• Pˇri norm´ aln´ım ukonˇcen´ı spojen´ı na u ´ rovni protokolu TCP kaˇzd´ a strana signalizuje, ˇze uˇz nebude nic zapisovat. To plat´ı i v pˇr´ıpadˇe pouˇzit´ı close() nebo shutdown(fd, SHUT RDWR). Pˇri pouˇzit´ı shutdown(fd, SHUT WR) lze ze soketu d´ al ˇc´ıst. Druh´ a strana dostane EOF pˇri ˇcten´ı, ale m˚ uˇze d´ al zapisovat.
169
Dalˇ s´ı funkce pro sokety int setsockopt(int socket, int level, int opt name, const void *opt value, socklen t option len ); • nastaven´ı parametr˚ u soketu int getsockopt(int socket, int level, int opt name, void *opt value, socklen t *option len ); • pˇreˇcten´ı parametr˚ u soketu int getsockname(int socket, struct sockaddr *address, socklen t *address len ); • zjiˇstˇen´ı (lok´ aln´ı) adresy soketu int getpeername(int socket, struct sockaddr *address, socklen t *address len ); • zjiˇstˇen´ı adresy vzd´ alen´eho soketu (druh´eho konce spojen´ı)
• hodnota level v getsockopt() a setsockopt() je obvykle SOL_SOCKET. U getsockopt(), option len mus´ı b´ yt inicializov´ana na velikost opt value. • funkce getsockname() se pouˇz´ıv´ a, kdyˇz nevol´ame bind() a potˇrebujeme zjistit, jak´ a (lok´ aln´ı!) adresa byla j´adrem soketu pˇridˇelena. • vol´ an´ı getsockopt(sock, SOL_SOCKET, SO_ERROR, &val, &len) vr´ at´ı (a vymaˇze) pˇr´ıznak chyby na soketu. Asi nejuˇziteˇcnˇejˇs´ı je pˇri zjiˇst’ov´an´ı, jak dopadl neblokuj´ıc´ı connect(), viz strana 165. • pˇr´ıklad na setsockopt() je na stranˇe 168.
170
Poˇ rad´ı bajt˚ u • S´ıt’ov´e sluˇzby pouˇz´ıvaj´ı poˇrad´ı bajt˚ u, kter´e se m˚ uˇze liˇsit od poˇrad´ı pouˇz´ıvan´eho na lok´aln´ım syst´emu. Pro pˇrevod lze pouˇz´ıt funkce (makra): – uint32 t htonl(uint32 t hostlong ); host → s´ıt’, 32 bit˚ u – uint16 t htons(uint16 t hostshort ); host → s´ıt’, 16 bit˚ u – uint32 t ntohl(uint32 t netlong ); s´ıt’ → host, 32 bit˚ u – uint16 t ntohs(uint16 t netshort ); s´ıt’ → host, 16 bit˚ u • S´ıt’ov´e poˇrad´ı bajt˚ u je big-endian, tj. nejprve vyˇsˇs´ı bajt. Pouˇz´ıv´ a se hlavnˇe ve funkc´ıch pracuj´ıc´ıch s adresami a ˇc´ısly port˚ u.
• Pokud lok´aln´ı syst´em pouˇz´ıv´ a stejn´e poˇrad´ı bajt˚ u jako s´ıt’, nedˇelaj´ı pˇrevodn´ı funkce nic.
171
ˇ ısla protokol˚ C´ u a port˚ u struct protoent *getprotobyname(const char *name ); at´ı ˇc´ıslo protokolu se jm´enem name (napˇr. • v poloˇzce p proto vr´ pro "tcp" vr´ at´ı 6). • ˇc´ısla protokol˚ u jsou uloˇzena v souboru /etc/protocols. struct servent *getservbyname(const char *name, const char *proto ); • pro zadan´e jm´eno sluˇzby name a jm´eno protokolu proto vr´ at´ı v poloˇzce s port ˇc´ıslo portu. • ˇc´ısla port˚ u jsou uloˇzena v souboru /etc/services. funkce vrac´ı NULL, kdyˇz v datab´ azi nen´ı odpov´ıdaj´ıc´ı poloˇzka.
• v uveden´ ych souborech je definov´ano mapov´an´ı mezi jm´eny a ˇc´ısly pro standardn´ı protokoly a sluˇzby. • pozor na to, ˇze protokolem zde nemysl´ıme www, SSH, telnet nebo FTP – to jsou zde sluˇzby, reprezentovan´e ˇc´ısly port˚ u. Protokol je TCP, UDP, OSPF, GRE apod., tedy to, co je pˇren´ aˇseno v IP paketu v poloˇzce Protocol, viz RFC 791.
172
Jm´ ena a IP adresy struct hostent *gethostbyname(const char *name ); • pro dan´e jm´eno vr´ at´ı v poli char **h addr list seznam pˇr´ısluˇsn´ ych s´ıt’ov´ ych adres. Za posledn´ı adresou je ukazatel NULL. D´elka jedn´e adresy je v poloˇzce h length. struct hostent *gethostbyaddr(const char *addr, size t len, int type ); • pro danou adresu addr o d´elce len v dom´enˇe type vr´ at´ı jm´eno v poloˇzce h name a pˇr´ıpadn´e aliasy v nulou ukonˇcen´em poli h aliases. − pˇri vyhodnocov´an´ı dotaz˚ u na adresy a jm´ena se pouˇz´ıv´ a DNS a lok´aln´ı datab´ aze uloˇzen´ a v souboru /etc/hosts. − vrac´ı NULL, kdyˇz v datab´ azi nen´ı hledan´ y z´ aznam.
• lze stanovit, zda m´a prioritu DNS nebo lok´aln´ı datab´ aze adres. • jm´eno dom´eny a adresy name server˚ u jsou v souboru /etc/resolv.conf. • m´ısto zde uveden´ ych funkc´ı se doporuˇcuje pouˇz´ıvat obecnˇejˇs´ı vol´an´ı getaddrinfo().
173
Pˇ r´ıklad: TCP server int nclients = 10, fd, newsock, sz; struct servent *sp; struct protoent *pp; struct sockaddr in sa,ca; sp = getservbyname(argv[1], "tcp"); pp = getprotobyname("tcp"); fd = socket(AF INET, SOCK STREAM, pp->p proto); sa.sin family = AF INET; sa.sin port=sp->s port; sa.sin addr.s addr = INADDR ANY; bind(fd,(struct sockaddr *)&sa,sizeof(sa)); listen(fd, nclients); for(;;) { sz = sizeof(ca); newsock = accept(fd, &ca, &sz); /* Komunikace s klientem */ close(newsock); }
• Toto je obecn´ a kostra serveru. Jm´eno sluˇzby se zad´av´ a jako parametr programu, odpov´ıdaj´ıc´ı ˇc´ıslo portu hled´ a funkce getservbyname().
174
Pˇ r´ıklad: TCP klient char *host; struct servent *se; struct hostent *ha; struct protoent *pp; int sockfd; struct sockaddr in sa; host = argv[1]; se = getservbyname(argv[2], "tcp"); ha = gethostbyname(host); pp = getprotobyname("tcp"); sockfd = socket(AF INET, SOCK STREAM, pp->p proto); sa.sin family = AF INET; sa.sin port = se->s port; memcpy(&sa.sin addr.s addr, ha->h addr list[0], ha->h length); connect(sockfd, &sa, sizeof(sa)); /* Komunikace se serverem */ close(sockfd);
• V pˇr´ıkladu vyuˇz´ıv´ ame automatick´eho pˇridˇelen´ı voln´eho portu syst´emem pˇri vol´ an´ı connect(), kter´emu nepˇredch´ azel bind().
175
ˇ an´ı na data: select() Cek´ int select(int nfds, fd set *readfds, fd set *writefds, fd set *errorfds, struct timeval *timeout ); • zjist´ı, kter´e ze zadan´ ych deskriptor˚ u jsou pˇripraveny pro ˇcten´ı, z´ apis, nebo na kter´ ych doˇslo k v´ yjimeˇcn´emu stavu. Pokud ˇza´dn´ y takov´ y deskriptor nen´ı, ˇcek´ a do vyprˇsen´ı ˇcasu timeout (NULL . . . ˇcek´ a libovolnˇe dlouho). Parametr nfds ud´ av´ a rozsah testovan´ ych deskriptor˚ u (0, ..., nfds-1). • pro nastaven´ı a test masek deskriptor˚ u slouˇz´ı funkce: – void FD ZERO(fd set *fdset ) . . . inicializace – void FD SET(int fd, fd set *fdset ) . . . nastaven´ı – void FD CLR(int fd, fd set *fdset ) . . . zruˇsen´ı – int FD ISSET(int fd, fd set *fdset ) . . . test
• motivace: jestliˇze chceme ˇc´ıst data z v´ıce deskriptor˚ u, jde nastavit pˇr´ıznak O NONBLOCK a neblokuj´ıc´ım read() stˇr´ıdavˇe testovat jednotliv´e deskriptory, mezi kaˇzd´ ym kolem test˚ u pak tˇreba pouˇz´ıt sleep(1). Lepˇs´ı ˇreˇsen´ı, tj. bez aktivn´ıho ˇcek´ an´ı a testov´an´ı, je pouˇz´ıt select() a n´ aslednˇe read() na ty deskriptory, kter´e select() ohl´ as´ı jako pˇripraven´e. • pˇripraven´y (ready) znamen´ a, ˇze read nebo write s vynulovan´ ym pˇr´ıznakem a data jsou pˇripraO NONBLOCK by se nezablokovalo, tedy ne nutnˇe ˇze nˇejak´ vena (read napˇr. m˚ uˇze vr´ atit 0 pro end-of-file) • mnoˇzina errorfds je pro vyj´ımky v z´ avislosti na typu deskriptoru; pro socket to je napˇr´ıklad pˇr´ıchod urgentn´ıch dat (flag U u TCP). Neznamen´ a to, ˇze na dan´em deskriptoru doˇslo k chybˇe! Chyba s nastaven´ ym errno se zjist´ı z ostatn´ıch mnoˇzin po n´ avratov´em k´odu -1 proveden´eho vol´an´ı, tj. napˇr´ıklad read(). • pˇri vol´ an´ı jsou v mnoˇzin´ ach deskriptory, kter´e chceme testovat, po n´ avratu z˚ ustanou nastaven´e jen ty deskriptory, na kter´ ych nastala testovan´ a ud´ alost. Je nutn´ e je tedy pˇ red dalˇ s´ım vol´ an´ım select() znovu nastavit. Typicky to jsou bitov´e masky, ale nemus´ı tomu b´ yt tak; z pozice program´ atora je to samoˇzˇrejmˇe jedno. • funkce select() je pouˇziteln´ a i pro ˇcek´ an´ı na moˇznost z´ apisu do roury nebo soketu – ˇcek´ a se, aˇz druh´ a strana nˇeco pˇreˇcte a uvoln´ı se m´ısto v bufferu pro dalˇs´ı data. • m´ısto mnoˇziny pro deskriptory je moˇzn´e uv´est NULL, speci´ aln´ı pˇr´ıpad pˇri nastaven´ı vˇsech mnoˇzin na NULL je vol´an´ı, kter´e se pouze zablokuje do pˇr´ıchodu sign´alu nebo do vyprˇsen´ı time-outu.
176
• po n´ avratu je nutn´e otestovat kaˇzd´ y deskriptor zvl´aˇst’, nen´ı k dispozici vol´an´ı, kter´e by v´am vytvoˇrilo mnoˇzinu pˇripraven´ ych deskriptor˚ u. • pokud obsluhuje s´ıt’ov´ y server v´ıce port˚ u, m˚ uˇze volat select() na pˇr´ısluˇsn´e deskriptory soket˚ u a n´ aslednˇe accept() na deskriptory, pro kter´e select() ohl´ asil pˇr´ıchod ˇza´dosti klienta (pˇripravenost ke ˇcten´ı). • vol´ an´ı connect() na neblokuj´ıc´ım soketu se hned vr´ at´ı, nav´az´an´ı spojen´ı ohl´ as´ı n´ asledn´ y select() jako pˇripravenost k z´ apisu. • dalˇs´ı moˇznost pouˇzit´ı select() je s´ıt’ov´ y server, kter´ y v jednom procesu obsluhuje paralelnˇe nˇekolik klient˚ u. Pomoc´ı select() se testuje stav deskriptor˚ u odpov´ıdaj´ıc´ıch spojen´ı s jednotliv´ ymi klienty a pˇres deskriptory pˇripraven´e pro ˇcten´ı/z´ apis se komunikuje. Aby se mohli pˇripojovat nov´ı klienti, testuje se i deskriptor soketu, kter´ y se pouˇz´ıv´ a pro accept(). Vyuˇz´ıv´ a se toho, ˇze select() ohl´ as´ı deskriptor s ˇcekaj´ıc´ı ˇza´dost´ı klienta o spojen´ı jako pˇripraven´ y pro ˇcten´ı. Na takov´ y deskriptor je moˇzn´e volat accept(). • jako v´ yjimeˇcnou ud´ alost (mnoˇzina errorfds) select() ohl´ as´ı pˇr´ıchod outof-band dat. • pozor na to, ˇze select m˚ uˇ ze zmˇenit strukturu timeval, existuje nov´e vol´an´ı pselect, kter´e (mimo jin´e) strukturu pro timeout nezmˇen´ı. • pro nfds je moˇzn´e pouˇz´ıt FD SETSIZE, coˇz je syst´emov´a konstanta pro maxim´ aln´ı poˇcet deskriptor˚ u. Typicky to vˇsak nebude pˇr´ıliˇs efektivn´ı, protoˇze tato konstanta je vˇetˇsinou 1024 na 32-bitov´ ych syst´emech, na Solarisu to vˇsak pro 64-bitov´e architektury je uˇz 65536. • pokud se ˇcas nastav´ı na 0 (tj. ted’ nemluv´ıme o nastaven´ı ukazatele na NULL), select se d´ a pouˇz´ıt pro tzv. polling – zjist´ı souˇcasn´ y stav a hned se vr´ at´ı.
ˇ an´ı na data: poll() Cek´ int poll(struct pollfd fds [], nfds t nfds, int timeout ); • ˇcek´ a na ud´ alost na nˇekter´em z deskriptor˚ u v poli fds o nfds prvc´ıch po dobu timeout ms (0 . . . vr´ at´ı se hned, -1 . . . ˇcek´ a libovolnˇe dlouho). • prvky fds: – fd . . . ˇc´ıslo deskriptoru – events . . . oˇcek´ avan´e ud´ alosti, OR-kombinace POLLIN (lze ˇc´ıst), POLLOUT (lze ps´at), atd. – revents . . . ud´ alosti, kter´e nastaly, pˇr´ıznaky jako v events, nav´ıc napˇr. POLLERR (nastala chyba)
177
• tato funkce je obdoba vol´ an´ı select(). • na Solarisu je to syst´emov´e vol´an´ı, select pak knihovn´ı funkce implementovan´ a pomoc´ı poll, poll je zde prefererov´ano. Je nutn´e poll pouˇz´ıt v pˇr´ıpadˇe, ˇze chcete testovat deskriptor vˇetˇs´ı nebo rovno neˇz FD SETSIZE. • ˇcas nastaven´ y na -1 je to sam´e jako NULL u select.
Pˇ r´ıklad: pouˇ zit´ı select() /* deskriptor fd odkazuje na soket, pˇ repisuje s´ ıt’ovou komunikaci na termin´ al a naopak */ int sz; fd set rfdset, efdset; char buf[BUFSZ]; for(;;) { FD ZERO(&rfdset); FD SET(0, &rfdset); FD SET(fd, &rfdset); efdset = rfdset; select(fd+1, &rfdset, NULL, &efdset, NULL); yjimka na stdin */; if(FD ISSET(0, &efdset)) /* V´ if(FD ISSET(fd, &efdset)) /* V´ yjimka na fd */; if(FD ISSET(0, &rfdset)) { sz = read(0, buf, BUFSZ); write(fd, buf, sz); } if(FD ISSET(fd, &rfdset)) { sz = read(fd, buf, BUFSZ); write(1,buf,sz); } }
• zde je typick´e pouˇzit´ı select(), kdy je tˇreba ˇc´ıst data souˇcasnˇe ze dvou zdroj˚ u. • pˇred kaˇzd´ ym vol´ an´ım select() se mus´ı znovu nastavit mnoˇziny deskriptor˚ u. • lepˇs´ı ˇreˇsen´ı je nastavit oba deskriptory jako neblokuj´ıc´ı a pouˇz´ıvat select() i na z´ apis. Logika ˇr´ızen´ı je pak takov´a, ˇze pro kaˇzd´ y smˇer datov´e komunikace m´ame samostatn´ y buffer. Pˇr´ısluˇsn´ y ˇctec´ı deskriptor bude v mnoˇzinˇe pro ˇcten´ı v select(), pr´avˇe kdyˇz je buffer pr´azdn´ y. Naopak z´ apisov´ y deskriptor bude v mnoˇzinˇe pro z´ apis, pr´avˇe kdyˇz je buffer nepr´ azdn´ y.
178
spr´ ava s´ıt’ov´ ych sluˇ zeb: inetd • servery s´ıt’ov´ ych sluˇzeb se spouˇst´ı bud’ pˇri startu syst´emu, nebo je startuje d´emon inetd pˇri pˇripojen´ı klienta. • d´emon inetd ˇcek´ a na portech definovan´ ych v /etc/inetd.conf a kdyˇz detekuje pˇripojen´ı klienta, nav´aˇze spojen´ı, spust´ı pˇr´ısluˇsn´ y server a pˇresmˇeruje mu deskriptory 0, 1 a 2 do soketu, pˇres kter´ y lze komunikovat s klientem. • pˇr´ıklad obsahu /etc/inetd.conf: ftp stream tcp nowait root /usr/etc/ftpd ftpd -l shell stream tcp nowait root /usr/etc/rshd rshd -L login stream tcp nowait root /usr/etc/rlogind rlogind exec stream tcp nowait root /usr/etc/rexecd rexecd finger stream tcp nowait guest /usr/etc/fingerd fingerd ntalk dgram udp wait root /usr/etc/talkd talkd tcpmux stream tcp nowait root internal echo stream tcp nowait root internal
• start pˇres inetd ˇsetˇr´ı prostˇredky, protoˇze pˇr´ısluˇsn´ y server bˇeˇz´ı pouze po ˇcas, kdy jsou jeho sluˇzby opravdu potˇreba. Nehod´ı se tedy pro pro spoustˇen´ı vyt´ıˇzen´ ych sluˇzeb (www) nebo sluˇzeb kde je velky overhead pˇri inicializaci (napˇr. SSH kv˚ uli generov´an´ı kl´ıˇce). • typicky se pomoc´ı inetd spouˇst´ı servery, kter´e se pouˇz´ıvaj´ı m´alo nebo jejichˇz inicializace je relativnˇe nen´aroˇcn´ a (telnetd, ftpd). Silnˇe vyt´ıˇzen´e a dlouho startuj´ıc´ı servery (httpd) se obvykle startuj´ı ze syst´emov´ ych inicializaˇcn´ıch skript˚ u a bˇeˇz´ı st´ ale. • ˇcasto m´a cenu inetd m´ıt vypnut´ yu ´ plnˇe. Pokud na vaˇsem stroji bˇeˇz´ı napˇr. pouze SSH, tak pro to se inetd ve vˇetˇsinˇe pˇr´ıpad˚ u nepouˇz´ıv´ a, inetd by byl jen dalˇs´ım serverem bˇeˇz´ıc´ım na stroji a zdroj potenci´ aln´ıho nebezpeˇc´ı, pokud by se v nˇem objevila bezpeˇcnostn´ı chyba.
179
Form´ at souboru /etc/inetd.conf sluˇ zba soket proto ˇ cek´ an´ ı uˇ ziv server argumenty • sluˇ zba . . . jm´eno s´ıt’ov´e sluˇzby podle /etc/services • soket . . . stream nebo dgram • proto . . . komunikaˇcn´ı protokol (tcp, udp) • c ˇek´ an´ ı . . . wait (inetd ˇcek´ a na ukonˇcen´ı serveru pˇred akceptov´an´ım dalˇs´ıho klienta), nowait (inetd akceptuje dalˇs´ıho klienta hned) • uˇ zivatel . . . server pobˇeˇz´ı s identitou tohoto uˇzivatele • server . . . u ´ pln´ a cesta k programu serveru nebo internal (sluˇzbu zajiˇst’uje inetd) • argumenty . . . pˇr´ıkazov´ y ˇra´dek pro server, vˇcetnˇe argv[0]
• soket typu stream: – wait . . . server dostane soket, na kter´ y mus´ı aspoˇ n jednou zavolat accept(). Teprve t´ım z´ısk´a nov´ y soket, pˇres kter´ y m˚ uˇze komunikovat s klientem. Po skonˇcen´ı serveru pˇreb´ır´ a ˇr´ızen´ı soketu zpˇet inetd. – nowait . . . inetd zavol´ a accept() a z´ıskan´ y soket pˇred´ a serveru, server tedy m˚ uˇze rovnou komunikovat (m˚ uˇze pouˇz´ıvat standardn´ı vstup a v´ ystup) a nemus´ı vˇedˇet, ˇze komunikuje po s´ıti. Mezit´ım inetd ˇcek´ a na dalˇs´ı klienty a podle potˇreby spouˇst´ı dalˇs´ı instance serveru. • pro soket typu dgram m´a smysl pouze wait. Server mus´ı pˇreˇc´ıst ze soketu aspoˇ n jeden datagram. • jestliˇze inetd restartuje server (kromˇe stream nowait) pˇr´ıliˇs ˇcasto (cca jednou za sekundu), usoud´ı, ˇze nastala chyba a po urˇcitou dobu (asi 10 minut) sluˇzbu zablokuje (nespouˇst´ı server a odm´ıt´a spojen´ı). Ostatn´ı servery spouˇst´ı norm´ alnˇe d´ al.
180
Obsah • u ´ vod, v´ yvoj UNIXu a C, program´ atorsk´e n´ astroje • z´ akladn´ı pojmy a konvence UNIXu a jeho API • pˇr´ıstupov´a pr´ava, perifern´ı zaˇr´ızen´ı, syst´em soubor˚ u • manipulace s procesy, spouˇstˇen´ı program˚ u • sign´aly • synchronizace a komunikace proces˚ u • s´ıt’ov´a komunikace • vl´ akna, synchronizace vl´ aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu
Vl´ akna • vl´ akno (thread ) = linie v´ ypoˇctu (thread of execution) • vl´ akna umoˇzn ˇ uj´ı m´ıt v´ıce lini´ı v´ ypoˇctu v r´ amci jednoho procesu • klasick´ y unixov´ y model: jednovl´ aknov´e procesy • vl´ akna nejsou vhodn´ a pro vˇsechny aplikace • v´ yhody vl´ aken: – zrychlen´ı aplikace, typicky na v´ıceprocesorech (vl´akna jednoho procesu mohou bˇeˇzet souˇcasnˇe na r˚ uzn´ ych procesorech) – modul´ arn´ı programov´an´ı • nev´ yhody vl´ aken: – nen´ı jednoduch´e korektnˇe napsat sloˇzitˇejˇs´ı k´od pouˇz´ıvaj´ıc´ı vl´ akna – obt´ıˇznˇejˇs´ı debugging
181
• pro aplikace, kde kaˇzd´ y krok z´ avis´ı na kroku pˇredch´ azej´ıc´ım, nemaj´ı vl´ akna pˇr´ıliˇs velk´ y smysl. • debuggery typicky maj´ı podporu vl´ aken, ale debugging zmˇen´ı timing, takˇze to co v re´ alu dˇel´ a probl´em se pˇri debuggingu v˚ ubec nemus´ı projevit. Toto samozˇrejmˇe nen´ı probl´emem u klasick´ ych 1-vl´ aknov´ ych proces˚ u. • jak bylo uvedeno na slajdech s doporuˇcenou literaturou, k vl´ akn˚ um to je [Butenhof]. On-line pak je tˇreba dostupn´ a velmi dobr´ a a obs´ ahl´ a kniha Multithreaded Programming Guide na http://docs.sun.com.
Implementace vl´ aken library-thread model • vl´ akna jsou implementov´ana v knihovn´ach, j´adro je nevid´ı. • run-time knihovna pl´anuje vl´ akna na procesy a j´adro pl´anuje procesy na procesory. ⊕ menˇs´ı reˇzie pˇrep´ın´an´ı kontextu ⊖ nem˚ uˇze bˇeˇzet v´ıce vl´ aken stejn´eho procesu najednou. kernel-thread model • vl´ akna jsou implementov´ana pˇr´ımo j´adrem. ⊕ v´ıce vl´ aken jednoho procesu m˚ uˇze bˇeˇzet najednou na r˚ uzn´ ych procesorech. ⊖ pl´anov´an´ı thread˚ u pouˇz´ıv´ a syst´emov´a vol´an´ı m´ısto knihovn´ıch funkc´ı, t´ım v´ıce zatˇeˇzuje syst´em. hybridn´ı modely • vl´ akna se multiplexuj´ı na nˇekolik j´adrem pl´anovan´ ych entit.
• p˚ uvodnˇe UNIX s vl´ akny nepracoval a prvn´ı implementace byly ˇcistˇe knihovn´ı, bez u ´ˇcasti j´ adra. Dnes se pouˇz´ıv´ a sp´ıˇse implementace vl´ aken v j´adru nebo sm´ıˇsen´ y model. • existuj´ı i jin´e typy implementace vl´ aken neˇz jsou zde uv´adˇen´ a POSIXov´a vl´ akna (napˇr. syst´emov´e vol´ an´ı sproc() v IRIXu, Cthreads, Solaris threads, . . . ). • zat´ımco pˇ ri pr´ aci s v´ıce procesy je nutn´ e vyvinou jist´ e u ´ sil´ı proto, aby dan´ e procesy mohly data sd´ılet, u vl´ aken je naopak nutn´ eˇ reˇ sit situaci, jak pˇ rirozen´ e sd´ılen´ı dat uhl´ıdat. • vl´ akna implementov´ana pouze v knihovnˇe mohou b´ yt preemptivn´ı i nepreemptivn´ı. Pro preemptivnost je moˇzn´e pouˇz´ıt ˇcasovaˇce a sign´aly. Pokud multithreading (= pouˇzit´ı vl´ aken v aplikaci) nen´ı pouˇzit pro zv´ yˇsen´ı v´ ykonu aplikace, typicky nen´ı probl´em s pouˇzit´ım nepreemptivn´ıch vl´ aken. Stˇr´ıd´an´ı vl´ aken se automaticky dos´ ahne pouˇz´ıv´ an´ım blokuj´ıc´ıch syst´emov´ ych vol´an´ı.
182
• pokud se vol´ an´ı v library-thread modelu zablokuje, zablokuje se cel´ y proces, tj. ˇza´dn´e vl´ akno nem˚ uˇze bˇeˇzet. To vypl´ yv´ a z toho, ˇze j´adro v tomto modelu o pojmu vl´ akno nic nev´ı. Knihovn´ı funkce jsou proto pˇreps´any tak, ˇze m´ısto blokuj´ıc´ıch vol´ an´ı se pouˇzij´ı neblokuj´ıc´ı a kontext se pˇrepne na jin´e vl´ akno.
Vytvoˇ ren´ı vl´ akna int pthread create(pthread t *thread, const pthread attr t *attr, void *(*start fun )(void*), void *arg ); • vytvoˇr´ı nov´e vl´ akno, do thread uloˇz´ı jeho ID. • nastav´ı atributy (velikost z´ asobn´ıku, pl´anovac´ı politika) podle attr (pouˇzije implicitn´ı atributy pˇri attr == NULL). • ve vytvoˇren´em vl´ aknu spust´ı funkci start fun() s argumentem arg. Po n´ avratu z t´eto funkce se zruˇs´ı vl´ akno. • s objekty pthread attr t lze manipulovat funkcemi pthread attr init(), pthread attr destroy(), pthread attr getstackaddr(), pthread attr setstackaddr(), ...
• pozor na konstrukce typu: for(i = 0; i < N; i++) pthread create(&tid, attr, start routine, &i); na prvn´ı pohled takto pˇred´ ame kaˇzd´emu vl´ aknu jeho -1. Jenˇze pl´anovaˇc m˚ uˇze zp˚ usobit to, ˇze neˇz si novˇe spuˇstˇen´e vl´ akno staˇc´ı pˇreˇc´ıst hodnotu i, pˇr´ıkaz for provede dalˇs´ı iteraci a hodnota se zmˇen´ı. Obecnˇe vl´ akno m˚ uˇze dostat m´ısto spr´ avn´e hodnoty i libovolnou vˇetˇs´ı. • co ale je moˇzn´e pouˇz´ıt, pokud potˇrebujeme pˇredat pouze jednu hodnotu, je toto: assert(sizeof(void *) >= sizeof(int)); for(i = 0; i < N; i++) pthread create(&tid, attr, start routine, (void *) i); . . . a ve funkci void *start routine(void *arg) pak pˇretypovat ukazatel zp´ atky na integer a m´ame potˇrebn´ y identifik´ator vl´ akna: printf("thread %d started\n", (int) arg);
183
• pokud potˇrebujeme pˇredat v´ıce bajt˚ u neˇz je velikost ukazatele, tak uˇz opravdu mus´ıme pˇredat ukazatel na pamˇet’ s pˇr´ısluˇsn´ ymi pˇred´ avan´ ymi daty nebo pouˇz´ıt glob´ aln´ı promˇenn´e.
Soukrom´ e atributy vl´ aken • ˇc´ıtaˇc instrukc´ı • z´ asobn´ık (automatick´e promˇenn´e) • thread ID, dostupn´e funkc´ı pthread t pthread self(void); • pl´anovac´ı priorita a politika • hodnota errno • kl´ıˇcovan´e hodnoty – dvojice (pthread key t key, void *ptr ) – kl´ıˇc vytvoˇren´ y vol´ an´ım pthread key create() je viditeln´ y ve vˇsech vl´ aknech procesu. – v kaˇzd´em vl´ aknu m˚ uˇze b´ yt s kl´ıˇcem asociov´ana jin´a hodnota vol´ an´ım pthread setspecific().
• kaˇzd´e vl´ akno m´a z´ asobn´ık pevn´e velikosti, kter´ y se automaticky nezvˇetˇsuje. • bˇeˇzn´e glob´ aln´ı promˇenn´e (a tak´e dynamicky alokovan´ a data) jsou spoleˇcn´e pro vˇsechna vl´ akna. Kl´ıˇcovan´e hodnoty pˇredstavuj´ı zp˚ usob, jak vytvoˇrit glob´ aln´ı promˇennou, kter´ a je ale lok´aln´ı v r´ amci vl´ akna. • kaˇzd´e vl´ akno m´a jeˇstˇe vlastn´ı sign´alovou masku, k tomu se tak´e dostaneme
184
Ukonˇ cen´ı vl´ akna void pthread exit(void *value ptr ); • Ukonˇc´ı volaj´ıc´ı vl´ akno. • Obdoba exit() pro proces int pthread join(pthread t thread, void **value ptr ); • poˇck´ a na ukonˇcen´ı vl´ akna thread a ve value ptr vr´ at´ı hodnotu ukazatele value ptr z vol´an´ı pthread exit() nebo n´ avratovou hodnotu hlavn´ı funkce vl´ akna. • obdoba ˇcek´ an´ı na synovsk´ y proces pomoc´ı wait() int pthread detach(pthread t thread ); • nastav´ı okamˇzit´e uvolnˇen´ı pamˇeti po ukonˇcen´ı vl´ akna, na vl´ akno nelze pouˇz´ıt pthread join().
• pokud nem´ ame v u ´ myslu po skonˇcen´ı vl´ akna volat pthread join(), je tˇreba zavolat pthread detach(). Jinak po ukonˇcen´em vl´ aknu zbydou v pamˇeti data nutn´a pro zjiˇstˇen´ı jeho v´ ysledku pomoc´ı pthread join(). To je podobn´ a situace, jako kdyˇz rodiˇcovsk´ y proces nepouˇz´ıv´ a wait() a v syst´emu se hromad´ı zombie. Ve funkci pro vl´ akno doporuˇcuji pouˇz´ıt toto: pthread detach(pthread self()); • ˇcekat na ukonˇcen´ı vl´ akna m˚ uˇze libovoln´e jin´e vl´ akno, nejen to, kter´e ho spustilo.
185
Inicializace int pthread once(pthread once t *once control, void (*init routine )(void)); av´ a ukazatel na staticky • v parametru once control se pˇred´ inicializovanou promˇennou pthread once t once control = PTHREAD ONCE INIT; • prvn´ı vl´ akno, kter´e zavol´ a pthread once(), provede inicializaˇcn´ı funkci init routine(). Ostatn´ı vl´ akna uˇz tuto funkci neprov´adˇej´ı, nav´ıc, pokud inicializaˇcn´ı funkce jeˇstˇe neskonˇcila, zablokuj´ı se a ˇcekaj´ı na jej´ı dokonˇcen´ı. • lze pouˇz´ıt napˇr. na dynamickou inicializaci glob´ aln´ıch dat v knihovn´ ach, jejichˇz k´od m˚ uˇze zavolat v´ıce vl´ aken souˇcasnˇe, ale je tˇreba zajistit, ˇze inicializace probˇehne jen jednou.
• variantou je inicializace glob´ aln´ıch dat jeˇstˇe pˇredt´ım, neˇz se proces rozdˇel´ı na vl´ akna, coˇz je bˇeˇznˇejˇs´ı zp˚ usob inicializace. • nen´ı definov´ano, co se m´a st´ at, pokud je once control automatick´a promˇenn´ a nebo nem´ a poˇzadovanou hodnotu.
186
Zruˇ sen´ı vl´ akna int pthread cancel(pthread t thread ); • poˇza´d´ a o zruˇsen´ı vl´ akna thread. Z´ avis´ı na nastaven´ı int pthread setcancelstate(int state, int *old ); • nastav´ı nov´ y stav a vr´ at´ı star´ y: – PTHREAD CANCEL ENABLE . . . povoleno zruˇsit – PTHREAD CANCEL DISABLE . . . nelze zruˇsit, ˇza´dost bude ˇcekat na povolen´ı int pthread setcanceltype(int type, int *old ); • PTHREAD CANCEL ASYNCHRONOUS . . . okamˇzit´e zruˇsen´ı • PTHREAD CANCEL DEFERRED . . . ˇza´dost ˇcek´ a na vstup do urˇcit´ ych funkc´ı (napˇr. open(), read(), wait()), nebo na vol´an´ı void pthread testcancel(void);
• vl´ akna je moˇzn´e zvenku ,,n´ asilnˇe” ruˇsit (obdoba ukonˇcen´ı procesu pomoc´ı sign´alu) bud’ okamˇzitˇe, nebo jen v urˇcit´ ych vol´an´ıch (tzv. cancellation points). To znamen´ a, ˇze v takov´em pˇr´ıpadˇe je moˇzn´e vl´ akno zruˇsit v ˇcase, kdy je vl´ akno vykon´ av´ a danou funkci. Pokud vl´ akno zrovna takovou funkci nevykon´ av´ a, informace o zruˇsen´ı se “doruˇc´ı” bˇehem vykon´ an´ı prvn´ı takov´e funkce od t´e doby. • funkce pthread_cancel() se podob´a zruˇsen´ı procesu sign´alem poslan´ ym vol´ an´ım kill(). • funkce pthread_setcancelstate() a pthread_setcanceltype() jsou obdobou zak´ az´an´ı a povolen´ı zruˇsen´ı procesu sign´alem pomoc´ı manipulace s maskou blokovan´ ych sign´al˚ u (sigprocmask()). • pˇri zruˇsen´ı vl´ akna se zavolaj´ı u ´ klidov´e handlery; viz d´ ale.
187
Pˇ r´ıklad: vl´ akna pthread_t id_a, id_b; void *res_a, *res_b; pthread_create(&id_a, NULL, do_a, "a"); pthread_create(&id_b, NULL, do_b, "b"); void *do_b(void *arg) { ... return arg; }
void *do_a(void *arg) { ... return arg; }
pthread_join(id_a, &res_a); pthread_join(id_b, &res_b);
• toto je trivi´ aln´ı pˇr´ıklad, kdy proces (hlavn´ı vl´ akno) vytvoˇr´ı dvˇe dalˇs´ı vl´ akna a poˇck´ a na jejich ukonˇcen´ı.
188
Soukrom´ e kl´ıˇ covan´ e hodnoty ve vl´ aknech int pthread key create(pthread key t *key, void (*destructor )(void *)); • vytvoˇr´ı kl´ıˇc, kter´ y lze asociovat s hodnotou typu (void *). Funkce destructor() se volaj´ı opakovanˇe pro vˇsechny kl´ıˇce, jejichˇz hodnota nen´ı NULL, pˇri ukonˇcen´ı vl´ akna. int pthread key delete(pthread key t key ); • Zruˇs´ı kl´ıˇc, nemˇen´ı asociovan´ a data. int pthread setspecific(pthread key t key, const void *value ); • pˇriˇrad´ı ukazatel value ke kl´ıˇci key. void *pthread getspecific(pthread key t key ); • vr´ at´ı hodnotu ukazatele pˇr´ısluˇsn´eho ke kl´ıˇci key.
• pˇri vytvoˇren´ı kl´ıˇce je s n´ım asociov´ana hodnota NULL. • pˇri ukonˇcen´ı nebo zruˇsen´ı vl´ akna se volaj´ı destruktory (v nespecifikovan´em poˇrad´ı) pro vˇsechny kl´ıˇce s hodnotou r˚ uznou od NULL. Aktu´aln´ı hodnota je destruktoru pˇred´ ana v parametru. Jestliˇze po skonˇcen´ı vˇsech destruktor˚ u zb´ yvaj´ı kl´ıˇce s hodnotou r˚ uznou od NULL, znovu se volaj´ı destruktory. Implementace m˚ uˇze (ale nemus´ı) zastavit vol´an´ı destruktor˚ u po PTHREAD DESTRUCTOR ITERATIONS iterac´ıch. Destruktor by tedy mˇel nastavit hodnotu na NULL, jinak hroz´ı nekoneˇcn´ y cyklus. • destruktor si mus´ı s´ am zjistit kl´ıˇc poloˇzky, ke kter´e patˇr´ı, a zruˇsit hodnotu vol´ an´ım pthread setspecific(key, NULL). • SUSv3 tento nesmysln´ y poˇzadavek odstraˇ nuje, protoˇze definuje, ˇze pˇred vstupem do destruktoru je hodnota automaticky nastaven´ a na NULL; destruktor se n´ aslednˇe vyvol´ a s pˇredchoz´ı hodnotou kl´ıˇce.
189
´ Uklid pˇ ri ukonˇ cen´ı/zruˇ sen´ı vl´ akna • vl´ akno m´a z´ asobn´ık u ´ klidov´ ych handler˚ u, kter´e se volaj´ı pˇri ukonˇcen´ı nebo zruˇsen´ı vl´ akna funkcemi pthread exit() a pthread cancel(). Jako prvn´ı se spouˇst´ı naposledy vloˇzen´ y handler. • po proveden´ı handler˚ u se volaj´ı destruktory priv´ atn´ıch kl´ıˇcovan´ ych dat vl´ akna. void pthread cleanup push(void (*routine )(void *), void *arg ); • vloˇz´ı handler do z´ asobn´ıku. void pthread cleanup pop(int execute ); • vyjme naposledy vloˇzen´ y handler ze z´ asobn´ıku. Provede ho, pokud je execute nenulov´e.
• handlery se volaj´ı jako routine(arg). • tyto handlery se daj´ı pouˇz´ıvat napˇr. na u ´ klid lok´aln´ıch dat funkc´ı (obdoba vol´ an´ı destruktor˚ u pro automatick´e promˇenn´e v C++).
190
fork() a vl´ akna (POSIX) • je nutn´e definovat s´emantiku vol´an´ı fork v aplikac´ıch pouˇz´ıvaj´ıc´ıch vl´ akna. Norma definuje, ˇze: – nov´ y proces obsahuje pˇresnou kopii volaj´ıc´ıho vl´ akna, vˇcetnˇe pˇr´ıpadn´ ych stav˚ u mutex˚ u a jin´ ych zdroj˚ u – ostatn´ı vl´ akna v synovsk´em procesu neexistuj´ı – pokud takov´a vl´ akna mˇela naalokovanou pamˇet’, z˚ ustane tato pamˇet’ naalokovan´ a (= ztracen´a) – obdobnˇe to plat´ı pro zamˇcen´ y mutex jiˇz neexistuj´ıc´ıho vl´ akna • vytvoˇren´ı nov´eho procesu z multivl´ aknov´e aplikace m´ a smysl pro n´ asledn´e vol´ an´ı exec (tj. vˇcetnˇe vol´an´ı popen, system apod.)
• pokud mˇelo jiˇz neexistuj´ıc´ı vl´ akno zamˇcen´ y mutex, tak je pˇr´ıstup k pˇr´ısluˇsn´ ym sd´ılen´ ym dat˚ um ztracen, protoˇze zamˇcen´ y mutex m˚ uˇze odemknout pouze to vl´ akno, kter´e ho zamknulo. • ostatn´ı vl´ akna pˇrestanou existovat, nevolaj´ı se ˇza´dn´e rutiny jako pˇri vol´an´ı pthread exit, pthread cancel nebo destruktory kl´ıˇcovan´ ych dat • je moˇzn´e pouˇz´ıt handlery pro fork pomoc´ı funkce pthread atfork. To my potˇrebovat nebudeme, z´ ajemce odkazuji napˇr´ıklad na [Butenhof]. • pozor na to, ˇze chov´an´ı fork() tak´e z´ avis´ı na pouˇzit´e knihovnˇe a verzi syst´emu, napˇr´ıklad na Solarisu pˇred verz´ı 10 znamenalo fork() v knihovnˇe libthread (jin´ a knihovna neˇz libpthread) to sam´e co forkall()
191
Sign´ aly a vl´ akna • sign´aly mohou b´ yt generov´any pro proces (vol´an´ım kill()), nebo pro vl´ akno (chybov´e ud´ alosti, vol´an´ı pthread kill()). • nastaven´ı obsluhy sign´al˚ u je stejn´e pro vˇsechna vl´ akna procesu, ale masku blokovan´ ych sign´al˚ u m´a kaˇzd´e vl´ akno vlastn´ı, nastavuje se funkc´ı int pthread sigmask(int how, const sigset t *set, sigset t *oset ); • sign´al urˇcen´ y pro proces oˇsetˇr´ı vˇzdy pr´avˇe jedno vl´ akno, kter´e nem´ a tento sign´al zablokovan´ y. • lze vyhradit jedno vl´ akno pro synchronn´ı pˇr´ıjem sign´al˚ u pomoc´ı vol´ an´ı sigwait(). V ostatn´ıch vl´ aknech se sign´aly zablokuj´ı.
• jestliˇze je pro sign´al nastaveno ukonˇcen´ı procesu, skonˇc´ı cel´ y proces, nejen jedno vl´ akno. • vytvoˇren´e vl´ akno dˇed´ı nastaven´ı sign´alov´e masky od vl´ akna, kter´e ho vytvoˇrilo • pˇri pouˇzit´ı sigwait tak vlastnˇe zablokujete pˇr´ısluˇsn´e sign´aly ve vˇsech vl´ aknech, vˇcetnˇe vl´ akna, ve kter´em chcete zpracov´av´ avat sign´aly. Viz pˇredn´ aˇska o sign´alech. Tento zp˚ usob zpracov´an´ı sign´al˚ u b´ yv´ a ˇcasto jedin´ y opravdu doporuˇcovan´ y pro vl´ akna, nav´ıc je i nejsn´ aze implementovateln´ y. • v re´ aln´e aplikaci nen´ı rozumn´e blokovat vˇsechny sign´ aly, tj. vˇcetnˇe sign´al˚ u pos´ılan´ ych jako ozn´ amen´ı chyb – SEGV, FPE atd. Viz pˇredn´ aˇska o sign´alech.
192
Synchronizace vl´ aken: mutexes (1) • nejjednoduˇsˇs´ı zp˚ usob zajiˇstˇen´ı synchronizovan´eho pˇr´ıstupu ke sd´ılen´ ym dat˚ um mezi vl´ akny je pouˇzit´ım mutexu • statick´e vers. dynamick´e mutexy • statick´a inicializace mutexu: pthread mutex t mutex = PTHREAD MUTEX INITIALIZER • inicializace mutexu mx s atributy attr (nastavuj´ı se pomoc´ı pthread mutexattr ...(), NULL = default) int pthread mutex init(pthread mutex t *mx, const pthread mutexattr t *attr ); • po skonˇcen´ı pouˇz´ıv´ an´ı mutexu je nutn´e ho zruˇsit: int pthread mutex destroy(pthread mutex t *mx );
• mutex = mutual exclusion (vz´ ajemn´e vylouˇcen´ı) • je to speci´ aln´ı forma Dijkstrov´ ych semafor˚ u – rozd´ıl mezi mutexy a bin´arn´ımi semafory je ten, ˇze zamˇcen´ y mutex m˚ uˇze odemknout pouze to vl´ akno, kter´e ho zamklo. To u semafor˚ u neplat´ı. • mutexy jsou urˇcen´e pouze ke kr´ atkodob´emu drˇzen´ı a mˇely by fungovat rychle. Slouˇz´ı pro implementaci kritick´ ych sekc´ı, podobnˇe jako lock-soubory nebo semafory (pouˇzit´e jako z´ amky). • inicializace statick´eho mutexu pomoc´ı zm´ınˇen´eho makra nastav´ı pro mutex jeho defaultn´ı atributy. • dynamick´e mutexy m˚ uˇzeme potˇrebovat napˇr´ıklad v situaci, kdy dynamicky alokujeme datovou strukturu, jej´ıˇz souˇca´st´ı je i mutex, kter´ y sd´ılen´a data struktury chr´an´ı. I zde, pˇred zavol´an´ım funkce free na datovou strukturu, by se mˇelo pouˇz´ıt vol´ an´ı pro zruˇsen´ı mutexu. • je moˇzn´e dynamicky inicializovat i statick´e mutexy, ale je tˇreba zajistit, ˇze se vˇzdy inicializuj´ı pˇred pouˇzit´ım (to samozˇrejmˇe plat´ı i pro dynamick´e mutexy) a ˇze se inicializuj´ı pouze jednou • kop´ırovat mutexy nen´ı korektn´ı – v´ ysledek takov´e operace nen´ı definov´an. Je moˇzn´e samozˇrejmˇe zkop´ırovat ukazatel na mutex a s t´ımto ukazatelem pak d´ ale pracovat.
193
Mutexes (2) • pro zamˇcen´ı a odemˇcen´ı mutexu pouˇzijeme vol´an´ı: int pthread mutex lock(pthread mutex t *mx ); a int pthread mutex unlock(pthread mutex t *mx ); • pokud je mutex jiˇz zamˇcen´ y, pokus o zamknut´ı vy´ ust´ı v zablokov´an´ı vl´ akna. Je moˇzn´e pouˇz´ıt i vol´an´ı: int pthread mutex trylock(pthread mutex t *mx ); . . . kter´e se pokus´ı zamknout mutex, a pokud to nelze prov´est, skonˇc´ı s chybou
• nelze zamknout mutex, pokud ho dan´e vl´ akno jiˇz zamˇcen´e m´a. Nˇekdy m˚ uˇze doj´ıt i k self dead-locku. • nelze odemknout nezamˇcen´ y mutex • nelze odemknout mutex, kter´ y zamknulo jin´e vl´ akno. Pokud je toto potˇreba, pouˇzijte bin´ arn´ı semafor. • pˇri vytv´aˇren´ı aplikace, kde je efektivita kl´ıˇcov´ ym faktorem, je nutn´e rozmyslet, jak, kde a kolik mutex˚ u pouˇz´ıvat. Z knihovny, kter´ a nebyla napsan´a pro pouˇzit´ı v aplikac´ıch pouˇz´ıvaj´ıc´ı vl´ akna, je moˇzn´e udˇelat thread-safe knihovnu tak, ˇze pˇred zavol´ an´ım jak´ekoli funkce knihovny zamknu jeden giant mutex a po skonˇcen´ı funkce ho odemknu. M´ am tedy pouze jeden mutex a ˇsetˇr´ım tak ˇcas, ale vl´ akna pouˇz´ıvaj´ıc´ı tuto knihovnu ˇcasto sp´ı pˇri ˇcek´ an´ı na pˇr´ıstup. Na druhou stranu, pokud zamyk´ am pˇr´ıstup ke konkr´etn´ım, mal´ ym sekc´ım, mohu potˇrebovat spoustu mutex˚ u a hodnˇe ˇcasu str´ av´ım ve funkc´ıch kter´e s mutexy pracuj´ı. Je proto ˇcasto nutn´e podle situace zvolit vhodn´ y kompromis.
194
Podm´ınkov´ e promˇ enn´ e (1) • mutexy slouˇz´ı pro synchronizaci pˇr´ıstupu ke sd´ılen´ ym dat˚ um • podm´ınkov´e promˇenn´e pak k pˇred´ an´ı informac´ı o tˇechto sd´ılen´ ych datech • z toho plyne, ˇze kaˇ zd´ a podm´ınkov´ a promˇ enn´ a je vˇ zdy asociov´ ana s jedn´ım mutexem • jeden mutex m˚ uˇze b´ yt asociov´an s v´ıce podm´ınkov´ ymi promˇenn´ ymi • spoleˇcnˇe pomoc´ı mutex˚ u a podm´ınkov´ ych promˇenn´ ych je moˇzn´e vytv´aˇret dalˇs´ı synchronizaˇcn´ı primitiva – semafory – bari´ery – ...
• jin´ ymi slovy – podm´ınkov´e promˇenn´e se pouˇz´ıvaj´ı v situaci, kdy vl´ akno potˇrebuje otestovat nˇejakou podm´ınku nad sd´ılen´ ymi daty (napˇr. stav ˇc´ıtaˇce poloˇzek ve frontˇe), a uspat se, kdyˇz nen´ı splnˇena. Sp´ıc´ı vl´ akno m˚ uˇze b´ yt probuzeno jin´ ym vl´ aknem, kter´e zmˇen´ı datovou strukturu tak, ˇze podm´ınka bude splnˇena. Dan´e vl´ akno vˇsak mus´ı explicitnˇe ozn´amit, ˇze data zmˇenilo. • nen´ı to tak, ˇze pˇri deklaraci podm´ınkov´e promˇenn´e, coˇz je pro program´ atora zcela transparentn´ı typ, definujete podm´ınku napˇr. “n je vˇetˇs´ı neˇz 7 ”. Podm´ınkovou promˇennou totiˇz m˚ uˇzete pˇrirovnat k praporu nˇejak´e barvy, a pokud ho zvednete, znamen´ a to, ˇze ta vl´ akna, kter´ a ˇcekaj´ı aˇz s t´ımto praporem nˇekdo zam´ av´ a nad hlavou, jsou o t´eto situaci informov´ana a mohou se podle toho zaˇr´ıdit. Nˇekter´ a vl´ akna tak mohou ˇcekat na to, aˇz n bude vˇetˇs´ı neˇz 7, jin´a mohou ˇcekat pouze na to, aˇz se n jakkoli zmˇen´ı. Je pouze na program´ atorovi, zda pro kaˇzdou konkr´etn´ı situaci pouˇzije jednu podm´ınkovou promˇennou, nebo jednu pro vˇsechny situace dohromady. Pro druhou situaci plat´ı, ˇze vl´ akna ˇcekaj´ıc´ı na nˇejakou ud´ alost mus´ı vˇzdy otestovat, kter´ a ze situac´ı nastala. Pokud ne ta, na kterou vl´ akno ˇcek´ a, znovu se usp´ı. Jak je vˇsak uvedeno d´ ale, z implementaˇcn´ıch d˚ uvod˚ u je nutn´e podm´ınku otestovat vˇzdy, i kdyˇz pro ni pouˇz´ıv´ ate samostatnou podm´ınkovou promˇennou – m˚ uˇze se st´ at, ˇze syst´em t´ım praporem zam´ av´ a s´ am, aniˇz by to udˇelalo to vl´ akno, kter´e v naˇs´ı n´ azorn´e situaci ten ˇc´ıtaˇc n mˇen´ı a n´ aslednˇe to oznamuje.
195
Podm´ınkov´ e promˇ enn´ e (2) int pthread cond init(pthread cond t *cond, const pthread condattr t *attr ); • Inicializuje podm´ınkovou promˇennou cond s atributy attr (nastavuj´ı je funkce pthread condattr ...()), NULL = default. int pthread cond destroy(pthread cond t *cond ); • zruˇs´ı podm´ınkovou promˇennou. int pthread cond wait(pthread cond t *cond, pthread mutex t *mutex ); • ˇcek´ a na podm´ınkov´e promˇenn´e dokud jin´e vl´ akno nezavol´a pthread cond signal() nebo pthread cond broadcast().
• je nutn´e, aby po t´e, co vl´ akno zamkne mutex a dˇr´ıve, neˇz vl´ akno zavol´a pthread cond wait, otestovat podm´ınku. Pokud tuhle operaci vl´ akno neprovede, mohlo by se uspat na neurˇcitou dobu, protoˇze hl´aˇska o splnˇen´ı podm´ınky od jin´eho vl´ akna by proˇsla “bez povˇsimnut´ı”. Jinak ˇreˇceno, nesm´ım se uspat pˇri ˇcek´ an´ı na situaci, kter´ a uˇz mezit´ım nastala. Nefunguje to jako sign´aly, kter´e pro v´as syst´em drˇz´ı, pokud je m´ate napˇr´ıklad zablokovan´e. Co je d˚ uleˇzit´e je to, ˇze ten test prov´ad´ıte pod ochranou mutexu, tedy si opravdu m˚ uˇzete b´ yt jist´ı hodnotou naˇseho ˇc´ıtaˇce n.
196
Podm´ınkov´ e promˇ enn´ e (3) int pthread cond timedwait(pthread cond t *cond, pthread mutex t *mutex, const struct timespec *abstime ); • ˇcek´ a na pthread cond signal() nebo pthread cond broadcast(), ale maxim´ alnˇe do vyprˇsen´ı timeoutu abstime. int pthread cond signal(pthread cond t *cond ); • probud´ı jeden proces ˇcekaj´ıc´ı na podm´ınkov´e promˇenn´e cond. int pthread cond broadcast(pthread cond t *cond ); • probud´ı vˇsechny procesy ˇcekaj´ıc´ı na podm´ınkov´e promˇenn´e cond.
• v parametru abstime funkce pthread cond timedwait() se pˇred´ av´ a absolutn´ı ˇcas, tj. timeout vyprˇs´ı, kdyˇz syst´emov´ y ˇcas dos´ ahne hodnoty vˇetˇs´ı nebo rovn´e abstime. Pro absolutn´ı ˇcas bylo rozhodnuto proto, aby program´ ator nemusel pˇri pˇr´ıpadn´ ych probuzen´ıch, kdy n´ aslednˇe zjist´ı, ˇze dan´a podm´ınka neplat´ı, pˇrepoˇc´ıt´ avat ˇcasov´ y rozd´ıl. • jak jiˇz bylo ˇreˇceno, jedna podm´ınkov´a promˇenn´a m˚ uˇze b´ yt pouˇzita pro hl´aˇsen´ı nˇekolika rozd´ıln´ ych situac´ı najednou – napˇr´ıklad pˇri vloˇzen´ı prvku do fronty i pˇri jeho vyjmut´ı. Z toho d˚ uvodu je nutn´e po probuzen´ı otestovat podm´ınku, na kterou se ˇcek´ a. Dalˇs´ı vˇec, kter´ a z toho vych´ az´ı je ta, ˇze v takov´em pˇr´ıpadˇe mus´ıte pouˇz´ıt broadcast. Staˇc´ı si pˇredstavit n´ asleduj´ıc´ı situaci – ˇcek´ ate na podm´ınku “zmˇenil se stav fronty”, na kter´e ˇcekaj´ı ˇcten´ aˇri i zapisovatel´e (pˇredpokl´adejme, ˇze jednu frontu pouˇz´ıv´ a v´ıce ˇcten´ aˇr˚ u i v´ıce zapisovatel˚ u). Pokud po vloˇzen´ı zpr´ avy pouze vypust´ıte jednu signalizaci, tak se m˚ uˇze st´at, ˇze tato signalizace probud´ı jin´eho zapisovatele, kter´ y ale samozˇrejmˇe ˇcek´ a na situaci, kdy ˇcten´ aˇr z fronty zpr´ avu odebere. T´ım se stane, ˇze ve frontˇe z˚ ustane zpr´ ava, kter´ a m˚ uˇze b´ yt vytlaˇcena aˇz dalˇs´ı signalizac´ı. • vl´ akno m˚ uˇze b´ yt probuzeno jin´ ym vl´ aknem i v pˇr´ıpadˇe, ˇze ˇcek´ a na jednu konkr´etn´ı ud´ alost, kter´ a vˇsak po probuzen´ı vl´ akna jiˇz neplat´ı a pˇritom pro jin´e ud´ alosti pˇr´ısluˇsn´a podm´ınkov´a promˇenn´a nen´ı pouˇzita (a pˇritom nebyl pouˇzit broadcast!). Pˇredstavte si, ˇze tˇesnˇe po t´e, kdy jin´e vl´ akno zahl´ as´ı splnˇen´ı podm´ınky, m˚ uˇze dalˇs´ı vl´ akno zamknout mutex a nˇejakou akc´ı, napˇr. vyjmut´ım prvku z fronty, zruˇsit platnost podm´ınky “ve frontˇe je zpr´ ava”. To vl´ akno, kter´e je probuzeno, tak najde frontu pr´azdnou. Dalˇs´ı d˚ uvod pro to, ˇze podm´ınkov´e promˇenn´e je nutn´e vˇ zdy testovat v cyklu. • v ˇr´ıdk´ ych pˇr´ıpadech je moˇzn´e, ˇze se vl´ akno probud´ı a podm´ınka nen´ı platn´a i d´ıky konkr´etn´ı implementaci. Z toho opˇet vypl´ yv´ a nutnost pouˇzit´ı cyklu. 197
Pouˇ zit´ı podm´ınkov´ ych promˇ enn´ ych pthread cond t cond; pthread mutex t mutex; ... pthread mutex lock(&mutex); while(!podminka(data)) pthread cond wait(&cond, &mutex); set data(data, ...); pthread mutex unlock(&mutex); ... pthread mutex lock(&mutex); set data(data, ...); pthread cond signal(&cond); pthread mutex unlock(&mutex);
• pro zopakov´an´ı – ke kaˇzd´e podm´ınkov´e promˇenn´e je potˇreba m´ıt jeˇstˇe mutex. • funkce pthread cond wait() atomicky odemkne mutex a usp´ı vl´ akno. Kdyˇz je vl´ akno probuzeno, nejprve se znovu zamkne mutex (tj. toto zamknut´ı se provede v r´ amci pˇr´ısluˇsn´e implementace podm´ınkov´ ych promˇenn´ ych!) a teat´ı. prve pak se vol´ an´ı pthread cond wait() vr´ • kdyˇz signalizujeme, ˇze se nˇeco zmˇenilo, neznamen´a to jeˇstˇe, ˇze po zmˇenˇe bude platit podm´ınka. Nav´ıc, pthread cond wait() se m˚ uˇze vr´ atit, i kdyˇz nebylo vol´ ano ani pthread cond signal() ani pthread cond broadcast(). uˇze tedy doj´ıt, i K probuzen´ı vl´ akna a n´ avratu z pthread cond wait() m˚ kdyˇz podm´ınka nen´ı splnˇena, proto je potˇreba znovu otestovat podm´ınku a pˇr´ıpadnˇe obnovit ˇcek´ an´ı. • vˇsimnˇete si, ˇze odemknut´ı z´ amku n´ asleduje aˇz po signalizaci podm´ınky
198
Read-write z´ amky (1) int pthread rwlock init(pthread rwlock t *l, const pthread rwlockattr t *attr ); • vytvoˇr´ı z´ amek s atributy podle attr (nastavuj´ı se funkcemi pthread rwlockattr ...(), NULL = default) int pthread rwlock destroy(pthread rwlock t *l ); • zruˇs´ı z´ amek int pthread rwlock rdlock(pthread rwlock t *l ); int pthread rwlock tryrdlock(pthread rwlock t *rwlock ); • zamkne z´ amek pro ˇcten´ı (v´ıce vl´ aken m˚ uˇze drˇzet z´ amek pro ˇcten´ı), pokud m´a nˇekdo z´ amek pro z´ apis, usp´ı volaj´ıc´ı vl´ akno (rdlock()) resp. vr´ at´ı chybu (tryrdlock()).
• najednou m˚ uˇze m´ıt z´ amek bud’ nˇekolik vl´ aken zamˇceno pro ˇcten´ı, nebo maxim´ alnˇe jedno vl´ akno zamˇceno pro z´ apis (a nikdo pro ˇcten´ı). • read-write z´ amky jsou obdobou zamyk´ an´ı soubor˚ u pomoc´ı fcntl().
199
Read-write z´ amky (2) int pthread rwlock wrlock(pthread rwlock t *rwlock ); • zamkne z´ amek pro z´ apis, pokud m´a nˇekdo z´ amek pro ˇcten´ı nebo z´ apis, ˇcek´ a. int pthread rwlock trywrlock(pthread rwlock t *rwlock ); uˇze zamknout, • jako pthread rwlock wrlock(), ale kdyˇz nem˚ vr´ at´ı chybu. int pthread rwlock unlock(pthread rwlock t *rwlock ); • odemkne z´ amek
• Zvl´ aˇstnost : pokud vl´ akno ˇcekaj´ıc´ı na z´ amek dostane sign´al, po n´ avratu z handleru se vˇzdy pokraˇcuje v ˇcek´ an´ı, tj. nenastane chyba EINTR. Takto se chovaj´ı i mutexy a podm´ınkov´e promˇenn´e. • SUSv3 k probran´ ym mechanism˚ um synchronizace vl´ aken pˇrid´ av´ a jeˇstˇe bari´erovou synchronizaci (funkce pthread barrier wait() a nˇekolik pomocn´ ych funkc´ı).
200
atomic add(3c) • pro architektury, kde operace sˇc´ıt´an´ı nen´ı atomick´a • od Solarisu 10 • v´ yraznˇe rychlejˇs´ı neˇz jin´e mechanismy pro z´ısk´an´ı exkluzivn´ıho pˇr´ıstupu • nutn´e pouˇz´ıt hlaviˇckov´ y soubor atomic.h • sada vol´ an´ı pro r˚ uzn´e celoˇc´ıseln´e typy, napˇr´ıklad: void atomic add int(volatile uint t *target, int delta); void atomic add 8(volatile uint8 t *target, int8 t delta); . . . a dalˇs´ı
pˇr´ıklad: program spust´ı dvˇe vl´ akna, kaˇzd´e vl´ akno pracuje se stejnou glob´ aln´ı promˇennou x, do kter´e v cyklu postupnˇe pˇriˇcte ˇc´ısla od jedn´e do velikosti parametru, kter´e program dostane na vstupn´ı ˇra´dce. Vl´akna bˇeˇz´ı paralelnˇe. for (i = 0; i < arg; ++i) x = x + i; pot´e se sˇc´ıt´ an´ı pro kontrolu provede v hlavn´ım vl´ aknu, a dvojn´ asobek se porovn´a s diskutovanou glob´ aln´ı promˇennou. Pokud nejsou v´ ysledky stejn´e, doˇslo k soubˇehu (pˇreteˇcen´ı promˇenn´e m˚ uˇzeme v t´eto situaci zcela ignorovat). Zde jsou v´ ysledky a ˇcasy bˇehu pro situace, kdy program pouˇzil obyˇcejn´e sˇc´ıt´an´ı, pot´e funkci atomic add int() a n´ aslednˇe zamyk´ an´ı pomoc´ı mutex˚ u. Testov´ano na multiprocesorov´em stroji UltraSparc T1000, zdroj´ak je v sekci uk´azkov´ ych pˇr´ıklad˚ u v adres´ aˇri race-condition pod jm´enem atomic-add.c: $ time ./a.out 99999999 RACE DETECTED result is 193053170 should be 1574919426 real user sys
0m2.816s 0m4.807s 0m0.009s
$ time ./a.out -a 99999999 everything OK, no race detected result is 1574919426 should be 1574919426
201
real user sys
0m9.628s 0m18.431s 0m0.010s
$ time ./a.out -m 99999999 everything OK, no race detected result is 1574919426 should be 1574919426 real user sys
1m23.558s 2m45.978s 0m0.088s
Bari´ era, semafory • bari´era (barrier ) je zp˚ usob, jak udrˇzet ˇcleny skupiny pohromadˇe • vˇsechna vl´ akna ˇcekaj´ı na bari´eˇre, dokud ji nedos´ahne posledn´ı vl´ akno; pak mohou pokraˇcovat • typick´e pouˇzit´ı je paraleln´ı zpracov´an´ı dat na multiprocesorech • bari´ery nemaj´ı API, je moˇzn´e je vytvoˇrit pomoc´ı mutex˚ ua podm´ınkov´ ych promˇenn´ ych
• semafory poch´ az´ı pˇr´ımo z POSIXu • jm´ena funkc´ı nezaˇc´ınaj´ı pthread , ale sem (sem init, sem post, sem wait, . . . ) • je moˇzn´e je pouˇz´ıt s vl´ akny
• bari´eru m˚ uˇzete vyuˇz´ıt napˇr. v situaci, kdy mezi jednotliv´ ymi f´azemi zpracov´an´ı je potˇreba prov´est jistou inicializaci, vl´ akna pˇred n´ı tedy na sebe vˇzdy mus´ı poˇckat, protoˇze inicializace dalˇs´ı f´aze m˚ uˇze zaˇc´ıt aˇz tehdy, kdy skonˇc´ı f´aze pˇredchoz´ı. • podm´ınka pro bari´eru je tˇreba hodnota ˇc´ıtaˇce rovnaj´ıc´ı se nule. Kaˇzd´e vl´ akno, kter´e dos´ ahne bari´ery, sn´ıˇz´ı ˇc´ıtaˇc, kter´ y je na zaˇca´tku inicializov´an na poˇcet vl´ aken. Pokud vl´ akno po dekrementov´an´ı ˇc´ıtaˇce zjist´ı, ˇze jeˇstˇe nen´ı roven nule, usp´ı se na podm´ınkov´e promˇenn´e. Pokud dan´e vl´ akno je t´ım vl´ aknem, kter´e sn´ıˇz´ı ˇc´ıtaˇc na nulu, m´ısto zavol´an´ı pthread cond wait poˇsle broadcast, kter´ y n´ aslednˇe probud´ı vˇsechna vl´ akna sp´ıc´ı na bari´eˇre (signalling zde nestaˇc´ı, chcete probudit vˇsechna vl´ akna, ne jen jedno!). Pˇred spuˇstˇen´ım dalˇs´ı f´aze zpracov´an´ı se ˇc´ıtaˇc reinicializuje na p˚ uvodn´ı hodnotu. I zde je nutn´e ˇreˇsit r˚ uzn´e probl´emy, napˇr´ıklad nen´ı moˇzn´e jen tak reinicializovat ˇc´ıtaˇc pot´e, co bari´ery dos´ ahne posledn´ı vl´ akno, protoˇze jak jiˇz v´ıme, vl´ akna po probuzen´ı
202
z pthread cond wait mus´ı vˇzdy otestovat, zda ˇc´ıtaˇc je opravdu nulov´ y a pokud nen´ı, opˇet se usp´ı. Takˇze by se v´am mohlo st´at, ˇze by se probudila jen nˇekter´ a vl´ akna, nebo taky ˇza´dn´a. Jak byste to ˇreˇsili? • funkce pro semafory se drˇz´ı klasick´e UNIXov´e s´emantiky – pˇri chybˇe vracej´ı -1 a nastav´ı errno
Typick´ e pouˇ zit´ı vl´ aken • pipeline – kaˇzd´e z vl´ aken prov´ad´ı svoji operaci nad daty, kter´ a se postupnˇe pˇred´ avaj´ı mezi vl´ akny – kaˇzd´e vl´ akno typicky prov´ad´ı jinou operaci . . . zpracov´an´ı obr´ azku, kde kaˇzd´e vl´ akno provede jin´ y filtr • work crew – vl´ akna prov´adˇej´ı stejnou operaci, ale nad jin´ ymi daty . . . zpracov´an´ı obr´ azku dekompozic´ı – kaˇzd´e vl´ akno zpracov´av´ a jinou ˇca´st obr´ azku, v´ ysledkem je spojen´ı zpracovan´ ych dat ze vˇsech vl´ aken; zde se hod´ı ˇreˇsen´ı s bari´erou • client – server
• dan´e rozdˇelen´ı je jen orientaˇcn´ı, pouˇzit´ı vl´ aken je samozˇrejmˇe neomezen´e, toto jsou asi ty tˇri nejˇcastˇejˇs´ı pouˇzit´ı
203
Thread-safe, reentrantn´ı funkce • thead-safe znamen´ a, ˇze k´od m˚ uˇze b´ yt vol´an z v´ıce vl´ aken najednou bez destruktivn´ıch n´ asledk˚ u – do funkce, kter´ a nebyla navrˇzena jako thread-safe, je moˇzn´e pˇridat jeden z´ amek – na zaˇca´tku funkce se zamkne, na konci odemkne – tento zp˚ usob ale samozˇrejmˇe nen´ı efektivn´ı. . . • slovem reentrantn´ı se ˇcasto mysl´ı, ˇze dan´a funkce byla navrˇzena s pˇrihl´ednut´ım na existenci vl´ aken – . . . tedy ˇze dan´a funkce pracuje efektivnˇe i ve v´ıcevl´aknov´em prostˇred´ı – takov´a funkce by se mˇela vyvarovat pouˇzit´ı statick´ ych dat a pokud moˇzno i prostˇredk˚ u pro synchronizaci vl´ aken, kter´e jinak zpomaluj´ı bˇeh aplikace
• v dneˇsn´ı dobˇe thread-safe vˇetˇsinou znamen´ a reentrantn´ı, tj. funkce jsou pˇreps´any tak, aby pracovaly efektivnˇe i s vl´ akny, ale je dobr´e vˇedˇet, ˇze nˇekdy to m˚ uˇze vyjadˇrovat rozd´ıl.
204
Nepˇ renositeln´ a vol´ an´ı • nepˇrenositeln´ a vol´ an´ı konˇc´ı ˇretˇezcem np (non-portable) • jednotliv´e syst´emy si takto definuj´ı vlastn´ı vol´an´ı • FreeBSD – pthread set name np(pthread t tid, const char *name) → umoˇzn ˇ uje pojmenovat vl´ akno • Solaris – pthread cond reltimedwait np(...) → jako timedwait, ale ˇcasov´ y timeout je relativn´ı • OpenBSD – int pthread main np(void) → umoˇzn ˇ uje zjistit, zda volaj´ıc´ı vl´ akno je hlavn´ı (= main())
• tyto informace jsou pro zaj´ımavost, abyste vˇedˇeli, ˇze se s podobn´ ymi vˇecmi m˚ uˇzete setkat. Nepˇrenositeln´ a vol´an´ı by se mˇela pouˇz´ıvat sp´ıˇse pro lad´ıc´ı u ´ˇcely. Nikdy nev´ıte, kdo bude potˇrebovat v´aˇs k´od spustit na jin´em syst´emu, coˇz se stane typicky a neˇcekanˇe po t´e, co zrovna opust´ıte vaˇsi spoleˇcnost a nem´ ate jiˇz ˇcas to opravit. • zjistit, jak´ a nepˇrenositeln´ a vol´ an´ı v´aˇs syst´em poskytuje je jednoduch´e, tˇreba pomoc´ı apropos np
205
Obsah • u ´ vod, v´ yvoj UNIXu a C, program´ atorsk´e n´ astroje • z´ akladn´ı pojmy a konvence UNIXu a jeho API • pˇr´ıstupov´a pr´ava, perifern´ı zaˇr´ızen´ı, syst´em soubor˚ u • manipulace s procesy, spouˇstˇen´ı program˚ u • sign´aly • synchronizace a komunikace proces˚ u • s´ıt’ov´a komunikace • vl´ akna, synchronizace vl´ aken • UNIX z pohledu spr´ avce • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu
tato kapitola bude pravdˇ epodobnˇ e nahrazena
206
ChangeLog 2007-12-12 pˇrid´ ano mnoho nov´ ych pozn´ amek ke slajd˚ um o s´ıt’ov´ e komunikaci. aken, p201. 2007-12-09 pˇrid´ an slajd pro atomic add(3c) v sekci synchronizace vl´ 2007-11-21 r˚ uzn´ a mal´ a doplnˇ en´ı k sign´ al˚ um (ve slajdech i pozn´ amk´ ach) 2007-11-18 opraveny dalˇs´ı v´ yskyty znaku ‘0’ m´ısto ˇretˇ ezce ‘len’, tentokr´ at na slajdech k mmap vol´ an´ı. 2007-11-07 doplnˇ eny pozn´ amky o soft updates (pˇrid´ an konkr´ etn´ı pˇr´ıklad) 2007-10-23 nov´ y slajd ,,API vers ABI”, p33, a doplnˇ en´ı pozn´ amek k z´ apisu do pojmenovan´ e roury, p85 2007-10-15 doplnˇ eny pozn´ amky ke slajd˚ um o promˇ enn´ ych prostˇred´ı a ˇ cten´ı/z´ apisu z/do roury (tam byly i faktick´ e chyby). 2007-10-07 ˇ ca ´steˇ cnˇ e pˇreps´ any pozn´ amky ke slajd˚ um o historii unixu, standardech a souˇ casn´ ych syst´ emech; doplnˇ eny pozn´ amky k dynamick´ emu linkeru, debugger˚ um a C. 2007-09-16 vyˇrazena kapitola o administraci, bude nahrazeno nˇ eˇ c´ım jin´ ym, pravdˇ epodobnˇ e aplikac´ı autoconf. 2007-09-16 opravy pˇreklep˚ u, gramatick´ ych chyb a pod. (d´ıky frido at devnull dot cz) 2007-07-18 generov´ an´ı tohoto textu bylo pˇresunuto z FreeBSD na Solaris a tak´ e jsem pˇreˇsel z CsLaTeX na Babel. M˚ uˇ ze to ovlivnit nˇ ekter´ e fonty a t´ım i sazbu textu.
207
Konec
208