239
SABIN BURAGA, GABRIEL CIOBANU ATELIER DE PROGRAMARE ÎN DE CALCULATOARE . ,J '<. LINUX

Atelier Programare În Rețelele de Programare - S. Buraga

Embed Size (px)

DESCRIPTION

kll

Citation preview

  • SABIN BURAGA, GABRIEL CIOBANU

    ATELIER DE PROGRAMARE N REELE DE CALCULATOARE

    . ,J '

  • Sabin Buraga este asistent universitar la Facultatea de Informatic, Universitatea Al.I. Cuza". Dintre domeniile sale de interes se enumer tehnologiile Web, reelele de calculatoare i sistemele de operare.

    Gabriel Ciobanu este cercettor principal I la Institutul de Informatic Teoretic, Academia Romn, filiala lai. ,

    2001 by Editura POLIROM

    http: //www.polirom.ro

    Editura POLIROM Iai, B-dul Copou nr. 4, P.O. BOX 266, 6600 Bucureti, B-dul l.C. Brtianu nr. 6, et. 7

    Descrierea CIP a Bibliotecii Naionale:

    BURAGA, SABIN Atelier de programare n refele de calculatoare I Sabin Buraga, Gabriel Ciobanu

    Iai, Polirom, 2001 240 p.; 24 cm

    ISBN: 973-6~3-755-6

    I. Ciobanu, Gabriel

    Printed in ROMANIA

  • Sabin Buraga, Gabriel Ciobanu

    llllllllllll I lllllll l\11 B.~~~~~~ASI POLIROM 2001

  • Dedicat lui W. Richard Stevens (1951-1999) din crfile cruia se pot nvfa attea lucruri interesante.

  • Cuprins

    Mulumiri

    Prefa

    1 Introducere 1.1 UNIX i Linux 1.2 Comenzi uzuale

    1.2.1 Exerciii 1.2.2 Rezolvri

    2 Gestiunea fiierelor 2.1 Prelucrarea fiierelor

    2.1.1 Primitiva open() 2.1.2 Primitiva read() 2.1.3 Primitiva wri te O 2.1.4 Primitiva lseek() 2.1.5 Primitiva close O 2.1.6 Exemplu ..... .

    2.2 Prelucrarea atributelor fiierelor . 2.2.1 Exemplu ........ .

    2.3 Prelucrarea directoarelor . . . . . 2.4 Prelucrarea fiierelor de sistem

    2.4.l Gestiunea conturilor de utilizatori 2A.2 Gestiunea grupurilor de utilizatori 2.4.3 Gestiunea sesiunilor de lucru

    2.5 Exerciii . 2.6 Rezolvri

    3 Procese 3.1 Noiuni fundamentale 3.2 Comenzi pentru procese

    5

    9

    11

    13 13 13 13 14

    31 31 32 33 33 33 34 34 37 39 41 42 42 43 44 46 47

    54 54 56

  • 3.3 Procesele din perspectiva programatorului 58 3.4 Exemplu. 62 3.5 Exerciii . 64 3.6 Rezolvri 64

    , 4 ) Semnale 69 4.1 Prezentare general . . . . . . . . . . . . . . . . . . . . . . . . . 69 4.2 Manipularea semnalelor . . . . . . . . . . . . . . . . . . . . . . 71

    4.2.1 Definirea unui anumit comportament la apariia unui semnal . . .

    4.2.2 Trimiterea unui semnal unui proces . 4.2.3 Ateptarea unui semnal . . 4.2.4 Suspendarea execuiei unui proces

    4.3 Exemplu . 4.4 Alarme. . . . .

    4.4.1 Exemplu . 4.5 Exerciii . . . .

    5 Comunicarea ntre procese. Pipe-uri 5.1 Preliminarii .. 5.2 Primitiva pipe O

    5.2.1 Exemplu . 5.3 Primitiva fifo()

    5.3.1 Exemplu . 5 .4 Exerciii 5.5 Rezolvri

    6 Duplicarea descriptorilor. Redirectri 6.1 Duplicarea descriptorilor

    6.1.1 Exemple . 6.2 Exerciii 6.3 Rezolvri

    7 Interfaa socket 7 .1 Preliminarii . . 7.2 Interfaa socket . . . .

    7.2.1 Primitiva socket O . 7.2.2 Exemplu ..... 7.2.3 Primitiva socketpair() 7.2.4 Exemplu .

    7.3 Exerciii . . . ...

    71 72 72 73 73 74 75 76

    77 77 77 79 80 81 82 83

    93 93 94 96 98

    120 120 122 123 125 128 128 130

  • 8 Modelul client/server - TCP 8.1 Modelul client/server .. 8.2 Server TCP iterativ . . . .

    8.2.1 Primitiva bind() .. 8.2.2 Primitiva listen () . 8.2.3 Primitiva accept() . 8.2.4 Primitivele send() i recv() 8.2.5 Primitivele close () i shutdown()

    8.3 Client TCP . . . . 8.4 Conversia datelor . 8.5 Alte primitive utile 8.6 Exemplu . 8. 7 Exerciii . 8.8 Rezolvri

    9 Modelul client/server - UDP 9.1 Datagrame 9.2 Exemplu . 9.3 Exerciii . 9.4 Rezolvri

    10 Multiplexarea intrrilor /ieirilor 10.1 Primitiva select() 10.2 Exemple . . . 10.3 Asincronism . 10 .4 Exerciii 10.5 Rezolvri . .

    11 RPC - Apelul procedurilor la distan 11.1 Sistemul RPC . . . . . . . . . . . . . . 11.2 Cum lucreaz sistemul RPC? ..... 11.3 Scrierea programelor server i client RPC 11.4 Exemplu . 11.5 Exerciii . . . . . . . . . . .

    12 Utilizarea bibliotecii MySQL 12.1 Formularea problemei ...

    12.1.1 Schi de rezolvare 12.2 Accesarea serverului MySQL .

    131 131 131 133 134 134 135 135 136 137 138 139 145 145

    150 150 151 156 156

    160 160 161 168 176 177

    186 186 189 190 194 196

    198 198 198 200

  • 13 Biblioteca ncurses 13.1 Descriere general ............. . 13.2 Prezentarea funciilor bibliotecii ncurses . _. 13.3 Enunul unei probleme . . . .

    13.3.1 Rezolvarea problemei .

    14 Mediul Glade 14.1 Formularea i rezolvarea problemei ...... . 14.2 Conceperea interfeei . . . . . . . . . . . . . . .

    14.2.1 Utilizarea mediului de dezvoltare Clade

    Bibliografie

    Glosar

    214 214 214 217 217

    227 227 227 227

    234

    235

  • !l 4 4 7 7

    7 7 7 7

    l

    Mulumiri

    Mulumim tuturor studenilor notri care au dat dovad de cele mai multe ori de devotament, pricepere i perseveren. Ei i-au depit adesea profesorii. Acest lucru ne-a oferit mare satisfacie i a atras admiraia noastr. Am avut de nvat multe lucruri de la studenii notri, iar unele dintre soluiile prezentate n cartea de fa le aparin. Acest lucru reprezint o recunoatere a calitilor lor i vrea s sublinieze c ntr-o instituie academic studenii buni reprezint de fapt sngele care oxigeneaz ntreg organismul.

    Se cuvin menionai Manuel ubredu, Iulian Videanu, Daniel Dumitriu, Ioana Matei, Oana Captarencu, Olivia Oanea pentru contribuiile la unele programe prezentate n volum, precum i Sinic Alboaie, Radu Filip, Victor Tarhon-Gnu pentru observaiile lor asupra coninutului.

  • Prefa

    Lucrarea de fa este inspirat de laboratoarele pe care autorii le-au susinut n ultimii ani cu studenii Facultii de Informatic de la Universitatea "Alexan-dru Ioan Cuza" din lai. Aceste laboratoare au fost dedicate programrii n reele de calculatoare utiliznd interfaa socket BSD {Berkeley System Distri-bution}, limbajul C i sistemul de operare Linux (UNIX).

    Cartea se adreseaz studenilor din anii 2, 3 i 4 din facultile care predau informatic, elevilor buni i profesorilor din liceele care predau informatic, tinerilor absolveni care au nevoie de cunotine de programare n reele, dar care nu au urmat un astfel de curs n timpul studiilor. La multe faculti exist acum cursuri obligatorii de reele de calculatoare, iar cunotine de programare n reele de calculatoare se solicit explicit la examenele de licen.

    Cartea reprezint doar partea de laborator a cursului de programare n reele de calculatoare prezentat la Facultatea de Informatic din lai. Cursul propriu-zis va aprea n curnd ca volum separat i va oferi celor interesai mai multe amnunte legate de colecia de protocoale TCP /IP i de interfaa socket BSD. Dei volumele au fost gndite a se susine reciproc, fiecare volum poate fi citit separat.

    Acest volum insist asupra aspectelor legate de dezvoltarea unor programe de comunicare ntre procese sub sistemul de operare Linux (UNIX). Dez-voltarea unor programe eficiente pentru reelele de calculatoare. presupune cunoaterea sistemului de operare cu care se lucreaz. Primele capitole ale crii se refer la acele aspecte din Linux (UNIX) care sunt utile n acest sens. Pentru comunicarea ntre procese pe acelai calculator am utilizat mecanisme tipice sistemului de operare UNIX (pipe-uri, semnale, duplicarea descriptorilor), iar pentru comunicarea ntre procese aflate pe calculatoare diferite conectate n

    reea s-a recurs la interfaa socket i modelul client/server, prin intermediul protocoalelor TCP i UDP, n manier iterativ i concurent. Se prezint mul-tiplexarea intrrilor i ieirilor cu ajutorul primitivei select(). Un capitol este dedicat sistemului RPC (Remote Procedure Call), iar altul descrie modul de dezvoltare a aplicaiilor n reea folosind serverul de baze de date MySQL. Ul-timele capitole ofer exemple de aplicaii interactive folosind biblioteca ncurses

    i mediul de dezvoltare Clade. Un obiectiv ceva mai dificil de ndeplinit pentru un astfel de volum este de

    a oferi aplicaii mai mari i mai complexe care s ghideze cititorul n realizarea unor proiecte mai ambiioase. Sperm c unele dintre exerciiile rezolvate n volum i vor ncuraja pe cititori s dezvolte aplicaii care s le furnizeze satis-facia unui lucru deosebit i bine fcut. De fapt volumul este scris cu gndul la sentimentul special de mulumire pe care-l are un programator atunci cnd '"

  • realizeaz c o aplicaie n care a ndrznit lucruri noi funcioneaz. Este un sentin1ent pe care sperm s-l ncerce ct mai muli dintre cititorii acestei cri.

    Autorii nu pot dect s spere c lucrarea ar putea constitui o iniiere util n programarea reelelor de calculatoare care s contribuie la formarea unei culturi profesionale adecvate cerinelor software actuale.

    Autorii tiu (relativ la cartea lor) i atenioneaz cititorul (relativ la pro-gramele sale) c ntotdeauna se poate i mai bine. Pentru a exprima observaii utile, pentru a fi informat despre adugiri, corecii, versiuni mbuntite ale unor programe i alte informaii legate de acest volum, autorii ncurajeaz cititorul s foloseasc adresa e-mail lrcinfo. uaic. ro, precum i pagina web http://www.info.uaic.ro/-lrc.

    Autorii Iai, iulie 2001

  • l

    Capitolul 1

    Introducere Acest capitol prezint succint principiile de baz ale sistemului de operare UNIX (Linux) i descrie, prin in-termediul unor exerciii rezolvate, cteva dintre comenzile utile puse la dispoziia utilizatorului.

    1.1 UNIX i Linux Sistemul de operare UNIX este un sistem multi-utilizator i multi-tasking. Un numr arbitrar de utilizatori poate avea acces la resursele calculatorului pe care este instalat sistemul n orice moment de timp de la diferite terminale plasate local sau la distan, putnd rula n manier concurent mai multe

    aplicaii. Una dintre variantele free-software, de succes, ale UNIX-ului este Linux,

    sistem pe care l vom utiliza n continuare. Exemplele din aceast carte au fost testate pe distribuiile Linux RedHat 6.2 i 7.0.

    Linux reprezint un sistem multi-tasking i multi-user compatibil UNIX pe 32/64 de bii, respectnd standardul POSIX, oferind utilizatorilor acces la cele mai populare utilitare GNU i posed o interfa grafic bazat pe XWindow System.

    Printre alte faciliti, Linux ofer suport pentru reea, implementnd suita de protocoale TCP /IP, i un mediu complet de dezvoltare a aplicaiilor n diverse limbaje de programare (e.g. C, C++, Perl, Tcl/Tk, Python).

    1.2 Comenzi uzuale Reamintim o parte dintre cele mai utilizate comenzi UNIX (Linux) prin inter-mediul ctorva ...

    1.2.1 Exerciii

    1. S se afieze numai intrrile de (sub)directoare din directorul curent. 2. Exist vreo deosebire ntre efectele comenzilor de mai jos?

    Dac da, explicai motivul.

  • 14 Atelier de programare n reele de calculatoare

    3. Precizai care este diferena ntre:

    cat < file cat file file cat

    4. Ce efect are urmtoarea linie de comenzi? echo' who I cut -c1-9 I sort I uniq >> users

    5. Ce anume realizeaz linia de comenzi de mai jos? cut -d: -f1,3 /etc/passwd I sort -t: +O -1

    6. S se afieze toi utilizatorii care au conturi terminate n litera "t''. 7. S se scrie, ntr-o singur linie de comenzi, irul de comenzi care verific

    dac exist peste 20 de sesiuni deschise i n caz afirmativ s se trimit un mesaj prin pota electronic administratorului de sistem.

    8. S se gseasc toate fiierele temporare (al cror nume se termin cu . bak sau cu caracterul -) i s se tearg.

    9. S se scrie linia de comenzi pentru generarea unui fiier coninnd numele i calea tuturor fiierelor sursa C din sistem.

    10. S se compare execuia celor dou linii de comenzi (care este mai efi-cient?): cat I wc (cat /tmp/f wc

  • Introducere 15

    stocarea persistent a informaiilor,

    numirea informaiilor (servicii de directoare), protecia i folosirea n comun a informaiilor (prin intermediul unui

    mecanism de comunicaie inter-proces), interfaa ntre structurile interne de date ale nucleului sistemului i

    utilizator,

    interfaa universal la resursele reelei.

    Sistemul de fiiere n Linux este caracterizat prin:

    structura logic arborescent a directoarelor,

    tratarea dispozitivelor (terminale, imprimante, memorie etc.), n manier unitar i consistent, ca fiiere (n sensul conceptual al termenului),

    protecia resurselor prin intermediul unui sistem de permisiuni.

    n UNJ;X i n particular Linux fiierele pot fi de mai multe tipuri:

    ordinare (obinuite) coninnd date, programe sau cod main, directoare (privite ca fiiere, ele stocheaz alte fiiere sau directoare,

    n mod recursiv), speciale: dispozitive periferice ( e.g. mouse, imprimant, modem),

    pipe-uri (FIFO-uri, utilizate n comunicaia ntre procese), socluri (socket-uri, folosite n cadrul comunicaiei n reea),

    legturi simbolice: shortcut-uri ctre un fiier sau director, pentru a putea fi mai uor de regsit sau accesat; astfel, un fiier poate fi regsit n cadrul sistemului de fiiere prin mai multe nume, eventual n directoare diferite. Legturile simbolice, spre deosebire de alte sisteme de operare, sunt implementate la nivel de sistem de fiiere

    i nu n cadrul interfeei cu utilizatorul.

    Sistemele de fiiere UNIX sunt destul de multe, n Linux putndu-se folosi cel puin cincisprezece ( e.g. ext, ext2, minix, msdos, vfat, proc, iso9660, sysv, hpfs), cel mai popular fiind ext2 (Second Extended File System) care a aprut n anul 1993.

  • 16 Atelier de programare n reele de calculatoare

    I-noduri

    n UNIX, numrul de fiiere care se pot afla la un moment dat pe un sis-tem de fiiere este limitat precis, nc de la crearea sistemului de fiiere. Fiecare fiier posed un i-nod ( i-node) care conine aproape toate in-formaiile legate de acel fiier, exceptnd coninutul i numele. Unele i-noduri sunt nefolosite la un moment dat. Cele mai multe cmpuri ale unui i-nod pot fi vizualizate cu ajutorul comenzii ls (despre ls vom discuta mai detaliat mai trziu n cadrul acestui capitol). Comanda ls are ca argumente nume de fiiere, nu i-noduri, dar cum fiecare fiier are un singuri-nod, ls va cunoate de unde s ia informaiile. Pentru a vedea ce i-nod corespunde unui fiier se poate da ls -i. De exemplu:

    (infoiasi):-$ ls -i *.cls 52227 oldthesis.cls 52228 wrnthesis.cls

    Structura de directoare

    Orice sistem de tip UNIX are structura standard de directoare urmtoare: / directorul rdcin (directoarele vor fi delimitate de caracterul slash); /bin comenzile externe uzuale ale sistemului; / dev dispozitivele sistemului (att cele periferice, ct i cele logice).

    Aceste fiiere speciale nu au lungime, ci o succesiune de dou numere desemnnd numrul major i minor al dispozitivului asociat. De exemplu, /dev/hda desemneaz primul hard-disk IDE, /dev/hda1 desemneaz prima partiie a primului hard-disk IDE, /dev/sda de-semneaz primul hard-disk SCSI, iar /dev/lp1 desemneaz primul port paralel i aa mai departe. De asemenea, exist un dispozitiv virtual /dev/null care joac rolul de "gaur neagr" a sistemului, orice am scrie ctre acest dispozitiv pierzndu-se definitiv.

    /etc utilitare de administrare i fiiere de configurare. Se pot meniona: I etc/passwd stocheaz informaii despre utilizatori; I etc/ group stocheaz informaii despre grupurile de utilizatori; /etc/services d informaii despre serviciile de reea suportate; /etc/protocols furnizeaz informaii despre protocoalele de reea

    suportate;

  • s-

    8.

    1-

    le

    n

    ~-

    Introducere 17

    I etc/hosts conine lista numelor mainilor din reeaua local n care activeaz sistemul.

    /usr anumite fiiere utilizate de membrii sistemului. Iat o parte dintre subdirectoarele acestui director:

    sbin executabile i daemoni suplimentari; lib biblioteci folosite de diverse programe; local director coninnd aplicaii cu caracter local; include fiiere antet (header) necesare dezvoltrii de aplicaii C; man fiierele de date pentru comanda man.

    /lib biblioteci i module partajabile utilizate de aplicaii sau de compi-latoare;

    /boot ncrctorul sistemului (aici se gsete i imaginea binar a nu-cleului);

    /tmp datele temporare generate de anumite comenzi sunt stocate aici;

    /home directoarele de lucru pentru fiecare utilizator n parte;

    /mnt director de montare a unor sisteme de fiiere externe (partiii FAT, CD-ROM etc.);

    /var conine o serie de fiiere log completate de sistem (/var/log/), plus cozi de ateptare (n directorul /var I spool/) pentru e-mail (lvar/spool/mail/), imprimant, comunicaii seriale, gestionarea timpului etc.;

    /proc gzduiete sistemul virtual de fiiere proc i conine cte un sub-director pentru fiecare proces existent, plus date despre conexiunile de reea. Acest sistem virtual de fiiere reprezint principala moda-litate prin care nucleul "afl" informaii despre starea sistemului i a proceselor.

    Comenzile pentru prelucrarea directoarelor sunt:

    mkdir path - creeaz an director rmdir path - terge un director gol, n sensul c el nu conine

    dect intrrile " . " i " .. " semnificnd directorul nsui i directorul printe, respectiv;

    cd [ path ] - schimb directorul curent de lucru n cale; pwd - afieaz numele directorului curent; ls [ optiuni ] [ path ] - listeaz coninutul unui director.

  • 18 Atelier de programare n reele de calculatoare

    Argumentul path reprezint o cale de directoare separate de caracterul slash. O comand poate avea un numr variabil de p(3,rametri (argumente) i de opiuni prefixate de caracterul "-" (se poate utiliza i varianta explicit a unei op i uni, prefixat de caracterele " - - "). Cum majoritatea comenzilor pot accepta un numr impresionant de op-iuni, cel mai bine este s consultm manualul oferit de sistem. Acest lucru se realizeaz prin intermediul comenzii man. Manualul sistem este structurat pe seciuni, acestea fiind:

    1 pentru comenzi ale sistemului (e.g. cele de mai sus); 2 pentru apeluri sistem ( i. e. open()); 3 pentru funcii de bibliotec ( i. e. fprintf ()); 4 pentru descrieri ale dispozitivelor I/O; 5 pentru formate de fiiere; 6 pentru jocurile furnizate de sistem; 7 pentru descrieri ale fiierelor speciale; 8 pentru procese ale sistemului.

    Astfel, pentru a afia pagina de manual corespunztoare comenzii mkdir vom da man mkdir sau man 1 mkdir, iar pentru a vedea pagina de ma-nual referitoare la apelul mkdirO vom introduce man 2 mkdir. Dac ntmpinai dificulti, putei afia pagina de ajutor pentru man urmnd reeta man man. Alte comenzi nrudite cu man sunt apropos sau whatis.

    Vom putea utiliza ls pentru a, rezolva primul exerciiu. Dnd man ls, constatm c pentru a vizualiza doar numele subdirectoarelor coninute de directorul curent putem folosi opiunea "-d". Alte opiuni utile ale comenzii ls sunt:

    -a afieaz toate fiierele (chiar i cele al cror nume ncepe cu " . " care n mod normal sunt invizibile);

    -1 listeaz n format lung (permisiuni, numr de blocuri ocupate, nu-mele proprietarului, numele grupului, lungimea n bytes, data ul-timei modificri, numele fiierului);

    -t listeaz sortat dup data modificrii;

  • . e

    )-

    ;t e

    Introducere 19

    -R afieaz recursiv toate (sub)directoarele (aceast opiune va putea fi folosit la multe comenzi UNIX) .

    Fiecare fiier aparine cuiva. Un identificator (UID) numeric asociat celui care posed fiierul (de obicei cel care l-a creat) este stocat n cadrul i-nod ului. Al treilea cmp de pe o linie listat de ls -1 indic utilizatorul. Utilizatorii, n UNIX, fac parte din unul sau mai multe grupuri. Fiecare fiier va aparine i el unui anume grup.

    (infoiasi):-$ ls -1 /home/lrc total 8 drwxr-xr-x 5 lrc users drwx------ 2 lrc users

    proprietar grup

    Drepturi de acces (permisiuni)

    4096 Mar 7 18:35 Desktop 4096 Apr 22 23:00 mail

    Fiecare fiier are asociate anumite drepturi de acces, numite i permisiuni, pe care le putem vedea n coloanele 2-10 din ceea ce afieaz ls -1. Drepturile sunt stocate sub forma a trei triplete de bii. Primul triplet

    arat ce poate face cu fiierul posesorul lui (un proces al unui utilizator cu UID egal cu cel al fiierului), al doilea arat ce pot face utilizatorii din grupul fiierului, iar al treilea triplet arat ce poate face restul lumii

    (ali utilizatori care nu aparin grupului fiierului). Cele trei drepturi asociate celor trei categorii (utilizator, grup, alii) sunt:

    r citire (Read); w scriere (Wri te); x execut (eXecute).

    Ele arat dac un fiier poate fi citit, modificat sau respectiv executat (pentru fiiere de tip director, poate fi cutat) de persoana respectiv. Ca exemplu concret s lum un fiier avnd permisiunile:

    r-x-w---x \_/\_/\_/

    \ \ \_ restul lumii \ \__ grupul

    \ ___ posesorul

    (others) (group) (user)

  • 20 Atelier de programare n reele de calculatoare

    Acest lucru nseamn c:

    posesorul fiierului poate citi i executa acest fiier, dar nu l poate modifica;

    utilizatorii din grupul fiierului pot modifica acest fiier, ns nu l pot citi sau executa;

    ceilali utilizatori pot doar executa fiierul, dar nu l pot citi sau modifica.

    De asemenea, fiierele mai au trei atribute speciale (bii). Acestea se numesc: bitul SUID, bitul SGID i bitul STICKY (lipicios). Semnificaia acestora este urmtoarea:

    bitul SUID (Set User ID): un fiier cu acest bit setat arat faptul c execuia acestui fiier d natere unui proces care are proprietar pe posesorul fiierului, i nu pe posesorul procesului care execut fiierul (vezi i capitolul 3). Un bit SUID setat este indicat de co-manda ls -1 printr-un "s" n loc de "x":

    (infoiasi):-$ ls -1 /usr/bin/chfn -rws--x--x 1 root root 13184 Aug 31 2000 /usr/bin/chfn

    Cnd utilizatorul busaco va executa acest fiier, procesul va avea UID root, i nu busaco, din cauza bitului SUID.

    bitul SGID (Set Group ID) are o funcie similar, acionnd asupra grupului procesului.

    Aceti doi bii nu au sens dect dac fiierul este executabil. Pentru fiiere care nu sunt executabile, ei sunt folosii pentru a indica faptul

    c utilizarea fiierului se poate face numai dup blocarea ( lock) lor. Aceti bii se vd cu litere mari "S".

    bitul Sticky (lipicios), notat cu "t" are urmtoarea semantic: la fiiere executabile iniial acest bit indica nucleului s opti-mizeze folosirea fiierului: odat executat (deci citit n memo-rie) va fi pstrat n memorie, chiar dac procesul se termin, pentru c probabil va fi reapelat din nou, n curnd. pentru directoare indic faptul c n directoarele n care oricine poate scrie ( e.g. /tmp), teoretic oricine poate terge orice fiier. Un bit lipicios pe un astfel de director va permite ns tergerea unui fiier doar de ctre proprietarul lui.

  • 8

    Introducere 21

    Un exemplu (opiunea -d a comenzii ls este necesar pentru a lista drepturile directorului /tmp i nu ale tuturor fiierelor cu prinse n el) : (infoiasi):-$ ls -ld /tmp drwxrwxrwt 3 root root 1024 Jul 26 00:10 /tmp/

    bit sticky

    Mai trebuie spus c orice operaie de deschidere a unui director pentru cutare este precedat de verificarea dreptului de a face aceast cutare.

    Exist dou tipuri de drepturi pentru directoare:

    bitul "r" ( read) la un director arat dreptul de a citi coninutul directorului (un tablou de intrri), deci dreptul de a afla care i-nod corespunde la un nume;

    bitul "x" (search) indic dreptul de a citi coninutul unui i-nod in-dicat de o legtur din acest director.

    Dac un director este accesibil, are de obicei drepturile r-x. Dac are numai drepturile r--, atunci se poate afla ce fiiere conine, dar ele nu pot fi deschise (nici i-nodurile lor nu pot fi accesate). Putem executa ls sau ls -i, ns nu ls -1 pe un astfel de director. Dac are numai drepturile --x nu putem vedea ce fiiere conine, dar dac tim unul dintre nume, l putem deschide. Pentru a modifica permisiunile asociate fiierelor vom utiliza comanda chmod. Drepturile de acces vor putea fi date fie simbolic (spre exemplu chmod +ux proiect care seteaz dreptul de execuie pentru proprietar), fie numeric (trei cifre n baza opt). Tabela 1.1 rezum valorile octale care pot fi folosite pentru alterarea drepturilor de acces.

    Tabela 1.1: Valorile octale pentru drepturile de acces folosite de chmod

    I Categorie I r I w I x J User 400 200 100

    Group 40 20 10 Others 4 2 1

    De exemplu, pentru ca fiierul program. c s poat fi citit i scris de posesor, s poate fi citit de grup i s poat fi executat de alii, vom da chmod 641 program.c:

  • 22 Atelier de programare n reele de calculatoare

    (infoiasi):-$ (infoiasi):-$

    (infoiasi):-$ (infoiasi) :-$ -rw-r----x

    chmod O program.c ls -1 program.c 1 lrc lrc chmod 641 program.c ls -1 program.c 1 lrc lrc

    14802 Jun 9 16:34 program.c

    14802 Jun 9 16:34 program.c

    2. Putem trece astfel la rezolvarea celui de-al doilea exerciiu. Pentru aceasta, vom reaminti cele mai populare comenzi de lucru cu fiierele:

    cp file1 file2 - copie fiiere; mv file1 file2 - mut/redenumete fiiere; ln file1 file2 - permite unui fiier s aib un nume complemen-

    tar (mai multe nume de fiiere pot desemna acelai fiier; legturile pot fi hard (se creeaz i o copie a coninutului fiierului) sau soft

    (legtura simbolic va conine doar numele ctre fiierul surs); rm file(s) - terge fiiere (atenie! fiierele terse nu pot fi recu-

    perate n nici un mod!). Pentru toate comenzile de mai sus, exist o serie de opiuni folositoare: -f foreaz ndeplinirea aciunii, fr confirmare din partea utilizatorului

    sau ignornd erorile care pot surveni; -i mod interactiv, interognd utilizatorul dac ntr-adevr dorete s

    realizeze ceea ce s-a specificat; -v afieaz mai multe informaii la execuia comenzii respective; -R mod recursiv, executndu-se asupra tuturor subdirectoarelor.

    n cadrul numelui unui fiier putem specifica aa-numitele metacaractere ( wildcards) care pot nlocui o serie de caractere. Cele mai des folosite sunt:

    * substituie zero, unul sau mai multe caractere; ? substituie un singur caracter, pe poziia pe care apare; [ ... ] substituie un caracter aparinnd unui grup de caractere. Caracterul - ( t'ilda) poate fi folosit pentru a substitui directorul home (propriu) al utilizatorului. n acest moment putei da rspunsul la exerciiul propus, fr a executa efectiv cele patru forme ale comenzii rm.

  • a,

    e

    r.i

    Introducere 23

    3. Pentru a rezolva acest exerciiu trebuie s vedem care sunt comenzile pentru prelucrarea coninutului fiierelor:

    cat file (s) - afieaz coninutul fiierelor specificate. n fapt, co-manda are drept scop primar concatenarea mai multor fiiere;

    more file (s) - afieaz paginat coninutul fiierelor (se iese cu : q); less file (s) - similar cu more, dar permite parcurgerea fiierului

    n ambele sensuri (un ecran n sus cu "p", un ecran mai jos cu spaiu, cutarea unui ir cu "/");

    tac file (s) - analog cu comanda cat, dar afieaz de la sfrit ctre nceput fiierul;

    wc file(s) - afieaz numrul de caractere, cuvinte i linii ale unui fiier:

    (infoiasi):-/tmp$ wc list_users 143 1218 8096 users_list

    head file(s) - afieaz primele n linii (implicit 10) dintr-un fiier; tail file(s) - afieaz ultimele n linii (implicit 10); file file(s) - afieaz tipul fiierelor (executabil, text, script,

    arhiv etc.): (infoiasi):-/tmp$ file*

    " datafiles: gaend:

    go: helpfiles: hintfiles: lastgaen.zip: mailspool: motd1: sabeav.zip: syslog.corn: syslog.link: userfiles: users_list:

    directory ELF 32-bit LSB executable,

    Intel 80386, version 1 ASCII text directory directory Zip archive data, directory AS,CII text

    at least v1.0 to extract

    Zip archive data, at least v2.0 trr extract English text ASCII text directory ASCII text

    (infoiasi):/dev$ file tty tty: character special

  • 24 Atelier de programare n reele de calculatoare

    stat file (s) - afieaz diverse informaii despre fiiere: (infoiasi):/$ stat -

    File: "/home/lrc" Size: 2048 Filetype: Directory Mode: (0755/drwxr-xr-x)

    Uid: (563/lrc) Gid: (202/lrc) Device: 8,0 !node: 79576 Links: 13 Access: Mon Nov 23 12:43:30 2000(00000.00:02:12) Modify: Mon Nov 23 12:43:31 2000(00000.00:02:11) Change: Mon Nov 23 12:43:31 2000(00000.00:02:11)

    O caracteristic a interpretorului de comenzi (shell-ul UNIX) este aceea de a permite utilizatorului s redirecteze intrarea sau ieirea unui program (comenzi). Implicit, fiecare program citete date de la intrarea standard (tastatura) i scrie rezultatele prelucrrii la ieirea standard (terminalul). Mesajele de eroare sunt trimise unui dispozitiv standard de eroare care n mod normal este terminalul. Pentru a redirecta intrarea/ieirea vom folosi operatorii: < redirecteaz intrarea standard (n loc de tastatur vor fi folosite datele dintr-un fiier specificat). > redirecteaz ieirea standard (n loc de terminal datele vor fi scrise ntr-un fiier care dac exist va fi suprascris). ca mai sus, dar datele vor fi adugate la sfritul fiierului, dac ex-ist). I ieirea unei comenzi va fi intrare pentru alta. Astfel, putem furniza soluia la exerciiul propus:

    Forma cat file va afia coninutul fiierului cu numele file, dac exist stocat n directorul curent.

    Forma cat > users

  • :l

    Introducere 25

    Pentru a putea rezolva acest exerciiu, s enumerm nc o serie de comenzi folositoare:

    cut options [file (s)] - decupeaz dintr-un grup de linii anumite caractere aflate pe diverse poziii sau cmpuri delimitate de anumite caractere.

    Pentru a decupa toate coloanele cuprinse ntre anumite poziii vom utiliza opiunea "-c", iar pentru a selecta diverse cmpuri vom folosi "-f" n conjuncie cu opiunea "-d" care precizeaz care va fi carac-terul utilizat ca delimitator (implicit este caracterul TAB). Pentru a determina GID-urile tuturor grupurilor de utilizatori vom executa:

    (infoiasi):-$ cut -f3 -d: /etc/group

    Pentru a decupa toate coloanele cuprinse ntre poziiile 5 i 10 vom apela comanda:

    (infoiasi):-$ cut -c5-10 .profile =$PATH rt PAT k 0033

    n

    s lynx s alta s g='t s dir= s p=pi s a='t

    sort [+pos1] [-pos2] [file(s)] [options] - este o comand care sorteaz conform unor anumite criterii un fiier (dup poziiile n cadrul liniei sau dup cmpuri).

    Dac pos 1 i pos2 sunt specificate, atunci se va sorta zona cuprins ntre coloanele pos1 i pos2. Pentru a ordona n fun~ie de anumite cmpuri se va utiliza opiunea '-t'.

    uniq file (s) - pstreaz dintr-un fiier doar liniile unice, n pre-alabil ordonate.

    Dup acestea, dm cteva comenzi referitoare la utilizatori (s nu uitm c UNIX este un sistem multi-utilizator):

  • 26 Atelier de programare n reele de calculatoar~------- who - afieaz. lista sesiunilor deschise ale utilizatorilor conectai

    (numele de cont, terminalul de conectare, data conectrii); f inger [name] - furnizeaz o list. mai detaliat a sesiunilor des-

    chise (incluznd i numele real al utilizatorilor) sau informaii despre un anumit utilizator (pe maina local sau pe alta): (infoiasi):-$ finger busaco Login: busaco Name: Sabin Buraga Directory: /home/busaco Shell: /bin/bash Last login Sun Apr 22 22:59 (EEST) on tty2 Mail last read Sun Apr 22 23:00 2001 (EEST) No Plan.

    w - reprezint o mixtur a celor dou comenzi de mai sus, afind i ultima comand executat de utilizatori.

    S ncercm s rezolvm misterul liniei propuse. Dup cum se poate bnui, comanda echo va afia un ir de caractere la terminal (ecran). La prima vedere, am putea obine irul: who I cut -cl-9 I sort I uniq Dar nu ar fi prea banal? Shell-ul va executa comenzile nlnuite prin operatorul " I" din interiorul accentelor grave, iar rezultatul obinut va fi returnat ca argument pentru comanda echo. Astfel, se va executa mai n-ti who care va furniza lista tuturor sesiunilor deschise. Lista va fi trimis spre prelucrare comenzii cut. Aceasta va decupa din fiecare linie primele 9 caractere (adic tocmai numele de cont ale utilizatorilor conectai), iar rezultatul va fi redirectat ctre intrarea comenzii sort care va or-dona datele primite. La final, uniq va pstra numai liniile unice, adic va trimite la ieirea standard lista - n ordine alfabetic - a utilizatorilor prezeni n sistem la momentul execuiei comenzilor de mai sus. Acest rezultat va fi dat ca argument pentru echo i va fi scris n fiierul denumit users. Folosind, scrierea se va face adugnd la vechiul coninut noua list generat. Dup cum se observ, puterea shell-ului rezid n abilitatea de a prelu-cra, ntr-un mod nlnuit, mai multe comenzi, folosind substituia prin intermediul apostroafelor grave.

    5. Pentru a rezolva acest exerciiu s ne reamintim cum sunt gestionai utilizatorii n cadrul unui sistem UNIX. Fiecare utilizator are asociat un identificator unic atribuit de sistem (sau de ctre administrator) n momentul crerii contului. Acest identifica-tor este un numr ntreg, mai mare sau egal cu zero, denumit UID.

  • :1

    Introducere 27

    De asemenea, fiecare utilizator poate aparine mcar unui grup de uti-lizatori. Grupul este identificat de identificatorul de grup GID. UID-ul i GID-ul se gsesc n fiierul /etc/passwd n care pentru orice utilizator se memoreaz pe o linie:

    numele contului su (login name), parola (din considerente de securitate, de cele mai multe ori parolele

    sunt "umbrite", fiind nregistrate n alt fiier, inaccesibil pentru uti-lizatorii obinuii, denumit /etc/shadow),

    UID-ul (O dac acel utilizator are drepturi depline), corespunznd utilizatorului special root,

    GID-ul (O dac acel utilizator are drepturi depline), corespunznd grupului special root,

    alte informaii (nume real, an de studii, telefon etc.), directorul personal al utilizatorului (home), interpretorul de comenzi (shell-ul) folosit (e.g. /bin/bash).

    Fiecare dintre aceste cmpuri va fi delimitat de caracterul ": ".

    Un fragment dintr-un fiier /etc/passwd poate fi (parola nu apare, ea fiind substituit de un "x"):

    busaco:x:500:500:Sabin-Corneliu Buraga:/home/busaco:/bin/bash

    Aadar, linia de comenzi cut -d: -f1,3 /etc/passwd I sort -t: +O -1 va afia, sortate dup numele de cont, informaiile coninute n fiierul I etc/passwd despre numele de cont i UID-ul tuturor utilizatorilor. De-cuparea informaiilor se va realiza pe cmpuri, cu ajutorul comenzii cut, delimit-atorul fiind":", dup care vor fi ordonate de sort.

    De notat faptul c utiliznd PAM (Pluggable Authentication Modules) este posibil s avem i alte baze de date coninnd informaii despre utilizatori, autentificndu-i jn mod transparent.

    6. Pentru a lista toi utilizatorii care au numele de cont terminat n litera "t" vom reoorge la cut i la grep. Trebuie s decupm /etc/passwd pentru a pstra doar primul cmp din fiecare linie. Comanda grep va afia numai liniile care conin un anumit subir (model sau pattern). Modelul poate fi o expresie regulat (vezi mai jos). Pentru detalii, consultai manualul.

  • 28 Atelier de programare n reele de calculatoare

    O prim variant (greit!) este: cut -d: -f1 /etc/passwd I grep t Aceasta ar afia toate numele de cont care vor.conine caracterul "t", nu doar cele n care apare pe ultima poziie. Pentru a putea rezolva exerciiul va trebui s recurgem la utilizarea expresiilpr regulate. O expresie regulat este un model care descrie un set de iruri de carac-tere, fiind construit asemntor expresiilor aritmetice, prin utilizarea diferiilor operatori. Cei mai uzuali operatori sunt: [ ... ] delimiteaz o list de caractere dintre care numai unul se va potrivi expresiei dorite (e.g. [Oi23456789] pentru a se potrivi cu o singur cifr sau [0-9A-Za-z] pentru un caracter alfa-numeric). - meta-caracter care indic nceputul unei linii. $ meta-caracter indicnd sfritul unei linii. ? operator care desemneaz zero sau cel mult o apariie a unui element al expresiei. * operator care desemneaz zero, una sau mai multe apariii. + operator care desemneaz una sau mai multe apariii. {n} operator care desemneaz exact n apariii. I specific o alternativ. Varianta corect care soluioneaz exerciiul propus este deci urmtoarea: cut -d: -f1 /etc/passwd I grep t$

    7. Pentru rezolvarea acestui exerciiu, ne vom folosi de facilitatea de execu-ie condiionat a comenzilor pe care o pune la dispoziie shell-ul. Construcia comandai && comanda2 are semantica urmtoare: comanda2 va fi executat numai n cazul n care comandai s-a executat i a returnat codul de terminare O. Reamintim faptul c orice comand n caz de succes va returna codul O, iar n caz de eec un cod diferit de O. Valoarea O n UNIX se consider echivalent cu true (adevrat), iar o valoarea nenul este echivalent cu false (fals). Vom utiliza comanda test pentru a realiza diferite comparaii i teste asupra existenei sau tipului fiierelor. Pentru mai multe amnunte, con-sultai man test. Recurgnd la substituia cu apostrofuri inverse, vom putea scrie urmtoarea linie de comenzi care ofer soluia: test ' who I wc -1' -gt 20 && mail root -s"Atentie"

  • Introducere 29

    8. Vom utiliza comanda find pentru a cuta fiiere folosind diverse criterii i, eventual, a realiza anumite aciuni asupra lor. Cutarea se va efectua pornind de la un anumit director care va fi explorat conform criteriilor de cutare alese. Sintaxa general a comenzii este: find [path] [expression] [action] unde path reprezint calea de directoare de la care se va ncepe cutarea, expression semnific o expresie definind criteriul de cutare, iar action specific aciunea care va fi efectuat la gsirea unui fiier.

    Cutarea se poate realiza dup:

    numele unui fiier - se folosete opiunea -name specificator, n care specificator reprezint un specificator de fiier (se pot utiliza, desigur, meta-caracterele de s~tituie);

    tipul unui fiier - se folosete -type tip, unde tip poate fi unul dintre caracterele f (fiier obinuit), d (director), 1 (legtur sim-

    bolic), s (socket) etc.; numele proprietarului - se utilizeaz opiunea -user nume, unde

    nume poate fi numele sau UID-ul proprietarului fiierului; grupul proprietarului - se folosete -group nume, unde nume poate

    fi un nume de grup sau un GID.

    Exist o multitudine de alte opiuni care pot compune o expresie de cutare; pentru amnunte citii paginile de manual dedicate comenzii

    find. De asemenea, mai multe criterii de cutare pot fi combinate prin utilizarea operatorilor logici ! (negaie - NOT), -a (i logic - AND) sau -o (sau logic - OR) i prin folosirea parantezelor. Ca aciune executat la gsirea unui fiier putem avea:

    afiarea numelui fiierului gsit - se folosete opiunea -print; execuia unei comenzi - se utilizeaz opiunea -exec. irul de ca-

    ractere "{}" va &ubstitui numele fiierului gsit i ~ putea fi dat ca argument al comenzii care va fi executat. Vom sfri lista argu-mentelor pasate comenzii cu caracterul punct-virgul.

    De exemplu, pentru a gsi toate fiierele surs scrise n limbajul C stocate de sistemul de fiiere, vom da: find I -name *.c -print

  • 30 Atelier de programare n reele de calculatoare

    Astfel, soluia la exerciiul propus poate fi urmtoarea (s-au utilizat ghilimelele pentru ca shell-ul s nu interpreteze caracterele speciale"{}" sau";"): find I -name *.bak -o -name *- -exec rm 11 {} 11 ";"

    Invitm cititorul s descopere de unul singur rezolvarea ultimelor dou exerciii propuse la nceputul acestui capitol.

  • Capitolul 2

    Gestiunea fiierelor Acest capitol prezint modurile de prelucrare a fiierelor ordinare i de tip director, plus diverse modaliti de acce-sare a informaiilor coninute de diferite fiiere sistem.

    2.1 Prelucrarea fiierelor

    Dup cum am vzut n capitolul precedent, sistemul de operare UNIX trateaz n manier unitar~ierele.

    Programatorul poate astfel gestiona att fiierele propriu-zise, ct i dis-pozitivele periferice sau alte abstraciuni prin intermediul aceleiai interfee. Astfel, avem la dispoziie dou moduri de prelucrare a fiierelor:

    prin intermediul descriptorilor - fiecrui fiier i se va asocia un ntreg denumit handler, iar operaiile asupra fiierului se vor realiza apelnd diferite primitive puse la dispoziie de nucleul sistemului de operare;

    prin intermediul structurii FILE definite n stdio. h, operaiile asupra fiierelor realizndu-se prin apelarea funciilor din biblioteca standard de intrare/ ieire.

    De reinut faptul c folosind prima modalitate recurgem la apelurile (pri-mitivele) sistem (prelucrare low-level ), iar n al doilea caz fiecare operaie de intrare sau de ieire se realizeaz prin intermediul funciilor de bibliotec, utilizndu-se un buffer intern. n implementarea lor intim, funciile de biblio-

    tec se vor baza pe primitivele puse la dispoziie de nucleul sistemului. Tabelul 2.1 sintetizeaz principalele operaii care se pot efectua asupra fiierelor.

    De reinut c pentru a crea un fiier se va putea utiliza apelul creat () sau apelul open(). Sfritul de fiier va fi desemnat de constanta EOF (pentru va-rianta folosind descriptori de fiier) sau va fi testat cu ajutorul funciei feof O (n cazul utilizrii structurii FILE).

    Cteva detalii privitoare la primitivele prezentate n tabel urmeaz n con-tinuare.

  • 32 Atelier de programare n reele de calculatoare

    Tabela 2.1: Operaii asupra fiierelor

    Operaie I Primitiv sistem I Funcie stdio. h I deschidere open()

    citire read()

    scriere writeO

    poziionare lseek()

    nchidere I close()

    2.1.1 Primitiva open() Apelul open() are urmtorul prototip:

    #include #include #include

    int open (const char *pathname, int oflag);

    fopen() freadO fgetc() fgetsO fscanf() fwriteO fputcO fputsO

    fprintf() fseek() ftell() fclose()

    int open (const char *pathname, int oflag, mode_t mode);

    Primitiva returneaz -1 n caz de eroare, altfel returneaz descriptorul de fiier asociat fiierului dorit a fi deschis.

    Parametrul oflag desemneaz opiunile de deschidere a fiierului. Este n realitate un ir de bii, putnd fi folosite constantele:

    O_RDONLY - deschidere numai pentru citire;

    O_WRONLY - deschidere numai pentru scriere;

    O_RDWR - deschidere pentru citire i scriere;

    O_APPEND - deschidere pentru adugare la sfrit;

    O_CREAT - crearea fiierului, dac el nu exist deja;

    (

    E

    ( fi a

    o

    a

  • Gestiunea fiierelor 33

    O_EXCL - utilizare "exclusiv" a fiierului: dac s-a folosit O_CREAT i fiierul exist deja, se va returna eroare;

    O_ TRUNC - dac fiierul exist, coninutul lui este ters; Parametrul mode se folosete numai dac fiierul este creat i specific drep-

    turile de acces. Pentru crearea fiierelor poate fi folosit i primitiva creat() echivalent

    cu specificarea opiunilor O_WRONLY I O_CREAT I O_TRUNC la open().

    2.1.2 Primitiva re ad O Citirea datelor dintr-un fiier deschis se realizeaz cu apelul re ad(): #include

    ssize_t read (int f

  • 34 Atelier de programare n reele de calculatoare

    #include #include

    off_t lseek (int fd, off_t offset, int pos)

    Se poziioneaz indicatorul la deplasamentul offset n fiierul corespun-ztor descriptorului fd, astfel:

    dac parametrul pos ia valoarea SEEK_SET, poziionarea se face relativ la nceputul fiierului;

    dac parametrul pos ia valoarea SEEK_CUR, atunci poziionarea se face relativ la poziia curent;

    dac parametrul pos ia valoarea SEEK_END, poziionarea se realizeaz relativ la sfritul fiierului.

    Parametrul offset poate lua i valori negative, reprezentnd deplasamen-tul, calculat n octei.

    n caz de eroare, se returneaz -1, altfel noua poziie n fiier, relativ la nceputul acestuia.

    2.1.5 Primitiva close () Aceast primitiv d posibilitatea ca descriptorul de fiier s fie din nou dispo-nibil pentru utilizare, nchiznd descriptorul. La terminarea unui proces, toate fiierele deschise sunt nchise automat.

    Apelul close () are forma urmtoare:

    #include

    int close (int fd);

    2.1.6 Exemplu

    Pentru a ilustra utilizarea apelurilor de mai sus, ne propunem s scriem un program care simuleaz comanda tac, afind coninntul unui fiier, linie cu linie, de la ultima la prima linie.

  • l-

    V

    Gestiunea fiierelor

    Codul surs al acestui program este dat mai jos:

    / simuleaza comanda 'tac' linie cu linie/

    #include #include #include

    #define NL 10 #define MAXLINE 4096

    /* codul sfirsitului de linie */ /* lungimea maxima a unei linii */

    35

    :e int

    :a

    :i-

    la

    0-

    te

    un

    cu

    main (int argc, char argv[]) {

    char c; char s = (char) malloc (MAXLINE * sizeof (char)); / 1 daca am term~at parcurgerea fisierului */ int gata = O; I pozitia in cadrul fisierului */ int pos = -1; I fisierul de intrare / FILE fi;

    I verificam numarul de parametri dati in linia de comanda /

    if (argc != 2) {

    }

    printf ("Sintaxa: %s \n", argv[O]); return -1;

    I incercam sa deschidem fisierul */ if ((fi = fopen (argv[1], "rt")) == NULL)

    {

    }

    printf ("Eroare: nu am putut deschide fisierul %s\n", argv[1]); return -1;

    I ne pozitionam la finalul fisierului */ fseek (fi, pos, SEEK_END); I cit timp mai putem parcurge fisierul ... I while ( ! gata)

    { / citim cite un caracter / fscanf (fi, "%c", &c); I este sfirsit de linie? */ if ((int) c == NL)

    {

  • 36

    }

    Atelier de programare n reele de calculatoare

    f * citim o linie intreaga si o afisam */ fgets (s, MAXLINE, fi);

    }

    printf ("%s", s); if (s[strlen (s) - 1] != '\n')

    puts ("");

    f * cu o pozitie spre inceputul fisierului */ pos--;

    }

    /* mai putem sa ne pozitionam? */ gata = (fseek (fi, pos, SEEK_END)

    f * af isam si prima linie din f isier */ fseek (fi, pos + 1, SEEK_END); fgets (s, MAXLINE, fi); printf ("%s", s); f * inchidem fisierul */ fclose (fi); free (s); f * terminam programul */ return O;

    -1);

    Paii importani pe care trebuie s-i urmm pentru a executa programul sunt urmtorii:

    editarea codului surs C, folosind unul dintre editoarele de texte puse la dispoziie de sistemul de operare (de la mai simplele pico sau joe,

    pn la complexul emacs). Vom presupune c programul dat mai sus se va numi mytac. c i deci pentru editarea lui vom putea folosi:

    joe mytac.c

    compilarea codului surs, utiliznd compilatorul C. Acesta se apeleaz prin gcc (sau cc pe alte sisteme UNIX) i are ca argument obligato-riu numele programului surs (program C avnd extensia . c, program C++ avnd extensiile . C, . cc ori . cxx sau program Objective-C cu ex-tensia .m). Dac nu se specific o alt opiune, n urma procesului de compilare i de editare de legturi va rezulta un program executabil de-numit a. out. Pentru a obine un fiier executabil cu alt nume vom folosi

    opiunea -o:

    gcc mytac.c -o mytac

    -

    I

    Il

  • ul

    se

    e,

    se

    z o-,m

    ~x-

    de le-)Si

    Gestiunea fiierelor 37

    Dac procesul de compilare i de editare de legturi s-a desfurat fr erori vom obine codul executabil al programului mytac. n cazul apariiei unor erori, va trebuie s trecem din nou la etapa de editare a codului. Uneori, la compilarea unor funcii care vor fi incluse ulterior n cadrul unei biblioteci va trebui generat numai codul obiect, fr a se iniia i procesul de editare de legturi. Pentru aceasta, vom utiliza opiunea -c, rezultnd un fiier obiect cu extensia . o, i nu un program executabil. Dac programele noastre vor necesita funcii de bibliotec al cror cod nu este inclus n cadrul bibliotecilor de sistem standard, atunci va trebui s specificm numele bibliotecii care va fi folosit n momentul editrii de legturi. Acest lucru se realizeaz apelnd la opiunea -1 urmat de numele bibliotecii:

    gcc gaen.c -o~aend -lcrypt

    Putem specifica de asemeni calea ctre fiierele de bibliotec prin op-iunea -L sau calea spre fiierele antet care vor fi utilizate apelnd la opiunea -I. Pentru optimizarea codului se va folosi opiunea -0, iar pentru controlul avertismentelor furnizate de compilator se va utiliza -W. Mai multe amnunte despre compilator n cadrul manualului (man gcc).

    execuia codului executabil produs de compilator se va realiza apelnd codul binar rezultat:

    2.2

    ./mytac

    Pe cele mai multe sisteme, pentru a putea executa un program aflat n directorul curent, va trebui s specificm explicit locul su de stocare, deoarece n mod uzual interpretorul de comenzi va cuta fiierele exe-cutabile numai n directoarele coninute de variabila de mediu PATH.

    Dac au aprut erori de execuie, va trebui s reeditm i apoi s recom-pilm programul. Pentru depanarea avansat a programelor noastre ne putem sluji de utilitarele gdb, strace sau ltrace.

    Prelucrarea atributelor fiierelor n afara posibilitii de a realiza operaii asupra coninutului fiierelor, progra-matorului i se pune la dispoziie o serie de apeluri de sistem pentru a consulta

  • 38 Atelier de programare n reele de calculatoare ~~~~~~~--~~~

    i/sau modifica diverse proprieti Sal~ atribute ale fiierelor. Aceste primitive

    de sistem n mod uzual sunt definite n fiierul antet unistd. h, iar detaliile explicative se pot regsi n seciunea 2 a manualului de sistem (astfel, pentru a afla amnunte privitoare la chmod() vom utiliza man 2 chmod).

    Se pun la dispoziie urmtoarele primitive pentru:

    verificarea permisiunilor de accesare: access (),

    modificarea permisiunilor: chmod (),

    schimbarea proprietarului (utilizator i grup): chown (),

    redenumirea/mutarea: rename (),

    asocierea unei legturi hard: link(),

    asocierea unei legturi soft: symlink(),

    tergerea unei legturi hard (dac numrul de legturi hard asociate unui fiier devine nul, atunci i acesta va fi ters, i-nodul su fiind eliberat): unlink(),

    furnizarea informaiilor despre atribute (tip, permisiuni, numr de legturi hard, UID-ul i GID-ul proprietarului, mrimea, timpul ultimei ac-

    cesri, modificri etc.): stat().

    Apelul stat O va utiliza o structur a crei definiie este urmtoarea (vezi i antetul sys/stat .h):

    struct stat {

    dev_t st_dev; /* device */ ino_t st_ino; /* inod */ mode_t st_mode; I* permisiuni */ nlink_t st_nlink; /* numar de leg. hard *I uid_t st_uid; /* UID proprietar */ gid_t st_gid; /* GID proprietar */ dev_t st_rdev; /* tip device off_t st_size; /* marime (in octeti) */ unsigned long st_blksize; I* lung. blocului pt. I/O */ unsigned long st_blocks; /* numarul de blocuri alocate */ time_t st_atime; /* timpul ultimului acces */ time_t st_mtime; I* timpul ultimei modificari */ time_t st_ctime; /* timpul ultimei schimbari a starii *I I

    };

  • Gestiunea fiierelor 39

    2 2.2.1 Exemplu

    l Un exemplu de utilizare a apelului stat() care ne va permite s furnizm diferite statistici despre un anumit fiier:

    #include #include #include #include #include

    /* afiseaza statistici despre un fisier / int show_file_stat (char filename[]) {

    I structura furnizata de stat() / struct stat st_str;

    li / structura pentru prelucrarea fisierului /etc/passwd / ): struct passwd pw_str; / structura pentru prelucrarea fisierului /etc/group / struct group gr_str; - /* 1 daca este un fisier de tip dispozitiv / c- int isdev = O;

    :zi

    / numele proprietarului / char name[30];

    if (stat (filename, &st_str) == -1) {

    }

    perror ("Eroare la stat"); return 1;

    printf ("Fisier: \"%s\"\t", filename); /* determinam tipul f isierului / if ((st_str.st_mode & S_IFMT) == S_IFDIR)

    printf ("director"); el se

    if ((st_str.st_mode & S_IFMT) {

    }

    printf ("bloc"); isdev = 1;

    S_IFBLK)

    *I i else if ((st_str.st_mode & S_IFMT) == S_IFCHR)

    { printf ("caracter");

  • 40 Atelier de programare n reele de calculatoare

    isdev = 1; }

    el se if ((st_ str. st_mode & S_IFMT) S_IFREG)

    printf ("ordinar"); el se

    if ((st_str.st_mode & S_IFMT) S_IFIFO) printf ("FIFO");

    el se printf ("socket");

    /* daca este dispozitiv ... *I if (isdev)

    { printf

    }

    ("Device: %d, %d", (int) (st_str.st_rdev >> 8) & 0377, (int) st_str.st,rdev & 0377);

    I* dispozitivul pe care este stocat fisierul ... */ printf ("\nStocat pe dispozitivul: %d, %d\n",

    (int) (st_str.st_dev >> 8) & 0377, (int) st_str.st_dev & 0377);

    printf ("I-node: %d; Legaturi: %d; Marime: %ld\n", (int) st_str.st_ino, (int) st_str.st_nlink, (long int) st_str.st_size);

    I* aflam proprietarul */ if (!(pw_str = getpwuid(st_str.st_uid)))

    strcpy (name, ""); el se

    strcpy (name, pw_str->pw_name); printf ("UID proprietar: %d (%s)\n",

    (int) st_str.st_uid, name); /* aflam grupul proprietarului */ if (!(gr_str = getgrgid(st_str.st_gid)))

    strcpy (name, ""); el se

    strcpy (name, gr_str->gr_name); printf ("GID proprietar: %d (%s)\n",

    (int) st_str.st_gid, name);

    I* afisam permisiunile speciale */ printf ("Permisiuni speciale: "); if ((st_str.st_mode & S_ISUID) S_ISUID)

    printf ("SUID "); if ((st_str.st_mode & S_ISGID) S_ISGID)

  • }

    Gestiunea fiierelor

    printf ("SGID "); if ((st_str.st_mode & S_ISVTX) == S_ISVTX)

    printf ("Sticky "); f * afisam permisiunile (in octal) */ printf ("\n>>>Permisiuni: Y.o\n",

    st_str.st_mode & 0777); /* timpii de acces, modificare, schimbare de stare */ printf ("Ultimul acces : Y.s",

    asctime(localtime(&st_str.st_atime))); printf ("Ultima modificare : Y.s",

    asctime(localtime(&st_str.st_mtime))); printf ("Ultima schimbare de stare : Y.s",

    asctime(localtime(&st_str.st_ctime))); return O;

    2.3 Prelucrarea directoarelor

    41

    Pentru prelucrarea directoarelor, vom recurge la funciile puse la dispoziie de fiierul antet dirent . h. Se vor utiliza tipurile DIR (stream director) i dirent

    (structur coninnd intrrile dintr-un director). Structura dirent are urm'"narea definiie:

    struct dirent {

    }

    long d_ino; unsigned short d_reclen; char d_name [NAME_MAX+1];

    f * numar i-nodului */ f * lungime d_name */ /* nume intrare (terminat cu '\0') */

    Astfel, cel mai simplu program care va afia toate intrrile dintr-un anumit director va fi:

    f * afiseaza intrarile dintr-un director */ #include #include #include

    int main(int argc, char *argv[]) {

    DIR *dp; struct dirent *dirp;

    if (argc != 2)

  • 42

    }

    Atelier de programare n reele de calculatoare

    printf ("Sintaxa: %s \n", argv[O]);

    /* incercam sa deschidem directorul */ if ( (dp = opendir (argv[1])) ==NULL)

    printf ("Nu se poate deschide %s\n", argv[1]); /* cit putem citi ceva, afisam intrarea */ while ( (dirp = readdir (dp)) != NULL)

    printf ("%s\n", dirp->d_name); /* inchidem directorul */ closedir (dp);

    Am folosit apelurile opendir(), readdirO i closedir(). Mai sunt puse la dispoziie i funciile rewinddir (), seekdir (), telldir () i scandir ().

    Lsm cititorului plcerea de a vedea care sunt sintaxa i semantica acestora. Pentru operaiile uzuale cu directoare vom putea folosi:

    chdir O schimb directorul curent;

    mkdir () creeaz un director; rmdir O terge un director gol;

    getcwd() furnizeaz directorul curent.

    2.4 Prelucrarea fiierelor de sistem Vom descrie n continuare o serie de modaliti pentru prelucrarea informaiilor

    coninute de unele dintre fiierele de sistem importante.

    2.4.1 Gestiunea conturilor de utilizatori Dup cte am vzut n capitolul 1, /etc/passwd este un fiier vital al sistemu-lui, coninnd informaii despre utilizatorii care au conturi pe acea main. Pentru a putea prelucra informaiile din acest fiier, vom recurge la funciile definite n antetul pwd. h:

    getpwnamO returneaz informaii despre un utilizator dac i cunoatem numele de cont;

    getpwuid () ca mai sus, dar vom furniza UID-ul utilizatorului.

  • e

    r

    Gestiunea fiierelor

    Prototipurile acestora sunt:

    #include #include

    struct passwd *getpwnam (const char *name); struct passwd *getpwuid (uid_t uid);

    Informaiile returnate sunt stocate n structura passwd definit astfel:

    struct passwd { char *pw_name; /* numele utilizatorului */ char *pw_passwd; /* parola */ uid_t pw_uid; I* UID */ gid_t pw_gid; /* GID */ char *pw_gecos; /* numele real */ char *pw_dir; I* directorul 'home' */ char *pw_shell; /* shell-ul utilizat */

    };

    43

    Funcia getpwnam() se poate implementa cu ajutorul funciilor setpwent (), getpwent O i endpwent () n modul urmtor: struct passwd *getpwnarn (const char *name) {

    struct passwd *pw_str;

    /* ne pozitionam la inceputul fisierului /etc/passwd */ setpwent O; /* parcurgem linie cu linie pina cind ... *I while ((pw_str = getpwent ()) !=NULL)

    { if (!strcmp (name, pw_str->pw_narne))

    break; /* ... am gasit */ }

    /* inchidem f isierul */ e endpwent ();

    return (pw_str);

    ll

    }

    2.4.2 Gestiunea grupurilor de utilizatori Similar, pentru a avea acces la informaiile despre grupurile de utilizatori ne vom sluji de funciile getgrnam () i getgrgid O ale cror prototipuri sunt definite n grp. h.

  • 44 Atelier de programare n reele de calculatoare

    Structura ncapsulnd informaiile despre grupurile de utilizatori este:

    struct group { char *gr_name; char *gr_passwd; gid_t gr_gid; char **gr_mem;

    };

    I* numele grupului */ I* parola grupului */ I* GID */ I* membrii grupului */

    2.4.3 Gestiunea sesiunilor de lucru

    Vom descrie n cele ce urmeaz anumite detalii de implementare a comenzilor last (furnizeaz cele mai recente conectri ale utilizatorilor) i who (listeaz sesiunile curente ale utilizatorilor conectai).

    Aceste informaii sunt stocate n dou fiiere: /var /run/utmp folosit de who i /var/log/wtmp utilizat de comanda last. Pentru a accesa prin program aceste informaii, ne vom folosi de structura utmp care este definit n antetul utmp. h astfel:

    struct utmp { short ut_type; pid_t ut_pid;

    };

    char ut_line[UT_LINESIZE]; char ut_user[UT_NAMESIZE]; char ut_host[UT_HOSTSIZE]; long ut_session; struct timeval ut_tv; int32_t ut_addr_v6[4];

    /* tipul de login */ /* PID-ul procesului login */ /* numele terminalului fara "/dev/" */ /* numele utilizatorului */ /* numele hostului de conectare */ /* identificatorul sesiunii */ /* timpul de conectare */ /* adresa IP a hostului */

    Vom utiliza aceast structur pentru a parcurge unul dintre cele dou fiiere de mai sus. Astfel, vom putea implementa funcia de mai jos care simuleaz comportamentul comenzii who (datele nu vor fi afiate la ieirea standard, ci vor fi memorate ntr-un fiier):

    /* genereaza un fisier care va contine informatii despre sesiunile utilizatorilor curenti din sistem */

    int who (char *utmpfilename, char *whofilename) {

    FILE *infp, *outfp; struct utmp wutmp; struct stat tty_st; int wusers = O;

  • r

    :1

    )

    1

    e

    ;i

    Gestiunea fiierelor

    int idle; char *ptr_date, idle_str[6], utty[80];

    /* deschidem f isierul utmp */ if ( (infp = fopen (utmpfilename, 11 r 11 ))

    { NULL)

    return -1; /* eroare */ }

    /* cream fisierul de iesire */ if ((outfp = fopen (whofilename, "w 11 ))

    { NULL)

    fclose (infp); return -2; /* eroare */

    } /* parcurgem intreg fisierul, citind structura utmp */ while (fread (&wutmp, sizeof (wutmp), 1, infp) !=O)

    { if (wutmp.ut_name != NULL && wutmp.ut_type == USER_PROCESS)

    { /* proces utilizator */

    ptr_date = ctime (&wutmp.ut_time); /*luam timpul*/ ptr_date[strlen (ptr_date) - 1] = '\0'; I* determinam timpul de inactivitate

    dupa timpul de accesare a terminalului */ sprintf (utty, 11 /dev/%s 11 , wutmp.ut_line); if (stat (utty, &tty_st))

    idle = time(O) - tty_st.st_atime; el se

    {

    }

    sprintf (utty, 11 /dev/pts/%s 11 , wutmp.ut_line); if (stat (utty, &tty_st))

    idle time (O) - tty_st.st_atime; el se

    id le -1;

    /* timpul de inactivitate (in minute) *I if (idle != -1)

    sprintf ( idle_str, 11 %02d' 11 , ( idle % 3600) I 60) ; el se

    strcpy ( idle_str, 11 - 11 ) ; /* scriem datele in fisier */ fprintf (outfp, 11 %-10s %-12s %-3s %12s %16s\n",

    wutmp.ut_name, wutmp.ut_line, idle_str, ptr_date + 4, wutmp.ut_host !=NULL? wutmp.ut_host : 1111 );

    wusers++;

    45

  • 46

    }

    Atelier de programare n reele de calculatoare

    } }

    fprintf (outfp, "\nTotal %d sesiuni.\n", wusers); /* inchidem f isierele */ fclose (infp); fclose (outfp); return O;

    Ni se pun la dispoziie, de asemenea, funciile getutent O, getutid O i getutline O pentru a accesa mai uor datele din aceste fiiere.

    2. 5 Exerciii i la acest final de capitol propunem cteva exerciii spre rezolvare:

    1. S se conceap un program care s adauge la sfritul unui fiier coninutul unui alt fiier.

    2. S se scrie un program care implementeaz comanda tail.

    3. S se realizeze un program care simuleaz comanda grep.

    4. S se scrie o variant interactiv a comenzii chmod care va afia meniuri pentru schimbarea permisiunilor fiierelor.

    5. S se implementeze ct mai multe opiuni ale comenzii ls.

    6. S se realizeze un program care furnizeaz informaii despre un utilizator care are cont pe maina executnd acest program.

    7. S se scrie un program care mparte utilizatorii din /etc/passwd n fiierele info1, info2, info3, info4, master, colegi, coleg2, coleg3, biro1, biro2, biro3, profs i others, corespunztoare categoriilor de utilizatori care au conturi pe maina local (n acest caz serverul stu-

    denilor de la Facultatea de Informatic, Universitatea "Alexandru Ioan Cuza" din Iai). Fiecare dintre fiierele generate va avea formatul urmtor:

    login name [ nume si prenume real ] uid, gid (nume grup), shell, director home

  • Gestiunea fiierelor 47

    2.6 Rezolvri Vom furniza pentru cteva dintre exerciiile propuse rezolvrile acestora.

    Exerciiul 1. Concatenarea a dou fiiere Pentru primul exerciiu, o variant de rezolvare este urmtoarea - am utilizat primitivele open O, read (), wri te O i close O: i

    #include #include #include #include #include

    int i- main (int argc, char **argv)

    { int n, in, out; char buf [1024],;

    /* nu s-au dat argumentele */ if (argc != 3)

    lri {

    }

    write (2, "Sintaxa: append \n", 33); exit (1);

    or /* deschidem primul fisier pentru citire */ if ((in =open (argv[1], O_RDONLY)) O)

    write (out, buf, n);

  • 48 Atelier de programare n reele de calculatoare

    /* inchidem f isierele */ clase (out); clase (in); exit (O);

    }

    De remarcat utilizarea funciei perror () care va afia un mesaj de eroare corespunztor erorii survenite la apelul unei anumite primitive. Mesajul de eroare va fi trimis la ieirea standard n caz de eroare ( stderr). Despre tratarea erorilor vom discuta n amnunt n capitolul 3.

    Exerciiul 5. Implementarea opiunilor comenzii ls

    Urmtorul program implementeaz opiunile '-1', '-a' i '-t' ale comenzii ls:

    /* implementeaza "ls" cu optiunile -1, -a, -t */ #include #include #include #include #include #include #include #include #include #include

    /* numarul maxim de intrari intr-un director */ #define MAXFILES 500

    /* statistici despre fisier */ struct stat Infos; /* lunile anului */ char *Months[] =

    {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Ju;J..", "Aug", "Sep", "Oct", "Nov", "Dec"};

    /* structura pentru sortarea in functie de data */ struct Files {

    time_t f_mtime; struct dirent *file;

    };

  • Gestiunea fiierelor

    /* afiseaza permisiunile f isierelor */ void Rights () {

    }

    int R, i;

    if (Infos.st_mode & S_IFDIR) printf ("d");

    el se printf ("-");

    for (i = O; i < 3; i++) {

    }

    int P = 1, j; for (j =O; j < (2 - i); j++) /* calculeaza s-(2-i) */

    p *= 8; R = Ox04 * P; if (Infos.st_mode & R)

    printf ("r"); el se

    printf ("- "); R /= 2; if (Infos. s.t_mode & R)

    printf ("w"); el se

    printf ( "-") ; R /= 2; if (Infos. st_mode & R)

    printf ("x"); el se

    printf ("- ");

    /* afiseaza proprietarul f isierului */ void OwnerName () {

    struct passwd *nume;

    if ((nume = getpwuid (Infos.st_uid)) == NULL) {

    }

    printf ("Could not get user name with uid: %d\n", Infos.st_uid);

    exit (2);

    printf (" %-8s", nume->pw_name);

    49

  • 50 Atelier de programare n reele de calculatoare

    }

    /* af iseaza grupul proprietarului */ void OwnerGroup () {

    }

    struct group *grup;

    if ((grup= getgrgid (Infos.st_gid)) ==NULL) {

    }

    printf ("Could not get group name with gid: %d\n", Infos. st_gid) ;

    exit (3);

    printf (" %-10s", grup->gr_name);

    /* afiseaza data fisierului */ void Date O {

    }

    struct tm *timp;

    timp= localtime (&Infos.st_mtime); printf (" %s %2d", Months[timp->tm_mon], timp->tm_mday); printf (" %2d:%2d", timp->tm_hour, timp->tm_min);

    /* programul principal */ int main (int argc, char *argv[]) {

    struct dirent **namelist; struct Files *Fisiere[MAXFILES]; intn,i,j; char FileName[256]; int CurrentDir = 1, List char Dir[50] = ".";

    O, Time O, All O;

    /* vedem ce optiuni au fost date */ if (argc > 1)

    { for (i = 1; i < argc; i++)

    { if (strcmp (argv[i], "--help") O)

    {

  • i I

    }

    }

    Gestiunea fiierelor

    printf ("Usage: list [-1 -a -t] [dir]\n"); printf (" -1 for long list\n"); printf (" -a to show all files\n"); printf (" -t for time sorting\n"); return O;

    } /* este optiune ... */ if (argv[i] [O] == '-')

    {

    }

    for (j = 1; j < strlen (argv[i]); j++) {

    }

    switch (argv[i] [j]) {

    }

    case 'l': List = 1; break;

    case 'a': All = 1; break;

    case 't': Time= 1; break;

    default: printf ("Unknown option!\n"); return 2;

    continue;

    if (strncmp (argv[argc - 1], {

    li li - '

    1) != O)

    }

    strcpy (Dir, argv[argc - 1]); CurrentDir = O;

    /* scaneaza intrarile din director */ n = scandir (Dir, &namelist, O, alphasort);

    for (i = O; i < n; i++) {

    if ((Fisiere[i] =

    {

    }

    (struct Files *) malloc (sizeof (struct Files))} == NULL)

    printf ("Could not allocate memory\n"); return 1;

    if (CurrentDir == 1) strcpy (FileName, namelist[i]->d_name);

    51

  • 52

    }

    Atelier de programare n reele de calculatoare

    el se sprintf (FileName, "%s%s", Dir, namelist[i]->d_name);

    if (stat (FileName, &Infos) == -1) {

    }

    perror ("Error on stat\n"); return 3;

    Fisiere[i]->f_mtime = Infos.st_mtime; Fisiere[i]->file = namelist[i];

    if (n < O) {

    }

    printf ("Error reading directory ... "); return 1;

    if (Time == 1) {

    }

    inti,j; struct Files *Aux;

    for (i =O; i < (n - 1); i++) for (j = i + 1; j < n; j ++)

    if (Fisiere[i]->f_mtime > Fisiere[j]->f_mtime) {

    }

    Aux = Fisiere[i]; Fisiere[i] = Fisiere[j]; Fisiere[j] = Aux;

    for (i O; i < n; i++) {

    if (All == O) { '

    }

    I* incepe cu'.', este fisier invizibil*/ if (Fisiere [i] ->file->d_name [O] == ". ")

    continue;

    if (CurrentDir == 1) strcpy (FileName, Fisiere[i]->file->d_name);

    el se sprintf (FileName, "%s%s", Dir, Fisiere[i]->file->d_name);

    if (List == 1) {

    if (stat (FileName, &Infos) == -1) {

    perror ("Error on stat\n");

  • }

    }

    }

    return 2; }

    Rights ();

    Gestiunea fiierelor

    printf (" %3d", Infos.st_nlink); OwnerName O ; OwnerGroup (); printf (" %7d", Infos.st_size); Date O; printf (" %s\n", Fisiere[i]->file->d_name);

    printf ("%s\n", Fisiere[i]->file->d_name);

    printf ("\n"); return O;

    53

  • Capitolul 3

    Procese Acest capitol prezint noiunile fundamentale referitoare la procese i modalitile de gestiune a proceselor.

    3.1 Noiuni fundamentale

    Un program (cod binar) devine proces atunci cnd este apelat pentru rulare. Procesul, aadar, reprezint imaginea dinamic a unui program aflat n execu-ie. Pentru un program pot exista la un moment dat mai multe procese asociate pe care le vom numi instane ale acelui program.

    n UNIX (i n particular n Linux) fiecare proces va avea asociate mai multe atribute, cele mai importante fiind:

    identificatorul de proces (PID) este un ntreg mai mare dect zero; la ini-ializare, nucleul sistemului de operare va crea un pseudo-proces cu PID nul care va genera procesul init al crui PID va fi 1. Fiecare proces va avea un printe care l-a creat, iar procesul init va putea fi considerat

    strmoul tuturor proceselor existente la un moment dat n sistem. Va-loarea PID-ului unui proces va fi stocat de tipul pid_ t definit n antetul sys/types. h.

    identificatorul procesului printe (PPID) reprezint identificatorul pro-cesului care a creat procesul curent; dac un proces i-a pierdut printele, n mod automat l va avea drept printe pe procesul init (deci PPID-ul

    su va fi 1).

    identificatorul grupului de procese -- fiecare proces poate aparine unui grup ele procese; dac PID-ul procesului este egal cu identificatorul grupu-lui de procese, atunci acel proces este lider al acelui grup.

    terminalul de control asociat procesului; este primul terminal deschis de lide-rul grupului ele procese (n cazul proceselor utilizator este terminalul la care s-a conectat acel utilizator). Accesul la terminalul asociat se va face prin intermediul fiierului I dev /tty1 . Dac un proces interacioneaz cu utilizatorul prin intermediul terminalului, atunci acel proces ruleaz n

    1 La ultimele versiuni de nuclee, n directorul I dev exist i alte informaii suplimentare.

  • c-

    e

    u

    i-)

    )-

    e,

    11

    ui Ll-

    e-

    l a ce

    ~u

    n

    :e.

    Procese 55

    prim-plan (foreground). Un proces care nu are asociat un terminal (deci care nu interacioneaz direct cu utilizatorul) se numete proces de fundal (background) sau daemon.

    UID-ul procesului este UID-ul utilizatorului care execut acel proces. GID-ul procesului este GID-ul grupului din care face parte utilizatorul care

    execut acel proces.

    UID-ul efectiv (EUID) coincide de cele mai multe ori cu UID-ul, servind la determinarea dreptului de acces la resursele unui anumit proces. n unele cazuri anumite procese2 trebuie s ruleze sub auspicii de super-utilizator - root - i atunci EUID-ul acelor procese va fi egal cu UID-ul utilizatorului root, adic O. Ca exemplu, cazul n care un utilizator

    obinuit dorete s-i modifice informaiile de finger prin intermediul comenzii chfn care va opera asupra fiierului sistem /etc/passwd.

    GID-ul efectiv (EGID) ca mai sus, pentru grupul din care face parte uti-lizatorul care va executa procesul.

    starea n care se afl un proces: dup creare, dac s-a reuit alocarea de memorie pentru proces, procesul este n starea ready (pregtit pentru execuie) fiind deja introdus n coada de ateptare a planificatorului de procese. Atunci cnd va fi planificat pentru execuie, n funcie de o anumit prioritate, va trece n starea run (de rulare) - rularea poate fi la nivel de nucleu sau la nivel de utilizator. Atunci cnd procesul trebuie s

    atepte un eveniment (e.g. introducerea datelor de la terminalul asociat) va trece n starea wait (de ateptare). La terminarea normal (la apelul primitivei exi t ()) procesul" va trece n starea finished. Dac un proces nu a fost complet scos din tabela de alocare a proceselor se numete zombie. Procesele zombie, dei apar ca existente pe main, nu dispun de nici o resurs a sistemului, suprasaturnd inutil planificatorul de procese.

    valoarea nice este o component a prioritii totale pe care o va avea un proces, putnd fi ajustat cu ajutorul comenzii nice. Prioritatea unui proces n Linux poate lua valori de la -20 la 20. Valorile de la -1 la -20 sunt asociate proceselqr sistemului, ele neputnd fi acordate proceselor unui utilizator obinuit. Cea mai mare prioritate a unui proces utilizator este O. mulimea descriptorilor de fiier - fiecare proces la momentul execuiei

    are la dispoziie descriptorii:

    2 Programele corespunztoare vor avea setat permisiunea Set-UID i, eventual, Set-GID.

  • 56 Atelier de programare n reele de calculatoare

    O descriptorul standard de intrare n mod obinuit asociat terminalului

    (corespunztor lui FILE* stdin); 1 descriptorul standard de ieire

    n mod obinuit asociat terminalului (corespunztor lui FILE* stdout);

    2 descriptorul standard de eroare n mod obinuit asociat terminalului

    (corespunztor lui FILE* stderr).

    Aceti descriptori pot fi folosii fr a trebui s fie deschii n prealabil. De asemenea, se pot redirecta prin intermediul operatorilor , sau I dup cum am vzut n capitolul 1.

    linia de comand conine argumentele pasate procesului; pot fi folosite prin intermediul primelor dou argumente ale funciei main (): main (int argc, char *argv[])

    mediul reprezentnd lista variabilelor de mediu puse la dispoziie de sistemul de operare (e.g. variabilele PATH, HOME sau TERM). Comanda set poate fi

    utilizat pentru afiarea variabilelor de mediu disponibile. Din program, putem avea acces la mediu fie prin al treilea argument al funciei main ():

    main(int argc, char *argv[], char *envp[]) fie prin intermediul funciilor getenv () i setenv ().

    Execuia unui program utilizator se poate face n dou moduri: n modul utilizator i n modul nucleu (sau sistem) asociate modului de funcionare al procesului. n modul utilizator procesele au acces numai la propria zon de cod, date i stiv utilizator, iar n modul nucleu un proces execut instruciuni privilegiate i poate avea acces la structurile de date ale nucleului. Nucleul, n termeni de proces, nu este un proces separat care se execut n paralel cu procesul utilizator, ci devine parte integrant a procesului utilizator.

    3.2 Comenzi pentru procese

    Urmtoarele comenzi UNIX pot fi folosite pentru manipularea proceselor:

    Comanda ps afieaz informaii despre procese. Pentru a afia toate proce-sele existente pe un anumit sistem putem da ps aux, iar pentru a vizualiza lista

  • l e

    1

    .,

    J

    ~-

    a

    Procese 57

    propriilor noastre procese (inclusiv cele din fundal) vom da ps ux. Comanda pune la dispoziie foarte multe alte opiuni, pentru detalii consultai manualul.

    Iat un exemplu de rulare a comenzii ps:

    (infoiasi):/$ ps ux USER PID %CPU %MEM vsz RSS TTY STAT START TIME COMMAND lrc 1524 o.o 0.6 2268 1284 tty3 s 15:30 0:00 -bash lrc 1545 0.5 1.2 6356 2456 tty3 T 15:30 0:00 pine lrc 1546 0.1 0.6 2268 1292 tty5 s 15:30 0:00 -bash lrc 1570 o.o 0.2 1436 576 tty5 s 15:31 0:00 script lrc 1571 o.o 0.3 1440 612 tty5 s 15:31 0:00 script lrc 1572 0.3 0.6 2336 1324 pts/4 s 15:31 0:00 bash -i lrc 1583 o.o 0.3 2556 756 pts/4 R 15:32 0:00 ps ux

    O alt comand util este top care va afia un clasament al proceselor executate pe sistem la momente periodice.

    Un proces poate fi rulat n fundal punnd la sfritul comenzii semnul '&', dup cum se poate vedea n exemplul de mai jos (intrarea standard este redi-rectat ctre fiierul cu numele listing, iar ieirea de eroare spre dispozitivul special /dev/null, deci mesajele de eroare nu vor mai fi afiate): (infoiasi) :/$find I -name *.c -print >listing 2>/dev/null &

    Suspendnd un proces (prin combinaia de taste CTRL+Z), l vom readuce ruleze n prim-plan cu comanda fg sau n fundal cu bg. Procesele rulnd n fun-dal se mai numesc i procese job, listarea tuturor acestor procese efectundu-se cu comanda jobs:

    (infoiasi) :/$ jobs [1] - Stopped [2]+ Stopped (infoiasi):/$ bg %1 [1]- pine &

    [1] + Stopped

    pine top

    pine

    De menionat faptul c un proces lansat n fundal se va termina odat cu terminarea sesiunii de lucru. Pentru ca acest proces s ruleze n fundal i dup logout, ne vom folosi de comanda nohup (vezi i capitolul 4).

  • 58 Atelier de programare n reele de calculatoare

    3.3 Procesele din perspectiva programatorului

    Programatorului i se pun la dispoziie o serie de pri~itive importante:

    fork() va crea un proces nou; procesul care va apela fork() se va numi proces printe, iar noul proces creat se va numi proces copil.

    #include

    pid_t fork(void);

    Dei apelat o singur dat 1 J2ri_mitiy~fork() va returna de do:gl!,_ori: o valoare pozitiv n procesul printe, desemnnd PID-ul procesUii.iI copil tocmai creat i o valoare nul n procesul copil. n caz de eec, fork () returneaz -1. Procesul printe i procesul copil devin dou procese care se vor executa n mod concurent, independent unul de cellalt, ncepnd cu prima instruciune care urmeaz lui fork (). Primitiva fork() reprezint singura modalitate n UNIX de a crea pro-cese noi n cadrul unui program.

    Un proces va putea crea mai muli copii. Procesul copil va fi o copie a procesului printe, dar cele dou procese nu vor partaja memoria zonelor de date (statice sau dinamice) ori stiva. Un exemplu simplu:

    #include #include #include

    int main (void) {

    }

    pid_t pid; /* creaza un proces nou */ pid = fork O; printf ("Sunt procesul cu PID-ul %d,\n"

    " iar apelul forkO a returnat valoarea %d\n", getpid O , pid) ;

    return O;

  • Procese

    Un posibil rezultat afiat ar putea fi:

    Sunt procesul cu PID-ul 347, iar apelul fork() a returnat valoarea 349

    Sunt procesul cu PID-ul 349, iar apelul fork() a returnat valoarea O

    59

    Pentru a vedea cum sunt alocate pentru rulare n manier concurent procesele, vom scrie programul de mai jos care realizeaz suma primelor N numere naturale:

    #include #include

    #define MAXNUM 20000

    long sum;

    int main o {

    int i;

    sum = O;

    if (fork () < O) {

    /* suma numerelor */

    I* iterator */

    perror ("Eroare la fork() "); return (1);

    }

    } for (i = O; i

  • 60 Atelier de programare n reele de calculatoare

    wai t O va putea fi folosit, n procesul printe, pentru a atepta terminarea execuiei unui proces fiu. Primitiva wai.t () se va bloca pn la terminarea unuia dintre copiii procesului. Atunci cnd un1:11 dintre copii se termin, wai t () va returna PID-ul procesului copil care s.:a terminat, plus starea de terminare a acestuia. n caz de eroare, se va returna valoarea -1. Prototipul primitivei wai t () este:

    #include #include

    pid_t wait(int status)

    Aa cum am vzut mai sus, un proces care s-a terminat i pentru care procesul printe nu a executat wai t () se numete zombie. Aadar, pentru a nu crea procese zombie, trebuie s ateptm terminarea unui proces copil prin utilizarea lui wai t O.

    wai tpid () este un apel nrudit cu wai t O, dar mai flexibil:

    pid_t waitpid(pid_t pid, int status, int options);

    Se permite ateptarea unui proces copil sau aparinnd unui grup de pro-cese (pentru amnunte consultai man waitpid). De asemenea, waitpid() poate fi apelat fr a se bloca. Apelurile wai t O i wai tpid O pot ajuta la sincronizarea execuiei mai l multor procese concurente. \

    I Pentru a afla o serie de informaii despre un proces vom putea recurge la primitivele: - getpid() furnizeaz PID-ul procesului curent; - getppidO furnizeaz PID-ul procesului printe; - getpgrp O furnizeaz identificatorului grupului de procese al pro-

    cesului curent; o variant mai general este getpgid(); - setpgrp O seteaz identificatorul grupului de procese al procesului

    curent; o variant mai general este setpgid (); - getuid O furnizeaz UID-ul utilizatorului real care a lansat procesul; - getgid() returneaz GID-ul grupului utilizatorului care a lansat

    procesul;

    I I I I

  • Procese 61

    geteuid() returneaz UID-ul utilizatorului efectiv care a lansat procesul; getegid() returneaz GID-ul grupului utilizatorului efectiv care a lansat procesul; setuid O seteaz UID-ul utilizatorului efectiv al procesului curent; setgid O seteaz GID-ul grupului utilizatorului efectiv al procesu-lui curent.

    Tabela 3.1: Apelurile execXX()

    Primitiv j Argumente I Cale I Environment (mediu) execl O list director curent automat execv() vector director curent automat

    execlp () list PATH automat execvp() vector PATH automat execle() list director curent precizat execve() vector director curent precizat

    Apelurile execXX () ajut la invocarea unui program (cod executabil sau script) de ctre un proces. Procesul va fi nlocuit complet de ctre codul ) unui alt program (e.g. o comand UNIX).

    I '

    Astfel, avem posibilitatea de a executa n cadrul aplicaiilor noastre alte programe. Desigur, apelurile execXX() vor aprea n cadrul procesu-lui copil.

    Aadar, nucleul ncarc n zona de memorie noul program i procesul este continuat cu acest program, cruia i sunt transmise argumentele. n cadrul procesului nu se schimb dect programul, restul rmne nemo-dificat. La ncheierea execuiei programului se apeleaz primitiva sistem exi t () , care cauzeaz terminarea procesului fiu i ieirea din starea de ateptare a procesului printe. Se pun la dispoziie 6 primitive, prezentate n tabelul 3.1, -ale cror pro-totipuri sunt (pentru mai multe amnunte consultai manualul):

    #include

    int execl (const char *file, I* numele fisierului de executat */

    const char *arg,

  • 62 Atelier de programare n reele de calculatoare

    I* argumentele (se termina cu NULL) */ ... ) ;

    int execv (const char *file, I* numele f isierului de executat */

    char *const argv[] /* vectorul argumentelor */

    ) ; /* executie bazata pe valorile variabilei PATH */ int execlp (const char *file,

    I* numele f isierului de executat */ const char *arg, /* argumentele (se termina cu NULL) */ ... ) ;

    int execvp (const char *file, /* numele fisierului de executat */ char *const argv[] /* vectorul argumentelor */

    ) ; /* executie bazata pe mediul dat prin program */ int execle (const char *file,

    /* numele f isierului de executat */ const char *arg , I* argumentele (se termina cu NULL) */ ... '

    char *const envp[] I* variabilele de mediu VAR=valoare */

    ) ; int execve (const char *file,

    I* numele fisierului de executat */ char *const argv[], I* vectorul argumentelor */ char *const envp[] I* variabilele de mediu VAR=valoare */

    ) ;

    3.4 Exemplu

    Cele descrise mai sus ne vor ajuta s realizm urmtorul program care va executa comanda ls -a -1:

    #include #include #include

  • Procese

    int main () {

    }

    /* PID-ul procesului copil */ pid_t pid; /* starea de terminare a procesului copil */ int status;

    printf ("Vom executa comanda ... \n");

    if ((pid = fork ()) < O) {

    }

    perror ("fork() "); exit (1);

    else if (pid) {

    /* parinte */

    } el se

    {

    }

    if (wait (&status) < O) {

    perror ("wait()"); }

    printf ("Comanda a fost executata.\n"); exit (O);

    /* vom folosi execlp() */ execlp ("ls",

    /* comanda de executat (se va cauta in directoarele din PATH) */

    "ls", /* argv[O] */ 11-a", /* argv[1] */

    "-1", I* argv[2] */ NULL);

    /* daca ajungem aici inseamna ca nu s-a putut executa */ printf ("Eroare de executie!\n"); exit (1);

    63

    Putem, n locul lui execlp O, s folosim execl O dnd calea complet pentru a putea fi executat comanda ls:

    execl ( 11 /bin/ls", "ls", "-a", 11 -1 11 , NULL);

  • 64 Atelier de programare n reele de calculatoare

    3.5 Exerciii 1. Explicai efectul urmtoarei secvene de cod:

    int i; for ( i = O; i

  • > .,

    e

    e

    l-

    >r

    Procese

    int forkn(void (* tab_funct[])(void), int n, pid_t *tab_pid[]) {

    }

    static pid_t pid; int i, num_children;

    num_children = O; /* initial nici un proces nou */ for (i = O ; i < n i++)

    {

    }

    switch (pid = fork()) {

    }

    case O : (*tab_funct[i])(i); /*copilul executa functia */ exit (O);

    default: if ((tab_pid[i] = pid) > O) num_children++; /* retinem PID-ul copilului */

    return (num_children);

    65

    Funcia returneaz numrul de procese copil care au putut fi create, iar n tabloul tab_pid [] sunt stocate PID-urile proceselor lansate.

    n Exerciiul 3. Implementarea primitivei execvp() l.

    a

    e

    d

    O variant de implementare a primitivei execvp() este cea de mai jos: #include #include #include

    #define MAXPATH 50 /* lungimea maxima a variabilei PATH */ #define MAXARGS 20 /* numarul maxim de argumente */

    extern char **environ; /* mediul */

    void my_execvp (char *file, char *arg[]) {

    char *colon, *pathseq, path[MAXPATH], *newargv[MAXARGS]; char *getenv (), *strchr (); int i, len;

    if (strchr (file, '/') !=NULL I I (pathseq = getenv ( 11 PATH 11 )) == NULL)

    pathseq = 11 : 11 ; for (; (colon= strchr (pathseq, ':')) !=NULL;

  • 66

    {

    } }

    Atelier de programare n reele de calculatoare

    pathseq = colon + 1)

    I* se parcurg directoarele din PATH (desp,artite de ': ') */ len = colon - pathseq; strncpy (path, pathseq, len); path[len] = '\0'; if (len > O)

    strcat (path, "/"); strcat (path, file); I* executam comanda */ execv (path, arg); I* daca esueaza, inseamna ca nu se gaseste in acel director */

    int main () {

    }

    /* testam functia de mai sus executind "ls I -1" */ char *arg[]= { "ls", "/", "-1", NULL}; my_execvp (arg[O], arg);

    Exerciiul 7. Implementarea comenzii ps

    Pentru a implementa comanda ps va trebui s parcurgem intrrile directorului /proc (pentru mai multe amnunte consultai man proc).

    I* Implementarea comenzii "ps" utilizand /proc/pid/stat */ #include #include #include #include #include #include #include #include

    void syserr (char *msg) /* functie de af isare a erorilor */ {

    extern int errno, sys_nerr;

    fprintf (stderr, "ERROR : %s ( %d", msg, errno); if ((errno > O) && (errno < sys_nerr))

    fprintf (stderr, "; %s)\n", sys_errlist[errno]);

  • ll

    I I I

    }

    el se fprintf (stderr, ")\n");

    exit (1);

    Procese

    int main O {

    I* programul principal */

    DIR *dp, *dpp; FILE *from; struct dirent *dirp; char *pid, buf[100]; int fd, i, c, t, k; pid_t ourretval = -1; char dr[80];

    printf ("PID\tCMD\t\tSTATE\tPPID\n"); /* incercam sa parcurgem /proc */ if ((dp = opendir ("/proc")) ==NULL)

    { syserr ("proc");

    } chdir ("/proc"); while ((dirp = readdir (dp)) != NULL)

    { if (dirp->d_name[O] != '.')

    { if ((fd = open (dirp->d_name, O_RDONLY)) != -1)

    { ourretval = (pid_t) atoi (dirp->d_name); /* daca este o intrare numerica,

    atunci e un director asociat unui proces */ if (ourretval != O)

    { strcpy (dr, "/proc/") ; strcat (dr, dirp->d_name); if (chdir (dr) < O)

    syserr ("chdir 1");

    /* deschidem /proc/PID/stat */ if ( (from = fopen ("stat" "r"))

    { syserr ("stat");

    }

    NULL)

    /* citim informatiile despre procesul respectiv */

    67

  • 68

    }

    Atelier de programare n reele de calculatoare

    } }

    } } /* while */

    closedir (dp); return O;

    /* si le afisam */ for (i = 1; i

  • ~) ' ie

    ia la .e, : () \

    !

    Capitolul 4

    Semnale Acest capitol prezint noiunile fundamentale referitoare la semnale i modalitile de tratare a semnalelor. De asemenea, se prezint mecanismul de ataare de alarme la procesele utilizatorilor prin intermediul primitivei alarm().

    4.1 Prezentare general Un semnal este un scurt mesaj transmis unui proces, la apariia unui anumit eveniment (excepie). Semnalele pot fi ignorate sau redefinite de un anumit proces, exceptnd un singur semnal. Un semnal nu furnizeaz informaii supli-mentare despre acel eveniment, iar destinatarul unui semnal nu cunoate nici mcar care a fost expeditorul acestuia.

    Cauzele apariiei unui semnal pot fi grupate astfel:

    evenimente generate de hardware ( e.g. execuia unei instruciuni ilegale de ctre procesor,