Programov´an´ıv UNIXu

Jan Pechanec

verze: 17. prosince 2007

(c) 1999 – 2004 Martin Beran (c) 2005 – 2007 Jan Pechanec

SISAL MFF UK, Malostransk´en´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 • vˇsechny informace kter´ebudete potˇrebovat a materi´aly k pˇredn´aˇsce jsou na http://www.devnull.cz/mff, vˇcetnˇeaktu´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´yprogram • zkouˇska m´ap´ısemnou a ´ustn´ıˇc´ast, je nutn´ez´ıskat z´apoˇcet pˇred zkouˇskou, vˇcetnˇepˇredterm´ın˚u • zkouˇset se bude to, co bude odpˇredneseno (kromˇet´emat na vyplnˇen´ıpˇr´ıpadnˇe zbyl´eho ˇcasu). Vˇetˇsina informac´ıje ve slajdech, ale ˇrada d˚uleˇzit´ych podrob- nost´ım˚uˇze chybˇet. • pˇredpoklady: – uˇzivatelsk´aznalost UNIXu, programov´an´ıv shellu na ´urovni pˇredn´aˇsky ,,Uvod´ do UNIXu a TCP/IP” – znalost jazyka C – znalost z´akladn´ıch pojm˚uteorie operaˇcn´ıch syst´em˚u • tento text je pr˚ubˇeˇznˇedoplˇnov´an, ChangeLog zaˇc´ın´ana stranˇe207.

1 Obsah

• ´uvod, v´yvoj UNIXu a C, program´atorsk´en´astroje • z´akladn´ıpojmy a konvence UNIXu a jeho API • pˇr´ıstupov´apr´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´akomunikace • vl´akna, synchronizace vl´aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu

• budeme se zab´yvat hlavnˇeprincipy UNIXu a programov´an´ım pro UNIX pouze v jazyce C. • pˇredn´aˇska je pˇrev´aˇznˇeo 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´edobˇe(09/2007) jsou to posledn´ıverze syst´em˚uSolaris, 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

• ´uvod, v´yvoj UNIXu a C, program´atorsk´en´astroje • z´akladn´ıpojmy a konvence UNIXu a jeho API • pˇr´ıstupov´apr´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´akomunikace • 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

5. Herout, Pavel: Uˇcebnice jazyka C. 2 d´ıly. Kopp, Cesk´eˇ Budˇejovice, 2004 (4., respektive 2. pˇrepracovan´evyd´an´ı)

3 OhlednˇeUnixu doporuˇcuji sp´ıˇse literaturu v anglick´em jazyce.

1. vˇsestrann´y´uvod do UNIXu, ale dost struˇcn´a; Skoˇcovsk´yje autorem v´ıce ˇcesk´ych knih o Unixu, ale dnes jsou jiˇzv´ıce nebo m´enˇezastaral´e 2. pokroˇcilejˇs´ıpohled, ale pˇredpokl´ad´apˇredbˇeˇzn´eznalosti, m´ısty tˇeˇzko stravi- teln´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ˇev ˇceˇstinˇevyd´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´akniha, zab´yv´ase obecn´ymi myˇslenkami UNIXu a porovn´av´asyst´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ˇenen´ık dispozici a aktu´aln´ıpl´anovan´yterm´ın je 03/2008. Tˇeˇzko ale ˇr´ıci, jestli k tomu jeˇstˇenˇekdy v˚ubec dojde. 2. klasick´akniha o UNIXu, popis struktury a funkc´ıj´adra UNIX System V Rel. 2, ˇc´asteˇcnˇei 3; pˇrestoˇze je to kniha z dneˇsn´ıho pohledu jiˇzzastaral´a, lze ji poˇr´ad jednoznaˇcnˇedoporuˇcit protoˇze to je jedna z nejlepˇs´ıch knih, co byla kdy o UNIXu naps´ana. V roce 1993 vyˇsel ˇcesk´ypˇ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´yjako prvn´ı) 4. nejlepˇs´ıkniha o operaˇcn´ım syst´emu Solaris. Obsahuje podrobn´einformace o tom, jak tento syst´em funguje vˇcetnˇenejnovˇejˇs´ıch vˇec´ız verze 10 jako jsou z´ony, Crypto Framework, DTrace, Least Privilege model a dalˇs´ı. 5. domovsk´astrana 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´especifikace, viz http://www.unix.org 6. manu´alov´estr´anky (zejm. sekce 2, 3)

1. pravdˇepodobnˇenen´ı lepˇs´ıknihy o programov´an´ıpod Unixem (neobsahuje s´ıt’ov´eprogramov´an´ı, to je v knize 3) 2. aktualizovan´evyd´an´ıdalˇs´ız klasick´ych knih o programov´an´ıpod Unixem. Obsahuje i s´ıt’ov´eprogramov´an´ıa aˇcsamozˇrejmˇenen´ı tak podrobn´a jako spojen´ıknih 1 a 3, m˚uˇze to b´yt nˇekdy naopak v´yhodou. Tuto knihu jedno- znaˇcnˇedoporuˇcuji, pokud chcete nˇeco kupovat. 3. klasick´akniha o s´ıt’ov´em programov´an´ı, jedna z nejlepˇs´ıch k tomuto t´ematu; existuje i druh´yd´ıl UNIX Network Programming, Volume 2: In- terprocess Communications, kter´ase zab´yv´akomunikac´ımezi procesy (roury, POSIX IPC, System V IPC, synchronizace vl´aken, RPC). 4. velmi dobr´aa podrobn´akniha o programov´an´ıs vl´akny 5. domovsk´astr´anka posledn´ıch specifikac´ırozhran´ıUNIXu 6. podrobn´ypopis jednotliv´ych funkc´ı (v Linuxu bˇeˇznˇe ne zcela dostaˇcuj´ıc´ı; manu´alov´estr´anky v tomto syst´emu jsou ˇcasto horˇs´ıkvality neˇzu 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ˇehodnˇeknih 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´eslovo “unix”. Pokud byste z Amazonu nˇeco kupovali, dejte pozor na to, ˇze mnoho knih m´aaktu- alizovan´avyd´an´ıi po nˇekolika m´alo letech, nˇekdy i levnˇejˇs´ıneˇzta p˚uvodn´ı, kter´ajsou vˇsak st´ale na skladu a v on-line nab´ıdce; tak at’ zbyteˇcnˇenekoup´ıte starˇs´ıvyd´an´ıneˇzto aktu´aln´ı. Nav´ıc se vyplat´ızkontrolovat i u pˇr´ısluˇsn´eho vydavatelstv´ı, ˇze nen´ıv brzk´edobˇenapl´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´eruky, protoˇze jsou ˇcasto v´yraznˇelevnˇejˇs´ıneˇzknihy nov´e. Probl´em je, ˇze vˇetˇsinou nen´ımoˇzn´eje poslat pˇr´ımo do CR,ˇ ale mus´ıv´am je nˇekdo pˇriv´ezt.

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˚una webu; ˇcasto vˇsak obsahuj´ıc´ıne zcela pˇresn´e informace

• kapitola o BSD Unixu napsan´aMarshallem 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˚upro 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ˇeHoneywell vˇcetnˇeprojektu Multics, kter´yse pak pod jej´ıpatronac´ıd´ale vyv´ıjel (virtu´aln´ı pamˇet’, multiprocesory, . . . ). Posledn´ı instalace Multics- u fungovala na kanadsk´em Ministerstvu obrany (Canadian Department of National Defence) a syst´em byl napˇr´ıklad jeˇstˇeaktivnˇepouˇ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ˇc´atkem pr´ace na v´yvojov´em prostˇred´ıpro PDP-7 napsal Thompson program Space Travel, kter´ybyl 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ˇzDOS • 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´ybˇeh syst´emu. • Multics byl napsan´yv jazyce PL/I (Programming Language #1), tedy dˇr´ıve neˇzbyl UNIX pˇrepsan´ydo C ! • Multicsu byl v roce 1980 udˇelen jako prvn´ımu syst´emu level B2. Po nˇekolik let to byl jedin´ysyst´em s t´ımto bezpeˇcnost´ım levelem.

7 • GE byla zaloˇzena v roce 1892 slouˇcen´ım dvou spoleˇcnost´ı, z nichˇzjedna byla Edison General Electric Company zaloˇzen´aroku 1879 Thomasem Al- vou Edisonem (vyn´alezce ˇz´arovky, filmov´ekamery, . . . ); v souˇcasn´edobˇejej´ı dceˇrinn´espoleˇcnosti pokr´yvaj´ımnoho oblast´ı, vˇcetnˇedod´avky jednoho ze dvou typ˚umotor˚upro Airbus 380 nebo bankovnictv´ı. • PDP = Programmed Data Processor. Prvn´ıtyp, PDP-1, se prod´avala za $120.000 v dobˇe, kdy se jin´epoˇc´ıtaˇce prod´avaly za ceny pˇres mili´on. To byla tak´estrategie fy DEC - pojem computer tehdy znamenal drahou vˇec, potˇrebuj´ıc´ıs´al a t´ym lid´ı, kter´yse o to vˇsechno bude starat. Proto DEC sv´e maˇsiny nenaz´yval poˇc´ıtaˇci, ale pravˇeslovem PDPs. • PDP-11 je legend´arn´ımaˇsina od firmy DEC, postupnˇevznikaly verze PDP- 1 az PDP-16, kromˇePDP-2, PDP-13. Existuj´ıPDP-11 syst´emy, kter´ejeˇstˇe dnes bˇeˇz´ı, a tak´efirmy, kter´epro nˇevyr´abˇej´ın´ahradn´ıd´ıly.

Historie UNIXu, pokraˇcov´an´ı • ´unor 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ˇek 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´yVAX-11 • 1980 – Microsoft pˇr´ıch´az´ıs XENIXem, kter´yje zaloˇzen´yna UNIXu V7

• ACM = Association for Computing Machinery, zaloˇzena 1947. • akt pˇreps´an´ıUNIXu do jazyka C byl moˇzn´anejv´yznamnˇejˇs´ım mo- mentem v historii tohoto syst´emu ⇒ UNIX mohl b´yt mnohem jed- noduˇseji portov´an na jin´earchitektury • na verzi 6 je zaloˇzena legend´arn´ıkniha A commentary on the Unix Operating System, jej´ıˇzautorem je John Lions. • Microsoft neprod´aval XENIX pˇr´ımo, ale licencoval ho OEM v´yrobc˚um (Ori- ginal Equipment Manufacturer) jako byl Intel, SCO a jin´ı. Jin´efirmy pak XENIX d´ale portovaly na 286 (Intel) a 386 (SCO, 1987). Na webu je moˇzn´e naj´ıt zaj´ımav´einformace popisuj´ıc´ıtuto dobu a tehdy kladn´yvztah Micro- softu k UNIXu.

8 • pokud v´as v´ıce zaj´ım´ahistorie 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´yport 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˚uod embedded a handheld syst´em˚u(Linux), pˇres osobn´ı poˇc´ıtaˇce aˇzpo 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´epˇ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ˇzv r´amci BT. Na tˇechto r˚uzn´ych verz´ıch je pr´avˇezaloˇzena verze System III. Z´ajemce o v´ıce informac´ıodkazuji na web. • UNIX se rozˇstˇepil na dvˇehlavn´ı vˇetve: AT&T a BSD, jednotliv´ı v´yrobci pˇrich´azeli s vlastn´ımi modifikacemi. Jednotliv´eklony 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 a autor C-shellu) vytvoˇrili SW bal´ık Berkeley Soft- ware Distribution (BSD) a prod´avali ho v roce 1978 za $50. Tyto poˇc´ateˇcn´ı verze BSD obsahovaly pouze SW a utility (prvn´ı verze: Pascal pˇrekladaˇc, editor ex), ne syst´em ani ˇz´adn´ejeho zmˇeny. To pˇriˇslo aˇzs verz´ı3BSD. verze 4BSD vznik´aroku 1980 jiˇzjako projekt financovan´yagenturou DARPA a veden´yBillem Joyem. Trp´ıprobl´emy spojen´ymi s nedostateˇcn´ym v´ykonem a vznik´atak vyladˇen´ysyst´em 4.1BSD dod´avan´yod roku 1981.

9 • 4.1BSD mˇelo b´yt p˚uvodnˇe5BSD, 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ˇzps´at vlastn´ık´od, v´yvoj´aˇri z Berkeley se radˇeji nejdˇr´ıve pod´ıvali kolem, co je jiˇzhotov´e. Tak BSD napˇr´ıklad pˇrevzalo virtu´aln´ıpamˇet’ z Machu a nebo NFS-compatibiln´ık´od vyvinut´yna jedn´e kanadsk´euniverzitˇe. • v´yrobci hardware dod´avali varianty UNIXu pro sv´epoˇc´ıtaˇce a komercializace tak jeˇstˇezhorˇ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´avypadat syst´em navenek (pro uˇzivatele, program´atora a spr´avce), nezab´yv´ase implementac´ı. C´ılem je pˇrenositelnost aplikac´ıi uˇzivatel˚u. vˇsechny syst´emy totiˇzz 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 file- syst´emu, s´ıt’ov´earchitektuˇre i v architektuˇre virtu´aln´ıpamˇeti. • kdyˇzv roce 1987 firmy AT&T a Sun (jehoˇztehdejˇs´ıSunOS byl zaloˇzen´yna BSD) spojily svoje ´usil´ına vyvinut´ıjednoho syst´emu, kter´yby obsahoval to nejlepˇs´ız obou vˇetv´ı, kromˇenadˇsen´ych reakc´ıto vzbudilo i strach u mnoha dalˇs´ıch v´yrobc˚uunixov´ych syst´em˚u, kteˇr´ı se b´ali, ˇze by to pro obˇefirmy znamenalo obrovskou komerˇcn´ıv´yhodu. Vznik´aproto Open Software Foun- dation (nezamˇeˇnovat za FSF), a zakl´adaj´ıc´ımi ˇcleny byly mimo jin´efirmy Hewlett-Packard, IBM a Digital. Z toho vzeˇsl´ysyst´em OSF/1 ale nebyl pˇr´ıliˇs ´uspˇeˇsn´y, a dod´aval ho pouze Digital, kter´yho pˇrejmenoval na Digital UNIX. Zaj´ımavost´ıje, ˇze syst´em je postaven´yna mikroj´adru Mach. Po akvizici Digi- talu Compaqem byl syst´em pˇrejmenov´an na Tru64 a s t´ımto jm´enem je d´ale podporov´an firmou Hewlett-Packard, kter´ase v roce 2002 s Compaqem spo- jila. 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´ena Networking Release 2 ; Bill Jolitz vytvoˇril 6 chybˇej´ıc´ıch soubor˚ua 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˚uvych´azej´ıc´ıch). • (1995) 4.4BSD-Lite Release 2, po kter´en´asleduje rozpuˇstˇen´ıCSRG, kter´a skoro 20 let pilotovala v´yvoj BSD vˇetve. V´ıce jiˇzzm´ınˇen´akapitola o BSD Unixu.

10 Souˇcasn´eUNIXy Hlavn´ıkomerˇcn´ıunixov´esyst´emy: • Sun Microsystems: SunOS (nen´ıjiˇzd´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ˇzjsem 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´ypo mnoho let ovl´adal televizn´ı/filmov´ypr˚umysl (napˇr. Pixar studio 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´yv 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´ysyst´em m´asvoje ´uspˇechy. • jak jiˇzbylo zm´ınˇeno, Tru64 UNIX vych´az´ız OSF/1 firmy DEC. Ta pˇredt´ım dod´avala Ultrix, zaloˇzen´yna BSD unixu. • OpenSolaris je projekt vznikl´yv ˇcervnu 2005 a je zaloˇzen na podmnoˇzinˇe zdrojov´ych text˚uv´yvojov´everze Solarisu (kernel, knihovny, pˇr´ıkazy) k datu uveden´ı. Ofici´aln´ıdistribuce zaloˇzen´ana OpenSolarisu se jmenuje Solarix Ex- press. Distribuce vzeˇsl´ez 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ˇeto m´anˇeco pˇres 100MB. V obou pˇr´ıpadech je tedy spr´avn´e pouˇz´ıvat spojen´ı“Linux (OpenSolaris) distribuce”, kdyˇzse bav´ıme o cel´em syst´emu. • kaˇzd´akomerˇcn´ıvarianta vych´azela z jednoho ze dvou hlavn´ıch syst´em˚u– UNIX V nebo BSD, a pˇrid´avala si sv´evlastnosti • vznik´avelk´ypoˇ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˚ushodla 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´akniha”, kterou AT&T vydala poprv´ev roce 1985 – dnes ve verzi SVID3 (odpov´ıd´aSVR4) • POSIX (Portable Operating System based on UNIX) – s´erie standard˚uorganizace IEEE znaˇcen´aP1003.xx, postupnˇeje pˇrej´ım´avrcholov´ynadn´arodn´ıorg´an ISO • XPG (X/Open Portability Guide) – doporuˇcen´ıkonsorcia X/Open, kter´ebylo zaloˇzeno v r. 1984 pˇredn´ımi v´yrobci platforem typu UNIX • Single UNIX Specification – standard organizace The Open Group, vznikl´ev 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´eovˇeˇrilo, zda dan´ysyst´em odpov´ıd´a standardu. • POSIX (Portable Operating System Interface) je standardizaˇcn´ısnaha or- ganizace IEEE (Institute of Electrical and Electronics Engineers). Prvn´ım dokumentem je IEEE Std POSIX1003.1-1988, dˇr´ıve oznaˇcovan´yprostˇejako POSIX, dnes zn´am´ysp´ıˇse jako POSIX.1, protoˇze POSIXem se nyn´ım´ın´ısada vz´ajemnˇesouvisej´ı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´yjako POSIX1990. Tento standard byl s´am o sobˇevelk´y´uspˇech, ale st´ale jeˇstˇenespojoval t´abory System V a BSD, protoˇze v sobˇenapˇr´ıklad nezahrnoval BSD sockety nebo IPC (semafory, zpr´avy, sd´ılen´apamˇet’) ze System V. Oznaˇcen´ıPOSIX vymyslel Richard Stallman, tedy ˇclovˇek, kter´yv roce 1983 zaloˇzil GNU projekt. Souˇc´ast´ıstandardu je i “POSIX conformance test suite (PCTS)”, kter´yje volnˇek dispozici. • SUSV3 je spoleˇcn´ystandard The Open Group, IEEE (Std. 1003.1, 2003 Edi- tion) a ISO (ISO/IEC 9945-2003). • dnes je asi nejd˚uleˇzitˇejˇs´ıSingle UNIX Specification, kter´ese 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˚uj´adra v tomto materi´alu bude vˇetˇsinou vych´azet ze System V Rel. 4.

13 Jazyk C

• t´emˇeˇrcel´yUNIX je napsan´yv C, pouze nejniˇzˇs´ıstrojovˇez´avisl´a ˇc´ast v assembleru ⇒ pomˇernˇesnadn´apˇ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´epoˇc´ıtaˇce – siln´avazba na UNIX. • varianty jazyka: – p˚uvodn´ıK&R C – standard ANSI/ISO C • ´uspˇech jazyka C daleko pˇres´ahl ´uspˇech samotn´eho UNIXu

• CPL ⇒ BCPL ⇒ B (Thompson, interpret) ⇒ C • K&R C – jazyk C tak, jak je popsan´yv klasick´eknize 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ˇehotov, a je zn´am´ynejˇcastˇeji jako “ANSI C”, pˇr´ıpadnˇejako C89 (napˇr´ıklad pˇrekladaˇcpro tuto normu se v Sun Studiu jmenuje c89, jelikoˇz i to samotn´ejm´eno programu mus´ıb´yt podle normy). Druh´e vyd´an´ıK&R knihy (1988) je jiˇzupraven´epr´avˇepro 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ˇzna konci 90-t´ych let proˇsel dalˇs´ıreviz´ıv r´amci ISO a vznik´aISO 9899:1999, ˇcastˇeji oznaˇcovan´yjako C99. V roce 2000 pak naopak tento standard pˇrevzal ANSI. • rozd´ıly mezi C89 a C99 jsou mimo jin´ezahrnut´ıinline funkc´ı, definic´ıpro- mˇenn´ych napˇr´ıklad i do for konstrukce, jednoˇr´adkov´ych koment´aˇr˚upomoc´ı //, 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 • ˇr´adky textov´ych soubor˚ukonˇc´ıv UNIXu znakem LF (nikoliv CRLF). Vol´an´ı putc(’\n’) tedy p´ıˇse pouze jeden znak. • big endian – SPARC, MIPS, s´ıt’ov´epoˇrad´ıbajt˚u • little endian – Intel

• velk´ypozor na v´ystupy program˚utypu hexdump, kter´edefaultnˇevypisuj´ı soubor ve 2-bajtov´ych ˇc´ıslech, coˇzsv´ad´ıvidˇet soubor jinak, neˇzjak je opravdu zapsan´yna disku; viz pˇr´ıklad (i386, FreeBSD):

$ echo -n 1234 > test $ hexdump test 0000000 3231 3433 0000004

je samozˇrejmˇemoˇzn´epouˇz´ıt jin´yform´at v´ystupu:

$ hexdump -C test 00000000 31 32 33 34 |1234| 00000004

• 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´esoubory. V´yjimkou m˚uˇze asi jen to, pokud budete pracovat s k´odem, kter´ybyl napsan´ypodle K&R.

• r˚uzn´ymi z´apisy deklarac´ıse dost´av´ame rovnou i k r˚uzn´ym styl˚um psan´ızdro- jov´ych text˚u. Nˇekter´esyst´emy to pˇr´ıliˇsneˇreˇs´ı (Linux), jin´esyst´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´yUNIXov´ysyst´em m´aprogram indent(1), kter´yv´am pomoc´ıpˇrep´ınaˇc˚upˇreform´atuje jak´ykoli C zdrojov´ytext do poˇzadovan´eho v´ystupu.

16 C style

• vˇec zd´anlivˇepodˇradn´a, pˇritom extr´emnˇed˚uleˇzit´a– ´uprava zdrojov´ych k´od˚uprogramu • mnoho zp˚usob˚ujak 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 pro- gram´ator˚upracuje na jednom projektu, nen´ızas aˇztak d˚uleˇzit´e, na jak´em stylu se dohodnout (pokud je alespoˇntrochu rozumn´y), ale aby se dohodli. Jednotn´ya dobˇre zvolen´ystyl ˇsetˇr´ıˇcas a br´an´ızbyteˇcn´ym chyb´am.

17 C style (cont.) • mnoho zp˚usob˚ujak 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´ystyl zdrojov´ych k´od˚uje i vizitkou program´atora. Kdyˇzse v r´amci pˇrij´ımac´ıch pohovor˚uodevzd´avaj´ıi uk´azkov´ek´ody, tak hlavn´ı d˚uvod nen´ıten, aby se zjistilo, ˇze v´am dan´yprogram funguje. . . Uprava´ sa- motn´eho zdrojov´eho textu je jedn´ım z kriteri´ı, protoˇze to pˇrenesenˇem˚uˇze svˇedˇcit i o dalˇs´ıch skuteˇcnostech – nˇekdo napˇr. bude odhadovat, ˇze po- kud p´ıˇsete neˇcist´ya neupraven´yk´od, tak jste moˇzn´a jeˇstˇenepracovali 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ˇeje rozumnˇeˇcist´yk´od jednou z podm´ınek ´uspˇechu a jednou ze zkuˇsenost´ı, kter´ez takov´espolupr´ace vych´azej´ı. Toto je samozˇrejmˇe zˇc´asti subjektivn´ın´azor, ale ˇcist´ym k´odem nic nezkaz´ıte, minim´alnˇenekaz´ıte oˇci sv´ym cviˇc´ıc´ım.

18 Utility

cc, c99∗, gcc† pˇrekladaˇcC CC, g++† pˇrekladaˇcC++ ld spojovac´ıprogram (linker) ldd pro zjistˇen´ız´avislost´ıdynamick´eho objektu cxref ∗ kˇr´ıˇzov´eodkazy ve zdrojov´ych textech v C sccs∗, rcs,cvs spr´ava verz´ızdrojov´eho k´odu make∗ ˇr´ızen´ıpˇrekladu podle z´avislost´ı ar∗ spr´ava knihoven objektov´ych modul˚u dbx, gdb† debuggery prof, gprof † profilery ∗ UNIX 03 † 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˚uprogram˚uv C *.cc jm´ena zdrojov´ych soubor˚uprogram˚uv C++ *.h jm´ena hlaviˇckov´ych soubor˚u(header˚u) *.o pˇreloˇzen´emoduly (object files) a.out jm´eno spustiteln´eho souboru (v´ysledek ´uspˇeˇsn´ekompilace)

/usr/ koˇren stromu syst´emov´ych header˚u /usr/lib/lib*.a statick´eknihovny objektov´ych modul˚u /usr/lib/lib*.so um´ıstˇen´ıdynamick´ych sd´ılen´ych knihoven objektov´ych modul˚u

statick´eknihovny – pˇri linkov´an´ıse stanou souˇc´ast´ıv´ysledn´eho spustiteln´eho programu. Dnes se uˇzmoc nepouˇz´ıv´a. sd´ılen´eknihovny – program obsahuje pouze odkaz na knihovnu, pˇri spuˇstˇen´ı pro- gramu se potˇrebn´eknihovny naˇctou do pamˇeti ze soubor˚u *.so a pˇrilinkuj´ı.

• dnes se vˇetˇsinou pouˇz´ıvaj´ısd´ılen´eknihovny, protoˇze nezab´ıraj´ıtolik diskov´eho prostoru (knihovna je na disku jednou, nen´ısouˇc´ast´ı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ˇzjiˇzprogram´ator nem˚uˇze vytvoˇrit statickou bin´arku, aniˇzby nemˇel dostateˇcn´eznalosti syst´emu. • nˇekdy se bez statick´ych knihoven neobejdeme. V nˇekter´ych situac´ıch nen´ı moˇzn´epouˇz´ıt knihovny dynamick´e, spustiteln´esoubory jsou takzvan´e standa- lone binaries a pouˇzit´ınaleznou napˇr´ıklad pˇri bootov´an´ıoperaˇcn´ıho syst´emu.

20 Princip pˇrekladu

zdrojov´emoduly objektov´emoduly program main.c main.o a.out main() { main msg(); msg ?? main } msg util.c util.o msg() { pˇrekladaˇc msg msg puts(); } puts ?? linker puts

syst´emov´a puts puts knihovna

• u sloˇzitˇejˇs´ıch program˚ub´yv´azvykem rozdˇelit zdrojov´ytext programu do nˇekolika modul˚u, kter´eobsahuj´ıpˇr´ıbuzn´efunkce a tyto moduly se pak mohou pˇrekl´adat zvl´aˇst’ (dokonce kaˇzd´ymodul 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´eod posledn´ıho pˇrekladu) a jednak flexibilita (nˇek- ter´emoduly se mohou pouˇz´ıvat v r˚uzn´ych programech). Pro ˇr´ızen´ıpˇrekladu se obvykle pouˇz´ıv´autilita make. • pˇrekladaˇc jednotliv´ezdrojov´emoduly pˇreloˇz´ıdo tvaru tzv. objektov´ych mo- dul˚u, jeˇzobsahuj´ık´od programu (vˇcetnˇevol´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´yzkompletuje v´ysledn´yprogram vˇcetnˇevyˇreˇsen´ıextern´ıch odkaz˚umezi moduly a syst´emov´ymi knihovnami resp. mezi moduly navz´ajem. • pouˇzit´estatick´eknihovny jsou zkop´ırov´any do spustiteln´eho souboru. Na sd´ılen´eknihovny 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´ylinker na stranˇe31.

• pomoc´ı parametr˚ulinkeru lze urˇcit, zda se budou pouˇz´ıvat statick´enebo dynamick´eknihovny. Zdrojov´yk´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´aknihovna a daj´ıse volat jej´ıfunkce. T´ımto zp˚usobem m˚uˇzete tak´ezjistit, zda v syst´emu je pˇr´ıtomna pˇr´ısluˇsn´afunkcionalita a pokud ne, zachovat se podle toho.

21 Pˇreklad jednoho modulu (preprocesor)

main.c #include #include "mydef.h" main() main.i { int puts(char *s); puts(MSG); } extern int var a; /usr/include/stdio.h main()

int puts(char *s); preprocesor { mydef.h puts("Ahoj"); #define MSG "Ahoj" } extern int var a;

• preprocesor prov´ad´ıexpanzi maker, ˇcten´ıvloˇzen´ych () soubor˚ua vynech´av´a koment´aˇre. • v´ystup preprocesoru lze z´ıskat pomoc´ı cc -E pˇr´ıpadnˇepˇr´ımo zavol´an´ım cpp, nemus´ıto b´yt ale vˇzdz tot´eˇzprotoˇze nˇekter´epˇrekladaˇce maj´ıpreprocesor integrov´an v sobˇe. Preprocesor m˚uˇzete samozˇrejmˇepouˇz´ıvat i pro jin´epro- jekty, kter´es pˇrekladem zdrojov´ych soubor˚uv 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 sou- bor˚ua r˚uzn´ych definic z´avisl´ych na dan´ych podm´ınk´ach. Pˇri hled´an´ıp˚uvodce chyby v´am pr´avˇem˚uˇze hodnˇepomoci samostatn´eho zpracov´an´ıvstupn´ıho souboru pomoc´ıpreprocesuru, kde probl´em jiˇzvˇetˇsinou rozpozn´ate snadno. • cpp v´am dok´aˇze na standardn´ıchybov´yv´ystup zobrazit i cel´ystrom vkl´ada- n´ych soubor˚u, coˇzopˇ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 int puts(char *s); .type main, @function main: extern int var a; pushhl %ebp movl %esp, %ebp main() pushl $.LC0 call puts { kompil´ator addl $4, %esp puts("Ahoj"); .L1: } leave ret

• 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.o main: 457f 464c 0101 0001 pushhl %ebp movl %esp, %ebp 0000 0000 0000 0000 pushl $.LC0 0001 0003 0001 0000 call puts 0000 0000 0000 0000 addl $4, %esp assembler 00ec 0000 0000 0000 .L1: 0034 0000 0000 0028 leave ret

• pˇreklad z assembleru do strojov´eho k´odu • objektov´ymodul je v´ysledkem pˇr´ıkazu cc -c.

24 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ˇrpro hled´an´ıknihoven z -l -Olevel nastaven´ı´urovnˇeoptimalizace -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´einformace 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˚uovlivˇ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´amakra __FILE__, __LINE__, __DATE__, __TIME__, __cplusplus, apod. jsou standardn´ımakra kompil´atoru C/C++ unix vˇzdy definov´ano v Unixu mips, i386, sparc hardwarov´aarchitektura linux, sgi, sun, bsd klon operaˇcn´ıho syst´emu _POSIX_SOURCE, _XOPEN_SOURCE pˇreklad podle pˇr´ısluˇsn´enormy

pro pˇreklad podle urˇcit´enormy by pˇred prvn´ım #include mˇel b´yt ˇr´adek 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´yvyˇzaduje SUS3, na syst´emu podporuj´ıc´ım SUS3 (Solaris 10), ale pˇrekladaˇcem, kter´ypodpo- ruje pouze SUS2 (UNIX03 pˇrekladaˇcje c99).

$ cat standards.c #define _XOPEN_SOURCE 600 /* you must #include at least one header !!! */ #include 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..org. • v dokumentaci konkr´etn´ıho kompil´atoru je moˇzn´enaj´ıt, kter´adalˇs´ımakra se pouˇz´ıvaj´ı. Mnoˇzstv´ımaker je definov´ano tak´ev syst´emov´ych hlaviˇckov´ych souborech.

26 • POSIX.1 v sobˇezahrnuje ANSI C; tedy C89, ne C99 (o C standardech v´ıce na stranˇe13). • co se t´yˇce maker k jednotliv´ym standard˚um, velmi dobr´a je kapitola 1.5 v [Rochkind]. Tak´edoporuˇcuji C program c1/suvreq.c k t´eto kapitole, kter´y je moˇzn´enaj´ıt i na m´ych str´ank´ach v sekci uk´azkov´ych pˇr´ıklad˚u. • mal´ypˇr´ıklad na podm´ınˇen´ypˇ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´yprogram

• linker je program, kter´yvezme typicky v´ıce objekt˚uvygenerovan´ych pˇrekla- daˇ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´epˇrep´ınaˇce mohou liˇsit, napˇr´ıklad ld na Solarisu nezn´apˇ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˚uskl´adaj´ıc´ıch se z mnoha zdrojov´ych

27 soubor˚ua knihoven se obvykle oddˇeluje pˇreklad a linkov´an´ıa cel´yproces je ˇr´ızen utilitou make.

R´ızen´ıpˇrekladuˇ a linkov´an´ı(make) • zdrojov´etexty main.c util.h util.c #include "util.h" void msg(); #include "util.h" main() msg() { { msg(); puts(); } } • z´avislosti • soubor Makefile main.c prog : main.o util.o ց 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.o : util.c util.h ր cc -c util.c util.c

• program je moˇzn´etak´epˇ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 nˇekter´eho zdrojov´eho souboru pˇrekl´ad´ajenom to, co na nˇem z´avis´ı. Cast´yˇ zp˚usob pˇrekladu softwaru po aplikov´an´ızmˇen zp˚usobem“make clean; make all” je v situaci, kdy cel´ypˇreklad trv´aminuty (des´ıtky minut, hodiny. . . ), trochu nevhodn´y– pr´avˇeproto je d˚uleˇzit´em´ıt dobˇre napsan´y Makefile. • ˇr´adek “prog : main.o util.o” definuje, ˇze se m´anejprve rekurzivnˇezajistit 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´ase nic. Kdyˇzne, provede se pˇr´ıkaz na n´asleduj´ıc´ım ˇr´adku.

• make se spouˇst´ı typicky s parametrem urˇcuj´ıc´ı pˇr´ıˇsluˇsn´yc´ıl (target); pˇri spuˇstˇen´ıbez parametr˚use vezme prvn´ıtarget. Ten typicky b´yv´a all, coˇz vˇetˇsinou podle unixov´ekonvence 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ˇeuniverz´aln´ın´astroj, pouˇziteln´yi jinde neˇzu pˇreklad˚u

28 Syntaxe vstupn´ıho souboru (make)

• popis z´avislost´ıc´ıle: targets :[files ] • prov´adˇen´epˇr´ıkazy: command • koment´aˇr: #comment • pokraˇcovac´ıˇr´adek: line-begin\ line-continuation

• pozor na to, ˇze ˇr´adek s pˇr´ıkazem zaˇc´ın´atabul´atorem, nikoliv me- zerami. Kaˇzd´ypˇr´ıkazov´yˇr´adek se prov´ad´ısamostatn´ym shellem, pokud je potˇreba prov´est v´ıce ˇr´adk˚upomoc´ıjednoho shellu, mus´ıse vˇsechny aˇzna po- sledn´ıukonˇcit backslashem (shell je dostane jako jeden ˇr´adek). Viz pˇr´ıklad, ve kter´em dva posledn´ı echo pˇr´ıkazy jsou souˇc´ast´ı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ˇc´atku ˇr´adku potlaˇc´ıjeho v´ypis – make jinak standardnˇevypisuje nejdˇr´ıve to, co bude vykon´avat.

29 • znak - na zaˇc´atku ˇr´adku zp˚usob´ıignorov´an´ınenulov´en´avratov´ehodnoty; jinak make vˇzdy v takov´esituaci zahl´as´ıchyby a okamˇzitˇeskonˇc´ı. • test1: false echo "OK"

test2: -false echo "OK"

Makra (make)

• definice makra: name = string • pokraˇcov´an´ıvkl´ad´amezeru • nedefinovan´amakra jsou pr´azdn´a • nez´aleˇz´ına poˇrad´ıdefinic r˚uzn´ych maker • definice na pˇr´ıkazov´eˇr´adce: make target name =string • vyvol´an´ımakra: $name (pouze jednoznakov´e name ), ${name } nebo $(name ) • syst´emov´epromˇenn´ejsou pˇr´ıstupn´ejako makra

• kdyˇzje stejn´emakro definov´ano v´ıcekr´at, plat´ıposledn´ıdefinice. • makra nen´ımoˇzn´edefinovat rekurzivnˇe.

$ cat Makefile M=value1 M=$(M) value2 all: echo $(M) $ make Variable M is recursive.

• ˇcasto se pouˇz´ıvaj´ır˚uzn´erozˇs´ıˇren´everze make (napˇr. GNU, BSD), kter´eum´ı, podm´ınˇen´esekce v Makefile, redefinice promˇenn´ych, apod. • napsat Makefile kter´ybude fungovat najednou pro r˚uzn´everze make nemus´ı b´yt jednoduch´e, proto existuj´ıprojekty jako je napˇr. GNU automake. Pro jed- noduch´ypodm´ıneˇcn´ypˇreklad v z´avislosti na syst´emu a kde se daj´ıoˇcek´avat

30 r˚uzn´everze pˇr´ıkazu make, je moˇzn´epouˇ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´auvozovka, 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ˇenezbytn´e pouˇz´ıt programy typu autoconf nebo automake • make je velmi siln´yn´astroj, staˇc´ıse pod´ıvat do syst´emov´ych Makefile sou- bor˚ujak´ehokoli Unixov´eho syst´emu. Typickou spoleˇcnou vlastnost´ıje to, ˇze neexistuje dokumentace jak je dan´ymakefile framework postaven.

Dynamick´ylinker

Pˇri pˇrekladu je nutn´em´ıt vˇsechny potˇrebn´edynamick´eknihovny, protoˇze se kontroluje dosaˇzitelnost pouˇzit´ych symbol˚u, sestaven´ı kompletn´ıho programu se ale provede aˇzpˇri spuˇstˇen´ı. To je ´ukol pro dynamick´ylinker (run-time linker, loader). • seznam dynamick´ych knihoven zjist´ıze sekce .dynamic • syst´em m´anastaveno nˇekolik cest, kde se automaticky tyto knihovny hledaj´ı • v sekci .dynamic je moˇzn´edalˇs´ıcesty ke knihovn´am pˇridat pomoc´ıtag˚u RUNPATH/RPATH • nalezen´eknihovny 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 fungo- vat na jin´ych syst´emech, tak maj´ıekvivaletn´ın´astroje s podobnou funkcionalitou.

• proces spuˇstˇen´ıdynamicky slinkovan´eho programu prob´ıh´atak, ˇze kernel ve vol´an´ı exec() namapuje dan´yprogram do pamˇeti a zjist´ı, jak´ydynamick´ylin- ker se m´apouˇz´ıt (viz d´ale). Pˇred t´ım, neˇzj´adro pˇred´alinkeru kontrolu, linker

31 namapuje do pamˇet’ov´eho prostoru spouˇstˇen´eho procesu. Linker z hlaviˇcky programu zjist´ı, jak´edynamick´eknihovny se maj´ıd´ale namapovat, provede to a teprve pak pˇred´aˇr´ızen´ıvaˇsemu programu. V´aˇsprogram m˚uˇze za bˇehu d´ale vyuˇz´ıvat dynamick´ylinker pomoc´ıvol´an´ıtypu dlopen() a spol. (k tomu se tak´edostaneme pozdˇeji). Uvˇedomte si, ˇze dynamick´y linker zde nepracuje jako samostatn´yproces, jeho k´od se pouˇz´ıv´av r´amci pamˇet’ov´eho prostoru vaˇseho procesu; v´aˇsprogram, linker a knihovny dohromady tvoˇr´ıjeden pro- ces. • seznam sekc´ı se zjist´ı pomoc´ı elfdump -c (GNU m´apˇr´ıkaz readelf). O programov´ych sekc´ıch bude v´ıce na stranˇe115. • jak´ydynamick´ylinker 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´ylinker pro v´aˇsprogram. • dynamick´asekce se vyp´ıˇse pomoc´ı elfdump -d, dynamick´eknihovny jsou oznaˇcen´etagem NEEDED • z´avislosti na dynamick´ych knihovn´ach je moˇzn´edobˇre zjistit pomoc´ıpˇr´ıkazu ldd, kter´yzjist´ı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´ez´avislosti - tj. takov´eknihovny, kter´e jsou pouˇzit´eknihovnami, kter´epˇr´ısluˇsn´yprogram pouˇz´ıv´apˇr´ımo. Zjistit co je pˇresnˇez´avisl´ena ˇcem je moˇzn´epomoc´ıvolby -v. • jak´eknihovny byly pˇri spuˇstˇen´ınakonec pouˇzity m˚uˇze b´yt jin´eneˇzco uk´aˇze pˇr´ıkaz ldd, a to tˇreba d´ıky mechanismu LD PRELOAD, na Solarisu proto exis- tuje pˇr´ıkaz pldd, kter´ypomoc´ıˇc´ısla procesu uk´aˇze z´avislosti konkr´etn´ıho spuˇstˇen´eho programu. • v´ıce viz manu´alov´astr´anka pro dynamick´ylinker v Solarisu, ld.so.1, pˇr´ıpad- nˇ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´ylinker se typicky d´akonfigurovat 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´ylinker pouˇzijte:

LD_DEBUG=help date

• jin´ypˇr´ıklad, kdy linker hled´aknihovny i jinde neˇzv 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´epouze 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´ytext mohl pˇreloˇzit na vˇsech syst´emech podporuj´ıc´ıdan´eAPI ABI – Application Binary Interface • low-level rozhran´ı aplikace v˚uˇci syst´emu, knihovnˇeˇci jin´eˇc´asti sama sebe • ...aby se objektov´ymodul mohl pouˇz´ıt vˇsude tam, kde je podporov´ano stejn´eABI

• pˇr´ıkladem API je tˇreba API definovan´enormou POSIX.1 • ABI definuje konvenci vol´an´ı(to jak program pˇred´aparametry funkci a jak od n´ıpˇrevezme n´avratovou hodnotu), jak´ajsou ˇc´ısla syst´emov´ych vol´an´ı, jak se syst´emov´evol´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´emnoˇzinu vol´an´ıkter´a jsou knihovnou definov´ana, jejich parametry a typy tˇechto parametr˚u • n´asledn´auk´azka je pˇr´ıklad na to, kdy v´yvoj´aˇrzmˇen´ıvelikost argument˚uv baj- tech (tj. zmˇen´ıABI knihovny), a nahrad´ınovou verz´ıtu starou. Vˇsimnˇete si, ˇze dynamick´ylinker toto nezjist´ı; nem´atotiˇzjak, ˇr´ıd´ıse podle jm´ena knihovny v dynamick´esekci programu, a to se nezmˇenilo. Uveden´azmˇ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 ˇr´adkem deklarace funkce add(). To je ale ˇcasto probl´em (pˇre- kl´adejte cel´ysyst´em jen kv˚uli tomu), proto je tak d˚uleˇzit´edodrˇ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´epouˇ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´ase 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´eknihovny 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 ] start programu where vypiˇsz´asobn´ık print expr vypiˇsv´yraz set var = expr zmˇeˇnhodnotu promˇenn´e cont pokraˇcov´an´ıbˇehu programu next, step proved’ ˇr´adku (bez/s vnoˇren´ım do funkce) stop condition nastaven´ıbreakpointu trace condition nastaven´ıtracepointu command n akce na breakpointu (pˇr´ıkazy n´asleduj´ı) help [name ] n´apovˇeda quit ukonˇcen´ıdebuggeru

• z´akladn´ıˇr´adkov´ysymbolick´ydebugger, aby bylo moˇzn´eho plnˇevyuˇz´ıt, mus´ı b´yt program pˇreloˇzen s ladic´ımi informacemi (cc -g). Ladˇen´yprogram se startuje z debuggeru pˇr´ıkazem run, nebo se debugger pˇripoj´ık jiˇzbˇeˇz´ıc´ımu procesu. Pomoc´ı dbx lze analyzovat i havarovan´yprogram, kter´yvygeneroval soubor core. • je moˇzn´eho naj´ıt napˇr. na Solarisu, na Linuxu a FreeBSD defaultnˇenen´ı. • pro debugging se zdrojov´ymi k´ody nestaˇc´ıpouˇz´ıt volbu -g, je z´aroveˇnnutn´e m´ıt i zdroj´aky a objektov´emoduly tam, kde byly pˇri pˇrekladu. To je typicky bˇeˇzn´asituace, protoˇze lad´ıte na stroji, kde z´aroveˇn i vyv´ıj´ıte. Pokud tomu tak nen´ı, je nutn´esi zdroj´aky a objektov´emoduly zajistit, pokud k nim vede jin´acesta, 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´ym´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 ; 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 ] start programu bt vypiˇsz´asobn´ık print expr vypiˇsv´yraz set var = expr zmˇeˇnhodnotu promˇenn´e cont pokraˇcov´an´ıbˇehu programu next, step proved’ ˇr´adku (bez/s vnoˇren´ım do funkce) break condition nastaven´ıbreakpointu help [name ] n´apovˇeda quit 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 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 return0; (gdb) c Continuing. Program exited normally. (gdb) q • debuggery jsou v´yborn´ymi pomocn´ıky pokud v´aˇsprogram konˇc´ına chyby

36 typu “segmentation error” – tj. kdyˇzzkus´ıte nekorektnˇe pˇristoupit do pamˇeti, napˇr´ıklad tam kde nem´ate co dˇelat. Kdyˇzpˇri pˇrekladu pouˇzijete option -g, uk´aˇze v´am pak debugger pˇresnˇeˇc´ıslo ˇr´adku, kde nastal probl´em. Konkr´etn´ı pˇr´ıklad (proˇcse vlastnˇetento program chov´ajak 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="heyworld"; 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

• ´uvod, v´yvoj UNIXu a C, program´atorsk´en´astroje • z´akladn´ıpojmy a konvence UNIXu a jeho API • pˇr´ıstupov´apr´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´akomunikace • vl´akna, synchronizace vl´aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu

37 Standardn´ıhlaviˇckov´esoubory (ANSI C)

stdlib.h . . . z´akladn´ımakra a funkce errno.h . . . oˇsetˇren´ıchyb stdio.h . . . vstup a v´ystup ctype.h ... pr´ace se znaky string.h ... pr´ace s ˇretˇezci time.h ... pr´ace s datem a ˇcasem math.h . . . matematick´efunkce setjmp.h . . . dlouh´eskoky assert.h . . . ladic´ıfunkce stdarg.h . . . pr´ace s promˇenn´ym poˇctem parametr˚u limits.h . . . implementaˇcnˇez´avisl´ekonstanty signal.h . . . oˇsetˇren´ısign´al˚u

• hlaviˇckov´ysoubor (header file) je soubor s deklaracemi funkc´ı(forward decla- ration), promˇenn´ych a maker. Z pohledu preprocesoru je to obyˇcejn´ysoubor napsan´yv jazyce C.

• pozor na to, ˇze tyto hlaviˇckov´esoubory nejsou specifick´e pro UNIX. Jsou souˇc´ast´ı standardu ANSI C, kter´yjak jiˇzv´ıme (strana 13), je zahrnut v POSIX.1. • pˇr´ısluˇsn´yhlaviˇckov´ysoubor pro konkr´etn´ıfunkci najdete v manu´alov´estr´ance dan´efunkce, toto je zaˇc´atek manu´alov´estr´anky na Solarisu pro memcpy():

StandardCLibraryFunctions memory(3C)

NAME memory, memccpy, memchr, memcmp, memcpy, memmove, memset - memory operations

SYNOPSIS #include ......

• jednotliv´amakra obsaˇzen´av tˇechto souborech vˇetˇsinou nejsou vysvˇetlena, v´yznam jednotliv´ych maker je ale moˇzn´esi vyhledat v pˇr´ısluˇsn´ych specifi- kac´ıch, kter´ejsou on-line. Na nˇekter´ych syst´emech (Solaris) maj´ıjednotliv´e hlaviˇckov´esoubory svoji vlastn´ımanu´alovou str´anku (man stdlib.h). • makro assert() je moˇzn´ez bˇehem kompilace odstranit pomoc´ımakra NDEBUG

38 Standardn´ıhlaviˇckov´esoubory (2)

unistd.h . . . nejpouˇz´ıvanˇejˇs´ısyst´emov´avol´an´ı sys/types.h . . . datov´etypy pouˇz´ıvan´ev 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´eprocesy 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ˇzpatˇr´ıdo UNIXu. • zaj´ımav´em˚uˇze b´yt pod´ıvat se do sys/types.h

39 Standardn´ıhlaviˇckov´esoubory (3)

semaphore.h . . . semafory (POSIX) pthread.h . . . vl´akna (POSIX threads) sys/socket.h . . . s´ıt’ov´akomunikace arpa/inet.h . . . manipulace se s´ıt’ov´ymi adresami sys/ipc.h ... spoleˇcn´edeklarace pro System V IPC sys/shm.h . . . sd´ılen´apamˇ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ˇejeˇ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˚upˇr´ıkazov´eˇr´adky – 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´ehodnoty EXIT_SUCCESS (0) a EXIT_FAILURE (1)

ls-l/ argv[0] "ls" argv[1] "-l" argc 3 argv[2] argv argv[3] "/"

• prvn´ı parametr (typu int) ud´av´apoˇcet argument˚una pˇr´ıkazov´em ˇr´adku (vˇcetnˇeparametru 0 – jm´ena programu) a druh´yparametr (typu char**) je pole ukazatel˚una tyto ˇretˇezce. Za posledn´ım ˇretˇezcem je jeˇstˇe NULL pointer – a to je nˇeco jin´eho neˇzpr´azdn´yˇretˇezec. • pˇri spuˇstˇen´ıprogramu pˇred´ak´od dynamick´eho linkeru ˇr´ızen´ıfunkci main(), viz strana 31. Staticky slinkovan´emu programu se ˇr´ızen´ı pˇred´apˇr´ımo. Nepˇr´ıtomnost main() v programu zp˚usob´ı chybu pˇri pˇrekladu na ´urovni linkeru. T´eto funkci se pˇred´ajm´eno spuˇstˇen´eho programu, argumenty z pˇr´ıkazov´eˇr´adky a pˇr´ıpadnˇei promˇenn´eprostˇred´ı. Ukonˇcen´ıt´eto funkce znamen´akonec pro- gramu a n´avratov´ahodnota 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ˇc´ast´ı normativn´ıˇc´asti C standardu, pouze informativn´ı. Pˇrekladaˇce to ale typicky podporuj´ı. Varianta main() s promˇenn´ymi prostˇred´ıpak vypad´atakto: int main(int argc, char *argv [], char *envp []); • n´avratov´ytyp funkce main by mˇel b´yt vˇzdy int; pˇrekladaˇcsi 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´ahodnota 0 m´av 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ˇezavˇre streamy stdio a vol´afunkce zaregistrovan´epomoc´ı atexit(). V z´avislosti na syst´emu to mohou b´yt i dalˇs´ıakce. Napˇr´ıklad ve FreeBSD je exit() syst´emov´evol´an´ıa exit() knihovn´ıfunkce. V Solarisu jsou obˇesyst´emov´ymi vol´an´ımi.

41 Promˇenn´eprostˇred´ı

• seznam vˇsech promˇenn´ych prostˇred´ı(environment variables) se pˇred´av´ajako promˇenn´a extern char **environ; • je to pole ukazatel˚u(ukonˇcen´eNULL) na ˇretˇezce ve tvaru: promˇenn´a =hodnota environ

environ[0] "SHELL=/bin/bash" environ[1] "DISPLAY=:0" environ[2] environ[3] "LOGNAME=beran"

• shell pˇred´av´aspuˇstˇen´emu programu ty promˇenn´e, kter´ejsou oznaˇceny jako exportovan´e(Bourne-like shellech pˇr´ıkazem export variable ). Po zmˇenˇe obsahu jiˇzjednou exportovan´epromˇenn´esamozˇrejmˇenen´ıpotˇreba promˇennou znovu exportovat. Pˇr´ıkazem env v´am shell vyp´ıˇse aktu´aln´ıpromˇenn´eprostˇred´ı, a pˇridat promˇennou do prostˇred´ıspouˇstˇen´eho programu pak staˇc´ıprov´est na pˇr´ıkazov´eˇr´adce, aniˇzbyste 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´epole environ automaticky. Je moˇzn´eve vol´an´ıpˇr´ısluˇsn´evarianty funkce exec pˇredat pole jin´e. • jak´epromˇenn´eprostˇ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´estr´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´ahodnota co je v ukazatelu environ.

42 Manipulace s promˇenn´ymi prostˇred´ı

• je moˇzn´epˇr´ımo zmˇenit promˇennou environ • char *getenv (const char *name ); – vr´at´ıhodnotu promˇenn´ename • 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ˇeprostˇred´ıotce neovlivn´ı • existuj´ıi funkce setenv() a unsetenv()

• u putenv() se vloˇzen´yˇretˇezec stane souˇc´ast´ı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´yhodnotu promˇenn´ezkop´ıruje

• d˚uleˇzit´eje zapamatovat si, ˇze synovsk´yproces zdˇed´ı v okamˇziku sv´eho vzniku od rodiˇce vˇsechny promˇenn´eprostˇred´ı, ale jak´akoliv manipulace s nimi v synovi je lok´aln´ıa do otce se nepˇren´aˇs´ı. Kaˇzd´yproces m´asvou kopii pro- mˇenn´ych, proto ani n´asledn´azmˇena prostˇred´ı otce nezmˇen´ı promˇenn´ejiˇ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ˇzv´ıte, ˇze promˇenn´a environ je obyˇcejn´epole ukazatel˚una ˇretˇezce, m˚uˇzete s timto polem pracovat pˇr´ımo. Pozor vˇsak na to, ˇze v takov´em pˇr´ıpadˇepak jiˇznesm´ıte pouˇz´ıvat zde uveden´efunkce, jinak se m˚uˇzete do- stat do probl´em˚us jejich konkr´etn´ıimplementac´ı.

43 Zpracov´an´ıargument˚uprogramu

• obvykl´yz´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˚ulze 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ˇzzaˇc´ınaj´ıznakem ’-’. • tento tvar argument˚upoˇzaduje norma a lze je zpracov´avat automaticky funkc´ı getopt().

• argumenty lze samozˇrejmˇezpracov´avat vlastn´ıfunkc´ı, ale standardn´ıfunkce je pohodlnˇejˇs´ı. • argumenty se typicky mohou opakovat, ale to m´asmysl jen v nˇekter´ych si- tuac´ıch • poˇrad´ıpˇrep´ınaˇc˚um˚uˇze b´yt d˚uleˇzit´ea je na aplikaci, aby toto specifikovala • UNIX norma definuje pomoc´ı13 pravidel velmi pˇresnˇe, jak by mˇely vypa- dat n´azvy pˇr´ıkaz˚ua 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´e2–9 znak˚u, z pˇrenositeln´eznakov´esady. Pˇrep´ınaˇce bez argument˚uby mˇelo b´yt moˇzn´ed´at do skupiny za jedn´ım zna- kem ’–’. Atd. • pouˇz´ıvat ˇc´ıslice jako pˇrep´ınaˇce je zastaral´e; je to nˇekde v normˇeUNIX 03, i kdyˇzj´ato v n´ınenaˇsel. • pozor na Linux a jeho (ponˇekud zvl´aˇstn´ıa nestandardn´ı) permutov´an´ıargu- ment˚u(viz dalˇs´ıslidy). • pˇrep´ınaˇc -W by mˇel b´yt rezervovan´ypro 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 ˇr´adku, 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ˇzjsou 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´epˇrep´ınaˇce jsou zad´any v optstring, kdyˇzza znakem pˇrep´ınaˇce n´asleduje ’:’, m´apˇrep´ınaˇcpovinnou hodnotu. • pˇri chybˇe(nezn´am´ypˇ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´ehl´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´a- pisu parametr˚uprogramu) vypsat struˇcnou n´apovˇedu (pˇr´ıpadnˇes odkazem na podrobnˇejˇs´ıdokumentaci) a ukonˇcit program s chybou, tj. s nenulovou n´avratovou hodnotou. • pˇr´ıklad tak´eukazuje nebezpeˇcn´epouˇzit´ıfunkce strcpy() • z pouˇzit´ıfunkce getopt() je vidˇet, ˇze je stavov´a. Zpracovat dalˇs´ıpole ar- gument˚u, pˇr´ıpadnˇeznovuzpracovat p˚uvodn´ı, je moˇzn´enastaven´ım extern´ı promˇenn´e optreset na 1. • standardn´ı getopt() zachov´apoˇrad´ıpˇrep´ınaˇc˚upˇ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´ytvar pˇrep´ınaˇc˚u • poprv´ese objevilo v GNU knihovnˇe libiberty: --jm´eno nebo --jm´eno=hodnota • argumenty se permutuj´ıtak, aby pˇrep´ınaˇce byly na zaˇc´atku, napˇr. ls * -l je tot´eˇzjako ls -l *, standardn´ıchov´an´ılze doc´ılit nastaven´ım promˇenn´e POSIXLY_CORRECT. • zpracov´avaj´ıse funkc´ı getopt long(), kter´apouˇz´ıv´apole struktur popisuj´ıc´ıch jednotliv´epˇ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ˇzje NULL, funkce vrac´ı val, jinak vrac´ı0 a d´a val do *flag */ int val; /* n´avratov´ahodnota */ };

verze jak se objevila ve FreeBSD (funkce getopt long() nen´ıstandarizovan´a), m´an´asleduj´ıc´ıvlastnosti:

• pokud vˇsechny dlouh´epˇrep´ınaˇce maj´ınastaveny kr´atkou variantu ve val, je chov´an´ı getopt long() kompatibiln´ıs getopt() • je moˇzn´ezad´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ˇzse tyto dlouh´e pˇrep´ınaˇce bez kr´atk´evarianty zpracuj´ıv jedn´evˇetvi pˇr´ıkazu case • existuje i vol´an´ı getopt long only(), kter´epovoluje i dlouh´epˇrep´ınaˇce uvo- zen´ejednou uvozovkou (-option) • funkci getopt long() je moˇzn´epouˇz´ıvat dvˇemi zp˚usoby. Prvn´ızp˚usob je, ˇze kaˇzd´ydlouh´ypˇrep´ınaˇcm´akoresponduj´ıc´ıkr´atk´y – takto lze jednoduˇse pˇridat dlouh´epˇrep´ınaˇce do existuj´ıc´ıho programu a je kompatibiln´ı s getopt. Druh´yzp˚usob umoˇzˇnuje m´ıt samostatn´edlouh´epˇ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´efunguje

47 Dlouh´epˇ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´epˇrep´ınaˇce, longopts obsahuje adresu pole struktur pro dlouh´epˇrep´ınaˇce (posledn´ı z´aznam pole obsahuje sam´enuly) • pokud funkce naraz´ına dlouh´ypˇrep´ınaˇc, vrac´ıodpov´ıdaj´ıc´ı val nebo nulu (pokud flag nebyl NULL), jinak je chov´an´ıshodn´es getopt(). • do *longindex (kdyˇznen´ı NULL) d´anav´ıc -1 nalezen´eho pˇrep´ınaˇce v longopts.

• toto je upraven´ypˇr´ıklad z manu´alov´estr´anky na FreeBSD:

#include #include #include

int ch, fd, daggerset, bflag = 0;

static struct option longopts[] = { { "buffy", no_argument, NULL, ’b’ }, { "fluoride", required_argument, NULL, ’f’ }, { "daggerset", no_argument, &daggerset, 1 }, { NULL, 0, NULL, 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´yproces uˇzivatelsk´y pˇreruˇsen´ı n´avrat reˇzim

rozhran´ıvol´an´ıj´adra reˇzim subsyst´em j´adra ˇr´ızen´ısoubor˚u

buffery subsyst´em IPC ˇr´ızen´ı pl´anovaˇc znakov´e blokov´e proces˚u pˇridˇelov´an´ı ovladaˇce a pamˇeti pamˇeti

strojovˇez´avisl´avrstva ovl´ad´an´ıhardware

hardware

• toto sch´ema je pˇrevzato z [Bach86], viz literatura. Zd˚urazˇnuje dva ´ustˇ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´az´akladn´ı pˇredstava. • UNIX rozliˇsuje dva reˇzimy bˇehu procesoru: uˇzivatelsk´yreˇzim a reˇzim j´adra. V uˇzivatelsk´em reˇzimu nejsou pˇr´ıstupn´eprivilegovan´einstrukce (napˇr. ma- pov´an´ıpamˇeti, I/O, maskov´an´ıpˇreruˇsen´ı). Tyto dva reˇzimy mus´ıb´yt pod- porov´any na hardwarov´e´urovni (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ˇeasynchronn´ıch pˇreruˇsen´ı(hodiny, I/O). D´ale se v reˇzimu j´adra oˇsetˇru- j´ıv´yjimeˇcn´estavy procesoru (v´ypadek str´anky, naruˇsen´ıochrany pamˇeti, ne- zn´am´ainstrukce apod.). Nˇekter´especi´aln´ıakce jsou zajiˇst’ov´any syst´emov´ymi procesy, kter´ebˇeˇz´ıcelou dobu v reˇzimu j´adra. • klasick´eUNIXov´ej´adro je tvoˇreno monolitick´ym k´odem. P˚uvodnˇebylo po- tˇreba vygenerovat (tj. pˇreloˇzit ze zdrojov´ych text˚ua slinkovat) j´adro pˇri zmˇenˇenˇekter´eho parametru nebo pˇrid´an´ıovladaˇce zaˇr´ızen´ı. V novˇejˇs´ıch im- plementac´ı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ˇzˇnuj´ı rozˇsiˇrovat k´od j´adra za bˇehu pomoc´ıtzv. modul˚uj´adra (loadable kernel mo- dules). Napˇr´ıklad syst´em FreeBSD 5.4-RELEASE m´a392 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´azaˇr´ızen´ı (napˇr. termin´aly) umoˇzˇnuj´ıpracovat s jednotliv´ymi bajty a nepouˇz´ıvaj´ıvy- rovn´avac´ıpamˇet’.

49 • j´adro nen´ısamostatn´yproces, ale je ˇc´ast´ıkaˇzd´eho uˇzivatelsk´eho procesu. Kdyˇzj´adro nˇeco vykon´av´a, tak vlastnˇeproces, bˇeˇz´ıc´ıv reˇzimu j´adra, nˇeco prov´ad´ı.

Procesy, vl´akna, programy • proces je syst´emov´yobjekt charakterizovan´ysv´ym kontextem, identifikovan´yjednoznaˇcn´ym ˇc´ıslem (process ID, PID); jin´ymi slovy ,,k´od a data v pamˇeti” • vl´akno (thread) je syst´emov´yobjekt, kter´yexistuje uvnitˇr procesu a je charakterizov´an sv´ym stavem. Vˇsechna vl´akna jednoho procesu sd´ıl´ıstejn´ypamˇet’ov´yprostor kromˇeregistr˚u procesoru a z´asobn´ıku; ,,linie v´ypoˇctu”, ,,to, co bˇeˇz´ı” • program ... soubor pˇresnˇedefinovan´eho form´atu obsahuj´ıc´ı instrukce, data a sluˇzebn´ıinformace nutn´eke spuˇstˇen´ı; ,,spustiteln´ysoubor 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´yprostor procesu, obsah registr˚ua datov´estruktury j´adra t´ykaj´ıc´ıse dan´eho procesu • jinak ˇreˇceno – kontext procesu je jeho stav. Kdyˇzsyst´em vykon´av´aproces, ˇr´ık´ase, ˇ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ˇzpozdˇeji, p˚uvodnˇev 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´em´ıt v´ıce paraleln´ıch lini´ı v´ypoˇctu nad sd´ılen´ymi daty. • pamˇet’ov´eprostory proces˚ujsou navz´ajem izolovan´e, ale procesy spolu mohou komunikovat (pozdˇeji se dozv´ıme, ˇze mohou i ˇc´asteˇcnˇe sd´ılet pamˇet’).

• procesy jsou entity na ´urovni j´adra, vl´akna mohou b´yt ˇc´asteˇcnˇenebo zcela implementov´ana knihovn´ımi funkcemi (tj. vl´akna nemus´ı j´adro v˚ubec pod- porovat). S vl´akny je spojena menˇs´ıreˇzie neˇzs procesy. • syst´emov´yproces, kter´ybˇeˇz´ına pozad´ıobvykle po celou dobu bˇehu syst´emu a zajiˇst’uje nˇekter´esyst´emov´esluˇzby (inetd, cron, sendmail.. . ) se naz´yv´a d´emon (angl. daemon). Syst´em BSD tedy nem´ave znaku ˇcerta, ale d´emona.

50 J´adro, reˇzimy, pˇreruˇsen´ı(klasick´yUNIX)

• procesy typicky bˇeˇz´ıv uˇzivatelsk´em reˇzimu • syst´emov´evol´an´ızp˚usob´ıpˇrepnut´ıdo reˇzimu j´adra • proces m´apro kaˇzd´yreˇzim samostatn´yz´asobn´ık • j´adro je ˇc´ast´ıkaˇzd´eho uˇzivatelsk´eho procesu, nen´ıto samostn´y proces (procesy) • pˇrepnut´ına jin´yproces se naz´yv´a pˇrepnut´ıkontextu • obsluha pˇreruˇsen´ıse prov´ad´ıv kontextu pˇreruˇsen´eho procesu • klasick´ej´adro je nepreemptivn´ı

• j´adro nen´ıoddˇelen´amnoˇzina proces˚u, bˇeˇz´ıc´ıch paralelnˇes 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ˇr´ad v tom sam´em • pˇreruˇsen´yproces 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´az uˇzivatelsk´e- ho reˇzimu pˇr´ıstupn´anejsou; takt´eˇzm˚uˇze pˇristupovat k instrukc´ım (napˇr. in- strukce 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ˇzm˚uˇze zablokovat jen ze sv´evlastn´ıv˚ule. Modern´ıUnixy dnes pouˇz´ıvaj´ıinterrupt vl´akna, v jejichˇzkontextu se mohou drivery zablokovat. • to, ˇze klasick´eunixov´ej´adro je nepreemptivn´ıznamen´a, ˇze jeden proces nem˚uˇze zablokovat jin´yproces

• 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˚uje situace ˇcasto velmi rozd´ıln´a– obsluha pˇre- ruˇsen´ı, preeptivnost kernelu atd.; k nˇekter´ym vˇecem se moˇzn´ado- staneme 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´yOS

uˇzivatelsk´y uˇzivatelsk´y proces proces server server server j´adro j´adro j´adro j´adro j´adro

• pokud unixov´yproces vyˇzaduje proveden´ısyst´emov´esluˇzby, pomoc´ısyst´e- mov´eho vol´an´ıpˇred´aˇr´ızen´ıj´adru. J´adro je kus k´odu sd´ılen´yvˇsemi procesy (ovˇsem pˇr´ıstupn´yjen pro ty, kter´ejsou pr´avˇev reˇzimu j´adra). J´adro tedy nen´ısamostatn´yprivilegovan´yproces, ale vˇzdy bˇeˇz´ı v r´amci nˇekter´eho pro- cesu (toho, kter´ypoˇz´adal j´adro o sluˇzbu, nebo toho, kter´ybˇ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´aj´adrem. • aby to nebylo tak jednoduch´e, mohou existovat syst´emov´e procesy (oznaˇco- van´ejako kernel threads), kter´ebˇeˇz´ıcelou dobu v reˇzimu j´adra. Naprost´a vˇetˇsina syst´emov´ych proces˚uvˇ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´apr´ava. Pl´anovaˇc proces˚upˇrep´ın´amezi procesy a t´ım umoˇzˇnuje bˇeh v´ıce proces˚usouˇcasnˇei na jednom procesoru. Na multi- procesorov´ych poˇc´ıtaˇc´ıch pak funguje skuteˇcn´yparalelismus proces˚ua vl´aken (dokonce se proces m˚uˇze pˇri pˇrepl´anov´an´ıdostat i na jin´yprocesor). • v distribuovan´em operaˇcn´ım syst´emu m´aj´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´esluˇzby, kter´ejsou v UNIXu souˇc´ast´ı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´ym˚uˇze bˇeˇzet i na jin´em uzlu s´ıtˇe. • dostupn´ych mikrokernel˚uje v dneˇsn´ıdobˇemnoho. M˚uˇzete zkusit napˇr´ıklad Minix (unix-like v´yukov´ysyst´em), pˇr´ıpadnˇesyst´em HURD, kter´ybˇeˇz´ınad mikroj´adrem Mach.

52 Syst´emov´avol´an´ı, funkce • v UNIXu se rozliˇsuj´ı syst´emov´avol´an´ı a knihovn´ıfunkce. Toto rozliˇsen´ıdodrˇzuj´ıi manu´alov´estr´anky: sekce 2 obsahuje syst´emov´avol´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´avol´an´ımaj´ıtak´etvar 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ˇekategorie nerozliˇsuj´ı, protoˇze z hlediska program´atora je jedno, jestli urˇcitou funkci provede j´adro nebo knihovna.

• zjednoduˇsenˇelze ˇr´ıci, ˇze syst´emov´evol´an´ıje funkce, kter´ajen uprav´ısv´ear- gumenty do vhodn´epodoby, 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´anˇejakou netrivi´aln´ıˇcinnost v uˇzivatelsk´em reˇzimu. • v assembleru je moˇzn´ezavolat vol´an´ıj´adra pˇr´ımo • API j´adra je definovan´ena ´urovni vol´an´ıfunkc´ıstandardn´ıknihovny, nikoliv na ´urovni 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ˇzm˚uˇze liˇsit nejen v z´avislosti na hardwarov´eplatformˇe, ale i mezi r˚uzn´ymi verzemi syst´emu na stejn´em hardwaru.

53 N´avratov´ehodnoty syst´emov´ych vol´an´ı • celoˇc´ıseln´an´avratov´ahodnota (int, pid t, off t, apod.) – >= 0 . . . operace ´uspˇeˇsnˇeprovedena – == -1 . . . chyba • n´avratov´ahodnota typu ukazatel – != NULL . . . operace ´uspˇeˇsnˇeprovedena – == 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; • ´uspˇeˇsn´evol´an´ınemˇen´ıhodnotu v errno! Je tedy tˇreba nejprve otestovat n´avratovou hodnotu a pak teprve errno. • chybov´ehl´aˇsen´ıpodle hodnoty v errno vyp´ıˇse funkce void perror(const char *s ); • textov´ypopis 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´avol´an´ım˚uˇze m´ıt smysl i n´avratov´ahodnota -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ˇe0, coˇzje platn´ahodnota i pro spr´avn´y v´ysledek (a −1 je samozˇrejmˇeplatn´yv´ysledek tak´e). • je tedy vˇzdy nutn´esi pˇreˇc´ıst manu´alovou str´anku pro pˇr´ıˇsluˇsn´evol´an´ınebo knihovn´ıfunkci • pozn.: ´uspˇeˇsnost funkc´ıze stdio.h je tˇreba testovat pomoc´ı int ferror(FILE*stream );, protoˇze jinak nelze rozliˇsit mezi chybou a kon- cem 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

• ´uvod, v´yvoj UNIXu a C, program´atorsk´en´astroje • z´akladn´ıpojmy a konvence UNIXu a jeho API • pˇr´ıstupov´apr´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´akomunikace • vl´akna, synchronizace vl´aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu

Uˇzivatel´ea skupiny

beran:x:1205:106:Martin Beran:/home/beran:/bin/bash v´yznam jednotliv´ych pol´ı: uˇzivatelsk´ejm´eno, zak´odovan´eheslo (novˇev /etc/shadow), ˇc´ıslo uˇzivatele (UID); superuˇzivatel (root) m´a UID 0, ˇc´ıslo prim´arn´ıskupiny (GID), pln´ejm´eno, domovsk´yadres´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˚uskupiny

55 • informace o uˇzivatel´ıch v souborech /etc/passwd,a /etc/group jsou zpra- cov´av´any r˚uzn´ymi syst´emov´ymi programy, napˇr. login (pˇrihl´aˇsen´ıdo syst´e- mu na z´akladˇeuˇzivatelsk´eho jm´ena a hesla) nebo su (zmˇena identity). J´adro o tˇechto souborech nic nev´ı, pouˇz´ıv´apouze numerickou identifikaci uˇzivatele a skupiny.

• dnes jiˇzhesla nejsou z bezpeˇcnostn´ıch d˚uvod˚upˇr´ımo v /etc/passwd, ale napˇr´ıklad v /etc/shadow, kter´ybˇeˇzn´emu uˇzivateli pˇr´ıstupn´ynen´ı. A napˇr. na FreeBSD se soubor /etc/passwd generuje ze souboru /etc/master.passwd, kter´yzak´odovan´ahesla obsahuje.

• existuj´ı i jin´esyst´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´av /etc/passwd je prim´arn´ı. Tuto skupinovou iden- tifikaci dostanou napˇr. soubory vytvoˇren´eprocesy uˇzivatele. Dalˇs´ıskupiny, ve kter´ych je uˇzivatel uveden v souboru /etc/group, jsou doplˇnkov´e(supple- mentary) a rozˇsiˇruj´ıpˇr´ıstupov´apr´ava uˇzivatele: skupinov´ypˇr´ıstup je povolen ke vˇsem objekt˚um, jejichˇzskupinov´yvlastn´ık je roven bud’ prim´arn´ı, nebo jedn´ez doplˇnkov´ych skupin. • p˚uvodnˇemˇel v UNIXu kaˇzd´yuˇzivatel vˇzdy aktivn´ıpouze jednu skupinovou identitu. Po nalogov´an´ıbyl ve sv´eprim´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´aobdoba su, ˇr´ıd´ıse obsahem souboru /etc/group), kter´yspustil nov´yshell. • v novˇejˇs´ıch UNIXech nen´ıtˇreba pro pˇr´ıstup k soubor˚um mˇenit prim´arn´ısku- pinovou identitu procesu, pokud uˇzivatel patˇr´ıdo potˇrebn´eskupiny. Zmˇena identity je nutn´a, pouze kdyˇzchceme vytv´aˇret soubory s jinou skupinovou identitou neˇzje prim´arn´ıskupina uˇzivatele. Lok´alnˇe pro urˇcit´yadres´aˇrtoho 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´ena System V. U BSD staˇc´ızmˇenit poˇzadovanou skupinu u adres´aˇre. • druh´apoloˇzka v ˇr´adc´ıch /etc/group obsahuje zak´odovan´eskupinov´eheslo pouˇz´ıvan´epˇr´ıkazem newgrp, to se jiˇzdnes nepouˇz´ıv´a. Napˇr´ıklad na FreeBSD je pˇr´ıkaz newgrp pˇr´ıstupn´yuˇzjen 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´edatab´aze pouˇz´ıvaj´ıjak´e zdroje • knihovn´ıfunkce toto samozˇrejmˇemus´ıexplicitnˇepodporovat • je moˇzn´enˇekter´ezdroje kombinovat, napˇr´ıklad uˇzivatel se nejdˇr´ıve m˚uˇze hledat v /etc/passwd a pot´ev NISu • poprv´ese objevilo v Solarisu, pot´epˇ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 ˇc´ast skuteˇcn´eho souboru nsswitch.conf ze stroje u-us:

passwd: files ldap group: 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: files protocols: files rpc: files ethers: 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´yproces dan´eho uˇzivatele.

• soubor S m´avlastn´ıka (UIDS) a skupinov´eho vlastn´ıka (GIDS). • algoritmus testov´an´ıpˇr´ıstupov´ych pr´av pro proces P (UIDP ,GIDP ,SUPG) a soubor S(UIDS,GIDS): Jestliˇze pak P roces m´av˚uˇci Souboru

if(UIDP == 0) . . . vˇsechna pr´ava

else if(UIDP == UIDS) . . . pr´ava vlastn´ıka

else if(GIDP == GIDS ||

GIDS ∈ SUPG) . . . pr´ava ˇclena skupiny else . . . pr´ava ostatn´ıch

• procesy superuˇzivatele root mohou mˇenit svoji uˇzivatelskou a skupinovou identitu. Toho vyuˇz´ıv´anapˇr. proces login, kter´ybˇeˇz´ıjako root a po zkon- trolov´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´avˇzdy neomezen´ypˇr´ıstup). Pokud se shoduje uˇzivatel, nepouˇzij´ı se nikdy pr´ava skupiny nebo ostatn´ıch, i kdyˇzpovoluj´ıv´ıce neˇzuˇzivatelsk´apr´ava. Podobnˇe pr´ava ostatn´ıch se nepouˇzij´ı, jestliˇze se shoduje skupinov´aidentita. Tedy pokud m´am˚uj soubor nastaveny pr´ava ---rwxrwx, nemohu ho ˇc´ıst, zapisovat ani spustit (dokud nastaven´ıpr´av nezmˇen´ım). • nˇekter´esyst´emy se odkl´anˇej´ıod klasick´eho modelu, kdy mnoho proces˚ubˇeˇzelo pod uˇzivatelem s UID 0 a pˇri bezpeˇcnostn´ı chybˇev takov´e aplikaci ˇcasto ´utoˇ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´ea efektivn´ıUID/GID • u kaˇzd´eho procesu se rozliˇsuje: – re´aln´eUID (RUID) – kdo je skuteˇcn´ym vlastn´ıkem procesu – efektivn´ıUID (EUID) – uˇzivatel, jehoˇzpr´ava proces pouˇz´ıv´a – uschovan´eUID (saved SUID) – p˚uvodn´ıefektivn´ıUID • podobnˇese rozliˇsuje re´aln´e, efektivn´ıa uschovan´eGID 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ˇeSGID 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´epotˇrebuj´ıvˇetˇs´ıpˇr´ıstupov´a pr´ava, neˇzm´auˇzivatel, jenˇzje spouˇst´ı. Pˇr´ıkladem je program passwd, kter´y mus´ıaktualizovat soubory /etc/passwd a /etc/shadow, kde ten prvn´ıne- m˚uˇze bˇeˇzn´yuˇzivatel mˇenit a druh´yz nich ani ˇc´ıst. Dalˇs´ıpˇr´ıklad je program su. Ten mus´ım´ıt pr´avo libovolnˇezmˇenit uˇzivatelskou a skupinovou identitu, coˇzje privilegium proces˚us UID 0. • SUID a SGID programy by mˇely b´yt peˇclivˇenaprogramov´any, aby dovolily pouze ty operace, pro kter´ejsou urˇceny, a neumoˇznily zneuˇz´ıt jejich privilegia pro neopr´avnˇen´eakce (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´em´ı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´yuˇzivatel nem˚uˇze zmˇenit sv´eRUID nebo uschovan´eSUID (vyj´ımka je pˇri vol´an´ı exec(), viz strana 113) – proces m˚uˇze vˇzdy zmˇenit sv´eEUID na to z RUID nebo z uschovan´eho UID. Toto zaruˇcuje, ˇze v SUID programu je moˇzn´elibovolnˇemˇ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´ydan´yproces spustil) – root m˚uˇze vˇsechno, a kdyˇzzmˇen´ıRUID, tak se z´aroveˇn zmˇen´ıi uchovan´e UID – nemˇelo by smysl mˇenit jen jedno z nich kdyˇzkter´ekoli m˚uˇzete pouˇz´ıt pro nastaven´ıEUID.

59 Identifikace vlastn´ıka procesu

• uid t getuid(void) vrac´ıre´aln´euser ID volaj´ıc´ıho procesu. • uid t geteuid(void) vrac´ıefektivn´ıuser ID volaj´ıc´ıho procesu. • gid t getgid(void) vrac´ıre´aln´egroup 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´anejv´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˚ujako uid_t, gid_t, size_t, apod. Vesmˇes jsou to celoˇc´ıseln´etypy, ˇ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´epozn´amky na stranˇe59). – pro ostatn´ıprocesy nastavuje jen EUID, a uid mus´ıb´yt bud’ rovn´eRUID 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´etedy znamen´a: proces s efektivn´ımi pr´avy superuˇzivatele m˚uˇze libovolnˇemˇenit identitu. Ostatn´ıprocesory mohou pouze stˇr´ıdat sv´a re´aln´aa efektivn´ıpr´ava.

• program login vyuˇz´ıv´avol´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ˇznemˇel pr´ava na setgid() a setgroups(). • setgroups() nen´ıuvedeno v UNIX 98 ani UNIX 03.

• RUID/EUID jsou uloˇzen´ev z´aznamu tabulky proces˚upro pˇr´ısluˇsn´yproces a z´aroveˇnv tzv. u-area (viz napˇr´ıklad [Bach]). EUID v tabulce proces˚use naz´yv´ajiˇzzm´ınˇen´euschovan´eUID, neboli saved UID. Jak jiˇzbylo ˇreˇceno, uschovan´eUID se pouˇz´ıv´apro kontrolu, kdyˇzse proces chce vr´atit k EUID, se kter´ym byl spuˇstˇen (po t´e, co doˇcasnˇenastavil sv´e EUID na UID uˇzivatele, kter´yproces 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ˇzse v programu k EUID==0 nem˚uˇzete vr´atit. V tom pˇr´ıpadˇebyste museli pouˇz´ıt vol´an´ı seteuid(), kter´enastavuje pouze EUID.

61 Syst´em soubor˚u

• adres´aˇre tvoˇr´ıstrom, spolu se soubory acyklick´ygraf (na jeden soubor m˚uˇze existovat v´ıce odkaz˚u). • kaˇzd´yadres´aˇrnav´ıc obsahuje odkaz na sebe ’.’ (teˇcka) a na nadˇrazen´yadres´aˇr’..’ (dvˇeteˇcky). • pomoc´ırozhran´ısyst´emu soubor˚use pˇristupuje i k dalˇs´ım entit´am v syst´emu: – perifern´ızaˇr´ızen´ı – pojmenovan´eroury – sokety – procesy (/proc) – pamˇet’ (/dev/mem, /dev/kmem) – pseudosoubory (/dev/tty, /dev/fd/0,...) • z pohledu j´adra je kaˇzd´yobyˇcejn´ysoubor 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´estruktury nepouˇz´ıvaj´ı. Symbolick´elinky na adres´aˇre funguj´ıvˇsude.

• pojmenovan´eroury (viz strana 81) lze pouˇz´ıt i mezi procesy, kter´enejsou pˇr´ıbuzensky spˇr´ıznˇen´e. Jinak funguj´ıstejnˇejako nepojmenovan´eroury. • zmiˇnovan´esokety jsou v dom´enˇeUNIX, tj. slouˇz´ıpro komunikaci v r´amci jednoho syst´emu. Sokety z dom´eny INET, pˇres kter´eprob´ıh´as´ıt’ov´akomuni- kace, se v syst´emu soubor˚uneobjevuj´ı. S´ıt’ov´akomunikace zaˇc´ın´ana stranˇe 155. • debuggery pouˇz´ıvaj´ıpamˇet’ov´eobrazy proces˚udostupn´ev /proc. Ve vˇetˇsinˇe unix-like syst´em˚uobsahuje podstrom /proc ´udaje o j´adru syst´emu a bˇeˇz´ıc´ıch procesech ve formˇetextov´ych soubor˚u. • dneˇsn´ımodern´ıunixy m´ıvaj´ıspeci´aln´ıfilesyst´em devfs, jehoˇzobsah 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´ediskov´ezaˇr´ızen´ı. Po fyzick´em odpojen´ızaˇr´ızen´ıodkaz z adres´aˇrov´estruktury opˇet zmiz´ı.

62 Jednotn´yhierarchick´ysyst´em soubor˚u

/

dev etc usr home tty

• svazek (angl. file system) je ˇc´ast souborov´eho syst´emu, kterou lze samostatnˇe vytvoˇrit, pˇripojit, zruˇsit... Kaˇzd´yfilesyst´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´ypo s´ıti (nfs, afs). • po startu j´adra je pˇripojen´yjen koˇrenov´yfilesyst´em, dalˇs´ıfilesyst´emy se za- pojuj´ıdo hierarchie na m´ısta adres´aˇr˚upˇ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 obsa- hem 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 ˇz´adost (pˇri prvn´ım pˇr´ıstupu) a jeho odpojen´ıpo urˇcit´edobˇeneˇcinnosti. Tuto funkci zajiˇst’uje d´emon automounter (autofs, automount, amd). • UNIX nem´aˇz´adn´eA, B, C, D. . . disky apod.

63 Typick´askladba adres´aˇr˚u

/bin . . . z´akladn´ısyst´emov´epˇr´ıkazy /dev . . . speci´aln´ısoubory (zaˇr´ızen´ı, devices) /etc . . . konfiguraˇcn´ıadres´aˇr /lib . . . z´akladn´ısyst´emov´eknihovny /tmp . . . veˇrejn´yadres´aˇrpro doˇcasn´esoubory /home . . . koˇren domovsk´ych adres´aˇr˚u /var/adm . . . administrativn´ısoubory (ne na BSD) /usr/ . . . knihovny header˚upro C /usr/local . . . lok´alnˇeinstalovan´ysoftware /usr/man . . . manu´alov´estr´anky /var/spool . . . spool (poˇsta, tisk,...)

• v /bin, /lib, /sbin jsou pˇr´ıkazy a knihovny potˇrebn´epˇri startu syst´emu, kdy je pˇripojen pouze koˇrenov´yfilesyst´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ˇzjeho obsah nen´ıdostupn´ybˇehem startu syst´emu). • podstrom /usr obsahuje soubory, kter´ese nemˇen´ı pˇri bˇeˇzn´em provozu a nejsou z´avisl´ena konkr´etn´ım poˇc´ıtaˇci. Proto by mˇel j´ıt sd´ılet read-only. Na sv´estanici doma ho ale samozˇrejmˇebudete m´ıt read-write. • v podstromu /var jsou data, kter´ase za provozu mˇen´ıa jsou specifick´apro kaˇzd´ypoˇc´ıtaˇc. • r˚uzn´esyst´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´eporty – blokov´e . . . data proch´az´ısyst´emovou vyrovn´avac´ıpamˇet´ı(buffer cache) po bloc´ıch pevnˇedan´evelikosti, 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´ınej- prve v bufferu. Teprve kdyˇznejsou 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´yz´apis dat na disk. • disky v UNIXu jsou obvykle pˇr´ıstupn´epˇres znakov´e(pouˇz´ıvan´epˇri mkfs – vy- tvoˇren´ısvazku – a fsck – kontrola konzistence) i blokov´erozhran´ı(pouˇz´ıvan´e pˇri norm´aln´ım provozu syst´emu soubor˚u). Nˇekter´esyst´emy (FreeBSD) ale uˇz v /dev v˚ubec soubory pro blokov´azaˇr´ızen´ınemaj´ı, pouze znakov´a. • dˇr´ıve musel administr´ator syst´emu po zmˇenˇehardwarov´ekonfigurace upravit obsah adres´aˇre /dev skriptem MAKEDEV nebo ruˇcnˇe. Dnes (Linux, IRIX, Free- BSD, Solaris, . . . ) jiˇzspeci´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ˇe62). • okamˇzit´yz´apis na disk lze vynutit pˇres O DIRECT command ve vol´an´ı fcntl()

65 Fyzick´euloˇ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´emohou b´yt i na nˇekolika disc´ıch, do jednoho svazku.

• dalˇs´ımoˇznosti: striping, mirroring, RAID / /home /usr

disk1 disk2 disk3

• v´yraz syst´em soubor˚use pouˇz´ıv´av nˇekolika v´yznamech: – jeden filesyst´em, tj. to, co vyrob´ıpˇr´ıkaz mkfs – cel´ahierarchie pˇripojen´ych svazk˚uv syst´emu (v´ystup pˇr´ıkazu mount) – zp˚usob organizace svazku (tj. typ fs) a tomu odpov´ıdaj´ıc´ımodul j´adra, kter´ys daty manipuluje (UFS2, Ext3, XFS, ...) • striping je od slova stripe, ne strip; podle toho se tak´evyslovuje. Znamen´a, ˇze za sebou n´asleduj´ıc´ıbloky dat se ukl´adaj´ıparalelnˇena r˚uzn´edisky a t´ım se zvyˇsuje pˇrenosov´arychlost. • mirroring ukl´ad´akopie 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´aXOR prvn´ıch dvou, po hav´arii libovoln´eho disku jsou vˇsechna data st´ale ˇciteln´a. • jednotliv´e´urovnˇeRAID (Redundant Array of Inexpensive Disks) zahrnuj´ı striping, mirroring a vyuˇzit´ıparitn´ıch disk˚u. • na terminologii je tˇreba d´at velk´ypozor. Napˇr. to, co se v DOS svˇetˇenaz´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´anebo se s t´ım setk´av´ate v praxi, doporuˇcuji vaˇs´ıpozor- nosti ZFS, coˇzje filesyst´em a manaˇzer logick´ych odd´ıl˚u v jednom. Ze Solarisu se jiˇzdostal do FreeBSD od verze 7.0 a tak´edo Mac OS X Leopard (kter´y byl ofici´alnˇevyd´an v ˇr´ıjnu 2007). M˚uˇzete okamˇzitˇe zapomenout na jednotliv´e disky, co je d˚uleˇzit´eje pouze celkov´adiskov´akapacita v syst´emu.

66 Organizace syst´emu soubor˚u s5

blok ˇc. 0 zav´adˇec´ıblok (boot block) 1 superblok 0 912 2 oblast i-uzl˚u(i-nodes) ...

oblast datov´ych blok˚u

• p˚uvodn´ıUNIXov´ysyst´em soubor˚ustandardnˇepouˇz´ıvan´ydo verze System V Release 3; v BSD se prim´arnˇepouˇz´ıval do verze 4.1 • vlastnosti: – bloky d´elky 512, 1024 nebo 2048 bajt˚u – jedin´y(neduplikovan´y) superblok – datov´yprostor pevnˇerozdˇelen´yna oblast i-uzl˚u a oblast datov´ych blok˚u – pˇri velikosti bloku 1024 bajt˚ubyla teoretick´avelikost filesyst´emu pˇres 16 GB • boot block – pro uloˇzen´ızavadˇeˇce OSu • superblok – z´akladn´ıinformace o svazku: poˇcet blok˚upro 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´atabulka i-uzl˚u), z´amky pro seznamy vol- n´ych blok˚ua 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´apr´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˚una soubor, velikost souboru, 10 odkaz˚una datov´ebloky a 3 odkazy na nepˇr´ım´ebloky • maxim´aln´ıvelikost souboru: 2113674 blok˚u, tj. pˇribliˇznˇe1 GB pˇri pouˇzit´ı blok˚uvelikosti 512 B • jm´ena soubor˚u– max. 14 znak˚u(14 + 2 = 16, tedy mocnina dvou a tedy bezprobl´emov´euloˇzn´ıadres´aˇrov´ych poloˇzek do blok˚u)

67 • pˇri pouˇzit´ıtohoto filesyst´emu byla v´ykonnost disk˚uvyuˇzita jen cca na 2% a rychlost ˇcten´ıbyla v ˇr´adu jen nˇekolika des´ıtek kilobajt˚uza 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ˇzve verzi 4.0 (1988); tato verze z´aroveˇnzavedla diskov´evy- rovn´avac´ıpamˇeti, tedy to, co UNIX m´aod sv´eho vzniku v roce 1970...

Navigace v adres´aˇrov´estruktuˇre

i-nodes /etc/passwd i-node2 i-node37 i-node71

data ..2 37 root:x:0:... .. 2 .. 2

etc 37 passwd 71

• kdyˇzcesta zaˇc´ın´aznakem ’/’, zaˇc´ın´anavigace v koˇrenov´em adres´aˇri, jinak zaˇcne v pracovn´ım adres´aˇri procesu. • koˇrenov´yadres´aˇrm´atypicky ˇc´ıslo 2. 0 je pro oznaˇcen´ıpr´azdn´eho uzlu a 1 byla dˇr´ıve pouˇz´ıvan´apro soubor, do kter´eho se vkl´adaly vadn´ebloky, aby je syst´em uˇzd´ale nepouˇz´ıval. • cesta ve kter´eje v´ıce lom´ıtek za sebou je st´ale platn´a, tj. ///a///b///c je ekvivaletn´ı /a/b/c.

68 Linky

hard link origin´al symbolick´ylink /var /etc /usr password 20 passwd 20 passwd 31 ......

i-nodes 0...... 20 31

root:x:0:... data ../etc/passwd

Hard linky lze vytv´aˇret pouze v r´amci jednoho (logick´eho) filesyst´emu.

hardlink

• odkaz na stejn´yi-uzel • vlastnˇedruh´ejm´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´ylink (symlink, softlink)

• pouze odkaz na skuteˇcnou cestu k souboru jin´eho typu (ls -l ho o- znaˇcuje ’l’), tj. symbolick´ylink je typem odliˇsn´yod bˇeˇzn´eho souboru a jeho data obsahuj´ıobyˇcejn´yˇretˇezec – jm´eno cesty, at’ jiˇzrelativn´ınebo absolutn´ı • odliˇsn´echov´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ˇrnebo na neexistuj´ıc´ısoubor

Nejjednoˇsˇs´ızp˚usob jak si ovˇeˇrit, zda dan´edva linky ukazuj´ına stejn´ysoubor na disku je pouˇz´ıt -i pˇrep´ınaˇcpˇ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˚ua datov´ych blok˚ubl´ıˇzk sobˇe • UFS (Unix File System), p˚uvodnˇeBerkeley FFS (Fast File System) • ˇclenˇen´ına skupiny cylindr˚u, kaˇzd´askupina obsahuje – kopii superbloku – ˇr´ıdic´ıblok skupiny – tabulku i-uzl˚u – bitmapy voln´ych i-uzl˚ua datov´ych blok˚u – datov´ebloky • bloky velikosti 4 aˇz8 kB, fragmenty blok˚u • jm´ena dlouh´a255 znak˚u

• superblok v kaˇzd´ecylinder skupinˇeposunut tak, aby superbloky nebyly na stejn´eplotnˇ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 po- rovn´an´ıosmi r˚uzn´ych filesyst´em˚upodle r˚uzn´ych implementaˇcn´ıch krit´eri´ı; nezahrnuje v sobˇeale v´yvoj posledn´ıch let. • UFS byl st´ale 32-bitov´y, coˇzse 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ˇehav´arie, urychlen´ızotaven´ıpo hav´arii • ZFS – modern´ı128-bitov´yfilesyst´em vyvinut´ySunem, nyn´ıopen-source, v Solarisu 10, ted’ jiˇztak´ev Mac OS X 10.5 a FreeBSD 7.

70 V´yvoj ve spr´avˇeadres´aˇrov´ych poloˇzek

• maxim´aln´ıd´elka jm´ena souboru 14 znak˚unebyla dostaˇcuj´ıc´ı • FFS – d´elka aˇz255; kaˇzd´apoloˇzka z´aroveˇnobsahuje i jej´ıd´elku • nov´efilesyst´emy pouˇz´ıvaj´ıpro vnitˇrn´ıstrukturu adres´aˇr˚ur˚uzn´e varianty B-strom˚u – v´yraznˇezrychluje pr´aci s adres´aˇri obsahuj´ıc´ıvelk´emnoˇzstv´ı soubor˚u – XFS, JFS, ReiserFS, . . . • UFS2 zav´ad´ızpˇetnˇekompatibiln´ı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´epˇr´ıstupy do adres´aˇre jsou pak srovnateln´ese 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´ejestli FreeBSD v budoucnu pˇrejde na ZFS jako jejich hlavn´ı filesyst´em. • mal´esoubory se ˇcasto ukl´adaj´ıv i-nodech, ˇc´ımˇzse 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 v data v data v data v data INODE INODE INODE INODE INODE s realvp

ufs vnodeops s5 vnodeops spec vnodeops ufs file system s5 file system spec file system

• FFS uveden´yve 4.2BSD byl historicky druh´yunixov´yfilesyst´em. Nˇekteˇr´ı dodavatel´eunixov´ych syst´em˚uho zaˇcali preferovat vzhledem k jeho lepˇs´ımu v´ykonu a nov´ym moˇznostem, jin´yz˚ust´avali d´ale u s5fs z d˚uvodu zpˇetn´ekom- patibility. To d´ale prohlubovalo probl´em jiˇztak nedostateˇcn´einteroperability mezi r˚uzn´ymi unixov´ymi syst´emy. Nˇekter´ym aplikac´ım nav´ıc plnˇenevyho- voval ani jeden z tˇechto filesyst´em˚u. Postupnˇese tak´eobjevovala 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˚umezi jednotliv´ymi syst´emy, coˇzmˇelo za n´asledek vznik distribuovan´ych filesyst´em˚u– napˇr. NFS (Network File System). • FFS zaveden´ydo syst´emu s VFS se zaˇcal naz´yvat UFS. • vzhledem k v´yˇse popsan´esituaci bylo jen ot´azkou ˇcasu, kdy dojde k funda- ment´aln´ım zmˇen´am v infrastruktuˇre syst´emu soubor˚u, aby souˇcasnˇepodpo- roval v´ıce typ˚ufilesyst´em˚u. Vzniklo nˇekolik r˚uzn´ych implementac´ıod r˚uzn´ych v´yrobc˚u, aˇzse nakonec de facto standardem stala VFS/vnode architektura od firmy Sun Microsystems. V dneˇsn´ıdobˇeprakticky vˇsechny unixov´ea unix- like syst´emy podporuj´ı VFS, i kdyˇzˇcasto se vz´ajemnˇenekompatibiln´ımi ´upravami. VFS se poprv´eobjevilo v roce 1985 v Solarisu 2.0; brzy bylo pˇrevzato BSD – FFS s podporou VFS je pr´avˇeUFS. • hlavn´ı myˇslenka: kaˇzd´emu otevˇren´emu souboru v syst´emu pˇr´ısluˇs´ı struk- tura file (to by vlastnˇebyl jeden slot v n´ami jiˇzzn´am´esyst´emov´eta- bulce otevˇren´ych soubor˚u). Ta ukazuje na vnode (virtual node). Vnode ob- sahuje ˇc´ast nez´avislou na konkr´etn´ım syst´emu soubor˚ua ˇc´ast z´avislou, coˇz m˚uˇze b´yt napˇr´ıklad struktura inode. Ta je specifick´apro kaˇzd´ytyp soubo- rov´eho syst´emu. Kaˇzd´ytyp filesyst´emu implementuje pevnˇedanou

72 sadu funkc´ı pro jednotliv´eoperace nad soubory, na kterou se od- kazuj´ı vˇsechny virtu´aln´ı uzly odpov´ıdaj´ıc´ıdan´emu typu filesyst´emu. Tato sada funkc´ıtedy definuje vnode interface. Kdyˇztedy zavol´ate napˇr´ıklad open(), j´adro zavol´apˇr´ısluˇsnou implementaci v z´avislosti na typu filesyst´emu (napˇr. z modulu ext2fs). Implementaˇcnˇez´avisl´aˇc´ast struktury vnode je pˇr´ıstupn´apouze z funkc´ıpˇr´ısluˇsn´eho typu filesyst´emu; j´adro do n´ıtedy ,,ne- vid´ı” pˇr´ımo. Jak uvid´ıte na dalˇs´ım slajdu, existuje jeˇstˇejedna sada funkc´ı, kter´ase t´yka pr´ace s filesyst´emy jako takov´ymi. Ta pak definuje VFS inter- face. Tyto dvˇesady spoleˇcnˇetvoˇr´ıvnode/VFS rozhran´ı, kter´emu se bˇeˇznˇe ˇr´ık´ajen VFS. • (nebudu zkouˇset) – u speci´aln´ıch soubor˚uje situace sloˇzitˇejˇs´ı, v SVR4 struk- tura file ukazuje na snode (shadow-special-vnode), kter´ydefinuje operace se zaˇr´ızen´ım (pomoc´ısouborov´eho syst´emu spec) a prostˇrednictv´ım ukazatele s realvp se odkazuje na re´aln´yvnode pro operace se speci´aln´ım souborem; 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´esnodes pro jedno zaˇr´ızen´ımaj´ı ukazatel s commonvp na jeden spoleˇcn´ysnode (toto nen´ı na obr´azku zachyceno). Pˇri otevˇren´ı speci´aln´ıho souboru se hled´av hash tabulce snodes otevˇren´ych zaˇr´ızen´ıpo- loˇzka odpov´ıdaj´ıc´ıspeci´aln´ımu souboru (podle major a minor ˇc´ısla zaˇr´ızen´ı). Kdyˇzsnode nen´ınalezen, vytvoˇr´ıse nov´y. Tento snode se pak pouˇz´ıv´apˇri operac´ıch se zaˇr´ızen´ım. V´ıce viz napˇr´ıklad [Vahalia].

Hierarchie souborov´ych syst´em˚u

vfs mount list struct file rootvfs f vnode VFS VFS v vfsp VNODE

vfs next vfs next v op INODE vfs data vfs data root vnode root vnode vnodeops super block super block vfs mountedhere vfs op vfs op vfs vnodecovered VNODE mount v vfsp vfsops vfsops v op point INODE in vfssw[] vsw vfsops rootvfs

vnodeops

• struktura vfs obsahuje implementaˇcnˇenez´avisl´einformace o filesyst´emu, nez´avisl´ena konkr´ent´ım typu filesyst´emu (podobnˇejako vnode funguje pro soubory). Tato struktura nereprezentuje konkr´etn´ıtyp filesyst´emu, ale file- syst´em pˇrimontovan´ydo 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˚una tabulky vfsops pro vˇsechny syst´emem podporo- van´etypy filesyst´em˚u, z tabulky se vyb´ır´apˇ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´ypomoc´ıvnode • v vfsmountedhere – pouze ve vnode, kter´yreprezentuje mount point (ad- res´aˇr, na kter´em je pˇripojen koˇren jin´eho filesyst´emu); odkazuje na strukturu vfs reprezentuj´ıc´ıpˇripojen´yfilesyst´em • v vnodecovered – odkaz na vnode adres´aˇre, na kter´em je filesyst´em pˇripojen

Otevˇren´esoubory z pohledu j´adra I. user file descriptor table file table inode table

: count 1 process A   WRONLY @ @ Z Z @ Z @R count 1 Z Z /etc/group Z~ count 2 3 RDONLY Q  Q  Q process B Qs count 3  : - count 1  /etc/passwd RDWR

• 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ˇr´ad stejn´a. Realitˇev´ıce se podobaj´ıc´ıobr´azek je na pˇr´ıˇst´ım slajdu. • kaˇzd´yproces m´asvoji 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˚uje 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 ´uplnˇejedno)

74 • tabulka otevˇren´ych soubor˚usyst´emu, kter´avlastnˇevytv´aˇr´ıo jednu ´uroveˇn odkaz˚unav´ıc, je zde proto, aby r˚uzn´eprocesy mohly sd´ılet stejnou aktu´aln´ı pozici v souboru. • pˇri otevˇren´ısouboru pomoc´ıvol´an´ı open() se vˇzdy alokuje nov´yslot v tabulce deskriptor˚ua tak´ev syst´emov´etabulce 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˚usd´ıl´ıstejn´yslot v tabulce otevˇren´ych soubor˚usyst´emu nebo v pˇr´ıpadˇer˚uzn´ych proces˚upak pomoc´ı vytvoˇren´ınov´eho procesu pomoc´ı vol´an´ı fork(), viz strana 119.

Otevˇren´esoubory z pohledu j´adra II.

struct proc uf next uf next u first u proc struct struct struct struct user ufchunk ufchunk ufchunk

p cred ...21 0 ... 21 0 ... 21 0 uf ofile[]

f cred struct cred

f next struct struct struct struct struct struct file file file file file file *file f prev f vnode VNODE VNODE VNODE VNODE VNODE

• struktury proc a user vytv´aˇr´ıj´adro pro kaˇzd´yproces a drˇz´ıv nich sluˇzebn´ı informace o procesu. • struktura ufchunk obsahuje NFPCHUNK (obvykle 24) deskriptor˚usoubor˚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´ese 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 struk- turami file, pokud byl dan´ysoubor 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ˇesvazku slouˇz´ıpˇr´ıkaz fsck. Postupnˇetestuje moˇzn´enekonzistence: – v´ıcen´asobn´eodkazy na stejn´yblok – odkazy na bloky mimo rozsah datov´eoblasti syst´emu soubor˚u – ˇspatn´ypoˇcet odkaz˚una i-uzly – nespr´avn´avelikost soubor˚ua adres´aˇr˚u – neplatn´yform´at i-uzl˚u – bloky kter´enejsou obsazen´eani voln´e – chybn´yobsah adres´aˇr˚u – neplatn´yobsah superbloku • operace fsck je ˇcasovˇen´aroˇcn´a. • ˇzurn´alov´esyst´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(). Perio- dicky vyrovn´avac´ıpamˇeti ukl´ad´azvl´aˇstn´ısyst´emov´yproces (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´ysoubor ˇcek´ana 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˚us nekonzistenc´ımetadat na disku: – journalling – skupina na sobˇez´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´yfilesyst´em v Solarisu, kter´ypouˇz´ıv´a copy-on-write

• filesystem metadata = inodes, directories, free block maps • ext2 dokonce defaultnˇepouˇz´ıv´aasynchronn´ız´apis metadat a je pˇri pouˇz´ıt´ı synchronn´ıho z´apisu v´yraznˇepomalejˇs´ıneˇzUFS • z´avisl´eoperace jsou napˇr´ıklad smaz´an´ı poloˇzky z adres´aˇre a smaz´an´ı dis- kov´eho inode. Pokud by se stalo, ˇze se nejdˇr´ıve smaˇze diskov´yinode a pak te- prve poloˇzka v adres´aˇri, pˇri v´ypadku mezi tˇemito dvˇemi operacemi vnik´ane- konzistence – link ukazuje na diskov´ysoubor, kter´yneexistuje. Nen´ıprobl´em se tomuto vyhnout pˇri synchronn´ım z´apisu metadat (v´ıme kdy a co zapisu- jeme, urˇcujeme tedy poˇrad´ız´apisu), ale pˇri metodˇewrite-back je jiˇznutn´e ˇreˇsit z´avislosti jednotliv´ych blok˚una sebe, protoˇze pˇri klasick´esynchronizaci vyrovn´avac´ıch pamˇet´ına disk j´adro nezaj´ım´a, kter´y blok se zap´ıˇse dˇr´ıv a kter´ypozdˇeji. • ˇcasto jsou bloky na sobˇez´avisl´eve 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´yv´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´enabootovat. 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ˇes t´ım, jak rostou velikosti bˇeˇznˇe pouˇz´ıvan´ych disk˚u. Takovou chybou, kter´anebr´an´ınabootov´an´ı, ale je nutn´e ji odstranit je napˇr´ıklad blok, kter´yje oznaˇcen jako pouˇzit´y, ale ˇz´adn´ysoubor 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´aperioda z´apisu) m˚uˇze b´yt v´yraznˇevˇetˇs´ıhrozbou zde neˇzna /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 me- tadat 4 z´apisy – zv´yˇsen´ıpoˇctu odkaz˚uv inode, vytvoˇren´ınov´eadres´aˇrov´e poloˇzky, smaz´an´ıstar´e, a opˇetn´esn´ıˇzen´ıpoˇctu odkaz˚uv inode. Kdykoli by syst´em spadnul, nenastane nebezpeˇcn´asituace. Napˇr´ıklad 2 odkazy z ad- res´aˇr˚una inode s referenˇcn´ım poˇctem 1 je probl´em, protoˇze po zruˇsen´ıjed- noho odkazu to bude vypadat, ˇze soubor je st´ale na disku, kdyˇzuˇzbyls jeho data d´avno smaz´ana. Nen´ıteˇzk´esi asi pˇredstavit, co by to mohlo znamenat v pˇr´ıpadˇe, ˇze soubor obsahoval opravdu d˚uleˇzit´adata – napˇr´ıklad z´alohu. Opaˇcn´asituace, tj. jeden odkaz na inode s referenc´ı2 sice tak´enen´ıspr´avn´asi- tuace, ale neohroˇzuje to moˇznost filesyst´em namontovat a norm´alnˇepouˇz´ıvat. V nejhorˇs´ım se stane, ˇze soubor vypad´aˇze jiˇzna 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´ebloky a pot´esn´ıˇzen´ı referenc´ı. A protoˇze zv´yˇsen´ı/sn´ıˇzen´ıse t´yk´astejn´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˚ua 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´euk´azat, ˇze nelze zapsat ˇz´adn´yz tˇech tˇr´ıblok˚uv kruˇznici tak, jak to je na konci operace re- name, ˇze je opravdu nutn´yten roll-back – mohli bychom uvaˇzovat, ˇze inode se vlastnˇenezmˇ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˚uv inodu by n´as totiˇzmohl dostat pˇresnˇedo situace, kter´aje pops´ana o p´ar ˇr´adk˚uv´yˇse.

Pˇr´ıstupov´apr´ava vlastn´ık (u) skupina (g) ostatn´ı(o)

nejvyˇsˇs´ıbit 4 z }| { z }| { z }| { 2 suid 1 sgid r sticky w x • SGID pro soubor bez pr´ava spuˇstˇen´ıpro skupinu v System V: kontrola z´amk˚upˇ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´esoubory budou m´ıt stejnou skupinu jako adres´aˇr(System V; u BSD syst´em˚uto funguje jinak, viz pozn´amky)

78 • SGID pro adres´aˇre u BSD syst´em˚uzp˚usob´ı, ˇze soubory a podadres´aˇre vy- tvoˇren´ev 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´yUFS 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ˇea 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´apro veˇrejn´eadres´aˇre (napˇr. /tmp). • p˚uvodnˇemˇel sticky bit v´yznam i pro spustiteln´esoubory: program s nasta- ven´ym sticky bitem z˚ustal po ukonˇcen´ıv pamˇeti a jeho opˇetovn´espuˇstˇen´ı bylo rychlejˇs´ı. Dnes se sticky bit v tomto v´yznamu uˇznepouˇz´ıv´a. • nˇekter´efilesyst´emy (XFS, AFS, UFS2, ZFS) maj´ı tzv. access control lists (ACL´s), kter´edovoluj´ı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´ysoubor nejprve otevˇr´ıt vol´an´ım open() nebo creat(). • otevˇren´esoubory jsou dostupn´epˇres deskriptory soubor˚u (file descriptors), ˇc´ıslovan´eod 0, v´ıce deskriptor˚um˚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´yv´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´afunkce, kter´aalokuje deskriptory (nejen open() a creat(), ale napˇr. i pipe(), dup(), socket()) alokuje vˇzdy voln´edeskriptory s nejniˇzˇs´ım ˇc´ıslem. • proces dˇed´ıotevˇren´esoubory od rodiˇce, tyto soubory nemus´ıznovu otv´ırat. Obvykle (ale ne vˇzdy) proces dostane otevˇren´ealespoˇndeskriptory 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´yjm´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ˇzneexistuje – O EXCL . . . chyba, kdyˇzexistuje (pouˇzit´ıs O CREATE) – O TRUNC . . . zruˇsit pˇredchoz´ıobsah (pr´avo z´apisu nutn´e) – . . . • pˇri O CREAT definuje tˇret´ıparametr mode pˇr´ıstupov´apr´ava

• pˇri pouˇzit´ı O CREAT se mode jeˇstˇemodifikuje podle umask. • mode nem´adefaultn´ıhodnotu, tj. vezme se to, co je na z´asobn´ıku i kdyˇztento parametr nen´ıpˇr´ıtomen! Pˇr´ıznaky i m´od jsou uloˇzeny v syst´emov´etabulce otevˇren´ych soubor˚u. • pokud se znovu pouˇzije dˇr´ıve vyuˇz´ıvan´apoloˇzka v tabulce deskriptor˚unebo v tabulce otevˇren´ych soubor˚u, vˇse potˇrebn´ese vynuluje (pozice v souboru, flagy deskriptoru, . . . ) • existuj´ıjeˇstˇedalˇs´ınastaviteln´epˇr´ıznaky: – O SYNC (O DSYNC, O RSYNC – nen´ına BSD) ... operace se souborem skonˇc´ıaˇzpo fyzick´em uloˇzen´ıdat (synchronized I/O) – O NOCTTY . . . pˇri otv´ır´an´ıtermin´alu procesem, kter´ynem´aˇr´ıdic´ıter- min´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´ejsou nastaven´epomoc´ı 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ˇejeden z tˇechto tˇr´ıflag˚u. • je moˇzn´eotevˇr´ıt a z´aroveˇnvytvoˇ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´eplat´ı, ˇze mus´ıte m´ıt pro dan´ysoubor 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´ehodnotˇepˇr´ıstupov´ych pr´av se vynuluj´ıbity, kter´ebyly 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´yv dan´echv´ıli nebyl otevˇren´ypro 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˚uje nutn´epouˇz´ıt speci´aln´ıvol´an´ı. • test, zda soubor existuje, a jeho pˇr´ıpadn´evytvoˇren´ıje atomick´aoperace. Toho se vyuˇz´ıv´apˇ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´apr´ava k perifern´ım zaˇr´ızen´ım. • hodnoty pro mode je moˇzn´enal´ezt vˇetˇsinou v manu´alov´estr´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˚udat a uloˇz´ıje od adresy buf. • vrac´ıpoˇcet skuteˇcnˇepˇ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˚udat uloˇzen´ych od adresy buf. – vrac´ıvelikost skuteˇcnˇezapsan´ych dat (<= nbyte).

• pro UNIX je kaˇzd´ysoubor posloupnost bajt˚ubez 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´ypoˇcet bajt˚umenˇs´ıneˇz nbyte, pokud v souboru zb´yv´am´enˇeneˇ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ˇeje k dispozici m´enˇeneˇ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´ypoˇcet bajt˚umenˇs´ıneˇz nbyte, jestliˇze se do souboru nevejde v´ıc dat (napˇr. zaplnˇen´ydisk), z´apis je pˇreruˇsen sign´alem nebo je nastaveno O_NONBLOCK a do roury, soketu nebo zaˇr´ızen´ı se vejde pouze ˇc´ast zapisovan´ych dat. Bez O_NONBLOCK se ˇcek´a, dokud se nepodaˇr´ı zapsat vˇse. Pokud nelze aktu´alnˇezapsat nic, blokuj´ıc´ı write() se zablokuje, dokud nen´ımoˇzn´ezapisovat, neblokuj´ıc´ı write() vr´at´ı -1 a nastav´ı errno na EAGAIN. • d˚uleˇzit´ev´yjimky vzhledem k rour´am jsou uvedeny na stranˇe85. • kdyˇz read() nebo write() vr´at´ım´enˇeneˇ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ˇzse podaˇr´ıpˇreˇc´ıst, resp. zapsat aspoˇnjeden bajt, zp˚usob´ın´avrat s hodnotou -1 a nastaven´ı errno na EINTR. • pˇr´ıznak otevˇren´ısouboru O_APPEND zajist´ıatomick´yz´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ˇzje poˇcet odkaz˚una soubor 0, j´adro uvoln´ıdata souboru. Tedy i po zruˇsen´ıvˇsech odkaz˚u(jmen) mohou se souborem pracovat procesy, kter´eho maj´ıotevˇren´y. Soubor se smaˇze, aˇz kdyˇzho zavˇre posledn´ıproces. • kdyˇzse 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´ysoubor, 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ˇzje takov´ysoubor zavˇren vˇsemi procesy, j´adro smaˇze jeho data z disku. • i operace close() m˚uˇze selhat. Napˇr. nˇekter´efilesyst´emy zapisuj´ıdata na disk aˇzv okamˇziku zavˇren´ısouboru, kdyˇzz´apis skonˇc´ıchybou, close() vr´at´ı -1. • otev´ır´an´ısoubor˚ua nezav´ır´an´ıje pˇri ukonˇcen´ıpr´ace s nimi vede k alokovan´e pamˇeti, kterou jiˇznem´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 najed- nou zpracov´avat rozumnˇevelk´ebloky. • pokud potˇrebujete pracovat s mal´ymi ˇc´astmi/buffery, je lepˇs´ıpouˇz´ıt stream orintovan´eknihovn´ıfunkce fopen(), fread(), . . . kter´edata vnitˇrnˇebufferuj´ı

84 Pr´ace s pojmenovanou rourou

• nemus´ıb´yt moˇzn´evytvoˇrit FIFO na s´ıt’ov´em filesyst´emu (NFS, AFS) • je nutn´ezn´at s´emantiku otev´ır´an´ıpojmenovan´eroury: – otevˇren´ıroury pouze pro ˇcten´ıse zablokuje do t´edoby, dokud se neobjev´ızapisovatel (pokud jiˇzneexistuje) – otevˇren´ıroury pouze pro z´apis se zablokuje do t´edoby, dokud se neobjev´ıˇcten´aˇr(pokud jiˇzneexistuje) – toto chov´an´ıje moˇzn´eovlivnit flagem O NONBLOCK • s´emantika ˇcten´ıa z´apisu je jeˇstˇeo nˇeco sloˇzitˇejˇs´ı, vˇenujte velkou pozornost pozn´amk´am pod t´ımto slajdem – a je to stejn´ejako u nepojmenovan´eroury (strana 118)

• je moˇzn´eotevˇr´ıt rouru pro z´apis i ˇcten´ıstejn´ym procesem najednou, aniˇzby pˇredt´ım existoval ˇcten´aˇrnebo zapisovatel • pokud rouru nem´aˇz´adn´yproces otevˇrenou pro z´apis, pro okamˇzit´evr´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ˇevr´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ˇev dobˇe, kdy ˇcten´aˇr existoval) poˇsle kernel procesu sign´al EPIPE (“broken pipe”). • v pˇr´ıpadˇeotev´ır´an´ıpouze pro z´apis s O NONBLOCK bez existuj´ıc´ıho ˇcten´aˇre se vr´at´ıchyba a errno se nastav´ına ENXIO. Tato asymetrie je snahou, aby v rouˇre nebyla data, kter´anebudou v kr´atk´edobˇepˇ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´ana pojmenovan´erouˇre a vykon´av´apoˇzadavky, mus´ıte ji otevˇr´ıt s flagem O RDWR i kdyˇzdo 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 akcep- tov´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´yjako 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 ˇc´asteˇcn´yz´apis (a vr´at´ı tak menˇs´ıˇc´ıslo neˇzvelikost dat kter´ase mˇela zapsat). • roura nem´apozici v souboru, z´apis tak vˇzdy pˇrid´av´ana konec.

85 • stejnˇese vzhledem ke ˇcten´ıa z´apisu chov´ai nepojmenovan´aroura, 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ˇc´atku souboru – SEEK CUR . . . od aktu´aln´ıpozice – SEEK END ... od konce souboru • vrac´ıv´yslednou pozici poˇc´ıtanou od zaˇc´atku souboru. • lseek(fildes, 0, SEEK CUR) pouze vr´at´ıaktu´aln´ıpozici.

• 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ˇc´asti budou sam´e nuly (samotn´e lseek() nestaˇc´ı). Nˇekter´efilesyst´emy takov´ebloky cel´ych nul pro ´usporu m´ısta ne- ukl´adaj´ı. • velikost souboru je moˇzn´ezjistit pomoc´ı lseek(fildes, 0, SEEK END). • nejˇcastˇejˇs´ıoperace s lseek jsou tˇri: nastaven´ıkonkr´etn´ıpozice od zaˇc´atku souboru, nastaven´ıpozice na konec souboru a zjiˇstˇen´ıaktu´aln´ıpozice v sou- boru (0 spoleˇcnˇese SEEK CUR) • pˇri pouˇzit´ı lseek se ˇz´adn´eI/O neprovede, ˇz´adn´ypˇr´ıkaz se nepoˇsle na ˇradiˇc disku • lseek nemus´ıslouˇzit jen pro operace read a write, ale tak´epro 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´adata. • standard ponech´av´anespecifikovan´e, jestli funguje prodlouˇzen´ı souboru (s vyplnˇen´ım pˇridan´eho ´useku 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´yobsah souboru pˇri otevˇren´ıse d´apˇr´ıznakem O TRUNC ve funkci open(). • podrobnosti je tˇreba hledat v manu´alov´estr´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´ydeskriptor, vr´at´ı nov´ydeskriptor, kter´yodkazuje na stejn´eotevˇ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´ydeskriptor se pouˇzije i u otev´ır´an´ıa vytv´aˇren´ısoubor˚u, viz strany 80 a 81. • duplikovan´ya p˚uvodn´ıdeskriptor sd´ıl´ıstejn´eotevˇren´ısouboru a tedy i ak- tu´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ˇzse budeme zab´yvat rourami. • je potˇreba si d´at pozor na stav deskriptor˚u. Druh´ypˇr´ıklad nebude fungovat, kdyˇzbude 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 R´ıdic´ıfunkceˇ soubor˚ua 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˚usouboru. 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´ase 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´ehodnoty cmd ve funkci fcntl(): – F DUPFD . . . duplikace deskriptoru – F GETFD . . . zjiˇstˇen´ıpˇr´ıznak˚udeskriptoru (FD CLOEXEC – uzavˇren´ıpˇri exec). FD CLOEXEC je jedin´yflag pro deskriptory, definovan´yv normˇe UNIX 03. – F SETFD . . . nastaven´ıpˇr´ıznak˚udeskriptoru – F GETFL . . . zjiˇstˇen´ım´odu ˇcten´ı/z´apis a pˇr´ıznak˚uotevˇren´ısouboru (jako u open()) – F SETFL . . . nastaven´ıpˇr´ıznak˚uotevˇren´ısouboru (O APPEND, O DSYNC, 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´esi uvˇedomit, ˇze jsou dva druhy pˇr´ıznak˚u – pˇr´ıznak(y) pro soubo- rov´ydeskriptor a pˇr´ıznaky pro otevˇren´ysoubor – 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´edalˇ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˚unejdˇr´ıv vˇzdy zjistˇete, jak´ebyly pˇredt´ım. I kdyˇz jste si jisti, ˇze v dan´echv´ıli jsou nulov´ea je tedy moˇzn´eprov´est fcntl(fd, O APPEND), nem˚uˇzete vˇedˇet, co se m˚uˇze zmˇenit (napˇr´ıklad o p´ar ˇr´adk˚uv´yˇse nˇejak´yflag pˇrid´ate, aniˇzbyste 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ˇepro odebr´an´ıflagu – je ˇspatn´eˇreˇsen´ınastavit hodnot˚uflag˚una 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´ycestou, 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´apr´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˚una soubor

• metadata jsou informace o souboru – tedy m´od, ˇcasy pˇr´ıstupu, d´elka, vlastn´ık a skupina atd. Nepatˇr´ımezi nˇeskuteˇcn´adata 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´epˇreˇc´ıst, i kdyˇzproces nem´apr´ava pro ˇcten´ıobsahu sou- boru. • touto funkc´ınez´ısk´am flagy deskriptoru ani flagy z pole tabulky otevˇren´ych soubor˚uv syst´emu, zde jde o informace ohlednˇesouboru uloˇzen´eho na pamˇe- t’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 definov´any konstanty S_IFMT (maska pro typ), S_IFBLK (blokov´yspeci´aln´ı), S_IFCHR (znakov´yspeci´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´apr´ava: S_IRUSR (ˇcten´ıpro vlastn´ıka), S_IWGRP (z´apis pro skupinu), atd. int lstat(const char *path, struct stat *buf ); • kdyˇzje zkouman´ysoubor symlink, stat() vr´at´ıinformace o souboru, na kter´yukazuje. Tato funkce vrac´ıinformace o symlinku.

• typ a pr´ava souboru jsou uloˇzena spoleˇcnˇev st_mode, proto existuj´ızmiˇno- van´amakra. • S IFMT specifikuje tu ˇc´ast bit˚u, kter´ejsou vˇenovan´etypu souboru, makra pro jednotliv´etypy pak nejsou masky, ale hodnoty, takˇze test na typ souboru je nutn´eudˇ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´yk´od.

92 Nastaven´ıˇcas˚usouboru

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ˇekop´ırovac´ıa archivaˇcn´ıprogramy, aby zajistily stejn´eˇcasy kopie a origin´alu. • shellov´erozhran´ı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´ak 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´apouˇ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 za- dan´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´ysetuid proces by mˇel za norm´aln´ıch okolnost´ık pˇr´ısluˇsn´emu sou- boru 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ˇzproces nem˚uˇze nikdy oˇsetˇrit. Reˇsen´ımˇ je vr´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´apr´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´enemohli obch´azet nastaven´equoty t´ım, ˇze sv´esoubory pˇredaj´ı nˇekomu jin´emu. • bˇeˇzn´yuˇzivatel m˚uˇze mˇenit skupinu sv´ych soubor˚ua mus´ıpˇritom patˇrit do c´ılov´eskupiny.

• parametr mode zde samozˇrejmˇeneobsahuje 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ˇeko- mu jin´emu, napˇr. v IRIXu je chov´an´ı chown() nastaviteln´ejako parametr j´adra. • nen´ıbˇeˇzn´evolat 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´yodkaz (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ˇeodkazu 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- uzlu. C´ıslaˇ i-uzl˚ujsou jednoznaˇcn´apouze v r´amci svazku, proto pro linky mezi filesyst´emy je nutn´epouˇz´ıt symlinky.

• parametr path2 nesm´ıexistovat, tedy nelze takto pˇrejmenov´avat • unlink() nefunguje na adres´aˇre • shellov´ypˇ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´esymlink ukazuje

96 Symbolick´elinky

int symlink(const char *path1, const char *path2 ); • vytvoˇr´ısymbolick´ylink 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´amax. bufsize znak˚uz cesty, na kterou ukazuje symlink path. • vr´at´ıpoˇcet znak˚uuloˇzen´ych do buf. • obsah buf nen´ızakonˇcen nulou (znakem ’\0’)!

• shellov´ypˇ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ˇejin´yhardlink. Naopak soubor (poloˇzku adres´aˇre i data) je moˇzn´esmazat, i kdyˇzna nˇej ukazuj´ınˇejak´esymlinky. • readlink() je pouˇziteln´yv situaci, pokud chci smazat soubor, na kter´ydan´y symlink ukazuje • bufsize se typicky d´av´ao 1 menˇs´ı neˇzvelikost bufferu, to pro ukonˇcen´ı znakem ’\0’

97 Manipulace s adres´aˇri int mkdir(const char *path, mode t mode ); • vytvoˇr´ınov´ypr´azdn´yadres´aˇr, (bude obsahovat pouze poloˇzky ’.’ a ’..’). int rmdir(const char *path ); • smaˇze adres´aˇr path. Adres´aˇrmus´ı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ˇr´ad´any, readdir() je m˚uˇze vracet v libo- voln´em poˇrad´ı. • v nˇekter´ych implementac´ıch (napˇr. FreeBSD) lze adres´aˇrotevˇ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´yvrac´ıraw data adres´aˇre. Kromˇetoho, norma nevyˇzaduje, aby adres´aˇrbylo moˇzn´eˇc´ıst funkc´ı read(), Linux to napˇr´ıklad nedovol´ı. • readdir() je stavov´afunkce. Pro vr´acen´ıse na zaˇc´atek je moˇzn´epouˇz´ıt funkci rewinddir(). S vl´akny pak pouˇz´ıvat readdir r(), protoˇze struktura dirent je statick´a. • d ino nen´ımoc uˇziteˇcn´e, protoˇze v pˇr´ıpadˇe, kdy dan´yadres´aˇrje mount point, tak ukazuje na adres´aˇr, na kter´yje dalˇs´ıfilesyst´em namontov´an, ne na koˇren namontovan´eho filesyst´emu • rmdir nefunguje na nepr´azn´yadres´aˇr, je moˇzn´epouˇ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˚ua zjiˇst’ov´an´ıdalˇs´ıch informac´ıpomoc´ı stat(). • konec adres´aˇre se pozn´atak, ˇ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ˇeje k´od chyby v promˇenn´e errno, v pˇr´ıpadˇeprvn´ım je errno nezmˇenˇena. Proto by errno mˇelo b´yt vˇzdy nastaven´ena 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ˇrprocesu

• kaˇzd´yproces m´asv˚uj aktu´aln´ı(pracovn´ı) adres´aˇr, v˚uˇci kter´emu jsou ud´av´any relativn´ıcesty k soubor˚um. Poˇc´ateˇ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´ypracovn´ıadres´aˇrprocesu. 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ˇno 1 vˇetˇs´ıneˇzd´elka cesty.

• funkci fchdir() se pˇred´av´adeskriptor z´ıskan´yvol´an´ım open() na adres´aˇr. • funkce getcwd() m˚uˇze vr´atit jinou cestu neˇztu, po kter´ejsme se do aktu´al- n´ıho adres´aˇre dostali, jesliˇze ˇc´ast cesty v chdir() byl symlink nebo doˇslo k pˇresunu (pˇrejmenov´an´ı) nˇekter´eho adres´aˇre na cestˇeod koˇrene. U souˇcasn´ych ˚uby se to ale uˇzst´at nemˇelo.

100 Obsah

• ´uvod, v´yvoj UNIXu a C, program´atorsk´en´astroje • z´akladn´ıpojmy a konvence UNIXu a jeho API • pˇr´ıstupov´apr´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´akomunikace • 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      adresovateln´e  uˇziv. programem       nelze adresovat z´asobn´ık  { oblast user  uˇziv. programem

• text ... k´od programu • data . . . inicializovan´epromˇenn´e • sekce text a data jsou uloˇzeny ve spustiteln´em souboru • bss . . . neinicializovan´epromˇ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´ysegment procesu. Velikost datov´eho segmentu lze mˇenit pomoc´ısyst´emov´ych vol´an´ı brk() a sbrk(). • (uˇzivatelsk´y) z´asobn´ık . . . automatick´epromˇenn´e, parametry funkc´ı, n´avra- tov´eadresy. Kaˇzd´yproces m´adva z´asobn´ıky, jeden pro uˇzivatelsk´yreˇzim a jeden pro reˇzim j´adra. Uˇzivatelsk´yz´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´ej´adrem, kter´e nejsou potˇrebn´e, kdyˇzje proces odloˇzen na disku (poˇcet otevˇren´ych soubor˚u, nastaven´ıoˇsetˇren´ısign´al˚u, poˇcet segment˚usd´ılen´epamˇeti, argumenty pro- gramu, promˇenn´eprostˇred´ı, aktu´aln´ıadres´aˇr, atd.). Tato oblast je pˇr´ıstupn´a pouze pro j´adro, kter´evˇzdy vid´ıpr´avˇejednu u-oblast patˇr´ıc´ıpr´avˇebˇeˇz´ıc´ımu procesu. Dalˇs´ıinformace o procesu, kter´ej´adro m˚uˇze potˇrebovat i pro jin´y neˇzpr´avˇebˇeˇz´ıc´ıproces, nebo i kdyˇzje 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 proce- sorem (v´ypadek str´anky, nezn´am´ainstrukce,...), ˇcasovaˇcem (v pravideln´ych intervalech je potˇreba aktivovat pl´anovaˇcproces˚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´evol´an´ı). • v pamˇeti je pouze jedna kopie k´odu a dat j´adra, sd´ılen´avˇsemi procesy. K´od j´adra je vˇzdy cel´yrezidentn´ıv pamˇeti, nen´ıodkl´ad´an na disk. • text j´adra . . . k´od j´adra operaˇcn´ıho syst´emu, zaveden´ypˇri startu syst´emu a rezidentn´ıv pamˇeti po celou dobu bˇehu syst´emu. Nˇekter´eimplementace umoˇzˇnuj´ı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´anov´yovladaˇc), nen´ıproto tˇreba kv˚uli kaˇzd´ezmˇenˇeregenerovat j´adro a restartovat syst´em. • data a bss j´adra . . . datov´estruktury pouˇz´ıvan´ej´adrem, souˇc´ast´ıje i u-oblast pr´avˇebˇeˇz´ıc´ıho procesu. • z´asobn´ık j´adra . . . samostatn´ypro kaˇzd´yproces, je pr´azdn´y, jestliˇze je proces v uˇzivatelsk´em reˇzimu (a tedy pouˇz´ıv´auˇzivatelsk´y z´asobn´ık).

103 Pamˇet’ov´esegmenty procesu

a segs read only struct as text s as shared s prev s next read/write data private struct proc read/write z´asobn´ık private

read/write sd´ılen´apamˇet’ shared

• kaˇzd´yproces m´atˇri z´akladn´ısegmenty (pamˇet’ov´esegmenty, nemluv´ıme o hardwarov´ych segmentech): – text – data – z´asobn´ık • d´ale lze do adresov´eho prostoru pˇripojit segmenty sd´ılen´epamˇeti (shmat()) nebo soubory (mmap()). • text je sd´ılen vˇsemi procesy, kter´eprov´ad´ıstejn´yk´od. Datov´ysegment a z´asobn´ık jsou priv´atn´ıpro kaˇzd´yproces. • z´akladn´ım rysem t´eto architektury je tzv. memory object, coˇzje abstrakce mapov´an´ımezi kusem pamˇeti a m´ıstem, kde jsou data norm´alnˇeuloˇzena (tzv. backing store nebo data object). Takov´em´ısto uloˇzen´ım˚uˇze b´yt napˇr´ıklad swap nebo soubor. Adresov´yprostor procesu je pak mnoˇzina mapov´an´ına r˚uzn´edatov´eobjekty. Existuje i anonymn´ıobjekt, kter´ynem´am´ısto trval´eho uloˇzen´ı(pouˇz´ıv´ase napˇr´ıklad pro z´asobn´ık). Fyzick´apamˇet’ pak slouˇz´ıjako cache pro data tˇechto namapovan´ych datov´ych objekt˚u. • tato zde velmi hrubˇepopsan´aarchitektura se naz´yv´aVM (od Virtual Me- mory), a objevila se v SunOS 4.0. Na t´eto architektuˇre je zaloˇzena architek- tura 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’ pamˇet’ pamˇet’ pamˇet’ procesu 1 procesu 2 procesu 3 procesu 4                

          re´aln´a     pamˇet’          

• kaˇzd´yproces vid´ısv˚uj adresov´yprostor 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´ysegment procesu (to je pr´avˇeto 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´astr´anka m´asv´eum´ı- stˇen´ıv r´amci fyzick´epamˇ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ˇeprom´ıch´any v˚uˇci jejich poˇrad´ı ve virtu´aln´ıadresov´em prostoru. • pokud nen´ıstr´anka pr´avˇepouˇz´ıv´ana, m˚uˇze b´yt tak´eodloˇzena na disk. • pamˇet’ov´ymanager j´adra zajiˇst’uje mapov´an´ımezi virtu´aln´ımi adresami po- uˇz´ıvan´ymi k´odem uˇzivatelsk´ych proces˚ui j´adra na fyzick´eadresy 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´ena fyzick´eadresy pˇrev´ad´ıhardware ve spolupr´aci s j´adrem syst´emu. • pˇri nedostatku voln´epamˇeti se odkl´adaj´ınepouˇz´ıvan´e´useky pamˇeti do odkl´adac´ıoblasti (swap) na disk. • pˇred verz´ıSVR2 se procesem swapper (nyn´ı sched) odkl´adaly cel´eprocesy. • od verze SVR2 se pouˇz´ıv´astr´ankov´an´ına ˇz´adost (demand paging) a copy-on-write. Str´anky se alokuj´ıaˇzpˇ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˚unastupuje aˇzpˇ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´asign´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´yproces, kter´ynen´ızam- ˇcen v pamˇeti, a na uvolnˇen´em´ısto zav´est dˇr´ıve odloˇzen´yproces. demand paging: pˇri ˇz´adosti procesu o pamˇet’ se pouze uprav´ıtabulka str´anek. Prvn´ıinstrukce adresuj´ıc´ıobsah str´anky vyvol´av´yjimku. J´adro ji oˇsetˇr´ıt´ım, ˇze alokuje str´anku. copy-on-write: v´ıce proces˚um˚uˇze sd´ılet zapisovatelnou fyzickou str´anku, kter´aje ale logicky priv´atn´ıpro kaˇzd´yproces (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´estr´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´auˇzje priv´atn´ı a proces ji m˚uˇze d´ale libovolnˇemˇenit. Ostatn´ıprocesy pouˇz´ıvaj´ıst´ale nezmˇe- nˇenou p˚uvodn´ıstr´anku.

str´anky k odloˇzen´ı se hledaj´ı algoritmem NRU (not recently used): kaˇzd´a str´anka m´apˇr´ıznaky referenced a modified, na zaˇc´atku vynulovan´e. Pˇri prvn´ım pˇr´ıstupu se nastav´ı referenced, pˇri zmˇenˇe modified. Oba pˇr´ıznaky se perio- dicky nuluj´ı. Pˇrednostnˇese uvolˇnuj´ıstr´anky, kter´e nejsou modifikovan´eani pouˇzit´e. Str´anky k´odu programu a mapovan´ych soubor˚use neukl´adaj´ıdo odkl´adac´ıho pro- storu, ale obnovuj´ıse z pˇr´ısluˇsn´eho souboru.

106 Stavy procesu

bˇeˇz´ı z´anik vznik v uˇziv. † wait reˇzimu exit * m´atoha kill pˇreruˇsen´ı n´avrat (zombie) preempce bˇeˇz´ı v reˇzimu usp´an´ı pˇripraven j´adra sp´ı pˇridˇelen´ı v pamˇeti procesoru v pamˇeti odloˇzen´ı zaveden´ı odloˇzen´ı probuzen´ı pˇripraven sp´ı na disku probuzen´ı 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 pro- cesu jeho n´avratovou hodnotu. Cel´apamˇet’ procesu je uvolnˇena, zb´yv´apouze struktura proc. Proces lze definitivnˇezruˇsit, aˇzkdyˇzse jeho rodiˇczept´ana 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´eprocesy, ale jednotliv´estr´anky pamˇeti. • proces je usp´an, kdyˇzo to s´am poˇz´ad´a, napˇr. zaˇcne ˇcekat na dokonˇcen´ıpe- rifern´ıoperace. Preempce je naopak nedobrovoln´eodebr´an´ıprocesoru pl´ano- vaˇcem.

107 Pl´anov´an´ıproces˚u

• preemptivn´ıpl´anov´an´ı – jestliˇze se proces nevzd´aprocesoru (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´am´a nejvyˇsˇs´ı prioritu. • v SVR4 byly zavedeny prioritn´ıtˇr´ıdy a podpora proces˚ure´aln´eho ˇcasu (real-time) s garantovanou maxim´aln´ıdobou odezvy. • na rozd´ıl od pˇredchoz´ıch verz´ıznamen´av SVR4 vyˇsˇs´ı ˇc´ıslo vyˇsˇs´ı prioritu.

• z´akladem preemptivn´ıho pl´anov´an´ı jsou pravideln´apˇreruˇsen´ıod ˇcasovaˇce, kter´aodeberou procesor bˇeˇz´ıc´ımu procesu a pˇredaj´ı ˇr´ızen´ıj´adru (aktivuje se pl´anovaˇcproces˚u).

• jin´avarianta je nepreemptivn´ı(kooperativn´ı) pl´anov´an´ı, kdy proces bˇeˇz´ı, do- kud se s´am nevzd´aprocesoru, tj. dokud nezavol´atakovou syst´emovou funkci, kter´apˇrepne kontext na jin´yproces. 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´apouze preemptivn´ıpl´anov´an´ıpro uˇzivatelsk´eprocesy. • tradiˇcn´ıUNIXov´ej´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 mo- dern´ıch UNIX˚ujsou jiˇzpreemtivn´ı– je to hlavnˇekv˚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ˇzse 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ˇre- d´ano jin´emu procesu. Proces si proto nikdy nem˚uˇze b´yt jist´y, ˇze urˇcitou operaci (v´ıce neˇzjednu intstrukci, kromˇesyst´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´eakce, mus´ıse procesy navz´ajem synchronizo- vat. 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´aprocesoru).

108 Prioritn´ıtˇr´ıdy • syst´emov´a – priorita 60 aˇz99 – rezervov´ana pro syst´emov´eprocesy (pageout, sched,...) – pevn´apriorita • real-time – priorita 100 aˇz159 – pevn´apriorita – pro kaˇzdou hodnotu priority definov´ano ˇcasov´ekvantum • sd´ılen´ıˇcasu (time-shared) – priorita 0 aˇz59 – promˇenn´advousloˇzkov´apriorita, pevn´auˇzivatelsk´a a promˇenn´asyst´emov´aˇc´ast – pokud proces hodnˇevyuˇz´ıv´a procesor, je mu sniˇzov´ana priorita (a zvˇetˇsov´ano ˇcasov´e kvantum)

• syst´emov´atˇr´ıda je pouˇz´ıv´ana pouze j´adrem, uˇzivatelsk´yproces bˇeˇz´ıc´ıv reˇzi- mu j´adra si ponech´av´asvou pl´anovac´ıcharakteristiku. • procesy ve tˇr´ıdˇere´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ˇesd´ılen´ıˇcasu usp´an a ˇcek´ana nˇejakou ud´alost, je mu doˇcasnˇepˇriˇrazena syst´emov´apriorita. Po probuzen´ı se takov´yproces dostane na procesor dˇr´ıve, neˇzostatn´ıprocesy, kter´enesp´ı. • pevn´aˇc´ast priority procesu ve tˇr´ıdˇesd´ılen´ıˇcasu se d´anastavit 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´yproces patˇr´ıdo skupiny proces˚u, tzv. process group • kaˇzd´askupina m˚uˇze m´ıt vedouc´ıproces, tzv. group leader • kaˇzd´yproces 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´ytermin´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˚ua ˇr´ıdit jejich pˇr´ıstup k termin´al˚um • session (relace) je kolekce skupin proces˚uvytvoˇren´apro ´uˇcely ˇr´ızen´ıjob˚u

• kdyˇzse uˇzivatel pˇrihl´as´ıdo syst´emu, je vytvoˇren´a nov´arelace, kter´ase skl´ad´a z jedn´eskupiny proces˚u, ve kter´eje jeden proces – ten kter´yvykon´av´a uˇzivatel˚uv shell. Tento proces je z´aroveˇnvedouc´ıt´eto jedin´eskupiny pro- ces˚ua tak´eje vedouc´ırelace. V pˇr´ıpadˇe, ˇze job control je povolen, kaˇzd´y pˇr´ıkaz nebo kolona pˇr´ıkaz˚uvytvoˇr´ınovou skupinu proces˚u, jeden z proces˚u v kaˇzd´eskupinˇese vˇzdy stane vedouc´ım procesem dan´eskupiny. Jedna ze skupin m˚uˇze bˇeˇzet na popˇred´ı, ostatn´ıbˇeˇz´ına pozad´ı. Sign´aly kter´ejsou ge- nerovan´ez 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´abˇeˇz´ına popˇred´ı. • pokud job control nen´ızapnut, znamen´aspustˇen´ıpˇr´ıkazu na pozad´ıpouze to, ˇze shell neˇcek´ana 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ˇzproces, kter´ym´akontroln´ıtermin´al, otevˇre soubor /dev/tty,tak se aso- ciuje se sv´ym kontroln´ım termin´alem. Tj. pokud dva r˚uzn´eprocesy 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´epatˇ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´apro volaj´ıc´ıproces)

skupiny proces˚u umoˇzˇnuj´ıpos´ılat sign´aly najednou cel´eskupinˇe. session (relace, sezen´ı) je kolekce proces˚uvytvoˇren´apro ´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´apˇ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ˇejen pro v´ystup nebo v˚ubec (nepovolen´aoperace s termin´alem pozastav´ıproces). Proces, kter´yjeˇstˇenen´ıvedouc´ım skupiny proces˚u, se m˚uˇze st´at vedouc´ım sezen´ıa z´aroveˇnskupiny proces˚uvol´an´ım setsid(). Jestliˇze proces uˇzje vedouc´ım skupiny, setsid() selˇze, pak je tˇreba prov´est fork() a setsid() zavolat v synovsk´em procesu. Takov´ypro- ces nem´aˇr´ıdic´ıtermin´al, m˚uˇze ho z´ıskat otevˇren´ım termin´alu, kter´yjeˇstˇe nen´ıˇr´ıdic´ım termin´alem sezen´ı, kdyˇzpˇri open() neuvede pˇr´ıznak O NOCTTY, nebo jin´ym implementaˇcnˇez´avisl´ym zp˚usobem. rodiˇcovsk´yproces: Kaˇzd´yproces (kromˇe swapperu, pid == 0) m´arodiˇce, tj. proces, kter´yho stvoˇril vol´an´ım fork(). Jestliˇze rodiˇcskonˇc´ıdˇr´ıve neˇzd´ıtˇe, adoptivn´ım rodiˇcem se st´av´aproces init s pid == 1, kter´yse tak´epostar´a o uklizen´ızombie po skonˇcen´ıprocesu.

111 Vytvoˇren´ıprocesu: fork()

getpid() == 1234 switch(pid = fork()) { case -1: /* Chyba */ case 0: /* D´ıtˇe */ default: /* Rodiˇc*/ }

rodiˇc(pokraˇcuje) d´ıtˇe(nov´yproces) getpid()==1234, pid==2345 getpid()==2345, pid==0 switch(pid = fork()) { switch(pid = fork()) { case -1: /* Chyba */ case -1: /* Chyba */ case 0: /* D´ıtˇe */ case 0: /* D´ıtˇe */ default: /* Rodiˇc*/ default: /* Rodiˇc*/ } }

• d´ıtˇeje t´emˇeˇrpˇresnou kopi´ırodiˇce, liˇs´ıse PID, d´ale parent PID a nˇekter´edalˇs´ı podrobnosti (´uˇctovac´ıˇcasy se nastav´ına 0, nedˇed´ıse nastaven´ı alarm() a z´amky soubor˚u). A pokud mˇel rodiˇcv´ıce vl´aken, m´asyn pouze to, kter´eza- volalo fork(); o tom v´ıce pozdˇeji aˇzse dostaneme k vl´akn˚um. Tyto informace by mˇely b´yt v manu´alov´estr´ance textttfork(2). • pro urychlen´ıa menˇs´ıspotˇrebu pamˇeti se adresov´yprostor nekop´ıruje, ale pouˇz´ıv´ase 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´yje PID syna kter´y byl pr´avˇevytvoˇren, nav´ıc v situaci, kdy jiˇzvytvoˇ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´ycelou cestou path, dalˇs´ıargumenty se pak pˇredaj´ıprogramu v parametrech argc a argv funkce main(). Seznam argument˚uje ukonˇcen pomoc´ı (char *)0, tj. NULL. arg0 by mˇel obsahovat jm´eno programu (tj. ne celou cestu) • ´uspˇeˇsn´evol´an´ı execl() se nikdy nevr´at´ı, protoˇze spuˇstˇen´y program zcela nahrad´ıdosavadn´ıadresov´yprostor procesu. • program dˇed´ıpromˇenn´eprostˇred´ı, tj. obsah environ. • handlery sign´al˚use nahrad´ıimplicitn´ıobsluhou. • zavˇrou se deskriptory soubor˚u, kter´emaj´ınastaven´ypˇr´ıznak FD CLOEXEC (implicitnˇenen´ınastaven).

• v path mus´ıb´yt cel´a(absolutn´ınebo relativn´ı) cesta ke spustiteln´emu sou- boru. Obsah promˇenn´eprostˇred´ı PATH se pouˇz´ıv´ajen pˇri vol´an´ıch execlp() a execvp(), kdyˇzargument path neobsahuje znak ’/’.

• nˇekdy se pouˇz´ıv´a argv[0] r˚uzn´eod jm´ena spustiteln´eho souboru. Napˇr. login vloˇz´ına zaˇc´atek jm´ena spouˇstˇen´eho shellu znak ’-’. Shell podle toho pozn´a, ˇze m´afungovat jako login-shell, tj. spustit /etc/profile. • exec() nepˇred´akontrolu naˇcten´emu programu v pamˇeti pˇr´ımo, ale pˇres dy- namick´ylinker (t´eˇznaz´yvan´y loader), pot´eco ho (= toho loadera) namapuje do adresov´eho prostoru procesu. Loader n´aslednˇenamapuje potˇrebn´edyna- mick´eobjekty a teprve pot´epˇred´akontrolu aplikaci. Viz tak´estrana 31. Pro jednoduch´eovˇ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´avol´an´ıse pouˇzij´ıjeˇstˇepˇredt´ım, neˇzse zavol´a open(). • exec() nezmˇen´ıhodnoty RUID a RGID. A pokud je to program s nasta- ven´ym SUID bitem, tak se EUID a uschovan´eUID nastav´ına UID majitele spustiteln´eho souboru. • dneˇsn´ıunixov´esyst´emy um´ıspouˇstˇet i skripty, kter´ezaˇc´ınaj´ıˇr´adkem #!/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ˇzposledn´ı 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´evˇzdy zad´avat celou cestu. • vˇsechny varianty kromˇe execle() a execve() pˇred´avaj´ıspouˇstˇen´emu pro- gramu sv´eaktu´aln´ıprostˇred´ı, tj. obsah pole environ. • z nˇejak´ych historick´ych d˚uvod˚uneexistuje 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´yv 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˚uobsahuj´ıc´ıch spustiteln´eprogramy. 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ˇzskonˇc´ınˇekter´ysynovsk´yproces, vr´at´ı jeho PID a do stat_loc uloˇz´ın´avratov´yk´od, kter´ylze 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˚ua nevolaj´ıse funkce nastaven´epomoc´ı 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´ynebyl testov´an od pokraˇco- v´an´ıprocesu po zastaven´ı – WNOHANG . . . neˇcek´a, pokud nen´ıstatus okamˇzitˇek dispozici – WUNTRACED . . . vr´at´ıstatus zastaven´eho procesu, kter´ynebyl testov´an po jeho zastaven´ı • pid ve waitpid(): – == -1 . . . libovoln´ed´ıtˇe – > 0 . . . jedno d´ıtˇe – == 0 . . . d´ıtˇeve stejn´eskupinˇeproces˚ujako volaj´ıc´ıproces – < -1 . . . d´ıtˇeve skupinˇe abs(pid) • rodiˇcby mˇel vˇzdy na sv´edˇeti zavolat wait() nebo waitpid(), protoˇze jinak se v syst´emu hromad´ı zombie (ukonˇcen´eprocesy, kter´epouze ˇcekaj´ı, aˇzsi rodiˇcpˇreˇcte jejich n´avratovou hodnotu).

116 Pˇr´ıklad: spuˇstˇen´ıprogramu a ˇcek´an´ı

int status; pid = fork()

rodiˇc d´ıtˇe if(pid > 0) else execl("/bin/ls", "/bin/ls", "/", NULL);

/bin/ls int main(int argc, char *argv[]) { ... exit(result); wait(&status); }

• toto je klasick´yzp˚usob jak spustit nˇejak´yprogram a po jeho skonˇcen´ıpo- kraˇcovat. Rodiˇcnemus´ı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´ahodnota je pouze souˇc´ast 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ˇzje roura pln´a, – ˇctouc´ıproces se zablokuje, kdyˇzje 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´aroura (vytvoˇren´avol´an´ım mkfifo()) funguje stejnˇe, ale m´apˇridˇelen´ejm´eno v syst´emu soubor˚ua mohou ji tedy pouˇz´ıvat libovoln´eprocesy.

• nepojmenovanou rouru vytv´aˇr´ıjeden proces a m˚uˇze ji pˇredat pouze sv´ym potomk˚um (pomoc´ıdeskriptor˚uzdˇ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´akon- stanta) bajt˚u, je zaruˇceno, ˇze z´apis bude atomick´y, tj. tato data nebudou proloˇzena daty zapisovan´ymi souˇcasnˇejin´ymi procesy. • v normˇeUNIX03 nen´ı specifikov´ano, zda fildes[0] je tak´eotevˇren´ypro z´apis a zda fildes[1] je t´eˇzotevˇren´ypro ˇ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´epoˇc´ıtat pouze s jednosmˇern´ymi rourami. • d˚uleˇzit´e: pro ˇcten´ı/z´apis z/do roury plat´ı stejn´epodm´ınky jako pro po- jmenovanou rouru, viz strana 85. To tak´eznamen´a, ˇze jedin´yzp˚usob, jak ˇcten´aˇrovi “poslat” end-of-file je, ˇze vˇsichni zapisovatel´ezavˇrou pˇr´ısluˇsn´ydeskrip- tor 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) case 0: default: 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", "/", NULL); "/bin/more", 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´evhodn´ezav´ırat (ozn. ⊲), protoˇze kdyˇzkonzument pˇredˇcasnˇeskonˇc´ı, dostane producent sign´al SIGPIPE. Kbyby deskriptor v producentovi nebyl zavˇren, producent se nedozv´ı, ˇze kon- zument 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 mo- hl vr´atit deskriptor 0 m´ısto poˇzadovan´eho 1. Tak´eje tˇreba testovat, zda neplat´ı pd[1] == 1, abychom si nechtˇenˇenezavˇreli rouru. Podobnˇeje 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´adata, zpracuje je, nˇeco vyp´ıˇse a teprve pak skonˇc´ı. shell ˇcek´ana otce, takˇze kdyˇzsmˇeˇruje roura od otce k synovi, otec skonˇc´ı, shell vyp´ıˇse prompt, ale pak jeˇstˇesyn vyp´ıˇse v´ystup. Moˇzn´ym ˇreˇsen´ım je ˇcek´an´ıotce na skonˇcen´ısyna, jenˇze to se ned´azajistit, 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ˇzk zaˇc´atku roury. • v shellu bash jsou vˇsechny procesy v rouˇre pˇr´ımo potomky shellu (shell vol´a fork() tolikr´at, jak dlouh´aje roura). Shell pˇred vyps´an´ım promptu ˇcek´a, aˇz vˇsechny procesy roury skonˇc´ı.

119 Sd´ılen´apamˇet’ – ´uvod • pajpy a soubory jako metody meziprocesov´ekomunikace vyˇzaduj´ısyst´emov´avol´an´ı • v´yhoda: procesy nemohou poˇskodit adresov´yprostor jin´eho procesu • nev´yhoda: velk´areˇzie pro syst´emov´avol´an´ı, typicky read, write • sd´ılen´apamˇet’ je namapov´an´ıˇc´asti 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´epamˇ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˚udo pamˇeti je jednou z implementac´ısd´ılen´epamˇeti. Pro popis ´useku sd´ılen´epamˇeti pouˇz´ıv´asoubor. • takto implementovan´asd´ılen´apamˇet’ je tedy pravidelnˇezapisov´ana na disk • pro sd´ılen´ıbez reˇzie z´apisu zmˇenˇen´ych dat na disk je moˇzn´epouˇz´ıt memory based filesyst´em, napˇr´ıklad tmpfs (Solaris, NetBSD – zde byl tmpfs naps´an v roce 2005 jako souˇc´ast Summer of Code sponzorovan´eho firmou Google, FreeBSD m´apodobnou vlastnost pod n´azvem memory disk). Jako tzv. bac- king store pro pamˇet’ov´estr´anky patˇr´ıc´ıtˇemto filsyst´em˚um je obecnˇemoˇzn´e pouˇz´ıt swap oblast na disku.

120 Mapov´an´ısoubor˚udo 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 ´usek d´elky len zaˇc´ınaj´ıc´ına pozici off souboru reprezentovan´eho deskriptorem fildes. • vrac´ıadresu namapovan´eho ´useku 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). • ve flags je OR-kombinace MAP PRIVATE (zmˇeny jsou priv´atn´ı 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˚udo pamˇeti je alternativou ke zpracov´an´ısoubor˚upomoc´ı read(), write(), lseek(). Po namapov´an´ılze se souborem pracovat jako s datovou strukturou v pamˇeti. Soubor se nekop´ıruje cel´ydo pamˇeti, alokuj´ı se pouze str´anky na kter´ese pˇristupuje. Pokud je potˇreba str´anku uvolnit, obsah se ukl´ad´azpˇet do souboru (pˇri MAP SHARED) nebo do swapu – pouˇz´ıv´a se mechanismus copy-on-write (pˇri MAP PRIVATE). • pˇri pouˇzit´ı MAP PRIVATE vid´ım na Solarisu vˇsechny zmˇeny proveden´ejin´ymi procesy, kter´enamapovaly sd´ılenˇe, aˇzdo t´edoby, kdy do str´anky zap´ıˇsu – v tom okamˇziku se vytvoˇr´ı kopie str´anky a dalˇs´ı takov´ezmˇeny jiˇzne- vid´ı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 sou- boru ale nelze zapisovat a soubor tak prodlouˇzit - proces by obdrˇzel sign´al SIGSEGV nebo SIGBUS. Sign´al dostanu stejnˇetak v situaci, kdy do read- only 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ˇezarovnan´e. Posledn´ıstr´anka je za koncem souboru doplnˇena nulami a tento ´usek se nikdy nepˇrepisuje do souboru. • pˇr´ıstup do namapovan´eho ´useku, ale za posledn´ıexistuj´ıc´ıstr´anku namapo- van´eho objektu, zp˚usob´ısign´al SIGBUS nebo SIGSEGV. • namapov´an´ısouboru nahrad´ıpˇr´ıpadn´epˇredchoz´ımapov´an´ıstr´anek v rozsahu addr aˇz addr+len.

121 • existuj´ıc´ırozˇs´ıˇren´ı(nejsou souˇc´ast´ıUNIX03): – pˇr´ıznak MAP ANON ve FreeBSD a Solarisu – vytvoˇren´ıanonymn´ıho seg- mentu bez vazby na soubor, deskriptor mus´ıb´yt -1. Mapuje se tak ano- nymn´ıobjekt, kter´yjak v´ıme m´am´ısto fyzick´eho uloˇzen´ına swapu (tedy nen´ıtrval´e). Linux m´apodobnou funkcionalitu pˇres MAP ANONYMOUS. – v IRIXu lze pomoc´ı MAP AUTOGROW automaticky zvˇetˇsit namapovan´yob- jekt pˇri pˇr´ıstupu za jeho st´avaj´ıc´ıkonec.

Mapov´an´ısoubor˚udo pamˇeti (2) int msync(void *addr, size t len, int flags ); • zap´ıˇse zmˇenˇen´estr´anky v ´useku len bajt˚uod adresy addr do souboru. Hodnota flags je OR-kombinace – MS ASYNC . . . asynchronn´ız´apis – MS SYNC . . . synchronn´ız´apis – MS INVALIDATE . . . zruˇsit namapovan´adata, kter´ase liˇs´ıod 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´apr´ava k namapovan´emu ´useku souboru. Hodnoty prot jsou stejn´ejako u mmap().

• uloˇzen´ızmˇen do souboru na disk je zaruˇcen´eaˇzpo proveden´ı msync() nebo munmap(), ale ostatn´ı procesy, kter´emaj´ı soubor namapov´an, vid´ı zmˇeny hned. • mapov´an´ıpamˇeti a nastavov´an´ıpˇr´ıstupov´ych pr´av pouˇz´ıv´anapˇr. knihovna Electric Fence, kter´aslouˇz´ıpro ladˇen´ıchyb pˇri pr´aci s dynamickou pamˇet´ı.

122 Pˇr´ıklad: mapov´an´ısoubor˚udo 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˚uv souboru (zap´ıˇse soubor od konce k zaˇc´at- ku).

123 Dynamick´ypˇ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´erelokace), RTLD LAZY (odloˇzen´erelokace), RTLD GLOBAL (symboly budou glob´alnˇedostupn´e), RTLD LOCAL (nebudou glob´alnˇedostupn´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´ypopis chyby pˇri pr´aci s knihovnami.

• pomoc´ıtˇechto funkc´ılze implementovat dynamicky nahr´avan´eplug-in mo- duly naˇc´ıtan´eaplikac´ıpodle potˇreby (napˇr. podle obsahu jej´ıho konfiguraˇc- n´ıho souboru).

• dynamick´ym naˇc´ıt´an´ım knihoven se tak´ed´avyˇreˇsit situace, kdy potˇrebujeme vyuˇz´ıt nˇekolik knihoven, kter´edefinuj´ı 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´aknihovna .so ve form´atu ELF ), napˇr´ıklad u gcc to znamen´apouˇz´ıt pˇrep´ınaˇc -shared. • konstanty pro mode: – RTLD NOW . . . vˇsechny relokace (vyˇreˇsen´ıvˇsech odkaz˚u) jsou provedeny okamˇzitˇepo nataˇzen´ı knihovny, aplikace m´ajistotu, ˇze jsou vˇsechny symboly pˇr´ıstupn´e – RTLD LAZY . . . relokace mohou b´yt odloˇzeny aˇzdo chv´ıle pouˇzit´ısymbolu – RTLD GLOBAL . . . symboly z knihovny mohou b´yt pouˇzity pˇri zpracov´an´ı relokac´ıv ostatn´ıch knihovn´ach a jsou dostupn´epomoc´ı dlopen(0, RTLD GLOBAL)

• speci´aln´ıhandle RTLD NEXT hled´asymbol pouze v knihovn´ach nahran´ych po knihovnˇe, ve kter´eje vol´an´ı dlsym(). Hod´ıse pro pˇredefinov´an´ıexistuj´ıc´ıch funkc´ı, pokud v redefinovan´efunkci potˇrebujeme volat p˚uvodn´ı. Knihovna s novou funkc´ıse nahr´av´ajako prvn´ı(napˇr. pomoc´ıpromˇenn´e LD PRELOAD), adresu p˚uvodn´ıfunkce z´ısk´avol´an´ım dlsym(RTLD NEXT, fn name ).

124 • vˇsechny tyto funkce jsou souˇc´ast´ıdynamick´eho linkeru, kter´ym´akaˇzd´ady- namicky slinkovan´aaplikace namapovan´yve sv´em adresov´em prostoru. Viz tak´estrany 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´afunkce sin() z matematick´eknihovny libm.so. • funkce dlsym() vr´at´ıadresu symbolu dan´eho jm´ena, ale vˇzdy jako ukazatel na void, neprob´ıh´aˇz´adn´atypov´akontrola ani nen´ık dispozici ˇz´adn´ainformace 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

• ´uvod, v´yvoj UNIXu a C, program´atorsk´en´astroje • z´akladn´ıpojmy a konvence UNIXu a jeho API • pˇr´ıstupov´apr´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´akomunikace • vl´akna, synchronizace vl´aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu

Sign´aly • informuj´ıproces o v´yskytu urˇcit´eud´alosti. • na uˇzivatelsk´e´urovni zpˇr´ıstupˇnuj´ımechanismy pˇreruˇsen´ı. • kategorie sign´al˚u: – chybov´eud´alosti generovan´ebˇ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´aud´alost. • zpracov´avaj´ıse asynchronnˇe– pˇr´ıchod sign´alu pˇreruˇs´ıbˇeh procesu a vyvol´ahandler.

126 • se sign´alem nen´ısv´az´ana ˇz´adn´ajin´ainformace 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ˇeproces˚u) podle hodnoty pid: – > 0 . . . procesu s ˇc´ıslem pid – == 0 . . . vˇsem proces˚um ve stejn´eskupinˇe – == -1 . . . vˇsem proces˚um, kromˇesyst´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 ˇz´adn´ysign´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´yposlal 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´yposlal 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´em˚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´auloˇzen´ıkompletn´ıho obsahu pamˇeti procesu do souboru, typicky se jm´enem core • vˇetˇsina sign´al˚uimplicitnˇeukonˇc´ıproces, nˇekter´enav´ıc vytvoˇr´ıjiˇzzmiˇnovan´y core dump, kter´yje moˇzn´en´aslednˇepouˇz´ıt pro ladic´ı ´uˇcely.

128 Pˇrehled sign´al˚u(1)

sign´aly je moˇzn´elogicky rozdˇelit do nˇekolika skupin. . . detekovan´echyby: SIGBUS pˇr´ıstup k nedef. ˇc´asti pamˇet’ov´eho objektu (core) SIGFPE chyba aritmetiky v pohybliv´eˇc´arce (core) SIGILL nepovolen´ainstrukce (core) SIGPIPE z´apis do roury, kterou nikdo neˇcte (exit) SIGSEGV pouˇzit´ınepovolen´eadresy v pamˇeti (core) SIGSYS chybn´esyst´emov´evol´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˚uvych´az´ız chyb programu • pro sign´aly BUS, FPE, ILL a SEGV nen´ınormou pˇresnˇedefinov´ana pˇr´ıˇcina, ale obvykle jsou to chyby detekovan´ehardwarem • pro tyto ˇctyˇri sign´aly tak´eplat´ıtato speci´aln´ıpravidla (podrobnosti viz ka- pitola 2.4 Signal Concepts v normˇeUNIX03): – pokud byly nastaven´ejako ignorovan´evol´an´ım sigaction, je chov´an´ı programu po t´e, co je mu takov´ysign´al posl´an, normou nedefinov´ano – n´avratov´ahodnova 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´achyba re´aln´a(sign´al nen´ı posl´an pˇres kill), v´aˇsprogram se pˇres tuto chybu nemus´ıv˚ubec dostat. Nen´ı bezpeˇcn´echybu 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´ea pak ukonˇcit program.

• pozn´amka: pokud je nˇeco normou nedefinov´ano (undefined), obecnˇeto zna- men´a, ˇze se neoˇcek´av´a, ˇze by program´ator potˇreboval zn´at pˇresn´echov´an´ıv takov´esituaci. Pokud je to potˇreba, pravdˇepodobnˇeje 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´euˇ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´ysign´al 1 (exit) SIGUSR2 uˇzivatelsky definovan´ysign´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 ˇz´adn´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˚uv uˇzivatelsk´em procesu (napˇr. pˇri implementaci timeout˚u). • sign´al SIGHUP se ˇcasto pouˇz´ıv´ajako zp˚usob, jak ozn´amit bˇeˇz´ıc´ımu d´emonu, ˇze se zmˇenil jeho konfiguraˇcn´ısoubor a m´asi ho proto znovu naˇc´ıst. • zaj´ımavost: FreeBSD 5.3 obsahuje chybu, kter´adovol´ı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˚unajednou m˚uˇze na termin´al zapisovat.

Pˇrehled sign´al˚u(4)

ˇcasovaˇce: SIGALRM pl´anovan´eˇcasov´epˇ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´aud´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´ev 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´avol´an´ı), SA NODEFER (neblokovat sign´al sig bˇehem obsluhy)

• kdyˇzje 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´avol´an´ıaktivn´ıv bodˇepˇr´ıchodu sign´alu skonˇc´ıs chybou EINTR. Restartov´an´ınemus´ıfungovat pro vˇsechna syst´emov´avol´an´ı, napˇr. na FreeBSD je select() pˇreruˇsen sign´alem vˇzdy, i kdyˇzje 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´esign´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´ejsou pro takov´epouˇzit´ıbezpeˇcn´e. Mus´ıbud’ b´yt reentrantn´ı, nebo je nutn´ezajistit, aby nepˇriˇsel sign´al v nevhodnou dobu (napˇr. uvnitˇrfunkce pˇrijde sign´al, v jehoˇzhandleru se vol´astejn´afunkce). Bezpeˇcn´efunkce ze syst´emov´eknihovny jsou vyjmenov´any v manu´alov´estr´ance k funkci sigaction() (v SUSv3 jsou v kapitole System Interfaces: General Information, Signal Concepts). • funkce sigaction() je obecnˇejˇs´ıneˇzstarˇs´ıfunkce signal() a sigset(). • pro v´yskok z handleru sign´alu jinam neˇzna 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´ıvy- kon´av´an´ıtakov´efunkce 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ˇeomezen´yvstup

#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ˇz1 s. Nastavuj´ıa testuj´ıse funkcemi setitimer() a getitimer(). Pˇri vyprˇsen´ıpos´ılaj´ısign´aly procesu, kter´yˇcasovaˇce nastavil: – ITIMER REAL . . . mˇeˇr´ıre´aln´yˇcas, pos´ıl´a SIGALRM – ITIMER VIRTUAL . . . mˇeˇr´ıvirtu´aln´ıˇcas (pouze ˇcas, kdy proces bˇeˇz´ı), pos´ıl´a SIGVTALRM – ITIMER PROF . . . mˇeˇr´ıvirtu´aln´ıˇcas a ˇcas, kdy syst´em bˇeˇz´ına konto procesu, pos´ıl´a SIGPROF • pozn: v pˇr´ıkladu je drobn´yprobl´em: funkce fprintf() nen´ıbezpeˇcn´apro pouˇzit´ıv handleru sign´alu.

133 Blokov´an´ısign´al˚u

• blokovan´esign´aly budou procesu doruˇceny a zpracov´any aˇzpo odblokov´an´ı.

int sigprocmask(int how, const sigset t *set, sigset t *oset ); • nastav´ımasku blokovan´ych sign´al˚ua vr´at´ıstarou masku. • how – SIG BLOCK pro pˇrid´an´ısign´al˚uco se maj´ıblokovat, pro odebr´an´ı SIG UNBLOCK, pro kompletn´ızmˇenu masky SIG SETMASK • pro manipulaci s maskou sign´al˚uslouˇz´ıfunkce: sigaddset(), sigdelset(), sigemptyset(), sigfillset(), sigismember() int sigpending(sigset t *set ); • vr´at´ıˇcekaj´ıc´ızablokovan´esign´aly.

• je rozd´ıl mezi ignorov´an´ım a blokov´an´ım sign´alu. Ignorovan´ysign´al j´adro zahod´ıa proces ho nedostane, blokovan´ysign´al proces dostane po jeho od- blokov´an´ı.

• z´avis´ına implementaci, zda pˇri v´ıcen´asobn´em doruˇcen´ıstejn´eho sign´alu pro- cesu, kter´ym´atento 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´yproces */ 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´yproces */ sigprocmask(SIG SETMASK, &osigs, NULL); ... }

• blokov´an´ıje vhodn´epouˇz´ıt tam, kde oˇsetˇren´ıpˇreruˇsen´ıuprostˇred posloup- nosti operac´ı by bylo pˇr´ıliˇssloˇ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´yhandler sign´al˚uneˇzrodiˇcovsk´yproces. • v uveden´em pˇr´ıkladˇeby bez blokov´an´ısign´al˚umohl synovsk´yproces dostat sign´al dˇr´ıv, neˇzstihne zmˇenit handler. • dalˇs´ıpˇr´ıklad je proces, kter´ypˇri vyprˇsen´ıtimeoutu pˇreruˇs´ıprov´adˇenou po- sloupnost operac´ıvol´an´ım siglongjmp() zevnitˇrhandleru sign´alu. Je potˇre- ba zablokovat sign´al SIGALRM bˇehem prov´adˇen´ıatomick´ych podposloupnost´ı (tj. takov´ych, kter´ese mus´ıprov´est bud’ cel´e, nebo v˚ubec ne).

135 Cek´an´ınaˇ sign´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˚uzmˇen´ına sigmask. int sigwait(const sigset t *set, int *sig ); • ˇcek´ana 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´ase handler sign´alu (to ale nen´ıv normˇejednoznaˇcnˇe definov´ano).

• pomoc´ıtˇechto funkc´ıa blokov´an´ısign´al˚use implementuje synchronn´ıobsluha sign´al˚u. Proces nejprve zablokuje sign´aly, kter´eho zaj´ımaj´ı, a pak na nˇeve 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

• ´uvod, v´yvoj UNIXu a C, program´atorsk´en´astroje • z´akladn´ıpojmy a konvence UNIXu a jeho API • pˇr´ıstupov´apr´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´akomunikace • 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´aoperace */ a = shared.a; b = shared.b; if (a != b) printf("NEKONZISTENTNISTAV");´ /* neatomick´aoperace */ shared.a = val; shared.b = val; } • jestliˇze tento cyklus spust´ıme ve dvou r˚uzn´ych procesech (nebo vl´aknech), kter´eobˇesd´ı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 ˇr´adc´ı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´anapˇr´ıklad Solaris sadu funkc´ı atomic add(3c), jejichˇzpouˇzit´ıje mnohem rychlejˇs´ıneˇzklasick´ezamykac´ımechanismy. V´ıce viz strana 201. • obecnˇeobdobn´yprobl´em nast´av´a, kdyˇzv´ıce proces˚u sd´ıl´ınˇejak´ysyst´emov´y zdroj.

Sc´en´aˇrkonfliktu

Procesy A(val==1) a B(val==2) a b 1. poˇc´ateˇ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˚uto 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ˇzproces B stihne zapsat poloˇzku b

138 Reˇsen´ı:ˇ vz´ajemn´evylouˇ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´yproces s n´ınem˚uˇze manipulovat. Procesy A(val==1) a B(val==2) a b 1. poˇc´ateˇcn´ıstav struktury ? ? 2. proces A zap´ıˇse do poloˇzky a 1 ?   3. proces B mus´ıˇcekat 1 ? 4. proces A zap´ıˇse do poloˇzky b 1 1   5. 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.

• je tˇreba zajistit vz´ajemn´evylouˇcen´ıi pˇri ˇcten´ı, aby ˇctouc´ıproces nepˇreˇcetl nekonzistentn´ıobsah struktury uprostˇred zmˇen. Pˇri z´apisu je nutn´evylouˇ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´asekce – kus k´odu, kter´yby mˇel prov´adˇet pouze jeden proces (vl´akno), jinak m˚uˇze doj´ıt k nekonzistenc´ım (ˇspatnˇepospojovan´yv´azan´yseznam, ne- odpov´ıdaj´ıc´ısi indexy v datab´azi, . . . ). Kritick´asekce by mˇela b´yt co nej- kratˇs´ı, aby ostatn´ıprocesy (vl´akna) ˇz´adaj´ı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˚uzapisuje protokol o sv´eˇcinnosti do spoleˇcn´eho log-souboru. Nov´yz´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ˇezapisovan´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ˇezapisovan´eho z´aznamu obdrˇz´ıme nespr´avn´a (ne´upln´a) data. • bˇehem operace z´apisu ze souboru nelze ˇc´ıst. Kdyˇznikdo nezapisuje, m˚uˇze v´ıce proces˚uˇc´ıst souˇcasnˇe.

na lok´aln´ım disku lze pro synchronizaci zapisovatel˚upouˇz´ıt ˇreˇsen´ıpomoc´ıpˇr´ı- znaku O APPEND, kter´eale 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´elogovac´ızpr´avy je proveden v´ıce neˇzjedn´ı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 Reˇsen´ı:ˇ zamyk´an´ısoubor˚u • zapisuj´ıc´ıproces zamkne soubor pro z´apis. Ostatn´ıprocesy (zapisovatel´ei ˇcten´aˇri) se souborem nemohou pracovat a mus´ı ˇcekat na odemˇcen´ız´amku. • ˇctouc´ıproces zamkne soubor pro ˇcten´ı. Zapisovatel´emus´ıˇcekat na odemˇcen´ız´amku, ale ostatn´ıˇcten´aˇri mohou tak´ezamknout 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ˇemnoho z´amk˚upro ˇcten´ı, ale ne oba typy z´amk˚usouˇcasnˇe. • z d˚uvodu efektivity by kaˇzd´yproces mˇel drˇzet z´amek co nejkratˇs´ı dobu a pokud moˇzno nezamykat cel´ysoubor, ale jen ´usek, se kter´ym pracuje. Preferovan´eje pasivn´ıˇcek´an´ı, aktivn´ıˇcek´an´ıje vhodn´ejen na velmi kr´atkou dobu.

dva zp˚usoby ˇcek´an´ı: aktivn´ı(busy waiting) – proces v cyklu testuje podm´ınku, na kterou ˇcek´a, do- kud 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ˇzdojde 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´eoperaˇcn´ım syst´emem – semafory (souˇc´ast System V IPC) – z´amky pro soubory (fcntl(), flock()) – synchronizace vl´aken: mutexy (ohraniˇcuj´ıkritick´esekce, pouze jedno vl´akno m˚uˇze drˇzet mutex), podm´ınkov´e promˇenn´e (zablokuj´ıvl´akno, dokud jin´evl´akno nesignalizuje splnˇen´ıpodm´ınky), read-write z´amky (sd´ılen´ea exkluzivn´ı z´amky, podobnˇejako 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´ajako prvn´ıˇreˇsen´ıprobl´emu vz´ajemn´eho vylouˇcen´ı dvou proces˚u, aniˇzby bylo nutn´eaplikovat naivn´ıalgoritmus striktn´ıho stˇr´ı- d´an´ı, tj. pokud druh´yproces nevykazoval z´ajem o vstup do kritick´esekce, 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 algori- thm”, “Peterson’s algorithm”) • my se nebudeme zab´yvat teoretick´ymi algoritmy vz´ajemn´eho vylouˇcen´ıani nebudeme popisovat hardwarov´emechanismy pouˇz´ıvan´ej´adrem. Zamˇeˇr´ıme se pouze na pouˇzit´ılock soubor˚u(kter´evyuˇz´ıvaj´ıatomiˇcnosti nˇekter´ych sou- borov´ych operac´ı) a speci´aln´ıch synchronizaˇcn´ıch n´astroj˚uposkytovan´ych j´adrem. • podm´ınkov´epromˇenn´e= conditional variables

142 Lock-soubory • pro kaˇzd´ysd´ılen´yzdroj existuje dohodnut´ejm´eno souboru. Zamˇcen´ızdroje se provede vytvoˇren´ım souboru, odemˇcen´ı smaz´an´ım souboru. Kaˇzd´yproces 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) ; /* Cek´ameˇ ve smyˇcce na odemˇcen´ı*/ close(fd); }

void unlock(char *lockfile) { unlink(lockfile); }

• kl´ıˇcem k ´uspˇechu je samozˇrejmˇepouˇzit´ıflagu O EXCL • pˇri hav´arii procesu nedojde ke zruˇsen´ıpˇr´ıpadn´ych z´amk˚ua ostatn´ıprocesy by ˇcekaly vˇeˇcnˇe. Proto je vhodn´esi do lock-souboru poznamenat PID pro- cesu, kter´yz´amek vytvoˇril. Proces, kter´yˇcek´ana 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´ytoto um´ı a dovoluje pouˇz´ıvat lock soubory z shellov´ych skript˚uje 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˚unajednou zjist´ı, ˇze proces drˇz´ıc´ız´amek uˇzne- existuje, 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´atot´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´yz´ısk´az´amek, otevˇre pro z´apis pojmenovanou rouru. Cekaj´ıc´ıprocesyˇ se usp´ıt´ım, ˇze zkus´ıˇc´ıst z roury. Souˇc´ast´ı 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˚upro soubor fildes se pouˇz´ıv´a cmd: – F GETLK . . . vezme popis z´amku z tˇret´ıho argumentu a nahrad´ıho popisem existuj´ıc´ıho z´amku, kter´ys n´ım koliduje – F SETLK . . . nastav´ınebo zruˇs´ız´amek popsan´ytˇ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˚usd´ılen´ych pˇres NFS zajiˇst’uje d´emon lockd. • z´amky jsou obecnˇedvou 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 sou- boru; 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´eho explicitnˇenekontroluj´ı – nedoporuˇcuj´ıse, ne vˇzdy funguj´ı(napˇr. lockd implementuje pouze advisory locking) – pro urˇcit´ysoubor se zapne nastaven´ım bitu SGID a zruˇsen´ım pr´ava spuˇstˇen´ıpro skupinu (tj. nastaven´ı, kter´ejinak nem´a smysl - m´ıt set UID executable bit na souboru, kter´ynen´ıspustiteln´y). Syst´em, kter´ymandatory 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 – F RDLCK . . . sd´ılen´yz´amek (pro ˇcten´ı), v´ıce proces˚u – 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 • l start . . . zaˇc´atek zamykan´eho ´useku vzhledem k l whence • l len . . . d´elka ´useku, 0 znamen´ado konce souboru • l pid . . . PID procesu drˇz´ıc´ıho z´amek, pouˇz´ıv´ase jen pro F GETLK pˇri n´avratu

• soubory se daj´ızamykat po ˇc´astech a d´ase 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ˇc´ast souboru zamˇcen´a, je struk- tura flock vr´acena bez zmˇeny kromˇeprvn´ıpoloˇzky, kter´aje nastavena na F UNLCK. • zamyk´an´ıpˇres fcntl i lockf m´ajednu d˚uleˇzitou vlastnost, kterou v´ystiˇznˇe popisuje napˇr´ıklad manu´alov´astr´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´ezdroje res1 a res2 chr´anˇen´ez´amky lck1 a lck2. Procesy p1 a p2 chtˇej´ıkaˇzd´yv´yluˇcn´ypˇr´ıstup k obˇema zdroj˚um. p1 p2 lock(lck1); /* OK */ lock(lck2); /* OK */ lock(lck2); /* Cek´anaˇ p2 */ lock(lck1); /* Cek´anaˇ p1 */ Deadlock use(res1, res2); use(res1, res2); unlock(lck2); unlock(lck2); unlock(lck1); unlock(lck1);

• pozor na poˇrad´ızamyk´an´ı!

• obecnˇedeadlock vznikne, jestliˇze proces ˇcek´ana ud´alost, kter´anem˚uˇze na- stat. Zde napˇr. na sebe dva procesy ˇcekaj´ınavz´ajem, aˇz ten druh´yuvoln´ı z´amek, ale k tomu nikdy nedojde. Dalˇs´ımoˇznost´ıje deadlock jednoho pro- cesu, kter´yˇcte z roury a pˇredt´ım zapomnˇel uzavˇr´ıt z´apisov´ykonec roury. Jestliˇze rouru nem´auˇznikdo dalˇs´ıotevˇrenou, pokus o ˇcten´ıze zablokuje, protoˇze nejsou zavˇreny vˇsechny kopie z´apisov´eho deskriptoru a tedy nena- stane konec souboru na rouˇre, ale ˇctouc´ı proces sv˚uj z´apisov´ydeskriptor 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´ese deadlocku snaˇzit vyvarovat spr´avn´ym naprogramov´an´ım a ne- spol´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´apamˇ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ˇejako soubory definovan´a pˇr´ıstupov´apr´ava (pro ˇcten´ıa z´apis) pro vlastn´ıka, skupinu a ostatn´ı.

• prostˇredky IPC existuj´ıi pot´e, kdy skonˇc´ıproces, kter´yje vytvoˇril. O je- jich zruˇsen´ı je nutno explicitnˇepoˇz´adat (ze shellu lze zjistit seznam IPC prostˇredk˚upˇr´ıkazem ipcs a smazat je pˇr´ıkazem ipcrm). Stav a obsah exis- tuj´ıc´ıch prostˇredk˚uIPC z˚ust´av´av platnosti, i kdyˇz s nimi pr´avˇenepracuje ˇz´adn´yproces (napˇr. data ve sd´ılen´epamˇeti z˚ust´avaj´ı, i kdyˇzji nem´aˇz´adn´y proces pˇripojenou). • dalˇs´ıdva prostˇredky System V IPC, kter´ese bˇeˇznˇev unixov´ych syst´emech vyskytuj´ıa jsou souˇc´ast´ınormy, tj. sd´ılen´apamˇet’ a zas´ı- l´an´ızpr´av, nebudeme prob´ırat. Pro sd´ılenou pamˇet’ je moˇzn´epouˇz´ıt jiˇzprobran´evol´an´ı mmap, m´ısto zas´ıl´an´ızpr´av je moˇzn´epouˇz´ıt so- ckety (budou v nˇekter´ez pˇr´ıˇst´ıch pˇredn´aˇsek).

147 Semafory • zavedl je E. Dijkstra • semafor s je datov´astruktura obsahuj´ıc´ı – cel´enez´aporn´eˇc´ıslo i (voln´akapacita) – 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´emˇenit o libo- volnou 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´apouze hodnotu 0 nebo 1

148 Vz´ajemn´evylouˇcen´ıpomoc´ısemafor˚u

• jeden proces inicializuje semafor sem s; init(s, 1); • kritick´asekce se dopln´ıo operace nad semaforem ... P(s); kritick´asekce; V(s); ...

inicializace semaforu na hodnotu n dovol´ıvstoupit do kritick´esekce n proces˚um. Zde semafor funguje jako z´amek, vˇzdy ho odemyk´a(zvyˇsuje hodnotu) stejn´yproces, kter´yho 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˚uasociovan´y s kl´ıˇcem key (kl´ıˇc IPC PRIVATE . . . priv´atn´ısemafory, pˇri kaˇzd´em pouˇzit´ıvr´at´ıjin´yidentifik´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´yparametr arg je typu union semun. int semop(int semid, struct sembuf *sops, size t nsops ); • zobecnˇen´eoperace P a V.

• nejvˇetˇs´ızaj´ımavost na System V implementaci semafor˚u je skuteˇcnost, ˇze dan´ysyscall neoperuje nad jedn´ım semaforem, ale nad polem semafor˚u, ato- micky. Vˇetˇsinou vˇsak budete potˇrebovat pouze jeden semafor, tj. pole o jed- nom prvku. • dalˇs´ıd˚uleˇzit´ainformace je, ˇze System V semafory jsou bohuˇzel znaˇcnˇesloˇzit´e • pˇr´ıstupov´apr´ava jsou jen pro ˇcten´ıa z´apis. • podobn´esch´ema API funkc´ı(funkce na vytvoˇren´ı, ˇr´ızen´ıa operace) dodrˇzuj´ı i ostatn´ıSystem V IPC mechanismy. • jakmile je jednou pole semafor˚ujedn´ım procesem vytvoˇreno, mohou i ostatn´ı procesy pouˇz´ıt semctl() a semop(), aniˇzby pˇredt´ım volaly semget(). To plat´ıi pro semafory vytvoˇren´es kl´ıˇcem IPC PRIVATE, pro kter´enelze volat semget(), protoˇze by se t´ım vytvoˇrilo nov´epole semafor˚u.

150 API pro semafory: semctl() • semnum . . . ˇc´ıslo semaforu v poli • moˇzn´ehodnoty cmd: – GETVAL . . . vr´at´ıhodnotu semaforu – SETVAL . . . nastav´ısemafor na hodnotu arg.val – GETPID ... PID procesu, kter´yprovedl 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˚udo pole arg.array – SETALL . . . nastav´ıvˇsechny semafory podle arg.array – IPC STAT ... do arg.buf d´apoˇcet semafor˚u, pˇr´ıstupov´a pr´ava a ˇcasy posledn´ıch semctl() a semop() – IPC SET . . . nastav´ıpˇr´ıstupov´apr´ava – IPC RMID . . . zruˇs´ıpole semafor˚u

vol´an´ı semctl(semid, semnum, SETVAL, arg) odpov´ıd´aobecn´esemaforov´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 ˇz´adn´y) na nsops semaforech podle pole sops struktur struct sembuf, kter´aobsahuje: – 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ˇznelze operaci hned prov´est, neˇcek´aa vr´at´ıchybu ∗ SEM UNDO . . . pˇri ukonˇcen´ıprocesu vr´atit operace se 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´ysemafor). Pˇri pouˇzit´ıatomick´eoperace zamˇcen´ıobou semafor˚unajednou bude ´uspˇeˇsn´yvˇzdy pr´avˇejeden proces, kter´yz´ısk´aoba semafory, druh´ybude ˇcekat. • SEM UNDO zajist´ı, ˇze pˇri ukonˇcen´ıprocesu dojde k odemˇcen´ısemafor˚u(pou- ˇzit´ych jako z´amky), kter´etento proces mˇel zamˇcen´e.

152 Vytv´aˇren´ıprostˇredk˚uIPC

• 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´ıˇcidentifikuj´ıc´ıprostˇredek IPC. Skupina proces˚u, kter´achce komunikovat, se mus´ıdomluvit na spoleˇcn´em kl´ıˇci. R˚uzn´eskupiny komunikuj´ıc´ıch proces˚uby mˇely m´ıt r˚uzn´ekl´ıˇce. key t ftok(const char *path, int id ); • vr´at´ıkl´ıˇcodvozen´yze zadan´eho jm´ena souboru path a ˇc´ısla id. Pro stejn´e id a libovolnou cestu odkazuj´ıc´ına stejn´ysoubor vr´at´ı stejn´ykl´ıˇc. Pro r˚uzn´a id nebo r˚uzn´esoubory na stejn´em svazku vr´at´ır˚uzn´ekl´ıˇce.

Pozn´amky k ftok(): • Z id se pouˇzije jen nejniˇzˇs´ıch 8 bit˚u. • Nen´ıspecifikov´ano, zda bude stejn´ykl´ıˇcvr´acen i po smaz´an´ıa znovuvytvoˇren´ı souboru. • R˚uzn´ekl´ıˇce pro r˚uzn´esoubory nejsou vˇzdy zaruˇcen´e. Napˇr. na Linuxu se kl´ıˇcz´ısk´akombinac´ı16 bit˚uˇc´ısla i-uzlu, 8 bit˚u id a 8 bit˚uvedlejˇs´ıho ˇc´ısla zaˇr´ızen´ı. Stejn´ykl´ıˇcpro r˚uzn´esoubory je vr´acen, pokud se ˇc´ısla i-uzl˚ushoduj´ı na spodn´ıch 16 bitech.

153 Dalˇs´ıprostˇredky IPC • POSIX a UNIX98 definuj´ıjeˇstˇedalˇs´ıprostˇredky komunikace mezi procesy: – sign´aly . . . pro uˇzivatelsk´e´uˇcely lze vyuˇz´ıt sign´aly SIGUSR1 a SIGUSR2 – POSIXov´asd´ılen´apamˇet’ pˇr´ıstupn´apomoc´ı shm open() a mmap() – POSIXov´esemafory . . . sem open(), sem post(), sem wait(),... – POSIXov´efronty zpr´av . . . mq open(), mq send(), mq receive(),... • Z BSD poch´az´ı sokety (sockets) umoˇzˇnuj´ı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´eIPC pouˇz´ıv´apro pojmenov´an´ıjednotliv´ych IPC objekt˚uˇretˇezce m´ısto numerick´ych identifik´ator˚u, proto do znaˇcn´em´ıry odpadaj´ıprobl´emy s identifikac´ızn´am´eze 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˚ua dostaly se i do normy UNIX 98.

154 Obsah

• ´uvod, v´yvoj UNIXu a C, program´atorsk´en´astroje • z´akladn´ıpojmy a konvence UNIXu a jeho API • pˇr´ıstupov´apr´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´akomunikace • vl´akna, synchronizace vl´aken • ??? - bude definov´ano pozdˇeji, podle toho kolik zbyde ˇcasu

S´ıt’ov´akomunikace UUCP (UNIX-to-UNIX Copy Program) – prvn´ıaplikace pro komunikaci UNIXov´ych syst´em˚upropojen´ych pˇr´ımo nebo pˇres modemy, souˇc´ast 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ˇena 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 ´urovni 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 Intercon- nect) – vrstvy (layers): 1. fyzick´a(physical) 2. linkov´a(data link) 3. s´ıt’ov´a(network) 4. transportn´ı(transport) 5. relaˇcn´ı(session) 6. prezentaˇcn´ı(presentation) 7. aplikaˇcn´ı(application) • UUCP je tvoˇreno aplikaˇcn´ımi programy, nevyˇzaduje ˇz´adnou podporu v j´adru. Implementace soket˚ua TLI jsou souˇc´ast´ıj´adra. TLI je ve verzi SVR4 imple- mentov´ano s vyuˇzit´ım mechanismu STREAMS. RPC existuje jako knihovna linkovan´ak aplikac´ım, kter´avyuˇz´ıv´asokety (funguje nad protokoly TCP a UDP). RPC bylo vyvinuto jako komunikaˇcn´ıprotokol pro NFS (Networked File System). • existuje v´ıce (vz´ajemnˇenekompatibiln´ı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ˇzlok´aln´ıTCP/IP komunikace pˇres loo- pback rozhran´ı localhost (127.0.0.1).

TCP/IP • protokoly – IP (Internet Protocol) – pˇr´ıstupn´yjen 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´erozhran´ı, nikoliv poˇc´ıtaˇc • port – 2 bajty, rozliˇsen´ıv r´amci 1 IP adresy, porty s ˇc´ıslem menˇs´ım neˇz1024 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´apro s´ıt’ovou komunikaci nejˇcastˇeji rodinu protokol˚uTCP/IP. Pro ´uˇcely programov´an´ı aplikac´ı n´as budou zaj´ımat pˇredevˇs´ım protokoly TCP (spojovan´aspolehliv´akomunikace) a UDP (nespojovan´anespolehliv´a komunikace). V obou protokolech je jeden konec komunikaˇcn´ıho kan´alu (od- pov´ıd´asoketu v dom´enˇe AF INET) identifikov´an IP adresou s´ıt’ov´eho rozhran´ı a ˇc´ıslem portu (pomoc´ı port˚use rozliˇsuj´ı s´ıt’ov´esluˇ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ˇc´ast´ıIP je Internet Control Message Protocol (ICMP), RFC 792. • UDP – jednoduch´anadstavba nad IP, pˇrid´av´aˇc´ısla port˚u, z˚ust´av´anespoleh- liv´ya datagramovˇeorientovan´y • TCP – vytv´aˇr´ıobousmˇern´espojen´ı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´adatab´aze, jej´ı struktura nemus´ı m´ıt nic spoleˇcn´eho se strukturou IP adres

Spojovan´esluˇ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 kli- enta. • syst´emov´avol´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 vy- tvoˇren soket (pak bude soket pˇrij´ımat ˇz´adosti klient˚u pouze pˇres toto rozhran´ı), nebo je to speci´aln´ıhodnota ,,libovoln´aadresa” (pak soket pˇrij´ım´apoˇ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ˇz´adost kli- enta o spojen´ı, vytvoˇr´ıspojen´ıa vr´at´ınov´ydeskriptor, pˇres kter´ybude 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() – ˇz´adost 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´ydeskriptor) • klient nemus´ıvolat bind(), v takov´em pˇr´ıpadˇemu j´adro pˇridˇel´ınˇekter´yvoln´y port. Existuj´ı sluˇzby (napˇr. rsh), kter´evyˇzaduj´ı, aby se klient spojoval z privilegovan´eho portu. Takov´yklient pak mus´ıprov´est bind() (a nav´ıc bˇeˇzet s rootovsk´ymi pr´avy alespoˇndo okamˇziku proveden´ı bind()).

Spojovan´esluˇzby (TCP), paraleln´ıobsluha

server s´ıt’ klient

fd = socket() fd = socket() bind(fd) listen(fd) fd2 = accept(fd) connect(fd) fork() close(fd2) syn while(waitpid( read(fd2) write(fd);read(fd) -1, stat, write(fd2) WNOHANG)>0) ; exit(0) close(fd)

• server akceptuje spojen´ı od klienta a na vlastn´ı komunikaci vytvoˇr´ı nov´y proces, kter´ypo uzavˇren´ıspojen´ıs klientem skonˇc´ı. Rodiˇcovsk´yproces m˚uˇze mezit´ım akceptovat dalˇs´ıklienty a spouˇstˇet pro nˇeobsluˇzn´eprocesy. 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ˇzexplicitn´ıignorov´an´ızabr´an´ıvzniku ˇziv´ych mrtv´ych, popˇr. lze instalovat handler, v nˇemˇzse vol´a wait().

Spojovan´esluˇzby, paraleln´ı accept()

server s´ıt’ klient

fd = socket() fd = socket() bind(fd) listen(fd) fork() ... fd2=accept(fd) connect(fd) read(fd2) write(fd);read(fd) write(fd2) close(fd2) close(fd)

• po bind() a listen() se vytvoˇr´ı nˇekolik synovsk´ych proces˚ua kaˇzd´yv cyklu vol´a accept() a obsluhuje klienty. J´adro vybere (nedeterministicky) pro kaˇzd´ypoˇzadavek jeden proces, v nˇemˇz accept() nav´aˇze spojen´ı. • jednotliv´eprocesy serveru mezi sebou mohou komunikovat, aby v pˇr´ıpadˇe, ˇze souˇcasn´ych poˇzadavk˚uje v´ıce neˇzserverov´ych proces˚u, se tato skuteˇcnost zjistila a hlavn´ıserver mohl dynamicky vytvoˇrit dalˇs´ı serverov´yproces. • takto funguje napˇr. webov´yserver Apache. • vˇsechny tˇri uveden´ezp˚usoby ˇcinnosti serveru funguj´ı se stejn´ym klientem – ˇcinnost klienta nez´avis´ına variantˇeserveru.

159 Datagramov´esluˇ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ˇejako v pˇr´ıpadˇeTCP, 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´ı ˇc´asti prvn´ıho datagramu. • v´yhodou nespojovan´esluˇzby je menˇs´ıreˇzie a to, ˇze pˇres jeden soket lze komu- nikovat s v´ıce procesy (pˇri spojovan´ekomunikaci je spojen´ıvˇzdy nav´az´ano s jedn´ım procesem). • pro UDP je moˇzn´evolat 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 specifiko- van´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´akomunikace, adresa je dvojice (IP adresa, port) • type: – SOCK STREAM . . . spojovan´aspolehliv´asluˇzba, poskytuje obousmˇern´ysekvenˇcn´ıproud dat – SOCK DGRAM . . . nespojovan´anespolehliv´asluˇ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´epˇres deskriptory soubor˚u. Po nav´az´an´ıspojen´ıje (spo- jovan´a) komunikace pˇres soket podobn´akomunikaci 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´ev socket() a konstanty AF_ (ad- dress family, napˇr. AF_INET, AF_UNIX) pouˇz´ıvan´epˇ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˚upro pˇr´ıstup k IP, ICMP, nebo k informac´ım z rou- tovac´ı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) – struct in addr sin addr . . . IP adresa (32 bit˚u) – unsigned char sin zero [8] . . . v´yplˇn

• vol´an´ı bind() pˇriˇrazuje soketu lok´aln´ıadresu, tj. zdrojovou adresu odes´ıla- n´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˚us jednou dvojic´ı(adresa, port). • vol´an´ı bind() lze vynechat, j´adro pak soketu (v pˇr´ıpadˇeTCP, UDP) pˇriˇrad´ı adresu INADDR_ANY a nˇekter´yvoln´yport. Obvykle bind() vol´apouze server, protoˇze klienti oˇcek´avaj´ı, ˇze bude poslouchat na pevn´em portu. Naopak klient pevn´yport 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˚ubylo vysvˇetleno na stranˇe15, dalˇs´ıinformace pak budou na stranˇe171. • v dom´enˇe AF_UNIX se pouˇz´ıv´aadresov´astruktura 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. Ob- vykl´ehodnoty jsou mezi 92 a 108.

162 Cek´an´ınaˇ spojen´ı: listen()

int listen(int socket, int backlog ); • oznaˇc´ısoket zadan´ydesktriptorem socket jako akceptuj´ıc´ı spojen´ı. • maxim´alnˇe backlog ˇz´adost´ıo spojen´ım˚uˇze najednou ˇcekat ve frontˇena obslouˇzen´ı(implementace m˚uˇze hodnotu backlog zmˇenit, pokud nen´ıv podporovan´em rozsahu). Z´adosti,ˇ kter´ese nevejdou do fronty, jsou odm´ıtnuty (tj. vol´an´ı connect() skonˇc´ıs chybou). • soket ˇcek´ana spojen´ına adrese, kter´amu 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´aznamen´alibovolnou adresu.

• hodnota INADDR ANY se pouˇz´ıv´anejˇcastˇeji. Konkr´etn´ıIP adresa serveru se zad´av´atehdy, jestliˇze je potˇreba rozliˇsit, pˇres kter´es´ıt’ov´erozhran´ı pˇriˇsel poˇzadavek na spojen´ı(pro kaˇzd´erozhran´ım´ame jeden soket). Tuto moˇznost vyuˇz´ıvaj´ıweb servery, kter´epodle IP adresy rozliˇsuj´ıvirtu´aln´ıservery . Ob- vykle 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˚upodle HTTP hlaviˇcky ,,Host:” uˇznepotˇ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´ydˇr´ıve zavolal listen()) a vzd´alen´ym soketem, kter´yˇz´adal o spojen´ı pomoc´ı connect(). Vr´at´ıdeskriptor (nov´ysoket), kter´ylze pouˇz´ıvat pro komunikaci se vzd´alen´ym procesem. P˚uvodn´ı deskriptor socket umoˇzˇnuje pˇrij´ımat dalˇs´ıspojen´ıpomoc´ı accept(). • v address vr´at´ıadresu vzd´alen´eho soketu. • address len je velikost struktury pro uloˇzen´ıadresy, po n´avratu obsahuje skuteˇcnou d´elku adresy. • podobnˇejako bind() i accept() pouˇz´ıv´apro adresy v dom´enˇe AF INET strukturu sockaddr in.

• vytvoˇren´ıdruh´eho deskriptoru pro komunikaci umoˇzˇnuje na tom p˚uvodn´ım ihned znovu volat accept(). • jestliˇze se v´ıce klient˚uze stejn´eho poˇc´ıtaˇce najednou pˇripoj´ık jednomu ser- veru (tj. na jednu serverovou IP adresu a jeden port), jsou jednotliv´aspojen´ı rozliˇsena jen ˇc´ıslem portu na klientsk´estranˇe. • address m˚uˇze b´yt zad´ana jako NULL, ˇc´ımˇzoznamujeme, ˇze n´as konkr´etn´ı adresa naˇseho nov´eho soketu nezaj´ım´a. V tomto pˇr´ıpadˇeby i address len mˇelo b´yt 0. • socket vr´acen´yz vol´an´ı accept() si m˚uˇzeme pˇredstavit tak, ˇze reprezentuje ten konec spojen´ı, na kter´em poslouch´aa z kter´eho ˇcte druh´astrana.

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´ypomoc´ı listen() a accept() ˇcek´ana 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´anepouˇzit´aadresa 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´ysoket.

• po ´uspˇeˇsn´em nav´az´an´ıspojen´ımohou server i klient pro komunikaci pouˇz´ıvat bˇeˇzn´asouborov´avol´an´ı write() a read(), nebo funkce send(), recv(), sendmsg(), recvmsg(). Chov´an´ıfunkc´ıpro z´apis a ˇcten´ıdat je podobn´ejako write() a read() pro roury. • i pro nespojovan´esluˇzby (UDP) se d´avolat connect(), t´ım se nenav´aˇze spo- jen´ı, ale pouze se omez´ıadresa protistrany, se kterou m˚uˇze soket komunikovat. V tomto pˇr´ıpadˇemohou connect() volat obˇestrany komunikace. • pokud je socket nastaven jako neblokuj´ıc´ı, viz strana 170, connect() se ne- zablokuje ˇcek´an´ım na spojen´ı. M´ısto toho vr´at´ı -1 s errno nastaven´ena EINPROGRESS (= “nelze vytvoˇrit spojen´ıokamˇzitˇe”), a ˇz´adost o spojen´ıse uloˇz´ıdo syst´emov´efronty pro n´asledn´evyˇr´ızen´ı. Do t´edoby, neˇzje spojen´ı pˇripraveno, vol´an´ı connect() vrac´ı -1, nyn´ıale s chybou EALREADY. Nen´ı ale takto vhodn´etestovat pˇripravenost spojen´ı, protoˇze pokud connect() skonˇc´ı s chybou, dalˇs´ı vol´an´ı connect() provede nov´ypokus o spojen´ı a jsme opˇet tam, kde jsme byli. . . Je moˇzn´eale pouˇz´ıt select() nebo poll() pro ˇcek´an´ına z´apis (ne ˇcten´ı) do socketu, viz strany 176, 178. Zda spojen´ı probˇehlo ´uspˇeˇsnˇese 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) – MSG OOB . . . posl´an´ıurgentn´ıch (out-of-band) dat, jejichˇz v´yznam je z´avisl´yna protokolu

• pouˇz´ıv´ase hlavnˇepro 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 datagramo- vou sluˇzbu se nav´ıc data berou jako nedˇeliteln´a, tj. bud’ se akceptuj´ıcel´a, nebo se vol´an´ızablokuje – neexistuje ˇc´asteˇcn´yz´apis. Stejnˇejako je tomu u souborov´ych deskriptor˚u, je i zde moˇzn´esocket nastavit jako neblokuj´ıc´ı, viz strana 170. • m´ısto sendto() se d´apouˇz´ıt obecnˇejˇs´ıfunkce sendmsg(). • pro sokety, na kter´ebylo vol´ano connect(), se m´ısto sendto() m˚uˇze pouˇz´ıt send(). • ´uspˇeˇsn´yn´avrat z libovoln´efunkce zapisuj´ıc´ı data do soketu neznamen´a ´uspˇeˇsn´edoruˇ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´yd˚uvod proˇcnepouˇz´ıt pˇr´ımo write() by tak byla moˇznost pouˇz´ıt flagy. V tom pˇr´ıpadˇeje 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´aadresu odes´ılatele zpr´avy, do address len d´elku adresy. Vr´at´ıd´elku zpr´avy. Kdyˇzje zpr´ava delˇs´ıneˇz len, nadbyteˇcn´adata se zahod´ı(SOCK STREAM nedˇel´ıdata na zpr´avy, data se nezahazuj´ı). • ve flg mohou b´yt pˇr´ıznaky: – MSG PEEK . . . zpr´ava se bere jako nepˇreˇcten´a, dalˇs´ı recvfrom() ji vr´at´ıznovu – MSG OOB . . . pˇreˇcte urgentn´ı(out-of-band) data – MSG WAITALL . . . ˇcek´a, dokud nen´ınaˇcten pln´yobjem dat, tj. len bajt˚u

• pouˇz´ıv´ase hlavnˇepro sokety typu SOCK DGRAM. V takov´esituaci opˇet ˇcek´a na celou zpr´avu (tj. nevr´at´ıv´am p˚ulku datagramu); opˇet je moˇzn´enastavit socket jako neblokuj´ıc´ı.

• m´ısto recvfrom() se d´apouˇz´ıt obecnˇejˇs´ıfunkce recvmsg(). • pro sokety, na kter´ebylo vol´ano connect(), se m´ısto funkce recvfrom() vol´a recv(). • address len mus´ı b´yt inicializovan´avelikost´ıbufferu. • po ´uspeˇsn´em n´avratu z recvfrom() je moˇzn´e address a address len beze zmˇeny pouˇz´ıt pro n´asledn´evol´an´ı sendto(). • stejnˇejako sendto(), je i recvfrom() moˇzn´epouˇ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´adata – l onoff == 1 && l linger != 0 . . . j´adro se snaˇz´ıpˇren´est zbyl´adata do vyprˇsen´ıtimeoutu l linger, kdyˇzse 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´yje definov´an protokolem TCP pˇri ukonˇcov´an´ıspojen´ı). Neˇzje soket zcela zruˇsen, nelze pouˇz´ıt jin´ysoket 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´apo uzavˇren´ıposlouchaj´ıc´ıho serverov´eho so- ketu znovu spustit server – volat socket(), bind(), listen() a accept() na stejn´eadrese a portu – i kdyˇzjeˇstˇedob´ıhaj´ıspojen´ıvytvoˇren´apˇ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 nasta- ven´ym pˇr´ıznakem RST. Na druh´estranˇese 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 ´urovni protokolu TCP kaˇzd´astrana sig- nalizuje, ˇze uˇznebude nic zapisovat. To plat´ıi v pˇr´ıpadˇepouˇzit´ı close() nebo shutdown(fd, SHUT RDWR). Pˇri pouˇzit´ı shutdown(fd, SHUT WR) lze ze soketu d´al ˇc´ıst. Druh´astrana 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˚usoketu int getsockopt(int socket, int level, int opt name, void *opt value, socklen t *option len ); • pˇreˇcten´ıparametr˚usoketu 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ˇznevol´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ˇe168.

170 Poˇrad´ıbajt˚u • S´ıt’ov´esluˇzby pouˇz´ıvaj´ıpoˇrad´ıbajt˚u, kter´ese 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´epoˇrad´ıbajt˚uje big-endian, tj. nejprve vyˇsˇs´ıbajt. Pouˇz´ıv´a se hlavnˇeve funkc´ıch pracuj´ıc´ıch s adresami a ˇc´ısly port˚u.

• Pokud lok´aln´ısyst´em pouˇz´ıv´astejn´epoˇrad´ıbajt˚u jako s´ıt’, nedˇelaj´ıpˇrevodn´ı funkce nic.

171 C´ıslaˇ protokol˚ua port˚u

struct protoent *getprotobyname(const char *name ); • v poloˇzce p proto vr´at´ıˇc´ıslo protokolu se jm´enem name (napˇr. pro "tcp" vr´at´ı6). • ˇc´ısla protokol˚ujsou uloˇzena v souboru /etc/protocols.

struct servent *getservbyname(const char *name, const char *proto ); • pro zadan´ejm´eno sluˇzby name a jm´eno protokolu proto vr´at´ı v poloˇzce s port ˇc´ıslo portu. • ˇc´ısla port˚ujsou uloˇzena v souboru /etc/services. funkce vrac´ı NULL, kdyˇzv datab´azi nen´ıodpov´ıdaj´ıc´ıpoloˇzka.

• v uveden´ych souborech je definov´ano mapov´an´ımezi jm´eny a ˇc´ısly pro stan- dardn´ı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´ejm´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´eadresy 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´ealiasy v nulou ukonˇcen´em poli h aliases. − pˇri vyhodnocov´an´ıdotaz˚una adresy a jm´ena se pouˇz´ıv´aDNS a lok´aln´ıdatab´aze uloˇzen´av souboru /etc/hosts. − vrac´ı NULL, kdyˇzv datab´azi nen´ıhledan´yz´aznam.

• lze stanovit, zda m´aprioritu DNS nebo lok´aln´ıdatab´aze adres. • jm´eno dom´eny a adresy name server˚ujsou v souboru /etc/resolv.conf. • m´ısto zde uveden´ych funkc´ı se doporuˇcuje pouˇz´ıvat obecnˇejˇs´ıvol´an´ı get- addrinfo().

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´akostra serveru. Jm´eno sluˇzby se zad´av´ajako parametr pro- gramu, odpov´ıdaj´ıc´ıˇc´ıslo portu hled´afunkce 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 Cek´an´ınaˇ data: select() int select(int nfds, fd set *readfds, fd set *writefds, fd set *errorfds, struct timeval *timeout ); • zjist´ı, kter´eze zadan´ych deskriptor˚ujsou pˇripraveny pro ˇcten´ı, z´apis, nebo na kter´ych doˇslo k v´yjimeˇcn´emu stavu. Pokud ˇz´adn´y takov´ydeskriptor nen´ı, ˇcek´ado vyprˇsen´ıˇcasu timeout (NULL . . . ˇcek´alibovolnˇedlouho). Parametr nfds ud´av´arozsah testovan´ych deskriptor˚u(0, ..., nfds-1). • pro nastaven´ıa test masek deskriptor˚uslouˇ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ˇetestovat jednotliv´edeskriptory, mezi kaˇzd´ym kolem test˚upak 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 O NONBLOCK by se nezablokovalo, tedy ne nutnˇeˇze nˇejak´adata jsou pˇripra- 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´ato, ˇ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´echceme testovat, po n´avratu z˚ustanou nastaven´ejen ty deskriptory, na kter´ych nastala testovan´aud´alost. Je nutn´eje tedy pˇred dalˇs´ım vol´an´ım select() znovu nastavit. Ty- picky to jsou bitov´emasky, ale nemus´ıtomu b´yt tak; z pozice program´atora je to samoˇzˇrejmˇejedno. • funkce select() je pouˇziteln´ai pro ˇcek´an´ına moˇznost z´apisu do roury nebo soketu – ˇcek´ase, aˇzdruh´astrana 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´euv´est NULL, speci´aln´ıpˇr´ıpad pˇri nastaven´ı vˇsech mnoˇzin na NULL je vol´an´ı, kter´ese pouze zablokuje do pˇr´ıchodu sign´alu nebo do vyprˇsen´ıtime-outu.

176 • po n´avratu je nutn´eotestovat kaˇzd´ydeskriptor zvl´aˇst’, nen´ık dispozici vol´an´ı, kter´eby v´am vytvoˇrilo mnoˇzinu pˇripraven´ych deskriptor˚u. • pokud obsluhuje s´ıt’ov´yserver v´ıce port˚u, m˚uˇze volat select() na pˇr´ısluˇsn´e deskriptory soket˚ua n´aslednˇe accept() na deskriptory, pro kter´e select() ohl´asil pˇr´ıchod ˇz´adosti 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´yserver, kter´yv jednom procesu ob- sluhuje paralelnˇenˇekolik klient˚u. Pomoc´ı select() se testuje stav deskrip- tor˚uodpov´ıdaj´ıc´ıch spojen´ıs jednotliv´ymi klienty a pˇres deskriptory pˇripra- ven´epro ˇcten´ı/z´apis se komunikuje. Aby se mohli pˇripojovat nov´ıklienti, testuje se i deskriptor soketu, kter´yse pouˇz´ıv´apro accept(). Vyuˇz´ıv´ase toho, ˇze select() ohl´as´ıdeskriptor s ˇcekaj´ıc´ıˇz´adost´ıklienta o spojen´ıjako pˇripraven´ypro ˇcten´ı. Na takov´ydeskriptor je moˇzn´e volat accept(). • jako v´yjimeˇcnou ud´alost (mnoˇzina errorfds) select() ohl´as´ıpˇr´ıchod out- of-band dat. • pozor na to, ˇze select m˚uˇze zmˇenit strukturu timeval, existuje nov´evol´an´ı pselect, kter´e(mimo jin´e) strukturu pro timeout nezmˇen´ı. • pro nfds je moˇzn´epouˇz´ıt FD SETSIZE, coˇzje syst´emov´akonstanta pro ma- xim´aln´ıpoˇcet deskriptor˚u. Typicky to vˇsak nebude pˇr´ıliˇsefektivn´ı, protoˇze tato konstanta je vˇetˇsinou 1024 na 32-bitov´ych syst´emech, na Solarisu to vˇsak pro 64-bitov´earchitektury je uˇz65536. • pokud se ˇcas nastav´ına 0 (tj. ted’ nemluv´ıme o nastaven´ıukazatele na NULL), select se d´apouˇz´ıt pro tzv. polling – zjist´ısouˇcasn´ystav a hned se vr´at´ı.

Cek´an´ınaˇ data: poll()

int poll(struct pollfd fds [], nfds t nfds, int timeout ); • ˇcek´ana ud´alost na nˇekter´em z deskriptor˚uv poli fds o nfds prvc´ıch po dobu timeout ms (0 . . . vr´at´ıse hned, -1 . . . ˇcek´a libovolnˇedlouho). • prvky fds: – fd . . . ˇc´ıslo deskriptoru – events . . . oˇcek´avan´eud´alosti, OR-kombinace POLLIN (lze ˇc´ıst), POLLOUT (lze ps´at), atd. – revents . . . ud´alosti, kter´enastaly, 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´evol´an´ı, select pak knihovn´ıfunkce implemento- van´apomoc´ı 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´yna -1 je to sam´ejako 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); if(FD ISSET(0, &efdset)) /* V´yjimka na stdin */; 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´epouˇzit´ı select(), kdy je tˇreba ˇc´ıst data souˇcasnˇeze 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´ekomunikace m´ame samostatn´ybuffer. Pˇr´ısluˇsn´yˇctec´ıdeskriptor bude v mnoˇzinˇepro ˇcten´ı v select(), pr´avˇekdyˇzje buffer pr´azdn´y. Naopak z´apisov´ydeskriptor bude v mnoˇzinˇepro z´apis, pr´avˇekdyˇzje 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´ana portech definovan´ych v /etc/inetd.conf a kdyˇzdetekuje pˇripojen´ıklienta, nav´aˇze spojen´ı, spust´ı pˇr´ısluˇsn´yserver a pˇresmˇeruje mu deskriptory 0, 1 a 2 do soketu, pˇres kter´ylze 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´yserver 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´ese pouˇz´ıvaj´ım´alo nebo jejichˇz inicializace je relativnˇenen´aroˇcn´a(telnetd, ftpd). Silnˇevyt´ıˇzen´ea dlouho startuj´ıc´ıservery (httpd) se obvykle startuj´ıze syst´emov´ych inicializaˇcn´ıch skript˚ua bˇeˇz´ıst´ale. • ˇcasto m´acenu inetd m´ıt vypnut´y´uplnˇe. Pokud na vaˇsem stroji bˇeˇz´ınapˇr. pouze SSH, tak pro to se inetd ve vˇetˇsinˇepˇr´ıpad˚unepouˇ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´esluˇzby podle /etc/services • soket . . . stream nebo dgram • proto . . . komunikaˇcn´ıprotokol (tcp, udp) • ˇcek´an´ı . . . wait (inetd ˇcek´ana 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 ... ´upln´acesta k programu serveru nebo internal (sluˇzbu zajiˇst’uje inetd) • argumenty . . . pˇr´ıkazov´yˇr´adek pro server, vˇcetnˇe argv[0]

• soket typu stream: – wait . . . server dostane soket, na kter´ymus´ı aspoˇnjednou zavolat accept(). Teprve t´ım z´ısk´anov´ysoket, pˇres kter´ym˚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´ysoket pˇred´aserveru, ser- ver 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´ana dalˇs´ıklienty a podle potˇreby spouˇst´ıdalˇs´ıinstance serveru. • pro soket typu dgram m´asmysl pouze wait. Server mus´ıpˇreˇc´ıst ze soketu aspoˇnjeden datagram. • jestliˇze inetd restartuje server (kromˇe stream nowait) pˇr´ıliˇsˇcasto (cca jed- nou za sekundu), usoud´ı, ˇze nastala chyba a po urˇcitou dobu (asi 10 minut) sluˇzbu zablokuje (nespouˇst´ıserver a odm´ıt´aspojen´ı). Ostatn´ıservery spouˇst´ı norm´alnˇed´al.

180 Obsah

• ´uvod, v´yvoj UNIXu a C, program´atorsk´en´astroje • z´akladn´ıpojmy a konvence UNIXu a jeho API • pˇr´ıstupov´apr´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´akomunikace • 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ˇzˇnuj´ım´ıt v´ıce lini´ıv´ypoˇctu v r´amci jednoho procesu • klasick´yunixov´ymodel: jednovl´aknov´eprocesy • vl´akna nejsou vhodn´apro vˇsechny aplikace • v´yhody vl´aken: – zrychlen´ıaplikace, typicky na v´ıceprocesorech (vl´akna jednoho procesu mohou bˇeˇzet souˇcasnˇena r˚uzn´ych procesorech) – modul´arn´ıprogramov´an´ı • nev´yhody vl´aken: – nen´ıjednoduch´ekorektnˇenapsat sloˇzitˇejˇs´ık´od pouˇz´ıvaj´ıc´ı vl´akna – obt´ıˇznˇejˇs´ıdebugging

181 • pro aplikace, kde kaˇzd´ykrok z´avis´ına kroku pˇredch´azej´ıc´ım, nemaj´ıvl´akna pˇr´ıliˇsvelk´ysmysl. • debuggery typicky maj´ıpodporu vl´aken, ale debugging zmˇen´ıtiming, takˇze to co v re´alu dˇel´aprobl´em se pˇri debuggingu v˚ubec nemus´ıprojevit. Toto samozˇrejmˇenen´ı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 [Bu- tenhof]. On-line pak je tˇreba dostupn´avelmi dobr´aa obs´ahl´akniha Multi- threaded 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˚upouˇz´ıv´asyst´emov´avol´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ˇeUNIX s vl´akny nepracoval a prvn´ıimplementace byly ˇcistˇeknihovn´ı, bez ´uˇcasti j´adra. Dnes se pouˇz´ıv´asp´ıˇse implementace vl´aken v j´adru nebo sm´ıˇsen´ymodel.

• existuj´ı i jin´etypy implementace vl´aken neˇzjsou zde uv´adˇen´aPOSIXov´a vl´akna (napˇr. syst´emov´evol´an´ı sproc() v IRIXu, Cthreads, Solaris threads, . . . ). • zat´ımco pˇri pr´aci s v´ıce procesy je nutn´evyvinou jist´e ´usil´ıproto, aby dan´eprocesy mohly data sd´ılet, u vl´aken je naopak nutn´eˇreˇsit situaci, jak pˇrirozen´esd´ılen´ıdat uhl´ıdat.

• vl´akna implementov´ana pouze v knihovnˇemohou b´yt preemptivn´ıi nepre- emptivn´ı. Pro preemptivnost je moˇzn´epouˇz´ıt ˇcasovaˇce a sign´aly. Pokud mul- tithreading (= 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´yproces, tj. ˇz´adn´evl´akno nem˚uˇze bˇeˇzet. To vypl´yv´az 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´evl´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´evl´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ˇcm˚uˇze zp˚usobit to, ˇze neˇzsi novˇespuˇstˇen´evl´akno staˇc´ı pˇreˇc´ıst hodnotu i, pˇr´ıkaz for provede dalˇs´ıiteraci a hodnota se zmˇen´ı. Obecnˇevl´akno m˚uˇze dostat m´ısto spr´avn´ehodnoty i libovolnou vˇetˇs´ı. • co ale je moˇzn´epouˇ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´yidentifik´ator vl´akna:

printf("thread %d started\n", (int) arg);

183 • pokud potˇrebujeme pˇredat v´ıce bajt˚uneˇzje velikost ukazatele, tak uˇzopravdu 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´eatributy vl´aken

• ˇc´ıtaˇcinstrukc´ı • z´asobn´ık (automatick´epromˇenn´e) • thread ID, dostupn´efunkc´ı pthread t pthread self(void); • pl´anovac´ıpriorita a politika • hodnota errno • kl´ıˇcovan´ehodnoty – dvojice (pthread key t key, void *ptr ) – kl´ıˇcvytvoˇren´yvol´an´ım pthread key create() je viditeln´yve 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´evl´akno m´az´asobn´ık pevn´evelikosti, kter´yse automaticky nezvˇetˇsuje. • bˇeˇzn´eglob´aln´ıpromˇenn´e(a tak´edynamicky alokovan´adata) jsou spoleˇcn´e pro vˇsechna vl´akna. Kl´ıˇcovan´ehodnoty pˇredstavuj´ı zp˚usob, jak vytvoˇrit glo- b´aln´ıpromˇennou, kter´aje ale lok´aln´ıv r´amci vl´akna. • kaˇzd´evl´akno m´ajeˇstˇevlastn´ısign´alovou masku, k tomu se tak´edostaneme

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´ana 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´yproces pomoc´ı wait() int pthread detach(pthread t thread ); • nastav´ıokamˇzit´euvolnˇen´ıpamˇeti po ukonˇcen´ıvl´akna, na vl´akno nelze pouˇz´ıt pthread join().

• pokud nem´ame v ´umyslu 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´apro zjiˇstˇen´ı jeho v´ysledku pomoc´ı pthread join(). To je po- dobn´asituace, jako kdyˇzrodiˇcovsk´yproces 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´ejin´evl´akno, nejen to, kter´eho spus- tilo.

185 Inicializace

int pthread once(pthread once t *once control, void (*init routine )(void)); • v parametru once control se pˇred´av´aukazatel na staticky inicializovanou promˇennou pthread once t once control = PTHREAD ONCE INIT; • prvn´ıvl´akno, kter´ezavol´a pthread once(), provede inicializaˇcn´ı funkci init routine(). Ostatn´ıvl´akna uˇztuto funkci neprov´adˇej´ı, nav´ıc, pokud inicializaˇcn´ıfunkce jeˇstˇeneskonˇ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ˇzk´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ˇepˇredt´ım, neˇzse proces rozdˇel´ına vl´akna, coˇzje bˇeˇznˇejˇs´ızp˚usob inicializace. • nen´ıdefinov´ano, co se m´ast´at, pokud je once control automatick´apromˇen- n´anebo nem´apoˇzadovanou hodnotu.

186 Zruˇsen´ıvl´akna int pthread cancel(pthread t thread ); • poˇz´ad´ao zruˇsen´ıvl´akna thread. Z´avis´ına nastaven´ı int pthread setcancelstate(int state, int *old ); • nastav´ınov´ystav a vr´at´ıstar´y: – PTHREAD CANCEL ENABLE . . . povoleno zruˇsit – PTHREAD CANCEL DISABLE . . . nelze zruˇsit, ˇz´adost bude ˇcekat na povolen´ı int pthread setcanceltype(int type, int *old ); • PTHREAD CANCEL ASYNCHRONOUS . . . okamˇzit´ezruˇsen´ı • PTHREAD CANCEL DEFERRED . . . ˇz´adost ˇcek´ana vstup do urˇcit´ych funkc´ı(napˇr. open(), read(), wait()), nebo na vol´an´ı void pthread testcancel(void);

• vl´akna je moˇzn´ezvenku ,,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ˇeje moˇzn´evl´akno zruˇsit v ˇcase, kdy je vl´akno vykon´av´adanou funkci. Pokud vl´akno zrovna takovou funkci nevy- kon´av´a, informace o zruˇsen´ıse “doruˇc´ı” bˇehem vykon´an´ıprvn´ıtakov´efunkce od t´edoby. • funkce pthread_cancel() se podob´azruˇsen´ı procesu sign´alem poslan´ym vol´an´ım kill(). • funkce pthread_setcancelstate() a pthread_setcanceltype() jsou ob- dobou 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´ı´uklidov´ehandlery; 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_a(void *arg) void *do_b(void *arg) { { ...... return 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ˇedalˇs´ıvl´akna a poˇck´ana jejich ukonˇcen´ı.

188 Soukrom´ekl´ıˇcovan´ehodnoty ve vl´aknech int pthread key create(pthread key t *key, void (*destructor)(void *)); • vytvoˇr´ıkl´ıˇc, kter´ylze asociovat s hodnotou typu (void *). Funkce destructor() se volaj´ıopakovanˇepro vˇsechny kl´ıˇce, jejichˇzhodnota nen´ı NULL, pˇri ukonˇcen´ıvl´akna. int pthread key delete(pthread key t key ); • Zruˇs´ıkl´ıˇc, nemˇen´ıasociovan´adata. 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. Imple- mentace m˚uˇze (ale nemus´ı) zastavit vol´an´ıdestruktor˚upo PTHREAD DESTRUC- TOR ITERATIONS iterac´ıch. Destruktor by tedy mˇel nastavit hodnotu na NULL, jinak hroz´ınekoneˇcn´ycyklus. • destruktor si mus´ıs´am zjistit kl´ıˇcpoloˇzky, ke kter´e patˇr´ı, a zruˇsit hodnotu vol´an´ım pthread setspecific(key, NULL). • SUSv3 tento nesmysln´ypoˇzadavek odstraˇnuje, protoˇze definuje, ˇze pˇred vstu- pem do destruktoru je hodnota automaticky nastaven´ana NULL; destruktor se n´aslednˇevyvol´as pˇredchoz´ıhodnotou kl´ıˇce.

189 Uklid´ pˇri ukonˇcen´ı/zruˇsen´ıvl´akna • vl´akno m´az´asobn´ık ´uklidov´ych handler˚u, kter´ese 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˚use 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´yhandler 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 ´uklid lok´aln´ıch dat funkc´ı(obdoba vol´an´ıdestruktor˚upro automatick´epromˇenn´ev C++).

190 fork() a vl´akna (POSIX)

• je nutn´edefinovat s´emantiku vol´an´ı fork v aplikac´ıch pouˇz´ıvaj´ıc´ıch vl´akna. Norma definuje, ˇze: – nov´yproces obsahuje pˇresnou kopii volaj´ıc´ıho vl´akna, vˇcetnˇe pˇr´ıpadn´ych stav˚umutex˚ua jin´ych zdroj˚u – ostatn´ıvl´akna v synovsk´em procesu neexistuj´ı – pokud takov´avl´akna mˇela naalokovanou pamˇet’, z˚ustane tato pamˇet’ naalokovan´a(= ztracen´a) – obdobnˇeto plat´ıpro zamˇcen´ymutex jiˇzneexistuj´ıc´ıho vl´akna • vytvoˇren´ınov´eho procesu z multivl´aknov´eaplikace m´a smysl pro n´asledn´evol´an´ı exec (tj. vˇcetnˇevol´an´ı popen, system apod.)

• pokud mˇelo jiˇzneexistuj´ıc´ı vl´akno zamˇcen´ymutex, tak je pˇr´ıstup k pˇr´ı- sluˇsn´ym sd´ılen´ym dat˚um ztracen, protoˇze zamˇcen´ymutex m˚uˇze odemknout pouze to vl´akno, kter´eho zamknulo.

• ostatn´ıvl´akna pˇrestanou existovat, nevolaj´ıse ˇz´adn´erutiny jako pˇri vol´an´ı pthread exit, pthread cancel nebo destruktory kl´ıˇcovan´ych dat • je moˇzn´epouˇ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´ez´avis´ı na pouˇzit´eknihovnˇea verzi syst´emu, napˇr´ıklad na Solarisu pˇred verz´ı10 znamenalo fork() v knihovnˇe libthread (jin´aknihovna neˇz libpthread) to sam´eco 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´eud´alosti, vol´an´ı pthread kill()). • nastaven´ıobsluhy sign´al˚uje stejn´epro vˇsechna vl´akna procesu, ale masku blokovan´ych sign´al˚um´akaˇzd´evl´akno vlastn´ı, nastavuje se funkc´ı

int pthread sigmask(int how, const sigset t *set, sigset t *oset ); • sign´al urˇcen´ypro proces oˇsetˇr´ıvˇzdy pr´avˇejedno vl´akno, kter´e nem´atento sign´al zablokovan´y. • lze vyhradit jedno vl´akno pro synchronn´ıpˇr´ıjem sign´al˚upomoc´ı 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´yproces, nejen jedno vl´akno. • vytvoˇren´evl´akno dˇed´ınastaven´ısign´alov´emasky od vl´akna, kter´eho vy- tvoˇrilo • pˇri pouˇzit´ı sigwait tak vlastnˇezablokujete pˇr´ısluˇsn´esign´aly ve vˇsech vl´ak- nech, vˇcetnˇevl´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˚ub´yv´a ˇcasto jedin´yopravdu do- poruˇcovan´ypro vl´akna, nav´ıc je i nejsn´aze implementovateln´y. • v re´aln´eaplikaci nen´ırozumn´eblokovat vˇsechny sign´aly, tj. vˇcetnˇesign´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´evers. dynamick´emutexy • statick´ainicializace 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´eho zruˇsit: int pthread mutex destroy(pthread mutex t *mx );

• mutex = mutual exclusion (vz´ajemn´evylouˇcen´ı) • je to speci´aln´ıforma Dijkstrov´ych semafor˚u– rozd´ıl mezi mutexy a bin´arn´ımi semafory je ten, ˇze zamˇcen´ymutex m˚uˇze odemknout pouze to vl´akno, kter´e ho zamklo. To u semafor˚uneplat´ı. • mutexy jsou urˇcen´epouze ke kr´atkodob´emu drˇzen´ıa mˇely by fungovat rychle. Slouˇz´ıpro implementaci kritick´ych sekc´ı, podobnˇejako lock-soubory nebo semafory (pouˇzit´ejako z´amky). • inicializace statick´eho mutexu pomoc´ızm´ınˇen´eho makra nastav´ıpro mutex jeho defaultn´ıatributy.

• dynamick´emutexy m˚uˇzeme potˇrebovat napˇr´ıklad v situaci, kdy dynamicky alokujeme datovou strukturu, jej´ıˇzsouˇc´ast´ıje i mutex, kter´ysd´ılen´adata 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´edynamicky inicializovat i statick´emutexy, ale je tˇreba zajistit, ˇze se vˇzdy inicializuj´ıpˇred pouˇzit´ım (to samozˇrejmˇeplat´ıi pro dynamick´emutexy) a ˇze se inicializuj´ıpouze jednou • kop´ırovat mutexy nen´ıkorektn´ı– v´ysledek takov´eoperace nen´ıdefinov´an. Je moˇzn´esamozˇrejmˇezkop´ı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ˇzzamˇcen´y, pokus o zamknut´ıvy´ust´ıv zablokov´an´ıvl´akna. Je moˇzn´epouˇz´ıt i vol´an´ı: int pthread mutex trylock(pthread mutex t *mx ); . . . kter´ese pokus´ızamknout mutex, a pokud to nelze prov´est, skonˇc´ıs chybou

• nelze zamknout mutex, pokud ho dan´evl´akno jiˇzzamˇcen´e m´a. Nˇekdy m˚uˇze doj´ıt i k self dead-locku. • nelze odemknout nezamˇcen´ymutex • nelze odemknout mutex, kter´yzamknulo jin´evl´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´erozmys- let, jak, kde a kolik mutex˚upouˇz´ıvat. Z knihovny, kter´a nebyla napsan´apro pouˇzit´ıv aplikac´ıch pouˇz´ıvaj´ıc´ıvl´akna, je moˇzn´eudˇ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˚ua hodnˇeˇcasu str´av´ım ve funkc´ıch kter´es mutexy pracuj´ı. Je proto ˇcasto nutn´epodle situace zvolit vhodn´ykompromis.

194 Podm´ınkov´epromˇenn´e(1) • mutexy slouˇz´ıpro synchronizaci pˇr´ıstupu ke sd´ılen´ym dat˚um • podm´ınkov´epromˇenn´epak k pˇred´an´ıinformac´ıo tˇechto sd´ılen´ych datech • z toho plyne, ˇze kaˇzd´apodm´ınkov´apromˇenn´aje 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ˇepomoc´ımutex˚ua 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´epromˇenn´ese 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ˇznen´ısplnˇena. Sp´ıc´ıvl´akno m˚uˇze b´yt pro- buzeno jin´ym vl´aknem, kter´ezmˇen´ıdatovou strukturu tak, ˇze podm´ınka bude splnˇena. Dan´evl´akno vˇsak mus´ıexplicitnˇeozn´amit, ˇze data zmˇenilo. • nen´ıto tak, ˇze pˇri deklaraci podm´ınkov´epromˇenn´e, coˇzje pro program´atora zcela transparentn´ıtyp, definujete podm´ınku napˇr. “n je vˇetˇs´ıneˇz7 ”. Pod- m´ınkovou promˇennou totiˇzm˚uˇzete pˇrirovnat k praporu nˇejak´ebarvy, a po- kud ho zvednete, znamen´ato, ˇze ta vl´akna, kter´aˇcekaj´ı aˇzs t´ımto praporem nˇekdo zam´av´anad hlavou, jsou o t´eto situaci informov´ana a mohou se podle toho zaˇr´ıdit. Nˇekter´avl´akna tak mohou ˇcekat na to, aˇz n bude vˇetˇs´ıneˇz 7, jin´amohou ˇcekat pouze na to, aˇzse n jakkoli zmˇen´ı. Je pouze na pro- gram´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´aze 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˚uje nutn´e podm´ınku otestovat vˇzdy, i kdyˇzpro ni pouˇz´ıv´ate samostatnou podm´ınkovou promˇennou – m˚uˇze se st´at, ˇze syst´em t´ım praporem zam´av´as´am, aniˇzby to udˇelalo to vl´akno, kter´ev naˇs´ın´azorn´esituaci ten ˇc´ıtaˇc n mˇen´ıa n´aslednˇeto oznamuje.

195 Podm´ınkov´epromˇ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´ana podm´ınkov´epromˇenn´edokud jin´evl´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 ne- provede, 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´auˇzmezit´ım nastala. Nefunguje to jako sign´aly, kter´epro v´as syst´em drˇz´ı, pokud je m´ate napˇr´ıklad zablokovan´e. Co je d˚uleˇzit´eje 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´epromˇenn´e(3)

int pthread cond timedwait(pthread cond t *cond, pthread mutex t *mutex, const struct timespec *abstime ); • ˇcek´ana pthread cond signal() nebo pthread cond broadcast(), ale maxim´alnˇedo vyprˇsen´ı timeoutu abstime. int pthread cond signal(pthread cond t *cond ); • probud´ıjeden proces ˇcekaj´ıc´ına podm´ınkov´epromˇenn´e cond. int pthread cond broadcast(pthread cond t *cond ); • probud´ıvˇsechny procesy ˇcekaj´ıc´ına podm´ınkov´epromˇenn´e cond.

• v parametru abstime funkce pthread cond timedwait() se pˇred´av´aabso- lutn´ıˇcas, tj. timeout vyprˇs´ı, kdyˇzsyst´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ˇezjist´ı, ˇze dan´apodm´ınka neplat´ı, pˇrepoˇc´ıt´avat ˇcasov´yrozd´ıl. • jak jiˇzbylo ˇreˇceno, jedna podm´ınkov´apromˇenn´am˚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´epo probuzen´ıotestovat podm´ınku, na kterou se ˇcek´a. Dalˇs´ıvˇec, kter´az 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´av´ıce ˇcten´aˇr˚ui 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ˇrz fronty zpr´avu odebere. T´ım se stane, ˇze ve frontˇe z˚ustane zpr´ava, kter´am˚uˇze b´yt vytlaˇcena aˇzdalˇs´ı signalizac´ı. • vl´akno m˚uˇze b´yt probuzeno jin´ym vl´aknem i v pˇr´ıpadˇe, ˇze ˇcek´ana jednu konkr´etn´ıud´alost, kter´avˇsak po probuzen´ıvl´akna jiˇzneplat´ıa pˇritom pro jin´eud´alosti pˇr´ısluˇsn´apodm´ınkov´apromˇenn´anen´ıpouˇzita (a pˇritom nebyl pouˇzit broadcast!). Pˇredstavte si, ˇze tˇesnˇepo t´e, kdy jin´evl´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ˇeje zpr´ava”. To vl´akno, kter´eje probuzeno, tak najde frontu pr´azdnou. Dalˇs´ıd˚uvod pro to, ˇze podm´ınkov´epromˇenn´eje 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´anutnost 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´epodm´ınkov´epromˇenn´eje potˇreba m´ıt jeˇstˇemutex. • 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´eimplementace podm´ınkov´ych promˇenn´ych!) a te- prve pak se vol´an´ı pthread cond wait() vr´at´ı.

• kdyˇzsignalizujeme, ˇze se nˇeco zmˇenilo, neznamen´ato 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(). K probuzen´ıvl´akna a n´avratu z pthread cond wait() m˚uˇze tedy doj´ıt, i kdyˇzpodm´ınka nen´ısplnˇena, proto je potˇreba znovu otestovat podm´ınku a pˇr´ıpadnˇeobnovit ˇcek´an´ı. • vˇsimnˇete si, ˇze odemknut´ız´amku n´asleduje aˇzpo 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´anˇ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 ma- xim´alnˇejedno vl´akno zamˇceno pro z´apis (a nikdo pro ˇcten´ı). • read-write z´amky jsou obdobou zamyk´an´ısoubor˚upomoc´ı fcntl().

199 Read-write z´amky (2)

int pthread rwlock wrlock(pthread rwlock t *rwlock ); • zamkne z´amek pro z´apis, pokud m´anˇekdo z´amek pro ˇcten´ı nebo z´apis, ˇcek´a. int pthread rwlock trywrlock(pthread rwlock t *rwlock ); • jako pthread rwlock wrlock(), ale kdyˇznem˚uˇze zamknout, 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´epromˇenn´e.

• SUSv3 k probran´ym mechanism˚um synchronizace vl´aken pˇrid´av´ajeˇstˇebari´e- rovou 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ˇerychlejˇs´ıneˇzjin´emechanismy pro z´ısk´an´ı exkluzivn´ıho pˇr´ıstupu • nutn´epouˇz´ıt hlaviˇckov´ysoubor atomic.h • sada vol´an´ıpro r˚uzn´eceloˇc´ıseln´etypy, 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ˇevl´akna, kaˇzd´evl´akno pracuje se stejnou glob´aln´ı promˇennou x, do kter´ev cyklu postupnˇepˇriˇcte ˇc´ısla od jedn´edo velikosti parame- tru, kter´eprogram dostane na vstupn´ıˇr´adce. Vl´akna bˇeˇz´ıparalelnˇe. for (i = 0; i < arg; ++i) x=x+i;

pot´ese sˇc´ıt´an´ıpro kontrolu provede v hlavn´ım vl´aknu, a dvojn´asobek se po- rovn´as diskutovanou glob´aln´ı promˇennou. Pokud nejsou v´ysledky stejn´e, doˇslo k soubˇehu (pˇreteˇcen´ıpromˇenn´em˚uˇzeme v t´eto situaci zcela ignorovat). Zde jsou v´ysledky a ˇcasy bˇehu pro situace, kdy program pouˇzil obyˇcejn´esˇc´ıt´an´ı, pot´efunkci atomic add int() a n´aslednˇezamyk´an´ıpomoc´ımutex˚u. Testov´ano na multiproce- sorov´em stroji UltraSparc T1000, zdroj´ak je v sekci uk´azkov´ych pˇr´ıklad˚uv adres´aˇri race-condition pod jm´enem atomic-add.c:

$ time ./a.out 99999999 RACEDETECTED result is 193053170 should be 1574919426 real 0m2.816s user 0m4.807s sys 0m0.009s

$ time ./a.out -a 99999999 everything OK, no race detected result is 1574919426 should be 1574919426

201 real 0m9.628s user 0m18.431s sys 0m0.010s

$ time ./a.out -m 99999999 everything OK, no race detected result is 1574919426 should be 1574919426 real 1m23.558s user 2m45.978s sys 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´epouˇzit´ıje paraleln´ızpracov´an´ıdat na multiprocesorech • bari´ery nemaj´ıAPI, je moˇzn´eje 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´eje pouˇz´ıt s vl´akny

• bari´eru m˚uˇzete vyuˇz´ıt napˇr. v situaci, kdy mezi jednotliv´ymi f´azemi zpra- cov´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ˇztehdy, 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´evl´akno, kter´edos´ahne bari´ery, sn´ıˇz´ıˇc´ıtaˇc, kter´yje na zaˇc´atku inicializov´an na poˇcet vl´aken. Pokud vl´akno po dekrementov´an´ıˇc´ıtaˇce zjist´ı, ˇze jeˇstˇenen´ı roven nule, usp´ıse na podm´ınkov´epromˇenn´e. Pokud dan´evl´akno je t´ım vl´aknem, kter´esn´ıˇz´ıˇc´ıtaˇcna nulu, m´ısto zavol´an´ı pthread cond wait poˇsle broadcast, kter´yn´aslednˇeprobud´ı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ˇcreinicializuje na p˚uvodn´ıhodnotu. I zde je nutn´eˇreˇsit r˚uzn´eprobl´emy, napˇr´ıklad nen´ımoˇzn´ejen tak reinicializovat ˇc´ıtaˇcpot´e, co bari´ery dos´ahne posledn´ıvl´akno, protoˇze jak jiˇzv´ıme, vl´akna po probuzen´ı

202 z pthread cond wait mus´ı vˇzdy otestovat, zda ˇc´ıtaˇcje opravdu nulov´ya pokud nen´ı, opˇet se usp´ı. Takˇze by se v´am mohlo st´at, ˇze by se probudila jen nˇekter´avl´akna, nebo taky ˇz´adn´a. Jak byste to ˇreˇsili? • funkce pro semafory se drˇz´ıklasick´eUNIXov´es´emantiky – pˇri chybˇevracej´ı -1 a nastav´ı errno

Typick´epouˇzit´ıvl´aken

• pipeline – kaˇzd´ez vl´aken prov´ad´ısvoji operaci nad daty, kter´ase postupnˇepˇred´avaj´ımezi vl´akny – kaˇzd´evl´akno typicky prov´ad´ıjinou operaci . . . zpracov´an´ıobr´azku, kde kaˇzd´evl´akno provede jin´yfiltr • work crew – vl´akna prov´adˇej´ıstejnou operaci, ale nad jin´ymi daty . . . zpracov´an´ıobr´azku dekompozic´ı– kaˇzd´evl´akno zpracov´av´a jinou ˇc´ast 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´erozdˇelen´ıje jen orientaˇcn´ı, pouˇzit´ıvl´aken je samozˇrejmˇeneomezen´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´anebyla navrˇzena jako thread-safe, je moˇzn´e pˇridat jeden z´amek – na zaˇc´atku funkce se zamkne, na konci odemkne – tento zp˚usob ale samozˇrejmˇenen´ıefektivn´ı. . . • slovem reentrantn´ı se ˇcasto mysl´ı, ˇze dan´afunkce byla navrˇzena s pˇrihl´ednut´ım na existenci vl´aken – . . . tedy ˇze dan´afunkce pracuje efektivnˇei ve v´ıcevl´aknov´em prostˇred´ı – takov´afunkce by se mˇela vyvarovat pouˇzit´ıstatick´ych dat a pokud moˇzno i prostˇredk˚upro synchronizaci vl´aken, kter´e jinak zpomaluj´ıbˇeh aplikace

• v dneˇsn´ıdobˇethread-safe vˇetˇsinou znamen´areentrantn´ı, tj. funkce jsou pˇre- ps´any tak, aby pracovaly efektivnˇei s vl´akny, ale je dobr´evˇedˇet, ˇze nˇekdy to m˚uˇze vyjadˇrovat rozd´ıl.

204 Nepˇrenositeln´avol´an´ı • nepˇrenositeln´avol´an´ıkonˇc´ıˇretˇezcem np (non-portable) • jednotliv´esyst´emy si takto definuj´ıvlastn´ıvol´an´ı • FreeBSD – pthread set name np(pthread t tid, const char *name) → umoˇzˇnuje pojmenovat vl´akno • Solaris – pthread cond reltimedwait np(...) → jako timedwait, ale ˇcasov´ytimeout je relativn´ı • OpenBSD – int pthread main np(void) → umoˇzˇnuje 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´avol´an´ı by se mˇela pouˇz´ıvat sp´ıˇse pro lad´ıc´ı ´uˇcely. Nikdy nev´ıte, kdo bude potˇrebovat v´aˇsk´od spustit na jin´em syst´emu, coˇzse stane typicky a neˇcekanˇepo t´e, co zrovna opust´ıte vaˇsi spoleˇcnost a nem´ate jiˇzˇcas to opravit. • zjistit, jak´anepˇrenositeln´avol´an´ıv´aˇssyst´em poskytuje je jednoduch´e, tˇreba pomoc´ı apropos np

205 Obsah

• ´uvod, v´yvoj UNIXu a C, program´atorsk´en´astroje • z´akladn´ıpojmy a konvence UNIXu a jeho API • pˇr´ıstupov´apr´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´akomunikace • 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´ekomunikaci. 2007-12-09 pˇrid´an slajd pro atomic add(3c) v sekci synchronizace vl´aken, p201. 2007-11-21 r˚uzn´amal´adoplnˇ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´yslajd ,,API vers ABI”, p33, a doplnˇen´ıpozn´amek k z´apisu do pojmenovan´eroury, 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´echyby). 2007-10-07 ˇc´asteˇcnˇepˇreps´any pozn´amky ke slajd˚um o historii unixu, standardech a souˇcasn´ych syst´e- mech; 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ˇeaplikac´ı 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´ejsem pˇreˇsel z CsLaTeX na Babel. M˚uˇze to ovlivnit nˇekter´efonty a t´ım i sazbu textu.

207 Konec

208