Simplitate – sistemele Unix originale erau de mici dimensiuni şi programatorii încercau sa profite la maxim de toate resursele disponibile.

Capitolul 1

INTRODUCERE
1.1. LINUX – scurt istoric
Ca şi dată de apariţie Linux este un sistem de operare relativ recent dar care are ca şi sursă de inspiraţie sistemul Unix. Acesta a fost iniţial dezvoltat în anii ‘70 de laboratoarele Bell din cadrul corporaţiei AT&T, fiind proiectat ca sistem de operare pentru calculatoarele PDP de la Digital Equipament. În timp el a devenit popular datorită caracteristicilor sale – sistem multiutilizator şi multitasking, acoperind o gamă variată de hardware – de la staţii de lucru până la servere cu mai multe procesoare sau supercalculatoare. Pornind de la sistemul Unix original s-au dezvoltat mai multe clone ale acestuia. Unul dintre cele mai cunoscute este Solaris care este dezvoltat de către Sun şi are ca ţintă sistemele bazate pe procesoare Sparc şi Intel. Alte implementări bazate pe Unix sunt BSD, SCO şi Linux. Datorită acestei multitudini de sisteme de operare bazate pe Unix au apărut probleme legate de compatibiliate a aplicaţiilor, acestea fiind reglementate prin impunerea unui standard POSIX, acesta fiind dezvoltat şi menţinut de IEEE. Linux este bazat pe un kernel de tip Unix, prima versiune a acestuia fiind realizată de Linus Torvalds la Universitatea din Helsinki fiind dezvoltat în continuare cu ajutorul a unei largi comunităţi programatori, bazată pe curentul open source. Sursa de inspiraţie, ce a stat la baza sistemului de operare Linux, a fost Minix – un sistem de tip Unix dezvoltat de Andy Tanembaum.

Conectivitate – pentru a permite realizarea de sarcini complexe utilizând mai multe comenzi legate împreună de utilizator, astfel ieşirea unui program devine intrare pentru un alt program. Flexibilitate – multe programe vor fi interconectate prin pipe-uri, deci nu se poate şti în ce context vor fi utilizate aplicaţiile scrise, astfel că trebuie evitată impunerea de limitări arbitrare. Dintre conceptele noi introduse de familia de sisteme de operare Unix(Linux) putem menţiona: Multitasking – permite calculatorului să desfăşoare mai multe sarcini de lucru simultan, de exemplu poate tipări un document în timp ce utilizatorul editează un alt fişier. Multiutilizatori – permite accesul mai multor utilizatori la calculator. Sistemul de operare nu poate satisface simultan cererile de tiparire a mai multor utilizatori, dar introduce un mecanism de priorităţi care să menţină totul în ordine. Tipic un sistem de tip Unix are o organizare pe trei nivele:

Nucleu (kernel) shell aplicaţii

1.2. Conceptul Unix&Linux
Unix îşi are rădăcinile în perioada de început a a anilor 70, când laboratoarele Bell se retrag din proiectul Multics, un grup de la laboratoarele Bell, printre care Ken Thompson şi Dennis Ritchie, încearcă să ofere o alternativă mai economică şi pun bazele noului sistem de operare, sistem de operare ce iniţial trebuia să fie de mici dimensiuni şi flexibil. Deşi ulterior Unix s-a extins şi a devenit un sistem monolitic şi cu aplicaţii de mari dimensiuni totuşi filozofia iniţială a fost păstrată. Caracteristicile de bază ale acestei filozofii ar fi: Nuclelul – este baza sistemului de operare. Utilizatorul, prin intermediul nucleului, poate controla diverse componente ale calculatorului. Interpretorul de comenzi (shell) – este un intermediar între utilizator şi sistemul de operare.

1.3. Distribuţii Linux

1

2

Sistemul de operare Linux este reprezentat de fapt de kernelul său, oricine putând să compileze sursele acestuia şi să-l instaleze împreună cu diverse programe distribuite liber obţinând astfel propria sa distribuţie Linux. Linux îşi datorează existenţa eforturilor conjugate ale unui număr mare de persoane, deoarece un sistem de operare ca să poată fi utilizat la dezvoltarea de aplicaţii necesită pe lângă nucleu (kernel) şi diverse aplicaţii şi servicii sistem. Comunitatea Linux acceptă conceptul de software liber, adică ne supus unor restricţii, subiect al unei GNU General Public License. Chiar dacă obţinerea acestui tip de software implică anumite costuri acesta poate fi utilizat în mod liber şi de obicei este distribuit sub formă de cod sursă. Iniţiatorul mişcării Free Software Foundation a fost Richard Stallman, autor al GNU Emacs. Stallman a fost un pionier al conceptului de software liber şi a început proiectul GNU în încercarea de a crea un sistem de operare şi un mediu de dezvoltare compatibile cu UNIX. Aplicaţiile distribuite în cadrul proiectului GNU sub licenţă publică includ: • Gcc Compilator C • G++ Compilator C++ • Gdb Utilitar pentru depanare • GNU make O versiune de UNIX make • Bash Interpretor de comenzi • GNU Emacs Editor de text Acest concept de soft liber este respectat şi în cazul distribuţiilor comerciale dintre care cele mai importante ar fi: RedHat – http://www.redhat.com Mandrake – http://www.mandrake.com Debian Project – http://www.debian.org Suse – http://www.suse.com

password: În cazul în care s-a făcut o greşeală în introducerea datelor de identificare sau dacă numele utilizatorului nu este recunoscut sistemul îi refuză acestuia accesul. Atenţie! Linux este un sistem de operare case sensitive, adică literele mari sunt tratate diferit faţă de litarele mici. Dacă procesul de autentificare s-a încheiat cu succes atunci pentru utilizatorul respectiv sistemul de operare va executa un interpretor de comenzi numit shell. Orice program rulat de către acesta poartă numele de proces. Interpretorul de comenzi este responsabil cu afişarea unui prompt pentru linia de comandă. La această linie de comandă se pot introduce comenzi împreună cu parametrii acestora iar shell-ul le va executa. La încheierea sesiunii de lucru iesirea din sistem se va face prin execuţia comenzii logout. Sistemul de operare face deosebirea între cel puţin două categorii de utilizatori: • root – este utilizatorul implicit pentru administratorul sistemului. El este cel care va putea putea utiliza toate uneltele de administrare. • utilizatorul obişnuit – implicit nu are drepturi de administrare.

O caracteristică importantă – Linux este un sistem bine documentat, de referinţă este în acest caz comanda man care este un important punct de plecare în studierea sistemului de operare Linux. De studiat! man man.

Una dintre opţiunile deosebit de importatnte ale comenzii man este -k aceasta realizează căutarea într-o bază de date după comenzi sau descrieri ale acestora care conţin cuvântul dat ca şi argument. De studiat! man apropos

1.4. Primul contact
Sistemul de operare Linux fiind un sistem multi-utilizator prima acţiune intreprinsă de acesta este identificarea utilizatorului cu care intră în contact printr-o invitaţie: login: La aceasta trebuie să se răspundă cu un nume de utilizator cunoscut sistemului.În cazul în care pentru utilizatorul respectiv a fost prevăzută o parolă procesul de identificare va continua prin solicitarea acesteia:
3

4

Capitolul 2

SISTEMUL DE FIŞIERE
1.1. STRUCTURA DE FIŞIERE
Una dintre cele mai importante caracteristici Linux este modul în care tratează fişierele. În Linux totul este un fişier(aproape totul), programele utilizează porturile seriale, imprimantele şi alte dispozitive în acelaşi mod în care utilizeză fişierele de pe discuri. Sistemul de operare Linux utilizează trei tipuri de fişiere: • Fişiere obişnuite – sunt doar colecţii de octeţi asupra cărora sistemul nu impune nici o structură particulară. Acestea sunt utilizate pentru fişierele text, fişierele de date pentru programe şi pentru fişierele executabile ale programelor. • Cataloage – un catalog este un mecanism care permite fişierelor să fie grupate împreună, reprezintă un container în care pot fi plasate alte cataloage şi fişiere. De fapt catalogul conţine doar numele fişierelor şi informaţii despre locaţia acestora. Deoarece un catalog poate cobţine subcataloage avem de-a face cu o structură ierarhică. • Dispozitive – această categorie este alcătuită din anumite tipuri de fişiere, cum ar fi cele asociate comunicaţiei între procese sau cele asociate comunicaţei cu diverse dispozitive hardware ca terminale, CD-ROM, modem, etc. Toate aceste fişiere sunt organizate într-o structură ierahică arborescentă, având ca şi vârf un singur catalog care poartă numel de “rădăcină”, el fiind repezentat prin “/”. Acest catalog “rădăcină” conţine o serie de alte cataloage ce au destinaţii speciale. Aceste destinaţii sunt doar convenţii şi nu sunt obligatorii. Semnificaţia acestor cataloage: /bin /dev /etc /home /lib executabilele comenzilor fişierele speciale fişierele de configurare cataloagele utilizatoriloe bibliotecile standard
5 6

/mnt /sbin /tmp /usr /var

comezi de administrare fişiere temporare date disponibile utilizatorilor fişiere mari în cazuri speciale

/

bin dev

etc

home

lib

mnt

sbin

tmp

usr var

X11

adm

bin

lib

local man

src

bin

lib

src

linux

Este posibil ca două fişiere să aibă acelaşi nume atâta timp cât sunt în cataloage diferite, referirea acestor fişiere făcându-se prin intermediul numelui de cale. Acesta poate fi: absolut ca punct de referinţă se ia catalogul “rădăcină”, ex: /home/user/a /home/dev/a relativ punctul de referinţă este reprezentat de un catalog arbitrar: /user/a /user/b

La fiecare schimbare a catalogului noul catalog devine catalogul curent de lucru. Acest lucru este foarte important deoarece pentru fiecare program executat în Linux va rezulta un proces care va avea ca parte a stării lui interne şi catalogul curent de lucru. Acesta fiind catalogul utilizat ulterior pentru referiri bazate pe calea relativă. Dacă procesul nu face aranjamente specifice pentru a utiliza un alt catalog de lucru, atunci implicit va utiliza catalogul în care a fost utilizatorul când programul a fost pornit.

bak Alt_fis 151 168 168 151 300 Şirul de inode 151 152 …. Fiecare pereche nume – inode dintr-un catalog poartă numele de legătură sau link. implicit utilizatorul va fi plasat într-un catalog asociat cu numele de login numit home. 7 8 . În acest fel este posibil ca acelaşi nume de inode să apară în unul sau mai multe link-uri. Cataloage Lucru Fis1 Fis2 Fis3 Backup Fis1.2. rmdir – şterge un catalog. Un catalog conţine pe lângă numele fişierului şi numărul de inode aociat acestuia.. 300 Parametrii – l afişare extinsă $ ls De studiat! Utilizând comenzile mai sus prezentate să se parcurgă şi să se afişeze conţinutul catalogului home. 168 ….. Deci un fişier poate avea unul sau mai multe nume de cale valide. De studiat! Să se creeze un catalog cu numele work în catalogul home al fiecărui utilizator. cine îi este proprietarul şi unde sunt stocate blocurile de date pentru fişierul respectiv)./ dev bin rădăcină 1. stocat pe disc. Fiecare element al şirului este un inode care stochează in formaţii specifice unui anumit fişier (când a fost creat. indiferent de tipul său i se asociază un număr numit inode care reprezintă de fapt un index al unei intrări într-un şir stocat pe disc. Cel mai simplu mod de-a afla unde se află catalogul home situat în ierarhia de cataloage este prin comanda: home a user Fişiere speciale a b pwd – present working directory parametrii – fără $ pwd /home/user rezultat – afişarea catalogului curent de lucru Pentru schimbarea catalogului curent de lucru este disponibilă comanda: cd – change directory parametrii – catalogul destinaţie ─ implicit catalogul home al utilizatorului $ cd / rezultat – catalogul curent de lucru este catalogul rădăcină Pentru a obţine o listă a numelor fişierelor şi cataloagelor se utilizează comanda: ls Fişiere obişnuite Fiecărui fişier. Este catalogul care va fi catalogul curent de lucru iniţial. Comenzi specifice cataloagelor: mkdir – creează noi cataloage. COMENZI SPECIFICE LUCRULUI CU FIŞIERE Imediat după intrarea în sistem. dacă nu conţine nici un fişier.

$cp fis1 fis2 rezultat: fis1 este copiat în fis2 $cp fis2 /home/user rezultat: fis2 este copiat în /home/user În cazul în care se doreşte să se şteargă un catalog trebuie ca în primul rând să se şteargă fişierele obişnuite conţinute în acel catalog. Dacă se doreşte mutarea unuia sau mai multor fişirere se poate utiliza utilitarul rm efectul este acelaşi ca şi în cazul utilizării combinate a comezilor cp şi rm. echo. Pentru cele de dimensiuni mai mari se foloseşte more. ln O categorie aparte de comezi pentru lucrul cu fişierele o reprezintă cele destinate prelucrării acestora. Acceptă ca şi parametrii calea a două fişiere obişnuite realizând o copie a primului în al doilea. tail De studiat! Să se afişeze numărul de cuvinte din fişierul etc/passwd (indicaţie folosiţi wc) De studiat! Să sa se studieze pagina de manual pentru următoarele comenzi: date. Pentru ştergerea acestor fişiere foloseşte comanda rm. Comanda cat se poate utiliza în mod eficient numai pentru afişarea unor fişiere de mici dimensiuni care să încapă pe ecran. Dintre acestea cele mai importante sunt: Cat – este prescurtarea pentru concatenate şi este folosită pentru concatenarea a două sau mai multe fişiere. O altă formă este cea în care al doilea parametru este un catalog. 9 De studiat! Să se afişeze pe ecran utilizând cat conţinutul fişierului fis1 utilizat în temele anterioare. de aceea se recomadă utilizarea cu atenţie a utilitarelor de ştergere. De studiat! – pagina de manual pentru: head. De studiat! – pagina de manual pentru: mv. $cat fis1 fis2 rezultat fis1 este adăugat la sfârşitul fis2 Cat se poate folosi şi fără argumente : $cat Acest text este doar un exemplu Ctrl-d Rezultatul este afişarea textului pe ecran pentru că implicit această comanda primeşte ca şi ieşire standard ecranul iar ca şi intrare standard tastatura. De studiat! Pentru toate comenzile prezentate pâna acum să se utilizeze comanda man pentru a se studia posibilele opţiuni. $rm fis1 rezultat: şterge fis1 Atenţie! În Linux ştergerea fisierelor nu este reversibilă.Comanda de copiere în Linux este cp. 10 . Comanda rm se poate folosi şi pentru modificarea numelui fisierelor primite ca şi argumente.

Acest catalog “rădăcină” conţine o serie de alte cataloage ce au destinaţii speciale. Deoarece un catalog poate cobţine subcataloage avem de-a face cu o structură ierarhică. Semnificaţia acestor cataloage: /bin /dev /etc /home /lib executabilele comenzilor fişierele speciale fişierele de configurare cataloagele utilizatoriloe bibliotecile standard 5 6 /mnt /sbin /tmp /usr /var comezi de administrare fişiere temporare date disponibile utilizatorilor fişiere mari în cazuri speciale / bin dev etc home lib mnt sbin tmp usr var X11 adm bin lib local man src bin lib src linux Este posibil ca două fişiere să aibă acelaşi nume atâta timp cât sunt în cataloage diferite. . fişierele de date pentru programe şi pentru fişierele executabile ale programelor. etc. Dacă procesul nu face aranjamente specifice pentru a utiliza un alt catalog de lucru. programele utilizează porturile seriale. atunci implicit va utiliza catalogul în care a fost utilizatorul când programul a fost pornit. el fiind repezentat prin “/”. Acesta fiind catalogul utilizat ulterior pentru referiri bazate pe calea relativă. imprimantele şi alte dispozitive în acelaşi mod în care utilizeză fişierele de pe discuri. • Dispozitive – această categorie este alcătuită din anumite tipuri de fişiere. Acest lucru este foarte important deoarece pentru fiecare program executat în Linux va rezulta un proces care va avea ca parte a stării lui interne şi catalogul curent de lucru. Toate aceste fişiere sunt organizate într-o structură ierahică arborescentă. În Linux totul este un fişier(aproape totul). Acestea sunt utilizate pentru fişierele text.Capitolul 2 SISTEMUL DE FIŞIERE 1.1. CD-ROM. având ca şi vârf un singur catalog care poartă numel de “rădăcină”. ex: /home/user/a /home/dev/a relativ punctul de referinţă este reprezentat de un catalog arbitrar: /user/a /user/b La fiecare schimbare a catalogului noul catalog devine catalogul curent de lucru. reprezintă un container în care pot fi plasate alte cataloage şi fişiere. De fapt catalogul conţine doar numele fişierelor şi informaţii despre locaţia acestora. STRUCTURA DE FIŞIERE Una dintre cele mai importante caracteristici Linux este modul în care tratează fişierele. modem. • Cataloage – un catalog este un mecanism care permite fişierelor să fie grupate împreună. cum ar fi cele asociate comunicaţiei între procese sau cele asociate comunicaţei cu diverse dispozitive hardware ca terminale. Sistemul de operare Linux utilizează trei tipuri de fişiere: • Fişiere obişnuite – sunt doar colecţii de octeţi asupra cărora sistemul nu impune nici o structură particulară. Acesta poate fi: absolut ca punct de referinţă se ia catalogul “rădăcină”. referirea acestor fişiere făcându-se prin intermediul numelui de cale. Aceste destinaţii sunt doar convenţii şi nu sunt obligatorii.

Comenzi specifice cataloagelor: mkdir – creează noi cataloage. cine îi este proprietarul şi unde sunt stocate blocurile de date pentru fişierul respectiv). În acest fel este posibil ca acelaşi nume de inode să apară în unul sau mai multe link-uri. indiferent de tipul său i se asociază un număr numit inode care reprezintă de fapt un index al unei intrări într-un şir stocat pe disc. Deci un fişier poate avea unul sau mai multe nume de cale valide. implicit utilizatorul va fi plasat într-un catalog asociat cu numele de login numit home.2. Este catalogul care va fi catalogul curent de lucru iniţial. COMENZI SPECIFICE LUCRULUI CU FIŞIERE Imediat după intrarea în sistem.bak Alt_fis 151 168 168 151 300 Şirul de inode 151 152 …. Cataloage Lucru Fis1 Fis2 Fis3 Backup Fis1. Fiecare pereche nume – inode dintr-un catalog poartă numele de legătură sau link.. rmdir – şterge un catalog./ dev bin rădăcină 1. dacă nu conţine nici un fişier. stocat pe disc. 168 …. 7 8 . Fiecare element al şirului este un inode care stochează in formaţii specifice unui anumit fişier (când a fost creat.. Cel mai simplu mod de-a afla unde se află catalogul home situat în ierarhia de cataloage este prin comanda: home a user Fişiere speciale a b pwd – present working directory parametrii – fără $ pwd /home/user rezultat – afişarea catalogului curent de lucru Pentru schimbarea catalogului curent de lucru este disponibilă comanda: cd – change directory parametrii – catalogul destinaţie ─ implicit catalogul home al utilizatorului $ cd / rezultat – catalogul curent de lucru este catalogul rădăcină Pentru a obţine o listă a numelor fişierelor şi cataloagelor se utilizează comanda: ls Fişiere obişnuite Fiecărui fişier. De studiat! Să se creeze un catalog cu numele work în catalogul home al fiecărui utilizator. Un catalog conţine pe lângă numele fişierului şi numărul de inode aociat acestuia. 300 Parametrii – l afişare extinsă $ ls De studiat! Utilizând comenzile mai sus prezentate să se parcurgă şi să se afişeze conţinutul catalogului home.

Dacă se doreşte mutarea unuia sau mai multor fişirere se poate utiliza utilitarul rm efectul este acelaşi ca şi în cazul utilizării combinate a comezilor cp şi rm. $cat fis1 fis2 rezultat fis1 este adăugat la sfârşitul fis2 Cat se poate folosi şi fără argumente : $cat Acest text este doar un exemplu Ctrl-d Rezultatul este afişarea textului pe ecran pentru că implicit această comanda primeşte ca şi ieşire standard ecranul iar ca şi intrare standard tastatura. $rm fis1 rezultat: şterge fis1 Atenţie! În Linux ştergerea fisierelor nu este reversibilă. Comanda cat se poate utiliza în mod eficient numai pentru afişarea unor fişiere de mici dimensiuni care să încapă pe ecran. tail De studiat! Să se afişeze numărul de cuvinte din fişierul etc/passwd (indicaţie folosiţi wc) De studiat! Să sa se studieze pagina de manual pentru următoarele comenzi: date.Comanda de copiere în Linux este cp. echo. O altă formă este cea în care al doilea parametru este un catalog. De studiat! – pagina de manual pentru: head. 9 De studiat! Să se afişeze pe ecran utilizând cat conţinutul fişierului fis1 utilizat în temele anterioare. De studiat! Pentru toate comenzile prezentate pâna acum să se utilizeze comanda man pentru a se studia posibilele opţiuni. Comanda rm se poate folosi şi pentru modificarea numelui fisierelor primite ca şi argumente. Dintre acestea cele mai importante sunt: Cat – este prescurtarea pentru concatenate şi este folosită pentru concatenarea a două sau mai multe fişiere. Pentru ştergerea acestor fişiere foloseşte comanda rm. $cp fis1 fis2 rezultat: fis1 este copiat în fis2 $cp fis2 /home/user rezultat: fis2 este copiat în /home/user În cazul în care se doreşte să se şteargă un catalog trebuie ca în primul rând să se şteargă fişierele obişnuite conţinute în acel catalog. de aceea se recomadă utilizarea cu atenţie a utilitarelor de ştergere. 10 . Acceptă ca şi parametrii calea a două fişiere obişnuite realizând o copie a primului în al doilea. ln O categorie aparte de comezi pentru lucrul cu fişierele o reprezintă cele destinate prelucrării acestora. De studiat! – pagina de manual pentru: mv. Pentru cele de dimensiuni mai mari se foloseşte more.

Capitolul 3 Facilităţi ale interpretoarelor de comenzi INTERPRETOARE DE COMENZI 3. Un alt caracter special utilizat în numele fişierelor pe care interpretorul de comenzi le va expanda este semul întrebării ?. şi a apărut la câţiva ani după sh. Acesta pastreză un mediu distinct pentru fiecare utilizator din sistem. acesta va fi şi intrepretorul de comenzi ce va fi utilizat în continuare.Rularea lui determin afişarea mesajului hello world la consola 3. Un alt interpetor de comenzi utilizat pe scară largă este C shell cunoscut şi sub numele de csh. ghilimele ‘ ‘ – orice şir de caractere cuprins între aceste marcaje îsi pierde sensul special al tuturor caracterelor cuprinse în el. Unul dintre cele mai cunoscute interpretoare de comenzi pentru Linux este bash (bourne again shell). Pentru Unix şi clonele acestuia (Linux) interpretoarele de comenzi sunt independente faţă de sistemul de operare ceea ce a dus la dezvoltarea unui număr mare de astfel de interpretoare. Ex: ‘a*?’ ghilimele duble “ “ – un şir de caractere pierde sensul special al caracterelor curpinse în el (este valabil numai pentru caracterele prezentate până acum) . Sensul comenzii este – toate fişierele din catalogul text vor fi copiate în catalogul backup. Atenţie! La intrarea în sistem fiecare utilizator primeşte o instanţă a interpretorului de comenzi.) din catalogul text. Interpretorul de comenzi din Linux deşi are unele similitudini cu procesorul de comezi DOS este un instrument mult mai puternic oferind utilizatorului o multitudine de facilităţi.2. Există mai multe metode prin care numele de cale să fie abreviate. şi numai caracterele conţinute în această listă să poată fi expandate. Interpretorul de comenzi este un program care realizează o interfaţă între utilizator şi sistemul de operare. Ex: \*.doc} Atenţie! Toate numele de cale sunt expandate de interpretorul de comenzi înainte a fi transmise către comenzi. Acestă listă este delimitată de paranteze drepte ([]). numit Bourne shell. Un astfel de caracter de abreviere este *. interfaţă care preia comezile utilizatorului şi le transmite sistemului de operare spre execuţie. Csh a fost scris de Billy Joy ca parte integrantă a unei distribuţii de Unix numită BSD (Berkley System Distribution). iar interpretorul de comenzi va expanda aceste nume înainte de ale transmite comenzilor. fapt ce îl face mult mai uşor în utilizare celor familiarizaţi cu limbajul C. Ex: $ mkdir /usr/tmp/{bin. Rolul acestuia este de a substitui un singur caracter : $ ls /dev/tty? /dev/tty0 /dev/tty2 /dev/tty4 /dev/tty6 /dev/tty8 /dev/tty1 /dev/tty3 /dev/tty5 /dev/tty7 /dev/tty9 De asemenea este posibilă precizarea unei liste de caractere. Acest interpretor de comenzi este cunoscut în cadrul sistemului de operare sub numele de sh. a fost dezvoltat de Steven Bourne şi a fost inclus într-o distribuţie de Unix lansată în 1979. Numele de Csh l-a primit datorită asemănării sintaxei comenzilor sale cu cea a instrucţiunilor limbajului C. Introducere Expandarea numelor de cale – În exemplele de până acum s-au folosit numai nume de cale complet precizate. cuvintele vor fi separate prin virgulă şi cuprinse între paranteze {}. Toate aceste interpretoare au fost iniţial dezvoltate pentru Unix dar s-au dezvoltat versiuni ale acestora şi pentru Linux.1. cu excepţia caracterelor $ şi \. Primul interpretor de comenzi important. Un exemplu de fişier de comenzi este: #!/bin/sh echo “hello world” 11 12 . De exemplu $ cp text/* backup Aici prin * se înţelege – toate fişierele (cu excepţia celor ce încep cu . În cazul în care se doreşte utilizarea caracterelor speciale cu sensul lor iniţial se poate folosi unul din următoarele trei mecanisme de marcare: caractere escape(\) – caracterul backslash îi indică interpretorului de comenzi să ignore sensul special al caracterelor de după. În multe sisteme Linux comanda /bin/sh este de multe ori un link către interpretorul de comenzi curent din sistemul de operare.Ex: $ ls /dev/tty[pq][235] /dev/ttyp2 /dev/ttyp3 /dev/ttyp5 /dev/ttyq2 /dev/ttyq3 /dev/ttyq5 O ultimă formă de expandare pentru numele de cale este prin precizarea unei liste de cuvinte.

Dacă este urmat de un nume de fişier atunci acest fişier este creat dacă nu există. Unele comenzi au această facilitate. aceasta poate primi ca şi parametru numărul de ordine al procesului întrerupt. prin punerea la dispoziţia utilizatorului a unui număr mare de funcţii simple.err Şi intrarea standard: $ wc –l < /etc/passwd 12 Rezultat: numarul de intrări din fişierul de parole.txt poate fi înlocuită prin: $ ls /usr/bin | wc –w 500 Controlul proceselor – Permite întreruperea unui poces şi de asemenea reluarea execuţiei acelui proces la un moment de timp ulterior. majoritatea comenzilor Linux sunt configurate astfel încât intrea acestora să o reprezinte tastatura iar datele de ieşire sunt trimise către monitor. Acest lucru se poate face implicit de către sistemul de operare prin intermediul unui pipe. dar dacă pentru fiecare în parte s-ar prevedea acest lucru dimesiunea executabilelor ar creşte.txt • • bg – se foloseşte pentru suspendarea unui proces. Un alt simbol utilizat de acest mecanism de redirectare este ‘>>’. iar apoi redirecţionarea aceluiaşi fişier ca şi intrare standard pentru altă comandă se pun practic în legătură două procese prin intermediul unui fişier temporar. de aceea acestă sarcină şi-a asumat-o sistemul de operare prin intermediul acestui mecanism de redirectare. Întreruperea execuţiei unui proces se poate face direct de la tastatură prin secvenţa Ctrl-z. Un exemplu clasic este cel de creare a unui nou fisier prin care ieşirea standard a comenzii cat este redirectată către fişierul care urmează a fi creeat: $ cat > text text de exemplu Ctrl-d $ Se observă că în această situaţie se foloseşte caracterul ‘>’.txt 500 $ rm /tmp/temp. Implicit. În cazul în care ieşirea este reprezentată de un fişier datele redirectate vor fi concatenate la sfârşitul fişierului. iar dacă există atunci va rescris. . 13 Pipes sau conectarea proceselor – prin redirecţionarea ieşirii standard a unei comenzi într-un fişier. respectiv ieşirea unei comenzi ar fi altele decât cele implicite. împreună cu informaţiile despre acestea sunt adăugate la fişierul text. De exemplu seria de comenzi: $ ls /usr/bin > /tmp/temp. Ex: $ ls –l /usr/bin >>text Rezultat: numele fişierele din /usr/bin. Acesta are rolul de a indica redirectarea ieşirii standard către o altă comandă. Pentru a relua execuţia ultimului proces întrerupt avem: $fg %1 cat >file. Redirectarea ieşirii şi a intrării – Filosofia de bază Linux este aceea de ţine lucrurile cât mai simple posibil. Identic cu redirectarea ieşirii se poate redirecta şi ieşirea de eroare: $ ls /usr/bin 2>text. În unele situaţii ar fi util dacă intrarea . command complition afişarea posibilelor comenzi pornind de la primele caractere ale numelor acestora. 14 Alte facilittăţi ale interpretorului de comenzi Linux: history posibilitatea de rechemare rapidă a ultimilor comezi executate.Ex: `cmd`. De exemplu: $ cale=`pwd` atribuie variabilei cale rezultatul comenzii pwd.txt Ctrl-z [1]+ Stopped $ Pentru gestionarea proceselor interpretorul de comenzi are implementate o serie de comenzi: • fg – este comanda prin care este reluată execuţia unui proces. echivalent cu Ctrl-z jobs – afişează procesele suspendate. Pentru aceatsa se foloseşte operatorul |. $ cat > file. după care procesul va fi întrerupt iar uitlizatorul reprimeşte controlul putând executa alte comenzi. Iar prin gruparea acestor funcţii înpreună se pot obţine comenzi mai complexe. Executa comnada cmd.txt $ wc –w </tmp/temp.

Ex: #!/bin/sh for i in $( ls ). Sinatxa interpretorului de comenzi. caz in care variabila nume ia pe rand ca valoare numele intrarilor din directorul curent. Chiar dacă nu se folosesc parametrii variabila tot este creată variabila $# dar va avea valoarea 0. – parametrii trasmişi scriptului. $SHELL – numele interpretorului de comenzi curent. $# – numărul de parametri transmişi fişierului de comenzi. Variabile de acest tip sunt: $1. Ex: . . Variabilele pot fi de mai multe tipuri: variabile predefinite sau speciale. implicit acesta este $. $* . Daca in valori este omis. dacă codul returnat este zero se executa declaraţiile ce urmeaza primului then.3. $0 – numele fişierului de comenzi. Şi în acest caz vom avea: Variabile – în mod normal nu sunt declarate decât atunci când este nevoie de ele. Ex: $ salut=Hello $ echo $salut Hello 15 . Parametrii poziţionali – Dacă la apelarea unui fişier de comenzi (script) sunt folosoţi parametrii atunci create câteva variabile suplimentare. Implicit variabilele snt considerate şi memorate ca şi şiruri de caractere. parametrii poziţionali sau variabile utilizator. chiar şi atunci când primesc valori numerice. se executa condiţia de după elif si daca codul returnat este zero se executa declaraţiile de dupa al doilea then. ciclul se executa pentru fiecare parametru pozitional.testarea tipului unui fişier ce este transmis ca şi argument: #!/bin/sh if test -f $1 then echo $1 este un fisier obisnuit elif test -d $1 then echo $1 este un catalog else echo $1 este altceva fi for – folosit pentru trecerea printr-un anumit număr de valori(pot fi şi şiruri de caractere). $2. $$ – numărul de identificare prin care sistemul identifică scriptul în momentul execuţiei. Pentru fiecare valoare se executa ciclul for. Conditia poate fi si in * . $IFS – Internal Field Separator utilizat pentru separarea cuvintelor şi a liniilor . do echo $i done 16 Programarea în cazul interpretorului de comenzi se face în acelaşi mod ca şi în cazul altor limbaje de programare. $PS1 – defineşte prompterul interetorului. Structuri de control – şi în acest caz se întâlnesc structuri de control la fel ca şi în cazul altor limbaje de programare. Cele mai importante sunt următoarele: $HOME – catalogul gazdă a utilizatorului curent $PATH – o listă de cataloage în care interpretorul caută pentru fişierul executabil asociat unei comenzi. Accesarea conţinutului variabileie se face prin prefixarea acesteia cu caracterul $. If – testează rezultatul unui comenzi şi în funcţie de rezultatul acesteia se execută una dintre ramurile de declaraţii. Format: if condiţie then declaraţii [ elif condiţie then declaraţii] [ else declaraţii] fi Se execută condiţie.lista cu parametrii transmişi scriptului. Variabile predefinite sau speciale – la pornirea sistemului o serie de variable sunt iniţializate cu valorile implicite mediului de operare. Sintaxa: for nume [in valori] do declaraţii done Variabila nume ia pe rand valorile din lista ce urmeaza lui in. In caz contrar..3.

Sintaxă: until lista_1 do lista done case – această structură are sintaxa: case condiţie in sablon_1) declaraţii. Echo – afişează argumentele la ieşirea standard Eval – realizeză evaluarea şi apoi execuţia argumentelor Exit – forţează întreruperea procesului curent care va returna ca şi cod de ieşire valoarea primită ca şi argument. Sintaxă: while conditie do declaraţii done Ex: #!/bin/sh i=0 while [ $i -lt 5 ].. Forma generală a unei astfel de proceduri este: nume() { lista.while – în cazul în care se doreşte trecerea printr-un numar mai mare de valori este utilă folosirea acestei structuri de control. do echo $i ((i=i+1)) done until – ciclu identic cu for dar în acest caz testarea condiţiilor se face la sfârşit. iar lista este o listă de comenzi.. sablon_2) declaraţii. } Unde nume este numele procedurii (sau funcţiei).. Export – face disponibile variabilele primite ca şi argument în subshell-urile viitoare. Ex:: Exit n Read – citeşte o linie din fişierul standard de intrare. Uzual test este folosit pentru a verifica tipul unor fişiere: test –e fişier – returnează adevărat dacă fişierul există test –f fişier – returnează adevărat dacă fişierul este unul obişnuit test –f fişier – returnează adevărat dacă fişierul este catalog Atenţie! Comenzile test condiţie şi [ condiţie ] sunt echivalente. Implicit variabilele create într-un shell nu sunt disponibile într-un shell creat ulterior. 17 Proceduri shell – reprezintă o modalitate de a grupa mai multe comenzi shell la un singur loc în vederea execuţiei lor ulterioare. esac De studiat! #/bin/sh echo "\$#=$#" echo "\$0='$0'" i=1 while [ $# -gt 0 ]. . şi pipe-uri care urmeză să fie executate. do echo "\$$i='$1'" i='expr $i + 1' shift done Se compara condiţie cu fiecare din sabloanele prezente si se executa lista de comenzi unde se constata potrivirea. în caz contrar va returna 1. 18 .. Ex: #!/bin/sh function hello { echo Hello World } function quit { sleep 5 exit } hello quit Câteva comezi specifice interpretorelor de comenzi: Test – este folosită pentru a pentru a testa o expresie dacă este adevărată atunci test va returna 0.

Set – folosit pentru activarea sau dezactivarea unor opţiuni sau pentru poziţionarea unor parametrii poziţionali. Return – revenirea dintr-o funcţie cu valoarea n. while sau until ce contine break. Sleep – suspendă execuţia pentru un număr n de secunde primit ca şi argument.Break – Comanda de parasire a celei mai interioare bucle for. Ex: #!/bin/sh function print { echo $1 } print NUME: read nume print "Hello: $nume" De studiat! • man bash • să se realizeze un script care să afişeze toate fişierele speciale dintr-un catalog dat. Continue – comanda permite trecerea la o nouă interaţie a buclei for. Daca n este specificat se iese din n bucle. unde n este valoarea transmisă ca şi argument. Dacă această valoare nu este precizată atunci codul returnat este cel al ultimei comenzi executate. 19 20 . while sau until.

Prin convenţie descriptorii 0. lseek şi close. implicit acest număr este 256. În continuare vor fi descrise primele două iar restul vor fi prezentate în capitolele următoare. Când un utilizator solicită o operaţie de intrare/ieşiere asupra fişierului. adică se încearcă ca intrarea şi ieşirea pentru divese dispozitive ca discuri. Sistemul de fişiere Linux – considerente generale Sistemul de fişiere din Unix este caracterizat prin: • • • o structură ierarhică tratare consistentă a fişierelor de date protejarea fişierelor În cazul sistemului de fişiere din Linux se urmăresc aceleaşi principii de bază ca şi în cazul Unix. dup şi pipe. Fiecare descriptor de fişiere alocat este asociat cu un descriptor de fişiere deschi s. mode_t mode). Un descriptor de fişiere conţine informaţii referitoare la un anumit fişier. open.1. chiar şi aparţinând unor procese separate să indice în acelaşi timp spre acelaşi descripor de fişier deschis. Acesta poate fi utilizat in apelurile sistem: read. dimensiune. 1 şi 2 sunt alocaţi de către sistem la pornire: • descriptorul 0 este utilizat ca şi intrare standard • descritorul 1 este ieşire standard • descriptorul 2 este ieşirea standard de eroare. modul de acces la fişier. funcţie de valoarea argumentului flags. kernel-ul sistemului de operare converteşte indexul curent într-un număr de bloc şi utilizează acest număr ca şi index în tabela adreselor de bloc şi scrie sau citşte un bloc. imprimate să se realizeze la fel ca şi lucrul cu fişiere obişnuite.h> #include <sys/stat. Fiecare proces putând avea deschise un anumit număr maxim de fişiere la acelaşi moment de timp. Argumentul al treilea este folosit doar la crearea de fişiere noi. Argumentul flags se formează printr-un SAU pe biti intre constantele: 29 30 Lucrul cu fişiere 4. informaţii referitoare la dată. . fcntl.4. IDU efectiv sau GIDU efectiv al procesului care execută apelul trebuie să aibe drepturi de citire/scriere. int open(const char *pathname. Funcţia returnează cel mai mic descriptor de fisier disponibil. Apelul sistem open: #include <sys/types. drepturile de acces. şi alte informaţii legate de acesta. Este posibil ca mai mulţi descriptori de fişiere. Există cinci apeluri care generează descriptori de fişiere: creat. Apelul cu doua argumente este folosit pentru citirea şi scrierea fişierelor existente. int flags).h> #include <fcntl. Pointerul din fişier este pozitionat pe primul octet din fişier. Numărul de argumente al acestei funcţii poate fi doi sau trei.h> int open(const char *pathname. referinţe către blocurile de date. Fiecare inode conţine descrirea fişierului: tipul fişierului. proprietarul. Returnează: descriptorul de fisier sau -1 in caz de eroare.2. Adresele blocurilor de date alocate fişierului sunt de asemenea stocate în cadrul inode-ului. int flags. Accesul la fişiere Capitolul 4 În Linux fişierele sunt acesate prin intermediul unor descriptori de fişiere. Ficare fişier este reprezentat de o structură numită inode. write. terminale. un index care precizează unde va avea loc următorul acces în fişier. adică informaţia dintr-o structură de decriptor deschis va fi utilizată simultan de mai mulţi descriptori de fişiere.

Interesant de semnalat la acest apel este faptul că scrierea fizică pe disc este întarziată. . Dacă fişierul există (este nevoie de permisiunea de căutare în director) conţinutul lui este pierdut şi el este deschis în scriere. poate fi o combinaţie de SAU pe biţi între constantele simbolice: . . Apelul scrie noct octeţi din tamponul buf în fişierul a cărui descriptor este fd. 0 la EOF. S_IWGRP.h> ssize_t read( int fd. Dacă procesul care a efectuat apelul.h> int creat( const char *path.O_WRONLY . S_IXUSR User: read. Ea se efectuează la iniţiativa nucleului fără ca utilizatorul să fie informat. mode_t mod).O_RDWR . Apelul sistem write Pentru a scrie un număr de octeţi într-un fişier.O_CREAT .h> #include <fcntl. Întârzie mult întregul sistem.O_NDELAY . size_t noct). write. size_t noct). Fişierul deschis va avea drepturile de acces specificate de argumentul al doilea din apel. Argumentul al treilea. In mod uzual. Citeste n oct octeţi din fişierul deschis referit de fd si îi depune în tamponul referit de buf.O_APPEND . citeşte datele care înca nu au fost scrise pe disc. . Argumentul al doilea este ignorat. Nu se modifică proprietarul sau drepturile de acces ale fişierului. Proprietarul (IDU efectiv si GIDU efectiv) procesului care execută acest apel trebuie să aibe permisiunea de scriere in director. const void *buf.La fişiere pipe şi cele speciale pe bloc sau caracter cauzează trecerea în modul fără blocare atât pentru apelul open cât şi pentru operaţiile viitoare de I/E. apelul open eşuează.h. execute . . write. Sintaxa este: #include <unistd. kernel-ul le citeşte înapoi din bufferele cache. de la poziţia curentă.O_EXCL .Dacă fişierul există si O_CREAT este pozitionat.S_IRUSR. este alocat un nou inode si o legatură spre el este plasată în directorul unde acesta a fost creat.h> #include <sys/stat.h> ssize_t write( int fd. O_WRONLY | O_CREAT | O_TRUNC. Apelul este echivalent cu: open( path. Dacă fişierul creat nu există. execute Acestea definesc drepturile de acces asupra unui fişier şi sunt definite în fişierul antet sys/stat. sau un alt proces.O_SYNC Forţează scrierea efectivă pe disc prin write. Pointerul în fişier este incrementat automat după o operaţie de citire cu numărul de octeţi citiţi.Scrierile succesive se produc la sfârşitul fişierului.Fişierul este deschis în adăugare (citire+scriere). In versiunile noi System V inlocuit cu O_NONBLOCK.. . Timpii de accces si modificare din inode sunt actualizaţi. . Returnează: descriptorul de fişier sau -1 in caz de eroare. iar mod drepturile de acces. se foloseste apelul write. kernel-ul încearcă să accelereze operatiile de citire citind în avans blocuri de disc consecutive pentru a anticipa eventualele cereri viitoare. S_IWOTH.S_IROTH.Dacă fişierul nu există el este creat. mod.Fişierul este deschis in scriere. Apelul întoarce cel mai mic descriptor de fişier disponibil. Apelul sistem creat: #include <sys/types. Procesul care execută o operaţie de citire aşteaptă pană când kernel-ul depune datele de pe disc in bufferele cache.h. -1 in caz de eroare. Scrierea întarziată este mai rapidă. Apelul sistem read: Pentru a citi un număr de octeţi dintr-un fişier.O_RDONLY . Fişierul 31 este deschis în scriere iar dimensiunea sa iniţiala este 0. Argumentul path specifică numele fişierului. . Urmatoarele constante simbolice sunt opţionale: .S_IRGRP. 32 . execute . write. se foloseşte apelul read.Fişierul este deschis in citire. Acestea sunt definite in fişierul antet fcntl. Returnează: numărul de octeţi scrişi şi -1 în caz de eroare. S_IXOTH Other: read. de la poziţia curentă. dar e eficace în cazuri critice. S_IWUSR. dar are trei dezavantaje: a) o eroare disc sau căderea kernelului produce pierderea datelor. void *buf. S_IXGRP Group: read. Returnează: numarul de octeţi citiţi efectiv.O_TRUNC Dacă fişierul există ii şterge conţinutul. mod). Sintaxa este: #include <unistd.

Apelul sistem lseek Pentru pozitionarea absolută sau relativă a pointerului de fişier pentru un apel read sau write se foloseşte apelul lseek. /* tip dispozitiv – fişiere speciale */ off_t st_size.h> #include <unistd. int fstat(int filedes. #include <unistd. /* data ultimei modificări a stării */ 34 33 . Gestionarea directoarelor Citirea unui director se poate face de oricine are drept de citire asupra directorului respectiv. /* ID proprietar*/ gid_t st_gid. /* ID grup*/ dev_t st_rdev. Cele trei funcţii produc acelaşi rezultat deosebirea constând în modul de specificare a fişierului la care se referă. #include <sys/types. Pentru a elimina aceste dezavantaje. /* dimensiune fişier*/ unsigned long st_blksize. Pentru obţinerea de informaţii despre fişierele nedeschise se folsesc apelurile sistem stat. /* inode */ mode_t st_mode. /* data ultimei modificări */ time_t st_ctime. /* tip fişier-drepturi */ nlink_t st_nlink. Apelul sistem close Pentru a disponibiliza descriptorul ataşat unui fişier în vederea unei noi utilizări se foloseşte apelul close. int lstat(const char *path. struct stat *buf). Apelurile open. /* număr de dispozitiv */ ino_t st_ino. Returneaza: 0 in caz de succes si -1 in caz de eroare.h> #include <sys/stat.b) un proces care a iniţiat o operaţie de scriere nu poate fi informat în cazul apariţiei unei erori de scriere. Returnează: 0 in caz de reuşită si -1 in caz contrar. Pentru fişierul specificat prin path sau filedes se completează o structură de informaţii care poate fi accesată prin intermediul lui buf. Temă! Să se realizeze un program care să relizeze copierea a unui fişier dintr-un director în altul. /* data ultimului acces*/ time_t st_mtime.h> int stat(const char *path. fstat un descriptor de fişier. /* numărul de blocui alocate */ time_t st_atime. Dar cum aceasta scade viteza sistemului si având în vedere fiabilitatea sistemelor UNIX de astazi se preferă mecanismul de lucru cu tampoane cache. Scrierea unui director poate fi făcută numai de către kernel.h> int close( int fd). precum şi pentru funcţiile înrudite cu acestea. Apelul nu goleşte bufferele kernel-ului şi deoarece fişierul este oricum închis la terminarea procesului apelul se consideră a fi redundant. iar lstat se referă la un link. struct stat *buf). int interp). /* dimensiunea blocului pentru I/E */ unsigned long st_blocks. creat.3. Dacă interp este SEEK_END poziţionarea este faţă de sfârşitul fişierului. în anumite cazuri. Daca un fişier este deschis folosind constanta simbolica O_APPEND se efectueaza un apel lseek la sfârşitul fişierului înaintea unei operaţii de scriere. De studiat! Studiaţi paginile de manual pentru funcţiile prezentate anterior.h> #include <unistd. /* număr de legături */ uid_t st_uid. Returneaza: un deplasament în fişier sau -1 în caz de eroare. fstat şi lstat: #include <sys/types. Nu se efectueaza nici o operaţie de I/O şi nu se trimite nici o comandă controlerului de disc. se foloseşte fanionul O_SYNC.h> off_t lseek( int fd. write şi read execută implicit lseek. Structura prin care se obţin informaţii referitoare la fişiere are următoarea formă: struct stat { dev_t st_dev. 4. Dacş interp este SEEK_CUR poziţionarea este relativă la poziţia curentă. â c) ordinea scrierilor fizice nu poate fi controlată. Dacă interp este SEEK_SET poziţionarea este faţă de începutul fişierului (primul octet din fişier este la deplasament zero). Astfel stat primeşte ca şi argument calea către fişierul respectiv. struct stat *buf). off_t offset.

din fiecare director sunt ignorate structura sistemului de fişiere este arborescentă. Crearea celei de a doua legături la un director este utilă pentru a-l putea muta în altă parte a arborelui sau pentru a-l putea redenumi. Una dintre caracteristicile esenţiale ale sistemului de operare Linux este securitatea. Apelul sistem link 35 4. int closedir( DIR *dp). Returneaza -1 în caz de eroare. NULL in caz de eroare. scriere şi execuţie 36 . Pentru a respecta această cerinţă doar superuser-ul are dreptul sa stabilească o nouă legătura la un director. Implementarea acesteia la nivel de structură de fisiere se face prin precizarea pentru fiecare fişier în parte a drepturilor de citire. NULL în caz de eroare.h> DIR *opendir( const char *pathname). De regulă nu este alfabetică.h> #include <dirent. la fel şi inode-ul. Returneaza pointer daca este OK.}. Doar superuser-ul poate să şteargă un director.4. Dacă legaturile . Primul apel readdir citeşte prima intrare dintr-un director.3+BSD definesc structura astfel incat ea contine cel putin doi membri: struct dirent { ino_t d_ino.h> S_ISREG() Fisier obisnuit (REGular file). şi . S_ISLNK() Legatura simbolica. Argumentul oldpath trebuie să fie o legatură existentă. S_ISCHR() Dispozitiv special in mod caracter. struct dirent *readdir( DIR *dp). Returneaza: 0 în caz de reuşită şi -1 în caz contrar. } Structura DIR este o structură internă folosită de aceste patru funcţii pentru a păstra informaţii despre directorul citit. char d_name[NAME_MAX +1]. ea furnizează numărul inode-ului. Apelul decrementează contorul de legături din inode şi şterge intrarea director. S_ISBLK() Dispozitiv special in mod bloc. În paragraful următor vor fi prezentate o serie de funcţii folosite pentru modificarea atributelor fişierelor. SVR4 si 4. Pentru a adăuga o nouă legătura la un director se foloseste apelul: Tipul de fisier codificat in campul st_mode poate fi determinat folosind macrourile: #include <unistd.h> int unlink( const char *path). Ordinea intrarilor dintr-un director este dependentă de implementare. S_ISFIFO() Fisier pipe sau fifo. Apelul sistem unlink Pentru a şterge o legătură (cale) dintr-un director se foloseşte apelul: #include <unistd. S_ISSOCK() socket? int link(const char *oldpath. De studiat! Să se realizeze un program asemănător cu comanda Linux ls Apelul sistem opendir #include <sys/types. Structura dirent definită in fisierul antet dirent. Atributele fişierelor Pentru o gestionare eficientă a fişierelor este necesară nu numai cunoaşterea unui n umăr cât mai mare de date referitoare la acestea dar şi posibilitatea modificării acestor date. Programe care parcurg structura ierarhica (de exemplu.h este dependenta de implementare. const char newpath).. De studiat! Să se realizeze un program care pentru un director dat afişează informaţiile referitoare la toate intrările conţinute în acel director(fişiere obişnuitefişiere speciale). Returnează pointer dacă este OK. S_ISDIR() Fisier director. Returnează: 0 in caz de reuşită si -1 in caz contrar. Dacă acesta devine 0 spaţiul ocupat de fişierul în cauză devine disponibil pentru o altă utilizare. comanda find) pot fi uşor implementate fără probleme de traversare multiplă sau bucla infinită.

valoarea implicită a acesteia este 022.h> #include <sys/stat. Fiecare utilizator primeşte la intrarea în sistem o mască. uid_t owner. sau este propietarul fi.pentru proprietarul fişierului. Returnează: 0 în caz de succes –1 în caz contrar.: int fd. De studiat! Să se realizeze un program care permite modifcarea drepturilor de acces respectiv proprietarului pentru un fişier sau un director. Pentru a modifica drepturile de acces la fişiere este necesar ca procesul să aibă cel putin UID(user id) efectiv efectiv egal cu al proprietarului fişieului. S_IXGRP – pentru grupul utilizatorului. gid_t group). 37 38 . Ex. S_IWGRP.S_IRGRP. Returnează: 0 in caz de succes şi -1 în caz contrar.S_ISGID sgid la executie (02000) . pentru grupul proprietarului şi pentru toate celelalte clase de utilizatori. scriere şi execuţie pentru propritarul fişirului.ierului dar în acest caz poate modifica doar grupul cu unul din care face parte.h> int chmod(const char *path. umask(0222).S_IRO. if((fd=creat("tmp".h> int chown(const char *path.r ..S_ISUID suid la executie (04000) . system("ls -l tmp")..r w . int fchmod(int fildes.h> mode_t umask(mode_t mask).-). S_IWO.) dar prin luarea în considerare a măştii s-a obţinut ca şi rezultat 0222(r . S_IXUSR – drepturile de citire. mode_t mode). mode_t mode).S_IRUSR. Parametrii mode pot fi precizaţi atât în formă octală cât si sub forma unor constante simbolice de forma: .r w . dar interpretorul de comenzi permite nodificarea acesteia prin intermediul comenzii umask. .h> #include <sys/stat.S_ISVTX bitul sticky (01000) . Apelul sistem chown: #include <sys/types. Returnează: valoarea măştii anterioare. În momentul în care se încearcă modificarea drepturilor de acces asupra unui fişier automat se ţine cont şi de valoarea atributului mask al funcţiei umask după formula mode & ~(mask).1 student user 0 Mar 24 11:05 tmp Drepturile de acces la fisierul tmp precizate prin intermediul apelului sistem creat erau 0666(r w .h> #include <unistd. S_IWUSR. dacă nu drept de superuser. Un alt element de securitate în ceea ce priveşte sistemul de fişiere este apelul sistem umask: #include <sys/types. S_IXO – pentru toţi ceilalţi utilizatori. . Rezultat: -r--r--r-.0666)==-1)) printf("creat error"). Efecte vizibile la aplicarea acestui apel sistem se pot obţine numai dacă utilizatorul are drepuri de superuser.r . Aceste drepturi pot fi modificate cu ajutorul apelurilor sistem chmod şi fchmod: #include <sys/types.

Dacă ID-ul utilizatorului efectiv al procesului este 0 (root) atunci procesului nu i se aplică nici o restricţie. Aceste sunt cunoscute sub numele de utilizatorul real şi identificatorul de grup. Un sistem de operare multi-tasking este un sistem de operare care permite execuţia simultană a mai multor procese. sau un proces. practic ele controlează toate ativităţile care se desfăşoara în sistem.” Firele de execuţie vor fi tratate în capitolele următoare. Acesul la fişier este acordat pe baza dreprturilor de acces ale celorlalţi utilizatori (others). Să presupunem că avem un utilizator cu UID 200 şi GID 20 care doreşte să ruleze programul /usr/bin/passwd.1. Fiecare utilizator poate rula mai multe programe sau mai multe instanţe ale aceluiaşi program. 3. toate la acelaşi moment de timp. UNIX. Dacă ID-ul grupului efectiv al procesului este acelaşi cu ID-ul de grup al fişierului atunci procesului i se acord accesul la fişier în conformitate cu drepturile de acces ale grupului fişierului. Dacă nu treci la pasul 3. 2. şi utilizatorul efectiv şi identificatorul lui de grup. Linux permite accesul simultan al mai multor utilizatori. Fiecare instanţă a unui program în execuţie constitue un proces. Id-ul real est folosit pentru a identifica utilizatorul pentru care este rulat un proces. Toate aceste numere de identificare se pot obţine cu ajutorul următoarelor funcţii: uid_t getuid(void) // obţine ID-ul utilizatorului real uid_t getgid(void) // obţine ID-ul grupului real uid_t geteuid(void) // obţine ID-ul utilizatorului efectiv uid_t getegid(void) // obţine ID-ul grupului efectiv uid_t getpid(void) // obţine ID-ul procesului uid_t getppid(void) // obţine ID-ul procesului părinte uid_t getpgrp(void)// obţine ID-ul de grup al procesului 40 6. sistemul iniţial din care a derivat Linux. ID-ul grupului real la 20.Capitolul 6 PROCESE Linux este unul din sistemele de operare cu adevărat multi-tasking. deci la un orice moment de timp pentru aceste resurse utilizate în comun va exista o singură copie în memoria sistem. Dacă ID-ul utilizatorului efectiv al procesului este acelaşi cu ID-ul utilizator al fişierului atunci procesului i se acordă accesul la fişier în conformitate cu drepturile de acces ale proprietarului acelui fişier. Deocamdată vom considera procesul ca un program care rulează. Acest program are UID 0 (root) şi GID 1 şi are de asemenea set bitul setiud. variabile. Identificatorul real al unui proces este de fapt UID(user ID) şi GID(group ID) ale utilizatorului care rulează procesul. fişiere deschise si mediul de rulare. Dacă nu treci la pasul 4. De obicei un sistem Linux va avea biblioteci si coduri sursă impărţite între mai multe procese. iar ID-ul grupului efectiv 20. Ca şi sistem de operare multi-user. 4. definea un proces ca şi “un spaţiu de adrese cu unul sau mai multe fire de execuţie care se execută în cadrul acelui spaţiu de adrese şi care solicită resurse sistem pentru acele fire de execuţie. ID-ul utilizatorului efectiv 0. Acest lucru se realizează pe baza următorului algoritm: 1. Simultan sistemul de operare rulează alte programe de gestionare a resurselor sistemul şi de control a acesului utilizatorilor la sistem. 39 . date.PID Fiecare proces ce rulează sub Linux are asociat un identificator de proces unic numit PID (Process Identity Numbers) prin care este cunoscut în sistem. Identificatorul efectiv este de obicei acelaşi cu cel al utilizatorului real cu excepţia cazului în care sunt setate opţiunile setuid şi setgid. Când se pune problema drepturilor de acces a proceselor la diverse fişiere se foloseşte un set de patru numere de identificare. Procesele reprezintă o parte fundamentală a acestui sistem de operare. Dacă una sau amândouă opţiunile sunt activate atunci UID sau GID efectiv al procesului vor primi valoarile UID sau GID asociate fişierului din care rulează procesul. Când este rulat programul procesul asociat va avea ID-ul utilizatorului real 200. Fiecare proces din sistem începând cu procesul 1 init are un părinte a cărui PID îi este cunoscut şi îl poate accesa. fiecare grup având la rândul lui un număr de identificare care este de fapt identificatorul de proces(PID-ul) procesului părinte al grupului. Fiecare proces aparţine unui grup de procese. ID-urile efective sunt folosite pentru a putea realiza o sortare a permisiunilor şi privilegiilor pe care procesele le au la accesarea unui fişier. Un program. care rulează este constituit din următoarele elemente: codul program. în caz contrar treci la pasul 2. Identificatoare de proces .

şi este constituit dintr-un set de intrucţiuni şi date folosite pentru iniţializarea segmentelor text şi de 41 42 fork() Returnează un nou Returnează 0 Procesul părinte Procesul fiu . Odată ce procesul fiu a fost creat. bash). Sub Linux. Segmentul de date sistem Procesul iniţial Segmentul de text conţine instrucţiunile în cod maşinp care urmeză să fie executate.2. Segmentul de date sistem conţine elemntele caracteristice mediului de lucru în care rulează un program. Programul are un caracter static. este localizat pe disc.text şi sistem. pentru crearea de noi procese se foloseşte apelul sistem fork: #include <sys/types.h> #include <unistd. Segmentul de date utilizator conţine toate datele cu care operează un proces pe parcursul execuţiei sale. Vom considera că un proces este construit din trei părţi separate conform figurii următoare: Segmentul de text Program Proces Segmentul cu date utilizator Practic acest apel realizează un nou proces. îşi cotinuă execuţia din interiorul apelului fork.6. Acest segment poate fi accesat numai pentru citire(deci codul sursă din acestă parte nu poate fi modificat). Crearea proceselor utilizând fork date ale proceselor. ceea ce permite utilizarea acestui segment de către două sau mai multe procese din sistem care rulează acelaşi program. iar procesul care a apelat fork devine proces părinte a noului proces creat. Datele din acest segment sunt modificabile şi în plus fiecare proces are câte un segment de date unic. Adică următoarea acţiune pentru fiecare proces în parte este să părăsească fork.părinte şi fiu. Acest segment relizează distincţia între programe şi procese.h> pid_t fork(void). Procesul are un caracter dinamic iar execuţia sa necesită interacţiunea între segmentele de date. fiecare returnând o valoare diferită. Cele două procese sunt identice în termeni de conţinut al segmentelor lor de date şi text şi aproap identice în ceea ce priveşte segmentele sistem. numit proces fiu. Singurele diferenţe între astfel de procese apar la nivelul unor atribute care trebuie să fie diferite (cum ar fi PID care trebuie să fie unic pentru fiecare proces în parte). De exemplu dacă mai mulţi utilizatori rulează aceeaşi versiune de interpretor de comenzi (de ex. ambele procese. aceste date includ şi variabilele ce vor fi utilizate de proces. în memoria principală va fi o singură copie a programului pe care o folosesc în comun toţi utilizatorii.

ls) pentru rularea ei acesta va apela la un anumit moment fork. Un exemplu de ieşire obţinut la rularea programului: PID inainte de fork: 16953 PID parinte: 16953 PID fiu: 16954 După execuţia apelului fork majoritatea atributelor procesului părinte sunt disponibile nemodificate procesului fiu.. const char *arg. Principalele atribute sunt: aparteneţa la grupul de procese. printf("PID inainte de fork: %d\n". terminalul în care rulează(dacă acesta există). .h> main(){ pid_t val. printf("terminat cu erori.h> #include <unistd. Prima va afişa valoarea PID-ului procesului înainte de apelul fork. 43 În puls toţi descriptorii de fişiere care în procesul părinte sunt asociaţi cu fişiere deschise vor fi duplicaţi în procesul fiu. masca pentru crearea fişierelor(umask).h> main(){ execl("/bin/ls"."ls". Adică atât descriptorii de fişiere ai procesuli fiu cât şi ai procesului părinte vor indica spre aceeaşi descriptori de fişiere deschise. Se pune problema cum se poate ca în procesul fiu în loc de o copie a interpretorului de comenzi să fie rulat ls? Soluţia în acest caz este utilizarea apelului sistem exec. Vom avea în continuare un exemplu de program care crează un nou proces: #include <sys/types. Acest apel sistem reprezintă de fapt modalitatea de rulare de programe sub Linux.. unde path este calea comletă către programul care urmează să fie executat. a doua valoarea PID a procesului părinte după aplelul fork. acesta este urmat de o listă variabilă de argumente ce trebuie transmise programului. În mod normal alocarea ID-urilor pentru procese începe cu 1 pentru procesul init. } 44 . } La rularea programului se vor genera trei linii de ieşire. UID şi GID reale şi efective. Acest lucru se poate realiza în C prin prin argumentele argc şi argv.). De fapt sunt mai multe versiuni ale acestui apel sistem dar toate realizează în esenţă acelaşi lucru."-l". directorul de lucru curent.(int)getpid()). deci apelul exec trebuie să acopere şi aceste aspecte. în timp ce procesului fiu îi returnează 0. Acest lucru se realizează relativ simplu prin faptul că fork returnează valori diferite celor două procese.h> #include <stdio. În această situaţie este nevoie de un mecanism prin care să se realizeze diferenţierea între cele două procese. O altă problemă ce ar putea apărea este faptul că majoritatea programelor ce sunt rulate în mod curent din linia de comandă primesc şi parametrii sau argumente.0).Două procese virtual identice care rulează simultan se pot dovedi utile numai în situaţia în care acestea realizează acţiuni diferite. Această listă trebuie terminată cu NULL(0). Cea mai simplă versiune de exec este execl.h> int execl( const char *path. Prototipul acestei funcţii este: #include <unistd. Apelul sistem exec Dacă se utilizează interpretorul de comenzi pentru a rula o comandă (ex. În continuare vom avea un exemplu de program care să ruleze comanda ls –l: #include <stdio.h> #include <unistd. Acet lucru îl realizează prin înlocuirea segmentelor de date şi text ale procesului care apeleză exec cu cele ale programului a cărui nume a fost transmis ca şi argumet. if(val=fork()) printf("PID parinte: %d\n". Astfel procesului părinte îi returnează PID-ul noului proces fiu creat. deci şi exec trebuie să i le transmită programului într-o formă asemănătoare. iar a treia va afişa valoarea PID a procesului fiu. 6.(int)getpid()).3.").(int)getpid()). else printf("PID fiu: %d\n".

funcţional toate se comportă la fel: int execlp( const char *file. Ca şi în cazul apelului fork majoritatea atributelor sunt păstrate în urma unui apel exec. int execvp( const char *file. Se poate pune problema ce face procesul părinte în timp ce procesul fiu rulează. În mod normal interpretorul de comenzi aşteaptă. acesta este fişierul care va fi rulat. char *const argv [].. Cel mai important aspect este acela că descriptorii de fişiere asociaţi cu cu descritporii de fişiere dechise rămân valabile şi după apelul lui exec. exstat = WEXITSTAUS(statval). Prototipul acestui apel sistem este următorul: #include <sys/types. iar –l argv[1]. char * const envp[]). Prototipul apelului sistem exit este următorul: #include <stdlib. char *const argv[]). pid_t pid. Ex: #include <sys/wait. până la terminarea execuţiei acesteia. În exemplu ls va fi argv[0]. char *const argv[]). Pentru ca procesul părinte să aştepte terminarea execuţiei unui proces fiu. Modificarea acestui flag se poate face prin intermediul apelului sistem fcntl. char *const envp[]).). pid = wait(&statval). Parametrul care îl primeşte apelul sistem wait este o referinţă către o locaţie care va primi valoarea stării de terminare a procesului fiu. int execv( const char *path.Primul parametru pentru execl este calea completă pentru programul ce urmeză a fi rulat. const char *arg . procesul părinte trebuie să execute apelul sistem wait(). 6..h> int statval. Acest lucru se datorează faptului că segmentul sistem rămâne intact în urma unui apel exec. Dacă acest flag nu este activat atunci descriptorul de fişier respectiv este valabil şi după apelul exec. pentru fiecare comandă introdusă din linia de comenzi. Pe parcursul rulării procesuli fiu procesul părinte fie aşteaptă ca acesta să îşi termine excuţia. Apelul sitem wait poate mai multe informaţii referitoare la starea procesului fiu la terminare prin intermediul valorii de stare..at mesjul terminat cu erori). În cazul ultimelor trei apeluri argumentele ce trebuie transmise programelor sunt organizate sub forma unei liste.. . Când un proces şi-a treminat execuţia el va executa apelul sistem call fie în mod explicit fie indirect prin intermediul unei biblioteci. Dar utilizatorul are posibilitatea indica interpretorului de comenzi să nu mai aştepte terminarea comenzii şi să îşi reia execuţia imediat prin adăugarea caracterului ‘&’ la sfârşitul comenzii.h> pid_t wait(int *status) Apelul sistem wait retrnează PID-ul procesului fiu ce şi-a încheiat execuţia. Atenţie! Din apelurile exec nu se revine. În plus pentru a face toţi aceşti parametrii disponibili noului program apelurile exec transmit o valoare şi pentru variabila: extern char **environ.h> #include <sys/wait. În cazul interpretorului de comenzi de exemplu. Deci mesajul terminat cu erori din exemplu nu va fi afişat. Pentru extragerea acestor informaţii se poate folosi un macro numit WEXITSTATUS. alegerea rămâne la latitudinea utilizatorului. int execle( const char *path. exstat. Apelurile sistem wait and exit 45 46 . de aici se obţin şi drepturile de acces la fişier(dacă are sau nu drept de execuţie) restul parametrilor sunt folosiţi pentru lista de argumente argv. fie îşi continuă execuţia.. Singura excepţie este un marcaj (flag) asociat descriptorilor de fişiere (nu este asociat descriptorilor de fişiere deschise) numit close on exec. Acest apel sistem mai conţine şi alte veriuni singurele diferenţe constând în modul de apelare. int execve (const char *filename. const char *arg.4. . Această variabilă are acelaşi format ca şi variabila argv diferenţa fiind că prin variabila environ se transmit valori ale variabilelor aparţinând mediului de lucru ale procesului original. Singura excepţie este cazul în care a apărut o eroare la apelul exec în această situaţie se revine la vechiul proces şi se returnează şi codul de eroare (în această situaţie va fi afi.h> void exit(int status) Acest apel sistem primeşte ca şi argument un parametru de stare care va fi transmis procesului părinte prin intermediul unei locaţii referită de parametrul apelului wait.

Când procesul părinte efectuează apelul wait.h> pid_t waitpid(pid_t pid.Schema bloc care descrie funcţionarea a două procese. În această situaţie procesul fiu va intra într-o stare numită zombie. int *status. >0 aşteaptă încheierea execuţiei procesului fiu a cărui PID este egal cu pid. 0 aşteaptă încheierea execuţiei oricărui proces fiu a cărui ID de grup este egal cu ID-ul de grup al procesului. arată astfel: Pentru parametrul pid aveum următoarele posibilităţi: < -1 aşteaptă încheierea execuţiei oricărui proces fiu a cărui ID de grup este egal cu pid. starea de ieşire este transmisă procesului părinte iar resursele ocupate cu structura de date a procesului fiu pot fi eliberate. iar al doilea proces fiu afişează litere din alfabet. primul proces fiu de la 5000-10000. Noul program Execuţia părintelui este exit Execuţia procesului părinte O altă versiune a apelului sistem wait este waitpid cu următorul prototip: #include <sys/types. fork - exec wait Există posibilitatea ca un proces fiu să îşi încheie execuţia înainte ca procesul părinte să apeleze wait. Prin parametrul options oferă facilităţi suplimentare. De exemplu în urma unnui apel waitpid procesul părinte nu îşi suspendă execuţia dacă nici un proces fiu nu are disponibilă starea de ieşire.h> #include <sys/wait. int options). Fiecare proces îşi afişează PID-ul. în care toate resursele ocupate de procesul fiu au fost eliberate cu excepţia structurii de date care conţine starea sa de ieşire. 47 48 . -1 comportament identic cu pid. unul părinte care aşteaptă terminarea execuţiei procesului fiu şi a procesului fiu care foloseşte apelul sistem exec şi a cărui execuţie se termină cu apelul exit. iar procesul părinte numără de la 15000. De studiat! Să se realizeze un program in C care să creeze trei procese (inclusiv părintele.

adică un proces poate primi un semnal la orice moment şi trebuie să fie pregătit să îi răspundă. 2 Comunicarea între procese Fiecare proces operează în propriul său spaţiu de adrese virtuale. 5) SIGTRAP 6) SIGIOT 7) SIGBUS 8) SIGFPE 9) SIGKILL – termină procesul. 2) SIGINT – întrerupere de la tastatură (opreşte procesul). Există mai multe tipuri semnale şi fiecare putând fi accesat utilizând pe baza unui nume 1 . La primirea unui semnal un proces poate alege modul de tratare a acestuia. 4) SIGILL – acţiune ilegală (opreşte procesul şi creează un fişier core).Capitolul 7 simbolic.1. Alternativ procesul poate specifica pentru un anumit semnal propria rutină de tratare. Apelul acestei rutine vafi facut de către kernel. Semnale Una dintre cele mai vechi metode de comunicare între procese în sistemele de tip Unix(Linux) se bazează pe semnale. acesta se încheie prin returnarea unui cod de eroare(EINTR) şi este sarcina procesului să refacă apelul întrerupt. 3) SIGQUIT – opreşte procesul (opreşte procesul şi creează un fişier core). iar adresa acestei este reţinută în structura sigaction. cozi de mesaje – este o listă legată de spaţiul de adresare a kernel-ului semafoare – sunt contoare utilizate pentru a gestiona accesul la resursele utilizat în comun de mai multe procese Cel mai des sunt folosite ca şi mecanism de blocare a unei resurse pentru a preveni accesul simultan a mai multor procese la o anumită resursă.permit realiazrea de conexiuni între procese locale sau în reţea. • Reface acţiunea implicită a semnalului. Sockets. să împartă resurse comune sau să îşi sincronizeze acţiunile. Nu orice proces din sistem poate trimite semnal oricărui alt proces. Această rutină va fi apelată de fiecare dată când este generat semnalul respectiv. o alternativă este să rămână la acţiunea implicită. acest lucru este valabil numai pentru procesele ce aparţin kernelului sau superutilizatorului (root). Implicit un proces poate trata un semnal în următoarele moduri: • Îşi încheie execuţia • Ignoră semnalul – două semnale nu pot fi ignorate: SIGKILL – care termină execuţia procesului şi SIGSTOP care îi opreşte execuţia. Există mai multe metode de comunicare între procese: • • • • • fişiere – este cel mai simplu mecanism de comunicare interproces. Implicit un proces nu poate comunica cu un alt proces numai dacă face uz de diverse mecanisme de comunicare gestionate de kernel(nucleul sistemului de operare). 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD – procesul fiu oprit sau terminat 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR De studiat! Consultaţi pagina de manual pentru signal din secţiunea 7. În continuare se prezintă o listă cu numărul şi numele simbolic a semnalelor folosite în mod uzual: 1) SIGHUP – terminal închis (opreşte procesul). SIGINIT). Procesele normale pot trimite semnale numai proceselor cu acelaşi UID şi GID. • 7. Dacă un semnal survine pe perioada execuţiei unui apel sistem. deci procesul va rula în mod kernelpe parcursul tratării semnalului. Un proces scrie într-un fişier iar celălat proces citeşte din fişier. Fiecare nume unic reprezintă o abreviere ce începe cu SIG (de ex. semnale – sunt utilizate pentru a semnala asincron evenimente între procese pipes – un pipe conectează ieşirea standard a unui proces cu intrarea standard a altuia. iar de evitarea interferenţelor dintre procese se ocupă sistemul de operare. De exemplu semnalul SIGSTOP va modifica starea curentă a procesului care îl primeşte în STOPED şi apoi va solicita kernelului(schedulerului) sau ruleze un alt proces. Există totuşi diverse situaţii în care procesele trebuie să coopereze. iar tratarea va fi făcută de kernel. O listă completă a setului de semnale din sistem se poate obţine prin intermediul comenzii kill –l. O caracteristică importantă a acestor semanle este că ele sunt asincrone. Fiecare tip de semnal are asociată o acţiune ce va fi executată de nucleul sistemului de operare (kernel) asupra procesului când procesul primeşte semnalul respectiv. sau proceselor din acelaşi grup.

După realizarea acestei legături orice semnal SIGINT primit deproces va determina execuţia rutinei de tratare (handler).h> void (*signal(int signum. Pentru acest tip de apel sistem apare o problemă legată de un scurt interval de timp în care tratarea semnalului se face în mod implicit. ca rutină de tratare a semnalului va fi folosită funcţia handler. Prototipul acestui apel sistem este următorul: #include <signal.handler). } } Acest segment de cod afişează la terminal. Semnalele nu sunt transmise proceselor imediat ce sunt generate. Această funcţie doar afişează mesajul CTRL-C. Un exemplu de utilizare a acestui apel sistem 3 . La orice tentativă de a opri programul de la tastatură prin secvenţa CTRL-C semnalul SIGINT va fi prins de program.. iar procesul este trecut în coada de execuţie.h> #include <signal. iar dacă pentru respectivul proces sunt semnale neblocate ele pot fi acum transmise procesului. } void main(void){ (void) signal(SIGINT. (void) signal(SIGINT. while(1){ printf("Hello World!\n").Generarea unui semnal se realizează prin prin setarea bit-ului coespunzător din structura task_struct. void (*sighandler)(int)))(int).h> void handler(int sig){ printf("CTRL-C\n"). La prima vedere acest prototip pare complicat dar la o analiză mai atentă se poate observa că sunt necesari doar doi parametrii: • • signum – un număr întreg care indică semnalul care va fi generat. sleep(1). Un exemplu clasic de terminare a unui proces este următorul: ps | grep processname kill -9 PID sau kill –KILL PID În cele ce urmeză se va face o scurtă prezentare a celor mai importante apeluri sistem folosite pentru lucrul cu semnale. ceea ce nu este de dorit.h> #include <unistd. ele trebuie să aştepte până în momentul în care procesul rulează din nou. La nivel de interpretor de comenzi o modalitate de trimitere a semnalelor către procese este prin intermediul comenzii kill. În acest fel procesul va fi luat în calcul între procesele care vor primi timp de procesor. Valoarea returnată de signal() este o referinţă către o funcţie care primeşte ca şi parametru un singur întreg şi nu returnează nimic (void). handler – o referinţă către o funcţie care va fi folosită ca rutină de tratare pentru semnalul respectiv. #include <stdio. Apelul sistem sigaction 4 Apelul sistem signal: În Linux sunt disponibile mai multe apeluri sistem ce pot fi utilizate pentru generarea semnalelor. Această refacere a legăturii între semnal şi rutina de tratare este necesară datorită faptului că după primirea unui semnal se revine la tratarea implicită (terminarea execuţieie programului). Apelul sistem signal poate fi întâlnit şi la celelate versiuni de Unix. adică va determina încheierea execuţiei programului. De fiecare dată când un proces părăseşte un apel sistem sunt verificate câmpurile sale signal şi blocked . Dacă procesul nu a blocat semnalul şi se află în aşteptare dar este întreruptibil atunci starea acestuia este modififcată în Running.handler). în buclă infinită. mesajul Hello World!. totodată reface legătura între semnal şi funcţie. Legătura între semnal (SIGINT) şi rutina de tratare (handler) se realizează la apelul din funcţia main. şi dacă pe parcursul acestui interval de timp survine un semnal acesta va fi tratat în mod implicit.

iar completarea acestei structuri o va face apelul sigaction() bazându-se pe vechea structură sigaction. dacă are permisiunile corespunzătoare. În locul referinţei către o funcţie în acest caz se transmite ca şi parametru o referinţă către o structură numită struct sigaction. dar înlătură problema apărută anterior. Spre deosebire de signal. void *). Dintre aceste flag-uri cele mai semnificative sunt: • • SA_ONESHOT – semnalul va fi tratat în mod implicit. SA_NOMASK . #include <unistd. Unde prin sig se precizează semnalul care va fi trimis. modificarea stării unui proces sau intervenţia utilizatorului de la consolă. Apelul sistem alarm() primeşte un singur parametru care este intervalul de timp. Când un semnal este generat. iar pid are următoarele sensuri: • • • • pid > 0 – semnalul sig va fi trimis procesului pentru care PID = pid. În marea majoritate a cazurilor mai importante sunt semnalele generate de apariţia unor evenimente: erori hardware. pid = -1 – semnalul sig va fi trimis tuturor proceselor din sistem cu excepţia procesului init şi a procesului ce a făcut apelul. pid < -1 – trimite semnalul sig tuturor proceselor pentru care GID=pid. Totodată structura mai conţine şi un câmp sa_mask acesta funcţionează ca şi o mască de semnale suplimentară. 5 . Conform acestor specificaţii fiecare proces are asociată câte o mască. Fiecare proces are asociat un ceas pe care îl poate flosi pentru a-şi trimite semnale de tipul SIGALRM după depăşirea unui anumit interval de timp. valoarea returnată de sigaction nu include o referinţă către vechea rutină de tratare.ignoră câmpul sa_mask din structura sigaction. el este automat adăugat la masca de semnale a procesului ţintă. Prototipul acestui apel sistem este următorul: #include <sys/types. } După cum se poate observa referinţa către rutina de tratare a fost introdusă în această structură. În afară de aceste situaţii există posibilitatea ca un proces să trimită semnale în mod deliberat unui alt proces. void (*sa_restorer)(void).va avea două procese: unul fiu şi unul părinte. int sa_flags. De studiat! Să se realizeze un program care are următoarele caracteristici: . Apelul sistem alarm. Aceasta are următoarea structură: struct sigaction { void (*sa_handler)(int).h> int kill(pid_t pid. Pentru obţinerea de informaţii adiţionale s-a introdus un parametru suplimentar siginfo_t. 6 Ambele flag-uri vor fi active dacă pentru se foloseşte apelul signal în loc de sigaction.Acest apel sistem este conform standardelor POSIX. siginfo_t *. în secunde.h> unsigned int alarm(unsigned int seconds). Apelul sistem kill. void (*sa_sigaction)(int. după care se va genera semnalul. utilizând apelul sistem kill. care precizează setul de semnale care nu pot fi livrate procesului la momentul curent. struct sigaction *oldact). este mai complex. În schimb primeşte un nou parametru.h> #include <signal. Prototipul pentru acest apel sistem este următorul: #include <signal. sigset_t sa_mask. Câmpul sa_flags se interpretează la nivel de bit. pid = 0 – semnalul sig va fi trimis tuturor proceselor cu acelaşi PID ca şi procesul ce a făcut apelul.h> int sigaction(int signum. Dacă totuşi procesul primeşte un semnal ce aparţine acestei liste el va fi pus într-o coadă de aşteptare şi va fi transmis procesului atunci când blocajul este înlăturat. Acesta este o altă referinţă către o structură de tipul sigaction. astfel încât orice instanţă viitoare a acestui semnal va fi blocată până la servirea semnalului curent. const struct sigaction *act. int sig). şi este valabilă pe parcursul execuţiei rutinei de tratare a semnalului.

Pipes În Linux.pid-ul său şi al fiului precum şi starea procesului fiu la terminarea execuţiei. Argumentul pfd este un tablou cu două elemente care după execuţia funcţiei va conţine: • • Descriptorul de fişier pentru citire – pfd[0]. iar celălat pentru a creea o cale de ieşire. Astfel din pfd[0]se citesc datele.procesul părinte va afişa litera "A" pentru o secundă. Aşa cum s-a mai precizat comunicarea prin intermediul pipe-urilor este specifică comunicării între procese. Dar situaţia se schimba daca după crearea pipe-ului are loc şi crearea unui proces prin intermediul apelului sistem fork.1 Apeluri sistem Ceea ce s-a prezentat până acum a fost doar o prezentare de ansamblu. iar exemplul a fost la nivelul interpretorului de comenzi. Apelul sistem fork – se creează procesele părinte şi fiu. Programul C corespunzător va realiza următorii paşi: • Apelul sistem pipe – creează descriptorii de citire/scriere. #include<unistd. În acestă situaţie procesul părinte va moşteni toţi descriptorii de fişiere deschişi pentru procesul părinte. iar pipe-ul este 7 8 . de citire şi scriere. în tabloul pfd. . majoritatea intrepretoarelor de comenzi acceptă redirectarea. Pentru a utiliza facilităţile oferite de pipe-uri în cadrul limbajului de programare C.procesul fiu va afişa litera "b" pentru o secundă.h> int pipe(int pfd[2]). Pipe-urile sunt una dintre cele mai vechi metode utilizate la comunicarea între procese. Din ceea ce s-a prezentat până acum utilizarea pipe-urilor nu ar avea nici un sens deoarece în această situaţie un pipe ar fi folosit doar pentru comunicarea in cadrul unui şi acelaşi proces. Deci un pipe este o metodă prin care se poate realiza conectarea ieşirii standard a unui proces cu intrarea standard altuia. 7. Returnează 0 în caz de succes şi -1 în caz de eroare. Această cale de comunicaţie se bazează pe cei doi descriptori de fişier care sunt returnaţi. Când un proces creează un pipe. şi le poate obţine prin fd1. • Din diagrama anterioară se poate observa modul de conectare a descriptorilor de fişiere. iar în pfd[1] se scriu datele. între două pocese. şi bazându-ne pe pe acest mecanism avem bazele comunicării intreproces (mai precis între procese aflate în relaţia părinte-fiu). deci în conjuncţie cu apelul sistem fork. care la rândul lui aparţine tot kernel-ului şi nu este legat de nici un sistem fizic de fişiere. Unul dintre descriptori este folosit pentru a creea o cale de intrare (scriere) în pipe. Apelul sistem pipe Este utilizat pentru creearea unui fişier de tip pipe. Un exemplu clasic în acest sens este secvenţa de comenzi: $cat /etc/passwd | wc -l Rezultatul acestei secvenţe de comenzi este că ieşierea standard a comenzii cat devine intrare standard pentru comanda wc. Un exemplu clasic este situaţia în care se doreşte ca procesul părinte să trasmită un mesaj procesului fiu.. . de unde şi denumirea de unidirecţionale (half-duplex).procesul părinte va aştepta teminarea execuţiei procesului fiu şi va afişa .2. cei doi descriptori se vor regăsi în ambele procese. În acest punct utililzarea unui pipe nu are o utilitate practică prea mare atâta vreme cât procesul poate comunica numai cu el însuşi.2. intrare Proces iesire Nucleu (kernel) Rolul acestui apel sistem este de a crea o cale de comunicaţie. O reprezentare a procesului şi a kernelului după ce a fost creat un pipe: reprezentat prin intremediul unui inode. de apelul sistem pipe. 7. Nici unul dintre procesele implicate nu sunt conştiente de aceste redirectări şi este sarcina intrepretorului de comenzi să gestioneze aceste pipe-uri. nuclelul (kernelul) va genera doi descriptori de fişiere care urmeză să fie utilizaţi împreună cu pipe-ul. La nivel de pipe transferul datelor se face în interiorul kernelui. Un proces poate trimite date (scrie) prin fd0. Descriptorul de fişier pentru scriere – pfd[1]. Comunicaţia realizată între două procese se poate realiza într-o singură direcţie.

#include <unistd. } else { } Deoarece descriptorul de fişier 0 (intrarea standard) a fost închis. O utilizare imediată a acestui apel sistem ar fi posibilitatea redirectării intrării/ieşirii standard. Atenţie! Cu dup2 vechiul descriptor este închis. execlp("sort". //trimite mesajul prin descriptorul de scriere . situaţie datorată apelului sistem fork. readbuffer). Dacă vom considera: . readbuffer. sau -1 în caz de eroare.h> #include <unistd. . exit(1). int dup2(int fd. //şi îl afisează printf("Received string: %s". pipe(pfd). Închiderea descriptorilor neutilizaţi este necesară din cauză că descriptorii de fişiere sunt aceeaşi atât în procesul părinte cât şi în procesul fiu. apelul dup forţează descriptorul de intare a pipe-ului ca intrare standard. (strlen(mesaj)+1)).• • • Procesul părinte va închide pfd[0] descriptorul de citire. NULL). descriptorul (numărul) returnat este cel mai mic disponibil. sizeof(readbuffer)). if(pid_fiu == 0) { // închide intrarea standard a procesului fiu close(0). Apelul dup – deşi vechiul şi noul descriptor pot fi utilizate pe rând. Acest apel sistem creează un nou descriptor de fişier pe lângă cel existent. . //citeşte mesajul din pipe nr_bytes = read(pfd[0]. readbuffer[80]. mesaj[20] = "Hello world!". } if(pid_fiu == 0) { //procesul fiu închide descriptorul de scriere close(pfd[1]). #include <stdio. apoi se realizează apelul sistem execlp() care suprascrie segmentul de cod a procesului fiu cu programul de sortare.h> #include <sys/types. Pentru că noul program executat moşteneşte căile de comunicare ale procesului din care a fost creat el moşteneşte de fapt ca şi cale de 9 10 //procesul părinte închide descriptorul de //citire close(pfd[0]). Se realizează transferul mesajului prin intermediul descritorilor de fişier rămaşi deschişi. // duplică partea de intrare a pipe-ului cu intrarea standard dup(pfd[0]). nr_bytes. } Apelul sistem dup şi dup2 Este utilizat pentru a duplica un descriptor de fişier. pid_fiu = fork(). "sort". write(pfd[1]. mesaj.h> int dup(int fd).h> int main(void){ int pid_t char char pfd[2]. Procesul fiu va închide pfd[1] descriptorul de scriere. Returnează noul descriptor în caz de succes. exit(0). int nfd). . } return(0). if((pid_fiu = fork()) == -1) { perror("fork"). Programul C corespunzător este prezentat în cele ce urmează. de obicei una dintre căile de comunicare standard sunt închise. pid_fiu.

intrare standard descriptorul de citire din pipe. atât apelul de duplicare a căii de comunicare standard cât şi operaţia de închidere a căii de comunicare standard. timp de 5 secunde. într-un singur apel sistem. adică nu va fi acceptată nici o cerere de întrerupere (semnal) din partea sistemului pe parcursul execuţiei acestui apel. În plus acest apel sistem este garantat a fi atomic. if(pid_fiu == 0) { //închide şi duplică intarea standard dup2(0. dat de intervalul dintre cele două apeluri. dupa 5 secunde va trimite celui de al doilea fiu o statistică cu numărul de caractere afişate. Procesul părinte va produce şi afişa caractere “A” ce vor fi trimise printr-un pipe primului fiu. În acest caz vom avea: . . "sort". Primul fiul va număra caracterele primite de le procesul părinte si pentru fiecare 10 caractere de tip “A” va afişa un caracter “B”. Al doilea fiu va afişa statistica primită de la primul fiu. Astfel tot ce trimite procesul părinte iniţial prin pipe ajunge la programul de sortare. Apelul dup2 – acest apel include. Adică aveam două apeluri sistem. • 11 12 . NULL). . } De studiat Să se realizeze un program cu următoarele caracteristici: • • • Va avea trei procese: un proces părinte şi două procese fiu. pfd[0]). execlp("sort". În cazul apelului sistem dup() trebuia realizată operaţia close înainte. . Dacă soseşte un semnal între cele două apeluri operaţia de duplicare nu mai are loc. ceea ce creea un anunit grad de vulnerabilitate. pid_fiu = fork().

numar de idebtificare proces. • PID .Gestionare de procese Sa se proiecteze si implementeze un program de vizualizare a proceselor in sistemul de operare Linux.Proiect . • stare (status). . dupa un anumit interval de timp (secunde) ce poate fi modificat dinamic in timpul rularii. Cerinte: • pentru fiecare proces sa se afiseze (vezi /proc): • nume. sa ii poata fi trimise semnale. identificat prin PID. • afisarea proceselor sa se relizeze periodic. • numele utilizatorului ce a lansat in executie procesul(vezi /etc/passwd). • unui anumit proces.

acest obiectiv se poate realiza prin utilizarea mecanismelor de comunicare interproces. 2 . O problemă majoră ce poate apare este acapararea de către un fir a întregului timp de procesor asociat unui proces (ex. dacă procesul execută o operaţie de I/E). date. copiate procesul fiu putând modifica oricare resursă fără a afecta proceslul părinte. Ambele thread-uri vor utiliza resursele sistem în comun. stivă. şi viceversa.a. La execuţia unui program. date. Avanatajul major este dat de planificarea controlată a firelor de execuţie (la expirarea cuantei de timp de procesor nucleul îi va întrerupe execuţia şi va transmite controlul firului următor). ansamblul acestor thread-uri reprezentând programul apelat.Capitolul 7 Fire de execuţie O caracteristică esenţială a unui sistem de operare de tip Unix/Linux este execuţia concurentă a mai multor procese. thread-ul este o “subdiviziune”. din thread-ul iniţial se pot creea thread-uri suplimentare. poate excuta doar un singur program la un moment dat. De asemenea datorită faptului ca un proces. nucleu sistemului de operare nu trebuie să ia măsuri suplimenatre de protecţie a resurselor acestora în cadrul unui proces. sistemul de operare crează un nou proces şi în cadrul procesului respectiv va creea un singur thread în care programul apelat se execută secvenţial. sistemului de operare care întrerupe rulerea thread-urilor la anumite intervale de timp pentru a oferi şi altora şansa de a fi rulate. şi de asemenea un anumit timp de execuţie pe procesor. firele de execuţie par a rula concurent. Dacă în cazul crearii unui nou proces. se definesc un set de rutine ce sunt responsabile de comutarea execuţiei între fire. În acest caz operaţiile necesare pentru comutarea între două fire de execuţie implică un efort semnnificativ mai mic decât în situaţia unor procese. care comparativ cu firele de execuţie implică un efort de comutare mai mare şi ca urmare se mai numesc şi “procese grele” (heavy weight processes – HWP’s) În general procesele pot fi implementate în două moduri: •În spaţiul nucleului – de obicei aceste procese sunt implementate în nucleul sistemului de operare. implementare ce se bazează pe controlul. deci este exclusă blocarea procesului datorată unor operaţii de I/E.numită şi multitasking cooperativ. memoria şi structurile da date. celelate thread-uri nu vor mai putea efectua operaţii asupra acelu descriptor de fişier. Aceste operaţii sunt necesare pentru a se asigura faptul că nu apar interferenţe între diferitelor resurse ale unor procese diferite. a tabelelor de semnale asociate firului de execuţie. •În spaţiul utilizatorului – în acest caz este evitat controlul nucleului asupra firului de execuţie – pentru o astfel implementare. opţional. Comutarea execuţiei între două procese implică un număr relativ mare de operaţii. la fel ca şi procesele. dacă un thread închide un descriptor de fişier. trimiterea unui semnal. Dacă se doreşte eliminarea acestor operaţii suplimentare atunci procele pot fi înlocuite cu fire de execuţie. Un proces. descriptori de fişiere. procesului din care face parte firul. are mai multe părţi componente cod. procesul fiu execută programul procesului părinte dar cu resursele părintelui. kernel-ul . ş. Tipic un fir de execuţie cedează in mod explicit procesorul.. fiecare thread executând părţi diferinte ale programului la momente diferite de timp. La polul opus se află procesele ca atare. dacă un thread apelează una dintre funcţiile exec execuţia celorlalte thread-uri este întreruptă. Conceptual. la creea rea unui thread nimic nu este copiat. în nucleu. în general. el există doar în cadrul unui proces. gobal. Deoarece firele de execuţie au fost proiectate astfel încât să fie cooperative. Din acest motiv firele de execuţie au primit denumirea de “procese uşoare”. În acest caz nucleul sistemului de operare este răspunzător de programarea execuţieie fiecarui fir. respectiv thread-urile asociate unui proces. 1 Firele de execuţie mai sunt numite şi “procese uşoare” (light weight procesess – LWP’s). În cazul firelor de execuţie vom avea mai multe procese care au alocate propriile lor intervale de timp de procesor dar folosesc în comun segementele de cod. iar în cazul sistemelor de calcul cu mai multe procesoare performanţa poate creşte proporţional cu numărul de procesoare adăugate sistemului. Dezavantajul acestei metode de control al timpului de execuţie este dat de apariţia unui efort suplimentar la comutarea utilizator-nucleu-utilizator. memorie virtuală. pentru atingerea unui scop comun. iar intervalul de timp alocat firului este scăzut din timpul alocat. responsabil de acestă “concurenţă” este nucleul. Totuşi şi în cazul utilizării acestor mecanisme sunt necesare operaţiile de comunicare între procese care impun operaţii suplimentare. prin apelul explicit a unei rutine. În situaţia în care se doreşte ca două sau mai multe procese să coopereze. o posibilă rezolvare este utilizarea unui semnal de timp care să determine comutarea între fire. această condiţie fiind necesară în cadrul unui sistem multiutilizator.

Prin acest argument se controleză modul în care thread-ul intreacţionează cu restul programului. O problemă ce suportă două moduri de tratare.h> //funcţia ce va fi executată de thread-uri 3 4 . iar la ieşirea din funcţie se termină şi execuţia thread-ului. sleep(1). O primă modalitate este cea prezentată în exemplul anterior în care funcţia tratată de thread se încheie prin aplelul return.h> #include <stdlib. Acesta trebuie să fie de tip void *."second thread error\n"). &print_message. // creearea celui de al doilea thread – tipăreşte World if(pthread_create( &thread2. O referinţă către un obiect de tip thread attribute. exit(1). de această dată realizat cu thread-uri: #include <stdio. iar la creeare ficar thread execută o funcţie. } // “adoarme” procesul părinte pentru o secundă. Funcţia pthread_creat creează noi thread-uri şi trebuie să i se transmită următoarele argumente: O referinţă către o variabilă de tip pthread_h.cc -D_REENTRANT -lpthread Opţiunea lpthread trebuie specificată la compilarea programului datorită faptului că suportul pentru thread-uri este adăugat sub forma unei biblioteci. iar revenirea din funcţie se face imediat. Argument către thread.Dacă argumentul transmis este NULL atunci se vor folosi setările implicite. Apelul sistem de bază pentru creearea de noi thread-uri este pthread_creat. // creearea primului thread – tipăreşte Hello if (pthread_create( &thread1. pthread_attr_t *attr. Un exemplu clasic de program este Hello World. } sleep(1). return NULL. void * void* print_message( void *ptr ){ char *message. (void*)message2)){ fprintf(stderr. *thread. NULL. O altă modalitate este prin intermediul apelului sistem pthread_exit. iar execuţia unui program nu trebuie să se bazeze pe o anumită ordine de rulare a thread-rilor.h> #include <pthread. În cadrul unui proces un thread este recunoscut pe baza unui identificator. &print_message.h> int pthread_create(pthread_t (*start_routine)(void *).În paragrafele ce urmează discuţia se va baza pe fire de execuţie POSIX numite şi pthreads (firele ce nu se conformează acestui standard se numesc cthreads). message = (char *) ptr. în care este salvat identificatorul thread-ului. Rferinţa către funcţie trebuie să fie de tipul: void* (* ) (void *). char *message2 = "World\n". void *arg). printf("%s ". în cazul thread-urilor.h> #include <unistd. } int main(){ pthread_t thread1. (void*)message1)){ fprintf(stderr. char *message1 = "Hello". este cea a terminării execuţiei şi a valorii returnate de thread. NULL. } Compilarea programului se realizează cu următoarea comandă: $ gcc -o hello_thread hello_thread. message). exit(1). iar valoarea transmisă este considerată ca fiind valoare returnată de thread. Acesta este o funcţie obişnuită ce conţine codul ce trebuie rulat de thread. O referinţă către funcţia ce urmează a fi executată de thread. thread2. exit(0). Rularea celor două thread-uri (părinte şi fiu) are loc asincron."first thread error\n"). #include <pthread. La apelul funcţiei phtread_creat are loc creaerea thread-ului. şi este transmis funcţiei thread-ului.

Dacă valoarea returnată de thread-ul th nu interesează. pthread_create(&thread2. Funcţia thread-ului primeşte ca şi argumenete referinţe către structuri de date definite ca şi variabile locale în thread-ul principal al programului. . Valoarea primită ca şi argument este valoarea ce va fi returnată de thread. Totuşi acest program ascunde o problemă de funcţionare.h> #include<unistd. } int main(){ pthread_t thread1.val='b'. 5 6 // creearea thread-urilor pthread_create(&thread1.h> int pthread_join(pthread_t th. Valoarea acetui parametru poate fi o referinţă către orice structură de date. int count.h> // definirea structurii de date ce va fi transmisă ca argument thread-ului struct chars { char val.thread2_arg. thread1_arg.i<c->count.&thread1_arg).#include <pthread. . Tot din exemplul anterior se poate observa că transmiterea parametrilor către thread se realizează prin ultimul parametru al apelului pthread_creat. . struct chars thread1_arg. Dacă nu se doreşte transmiterea unui parametru funcţiei thread-ului atunci argmentul respectiv primeşte valoare NULL. iar al doilea este o referinţă către o locaţie de memorie în care se va reţine valoarea returnată de threaul th.h> #include<stdlib. void **thread_return).count=100000. Acestă funcţie poate fi apelată direct din funcţia thread-ului sau din altă funcţie apelată direct sau indirect din funcţia thread-ului.i++) putchar(c->val).h> #include<pthread. thread2_arg. // funcţia thread-ului void *char_print(void *ptr){ struct chars* c = (struct chars*)ptr.h> void pthread_exit(void *retval). O consecinţă este faptul că este dealocată memoria rezervată structurilor de date transmise ca şi parametrii thread-urilor. Acest apel sistem suspendă execuţia thread-ului apelant până la încheierea execuţiei thread-ului identificat prin ID-ul th. thread2.val='a'.NULL. // stabilirea parametrilor pentru thread-uri thread1_arg.&char_print. Pthread_join primeşte două argumente – primul este ID-ul thread-ului după care thread-ul apelant trebuie să aştepete să îşi încheie execuţia. O rezolvare a acestei probleme este apelul sistem pthread_join: #include <pthread. totodată este şi un exmplu ce ilustrează modul în care se pot transmite funcţiei thread-ului argumente mai complexe. În consecinţă pentru ca exemplul anterior să fie funcţional trebuie completat cu următorul cod: .NULL. }. for(int i=0. pthread_exit(0). atunci al doilea argument va fi NULL. } Exemplul de mai sus creează două thread-uri fiecare afişând câte un caracter de câte un anumit număr de ori. // încheierea execuţiei exit(0). thread2_arg.count=150000.&char_print.&thread2_arg).Datorită faptului că nu există nici o regulă asupra ordinii de executare a thread-utilor este posibil ca thread-ul pricipal să îşi încheie execuţia înaintea celor două thread-uri de afişare a caracterelor. #include<stdio.

Relativ la acest tip de obiecte sunt definite mai multe apeluri sistem ditre care cele mai importante sunt: pthread_attr_init . 4. # include<pthread. Uneori este util de determinat care este thread-ul care execută un anumit cod. Pentru a defini un thread cu atribute particulare se aplică următorii paşi: 1. atunci se vor folosi atributele implicite pentru creearea thread-ului. #include <pthread. // încheierea execuţiei exit(0). alt_thread)) pthread_join(alt_thread.&thread2_arg). int pthread_equal(pthread_t thread1. De exenplu este o eroare ca un thread să apeleze pthread_join pentru el însuşi. NULL).Se creează un obiect de tipul thread attribute – se declară o variabilă de acest tip. Dacă se doreşte creeare unui thread pe baza unor atribute particulare este nevoie de creearea unui obiect de tip thread attribute. #include <pthread. Pentru a evita o astfel de eroare se poate utiliza următoarea secvenţă de cod: Observaţie: Un singur obiect de tip thread attribute poate fi utilizat pentru creearea mai multor thread-uri.NULL. Dacă referinţa este NULL. if(!pthread_equal(pthread_self().iniţializează obiectul primit ca şi argument cu valorile implicite. după care prezenţa acestui nu mai este necesară şi obiectul poate fi dealocat. De asemenea putem compara două thread-uri pe baza ID-urilor cu apelul pthread_equal. pthread_t thread2).NULL. // thread-ul părinte aşteaptă încheierea excuţiei thread-urilor fiu. pthread_create(&thread2.h> 7 8 .&char_print.Obiectul creeat este transmis ca şi parametru funcţiei pthread_attr_init – obiectul este iniţializat cu atributele implicite.// creearea thread-urilor pthread_create(&thread1. pthread_join(thread2. care are ca şi rezultat ID-ul thread-ului care îl apelează. 5.Resursele ocupate de thread attribute trebuie eliberate – se foloseşte pthread_attr_destroy. int pthread_attr_init(pthread_attr_t *attr).&thread1_arg).Se creea un thread pe baza apelului pthread_create căruia i se transmite ca şi argument nou creatul thread attribute.Se modifică atributele dorite. de thread-ul părinte sau de oricare alt thread. 3. 2. avem la dispoziţie apelul sistem pthread_self. De fapt acest apel sistem primeşte ca şi argument o referinţă către un obiect de tip thread attribute.NULL). Pentru a determina ID-ul unui thread.&char_print. pthread_join(thread1.h> Observaţie: Trebuie avut grijă ca orice tip de date ce este transmis unui thread prin referinţă să nu fie dealocat. pthread_t pthread_self(void). } Un alt aspect important în ceea ce priveşte creearea şi utilizarea thread-urilor atributele thread-urilor ce pot fi transmise ca şi argument funcţiei pthread_create. şi pthread_attr_destroy – dealocă resursele ocupate de obiectul primit ca şi argument.h> int pthread_attr_destroy(pthread_attr_t *attr).NULL).

Conform acestui atribut un thread poate fi creat fie joinable (implicit). Pentru ca un thread să fie asincron anulabil (implicit este sincron) se foloseşte apelul pthread_setcanceltype unde primul argument este PTHREAD_CANCEL_ASYCHRONOUS (PTHREAD_CANCEL_DEFFERD pentru a reveni la cazul sincron). pthread_setcanceltype( PTHREAD_CANCEL_ASYCHRONOUS . void pthread_testcancel(void). În cazul în care thread-ul creat are atributul detached... pthread_create(&thraed.void* thread_function(void *arg){ . 9 10 .&thread_function.h> int pthread_setcancelstate(int state.. starea de ieşire a thread-ului este automat preluată de sistemul de operare. restul atributelor se utilizează doar în cazul programelor de timp real.&attr.. exit(0). Mai există şi o a treia posibilitate în care un thread solicită unui alt thread încheierea execuţiei. În primul caz. . dacă lipseşte – NULL: #include <pthread. Observaţie:Implicit toate thread-urile sunt sincron anulabile. starea thread-ului nu este preluată de către sistemul de operare automat şi rămâne în sistem. În condiţii normale un thread îşi încheie execuţia fie prin terminarea rulării funcţiei thread-ului returnând o valoare. Aşa cum am s-a arătat anterior unui thread asincron anulabil i se poate solicita în orice moment să îşi încheie execuţia. fie prin apelul sistem pthread_exit. Observaţie:Chiar dacă un thread a fost creat iniţial ca şi joinable ulterior poate fi detached prin intermediul apelului sistem ptheread_detach. joinable. utilizarea lor şi în final dealocarea lor.. Dacă execuţia unui thread este anulată în mijlocul unui astfel de proces. ca şi în cazul proceselor zombie. Neanulabil – toate cererile de încheiere a execuţiei sunt ignorate. Sincron anulabil – se poate solicita din exterior în cheiera execuţiei.PTHREAD_CREATE_DETACHED). pthread_attr_destroy(&attr).NULL). pthread_attr_setdetachstate(&attr. int pthread_setcanceltype(int type. pthread_t thread. în timp ce un thread sincron anulabil îşi poate încheia execuţia doar în anumite momente numite puncte de anulare. Cererile de încheiere a execuţiei vor fi puse într-o coadă de aşteptare şi vor fi luate în considerare doar în anumite momente ale execuţiei. al doilea argument este o referinţă către o variabilă care conţine vechea stare.. pthread_attr_init(&attr). NULL). Acest mecanism de terminare a execuţiei unui thread pe baza unor cereri externe se bazează pe următoarele apeluri sistem: #include <pthread.. fie detached. int *oldstate ).h> int pthread_cancel(pthread_t thread). Acest atribut este detach state. int *oldtype). până când un alt thread apeleză pthread_join. resursele alocate iniţial se vor pierde. În acet caz este folosit apellul sistem pthread_cancel: Deseori execuţia normală a unui thread implică alocarea unor resurse. } int main(){ pthread_attr_t attr. } Pentru majoritatea programelor un singur atribut al thread-ului este important. Dezavantajul în acest caz este dat de dispariţia posibilităţii de sincronizare între thread-uri prin intermediul funcţiei pthread_join. În concluzie din acest punct de vedere un thread se poate afla în una din următoarele stări: Asincron anulabil – îşi poate încheia execuţia în orice moment. dar nu în orice moment.

fie nu se execută de loc. NULL). int old_cancel_state. ca un thread să poată trimite un semnal unui alt thread.&old_cancel_state) .... . Se poate indica din interiorul thread -ului secţiunile neanulabile prin apelul pthread_setcancelstate: . Dacă un thread începe execuţia unei astfel de secţiuni atunci trebuie să o continue până la sfârşitul secţiunii fără a fi anulat.. Diferenţa este dată de faptul că în Linux thread-urile sunt implemetate ca şi procese. Oricum acest proces este diferit de cel creat cu fork. .Pentru a testa dacă un thread este anulabil într-un anumit punct se utilizează – pthread_testcancel. pthread_setcancelstate(old_cancel_state. Să presupunem că un astfel de program primeşte un semnal din exterior... Pe baza acestui apel se pot implementa secţiuni critice. el utilizează în comun spaţiul de adrese şi resursele cu procesul original. Implementarea thread-urilor POSIX este diferită de implementarea din multe alte sisteme de operare de tip UNIX. O problemă ce se poate pune în cazul programelor implementate multithread este gestionarea semnalelor. se pune intrebarea care thread va apela funcţia de tratare a semnalului (handler-ul)? În cazul sistemelor de operare de tip Linux problema este rezolvată chiar de implementarea acestora de tip proces – semnalul va fi tratat de thread-ul ce rulează programul principal. care fie se execută în întregime. în interiorul unui program multithread este posibil.. pthread_setcancelstate(PTHREAD_CANCEL_DISABLE. De asemenea. Utilizarea acestui apel sistem este valabilă în cazul threaurilor asincrone. pentru aceasta se foloseşte apelul sistem pthread_kill. 11 12 .. Fiecare apel pthread_create creează un nou proces ce rulează thread-ul nou creat.

Sign up to vote on this title
UsefulNot useful