Programare Sub Linux
Programare Sub Linux
PROGRAMAREA APLICAŢIILOR
SUB SISTEMUL DE OPERARE LINUX
Cuprins
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 1
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 2
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 3
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
1.1 Introducere
Reamintim, cu această ocazie, că în sistemul de operare UNIX, noţiunea de
extensie a unui fişier nu are un rol determinant în stabilirea tipului de fişier, cum este cazul
în alte sisteme de operere cum ar fi MS-DOS sau WINDOWS. Totuşi, pentru buna
funcţionare a utilitarelor ce constituie mediul de dezvoltare al aplicaţiilor, fişerele sursă,
create cu ajutorul unui editor de texte cum ar fi vi sau emacs trebuie sa aibă numele
terminat cu extensia “.c” (prog.c, de exemplu). Odată fişierele sursă editate, acestea pot fi
compilate cu ajutorul comenzii cc pe care o vom descrie mai jos.
int j=0,i;
if (j>0)
i++;
else
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 4
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
i = j;
se va semnala faptul că variabila i este utilizată înainte de fi iniţializată, deşi, aşa cum se
observă, nu este cazul. Totuşi, programatorii sunt sfătuiţi să utilizeze înainte de compilare
această comandă. Forma generală a acestei comenzi este:
unde fisier este numele unui fişier sursă iar efectul utilizării opţiunilor este acela de a nu
furniza mesaj pentru anumite tipuri susceptibile de erori, cum ar fi:
-a atribuire de valoare a unei expresii de tip long unei variabile al cărei tip
nu este long;
Răspuns: cc verifica sintaxa si translateaza textul sursa in cod obiect, pe cand lint
analizeaza doar textul sursa fara furniza cod obiect.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 5
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
cpp, ccom, as şi ld, însă comanda cc, asigură lansarea acestor comenzi cu parametrii
adecvaţi, a căror editare este extrem de laborioasă şi care depind de versiunea de sistem.
Acest fapt poate fi observat lansând comada cc cu opţiunea
unde în lista de fişiere se pot menţiona fişiere sursă (cu extensia .c), fişiere precompilate
(cu extensia .i), fişiere text în limbaj de asamblare (cu extensia .s), fişiere obiect (cu
extensia .o) sau biblioteci de fişiere obiect (cu extensiile .a, .so sau .sl). Fişierlor cu
extensia .c li se vor aplica procedurile 1-3 de mai sus, celor cu extensia .i procedurile 2-3,
iar celor cu extensia .s procedura 3. Odată obţinute, pentru toate fişierele obiect se aplică
procedura 4, de editare de legături, în vederea obţinerii executabilului.
extensia .i.
extensia .s.
extensia .o.
putem sesiza uşor, este foarte util să analizăm rezultatul produs de precompilator.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 6
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
cpp generate:
-C nu se elimină comentariile
sursă linia
#define nume_identificator
cum ar fi:
#ifdef UNIX
a+b=c;
c=x;
#endif
Cele două atribuiri de mai sus vor fi luate în consideraţie doar în cazul în
$cc …. –DUNIX ……
va declanşa căutarea fişierelor header scrise de utilizator în directorul curent şi, în caz de
insucces, mai apoi, în d1, d2, d3 în această ordine.
4. Opţiuni destinate editorului de legături ld. Vor fi discutate în paragraful
următor.
Întrebare: Care este optiunea care permite indicarea directoarelor unde se afla fisierele ce
urmeaza a fi incluse de percompilatror?
Răspuns: Optiunea -I
executabilul generat sa fie valid, trebuie ca pentru tuturor invocărilor de obiecte externe
extensiile .a, .so sau .sl). Bibliotecile sunt colecţii de fişiere obiect. Editorul de legături va
căuta referinţa nerezolvată în aceste biblioteci, iar în cazul în care o găseşte, va extrage
obiectul necesar şi îl va lega la executabil. Legarea cu biblioteca libc.a (.so, .sl după caz),
legături le analizează doar o singură dată. De exemplu, avem referinţa nerezolvată r1.
fişier generează altă referinţă nerezolvată, r2. Se analizează b2.a şi se găseşte în referinţa
în fişierul f2.o. Legarea cu acest fişier generează referinţa nerezolvată r3 care se găseşte
în fişierul f3.o aflat în b1.a. Însă editorul de legături a analizat deja această bibliotecă şi, în
fie concepute în mod ierarhizat, iar modulele din bibliotecile de nivel inferior să nu invoce
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 9
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
nerezolvată. De asemenea, se mai face confuzia între declaraţia de prototip a unei funcţii
în fişiere de tip header şi codul obiect al funcţiei propriu zis. De exemplu, afirmaţia “funcţia
printf se găseşte în stdio.h” este complet falsă. În stdio.h se găseţte o declaraţie de forma
“extern int printf()”, pe când codul obiect al acestei funcţii se găseşte în biblioteca
standard.
efectiv la executabil.
(shared objects), sau .sl pe HP-UX şi sunt numite biblioteci partajate (shared libraries).
din biblioteci se încarcă atunci efectiv în memorie. Acest lucru se poate face în două
moduri: imediat, la lansarea executabilului sau diferenţiat, doar în momentul în care este
prima data nevoie de a se executa cod din asemenea biblioteci. O dată codul obiect al
ulterior în execuţie. În acest fel, codul obiect conţinut în aceste biblioteci nu va fi rezident în
executabilelor.
Opţiunea –lnume (fara spatiu dupa -l!) serveşte drept abreviere pentru
exemplu pentru libm.a folosim –lm, pentru libisam.a folosim –lisam etc. În ultimul exemplu
Editorul de legături va prefera varianta partajată în absenţa altei opţiuni specifice. Opţiunile
de acest tip diferă însa de la o versiune la alta. Spre exemplu, pe SYSTEM V avem:
-Bdynamic
Pe HP-UX avem:
opţiuni de forma -a
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 11
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
următoarea sintaxă:
+z. Această opţiune permite obţinerea aşa numitului cod PIC (Position Independent Code),
independent de locaţia de încărcare. De exemplu:
1.6 Comanda nm
Deseori avem la dispoziţie textul sursă scris de noi ce trebuie să facă apel
la referinţe externe din fişiere obiect al căror text sursă nu ne este pus la dispoziţie.
care ştim sigur că este într-unul din fişierele a căror sursă nu o deţinem. Sigur am omis să
adaugăm un fişier obiect (.o) în linia de comandă cu care am lansat cc. Ce este de făcut?
Comanda nm ne scoate din impas.
$nm fisier…
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 13
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
unde fişierele date în argument pot fi fie fişiere obiect, fie biblioteci. Această comandă ne
va furniza lista referinţelor publice oferite de fiecare fişier, precum şi lista referinţelor
externe invocate de fiecare fişier obiect.
fişiere sursă, unele dintre acestea conţinând mii de linii de cod. Compilarea cu ajutorul
unei comenzi de forma:
conduce la un timp enorm de compilare, posibil de ordinul orelor. S-a modificat o linie într-
unul din fişierele sursă şi suntem obligaţi să aşteptăm câteva ore pentru obţinerea noii
versiuni de executabil. Soluţia este comanda make, cu ajutorul căreia se recompilează
numai fişierele sursă modificate, după care se execută editarea de legături.
integrate de dezvoltare (IDE) create pentru diferite limbaje de programare (C, C++,
JAVA,etc.).
biblioteci, acestea la rândul lor de fişierele sursă şi aşa mai departe. În momentul în care
se doreşte construirea unui fişier obiect sau a unui executabil se analizeaza ierarhia de
dependinţe şi de îndata ce se constată că data ultimei modificări a unei destinaţii este
anterioară datei ultimei modificări a cel puţin unui fişier de care depinde aceasta, se trage
concluzia că destinaţia nu este actualizată şi se lansează comanda de reconstruire a
acesteia. Altfel nu se reconstruieşte destinaţia. Strategia de bază este aceea de a nu
reconstrui decât ce nu mai este de actualitate şi se bazează pe principiul “fiul este
întotdeauna mai tânăr decât părintele”.
unde:
În cazul folosirii opţiunii –f se indică numele fişierului ce descrie dependinţele, numit
fişier de tip makefile. Prin lipsă, acesta se consideră a avea numele makefile sau
Makefile.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 14
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Fişierele de tip makefile sunt de tip text, al căror conţinut îl vom prezenta
în continuare.
makefile două linii. Prima conţine denumirea fişierului destinaţie, urmată de caracterul „:‟ şi
apoi de lista fişierlor dependente, separate prin spaţii. A doua începe obligatoriu cu
caracterul <tab> („\t‟ conform simbolisticii limbajului C) şi conţine comanda ce trebuie
executată pentru reconstruirea dependinţei. Această comandă nu se execută decât dacă
una din dependinţe este “mai tânără” decât destinaţia.
Exemplu. Fie un proiect ale cărui fişiere obiect sunt obţinute prin
compilarea fişierelor sursă p1.c, p2.c şi p3.c, urmată de editarea de legături între modulele
obiect obţinute, obţinându-se, astfel, executabilul numit aplic. Fişierele sursă pot include,
eventual, unul sau mai multe dintre fişierele header p.h, q.h, r.h. Fişierul de tip makefile va
avea următorul conţinut:
<tab> cc –c p1.c
p2.o:p2.c
<tab> cc –c p1.c
<tab> cc –c p3.c
Modificarea fişierului q.h va duce la recompilarea fişierelor p1.c şi p3.c şi, implicit, la
efectuarea editărilor de legături. Comanda
$make
este echivalentă cu
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 15
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
$make aplic
pe când
va produce noi versiuni de fişiere obiect doar dacă au fost modificate, între timp, fişierele
p1.c, p2.c, p.h sau q.h.
un nou fişier de tip header, nu actualizeză, în totalitate, dependinţele din fişierul de tip
makefile. Acest fapt poate duce la situaţia neplăcută ca, după modificarea headerului, o
parte din fişierele sursă C să fie recompilate cu noua versiune, iar altele, care depind, de
asemenea, de acest fişier sa rămână compilate cu vechea versiune de header, lucru care
poate avea consecinţe nefaste în timpul execuţiei.
Alt fapt demn de menţionat, este acela că acest mecanism poate fi folosit
Fară alte opţiuni, decât cele prezentate mai sus, se execută toate
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 16
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Studiu de caz. S-a constatat că s-a mers într-o direcţie greşită în ceea ce priveşte
dezvoltarea unor facilităţi ale aplicaţiei. În consecinţă, toate modificările făcute în ultima
vreme trebuiesc anulate. Cum se poate realiza recuperarea de versiuni anterioare, fără
reeditarea fişierelor sursă, şi deci, fără a risca producerea de noi erori în programe?
unde sursa reprezintă numele fişierului nou creat, iar s.sursa este numele fişierului ce va fi
gestionat de SCCS. Prefixul s. este obligatoriu. Se va crea fişierul s.sursa care este read-
only şi va gestiona toate versiunile ulterioare ale fişierului cu numele sursa.
suficient pentru recuperarea atât a ultimei versiuni a fişierului cât şi, eventual, a unor
versiuni anterioare.
$help
ce are ca efect explicarea ultimei erori sau a ultimului avertisment dat de ultima comandă
SCCS lansată.
$get s.prog.c
Fişierul obţinut va fi read-only şi va fi folosit doar pentru compilare. Dacă vrem, în plus, să
edităm fişierul, în vederea obţinerii unei versiuni ulterioare, se va utiliza opţiunea –e a
comenzii get:
$get –e prog.c
dat comanda şi asupra căruia numai acesta are drept de scriere. Se consideră fişierul
deschis pentru editare de către proprietar, până când acesta va renunţa la acest privilegiu.
Se crează şi un fişier cu numele p.prog.c ce va fi prezent până când proprietarul va
renunţa asupra drepturilor sale. Acest fişier joacă un rol de lacăt, prezenţa acestuia
împiedicând alţi utilizători să dea o comandă $get –e asupra aceluiaşi fişier. În acest fel se
asigură faptul că, la un moment dat, cel mult o persoană poate edita fişierul în cauză.
$delta s.prog.c
$get -r10 ….
$get –r10, 20
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 18
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
$get –e –r10 …
există.
Comanda
$prs s.prog.c
gestiunea versiunilor altor tipuri de fişiere decât cele de tip sursă scrise în C. Pentru
acestea din urmă, programatorul poate insera în textul sursă declaraţia unei variabile
statice globale de forma :
acesteia trebuie prefixată obligatoriu de şirul “%Z%”. În cazul unei comenzi admin sau
delta, în fişierul SCCS s.prog.c, şirul de caractere asignat acestei variabile se va înlocui
după urmatoarele reguli:
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 19
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
tipul “get –e” se va înlocui şirul de caractere cu valoarea inţială, urmând ca la închiderea
versiunii cu ajutorul comenzii delta sau unget să se realizeze din nou substituţiile de
rigoare. Prezenţa unei asemenea declaraţii conduce la absenţa mesajului de eroare “No id
keywords”.
(comanda poate fi aplicată şi fişierelor obiect sau bibliotecilor). Această comandă caută
şirurile de caractere ce încep cu “@(#)‖ şi afişează fişierele în care identifică asemenea
şiruri de caractere, precum şi valoarea acestora.
producerea executabilului.
Prin lipsă, fişierul este luat din intrarea standard stdin. Opţiunile
semnifică:
-s indentare în stilul Kernigham-Ritchie, adică
if (i==0) {
a=b;
c=d;
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 20
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
if (i==0)
a=b;
c=d;
proiect a cărui dezvoltare necesită efortul mai multor programatori pe o perioadă mai lungă
de timp, de ordinul lunilor de zile, pe parcursul cărora se produce o cantitate impresionantă
de linii de cod. Recomandăm ca fiecare programator să indenteze cu stricteţe codul scris,
în conformitate cu regulile de indentare utilizate de echipa din care face parte, fără să
ajungă la situaţia de a utiliza comanda cb.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 21
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Răspunsurile
la test se vor
da în spaţiul
liber din
chenar, în
continuarea
enunţurilor
2. Avantajele
Răspunsurile la acestfolosirii
test sefisierelor
găsesc ladepagina
tip makefile
13 a acestei unităţi de
învăţare.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 22
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Testul 1.
p1.o: p1.c
cc –c p1.c
1.14 Lucrare de verificare pentru studenţi
p2.o: p2.c
cc –c p2.c
p3.o: p3.c
Descrieţi comenzile sistemul de operare UNIX, aşa cum reiese din cele de
mai suscc –c p3.c
Indicaţii de redactare:
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 23
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 24
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
$id
În UNIX, noţiunea de fişier are o semnificaţie mai largă decât în celelalte sisteme
de operare. În UNIX un fişier poate reprezenta la fel de bine un fişier de date (numit în
continuare fişier de tip obişnuit), ca în celelalte sisteme de operare, dar şi un periferic sau
un director. O altă particularitate, specifică sistemelor UNIX, este aceea că noţiunea de
“nume” de fişier nu există, deşi, prin abuz de limbaj, este folosită. Fiecare periferic pe
suport magnetic are în componenţă o tabelă de i-noduri ce conţin caracteristicile fiecărui
fişier. Printre acestea se găsesc proprietarul, drepturile de acces, etc… dar şi adresele de
unde se regăsesc datele. Fiecare fişier este determinat, în mod unic, de identificatorul
perifericului şi de numărul sau de i-nod. Ce reprezinta atunci aşa zisul “nume” al fişierului?
Fişierele speciale de tip director sunt alcătuite din înregistrari care conţin, eventual, printre
altele, un nume, numit numele înregistrării din director, şi numărul de disc şi de i-nod
corespunzător. În momentul în care un fişier este invocat prin “nume”, se caută
înregistrarea din director după nume, se identifică numărul de disc şi i-nod, şi prin
intermediul informaţiilor din i-nod se accesează datele. În acest fel un fişier poate fi
înregistrat sub acelaşi nume sau sub nume diferite în diverse directoare. O astfel de
înregistrare se numeşte legatură fizică. Una din caracteristicile unui fişier este numărul
de legături fizice. O nouă legătură fizică a fişierului cu numele fisier1 , din directorul dir1
înregistrată sub numele fisier2 în directorul dir2 se poate realiza cu ajutorul comenzii:
declara prin intermediul comenzii mount, unul din directoarele ce se situează pe ultimul
nivel al arborescentei, drept rădăcina pentru arborescenţa corespunzătoare fişierelor de pe
un disc secundar. De asemenea, cu ajutorul aceleiaşi comenzi mount , se poate monta pe
un director situat pe ultimul nivel al arborescenţei, arborele de directoare corespunzător
unui director aflat pe o altă maşina din reţea utilizând sistemul NFS (Network File System).
În acest fel, pentru utilizator, este transparent suportul fizic pe care se află un fişier.
Orice director conţine, în mod obligatoriu, două intrări: una cu numele “.”
(corespunzătoare propriului i-nod) iar alta cu numele “..” (corespunzătoare
i-nodului directorului părinte). Aceste două intrări nu pot fi şterse, iar un director se
consideră “gol” atunci când conţine numai aceste două intrări şi numai în acest caz
ştergerea directorului cu ajutorul comenzii rmdir reuşeste.
Un proces ia naştere prin lansarea în execuţie a unui fişier de tip executabil. O
caracteristică a sistemelor UNIX este faptul că procesele sunt organizate arborescent. Mai
precis, la lansare, sistemul alocă procesului o nouă intrare în tabela de procese active,
atribuiindu-i acestuia un număr de identificare numit în continuare pid. Printre
caracteristicile unui proces se numără pid-ul, identificatorul procesului părinte, numt în
continuare ppid şi directorul curent, moştenit de la procesul părinte, adică directorul ce
serveşte la identificarea fişierelor referite prin referinţă relativă (al căror nume nu începe cu
“/”). Bineînţeles că, după lansare, procesul poate modifica directorul curent moştenit de la
procesul tată.
$echo $?
Întrebare: Care sunt obiectele principale cu care lucreaza sistemul de operare UNIX?
Bibliotecile sistem sunt furnizate sub formă de biblioteci arhivă sau partajată. La
compilare se face impilicit editarea de legături cu biblioteca libc.a (.so, .sl, etc). Alte
biblioteci trebuie menţionate explicit în comanda de compilare. De exemplu, biblioteca
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 26
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
matematică libm.a (.so, .sl, etc) trebuie menţionată în comanda de compilare cu ajutorul
opţiunii –lm.
Menţionăm că în aceste fişiere de tip header sunt re-definite diverse tipuri de date
de tip întreg (char, short, int, long, etc…), în funcţie de particularităţile platformei. De
exemplu în locul definiţiei apelului
int getpid();
ce returnează pid-ul procesului curent, în fişierul <unistd.h> este definit tipul de date
pid_t, iar apelul este descris sub forma
pid_t getpid();
Facem convenţia ca, pentru tipurile de date care nu sunt descrise explicit, să le
considerăm de tip întreg, adaptate la particularităţile platformei.
Răspuns: Fisierul header este de tip text si contine definitii care sa permita compilatorului
generarea corecta a codului ce face apel la functiile din biblioteca
în loc de
if (errno == EACCES)
constituie o greşeală de programare deoarece pe alte versiuni de sistem, EACCES poate
să fie definită cu o altă valoare în errno.h.
În caz că un apel sistem se soldează cu eroare, putem obţine pe ieşirea standard a
programului un mesaj explicativ despre cauza erorii, pornind de la valoarea variabilei
errno, utilizând următorul apel:
void perror(char *mesaj_utilizator);
Parametrul mesaj_utilizator reprezintă un şir de caractere ales de utilizator ce va fi
afişat în urma apelului lui perror, urmat de explicaţia furnizată de sistemul de operare. De
exemplu în urma apelului:
perror(“Nu s-a putut deschide fisierul sursa”);
se va afişa pe ecran textul Nu s-a putut deschide fişierul sursă urmat de explicaţia
sistemului. Dicţionarul ce realizează corespondenţa între valorile posibile ale lui errno şi
mesajele explicative poate fi accesat şi direct prin intermediul altor două variabile globale
declarate, de asemenea, în errno.h, şi anume:
extern char **sys_errlist;
ce reprezintă vectorul mesaje explicative şi
extern int sys_errno;
ce reprezintă numărul de elemente din sys_errlist. De exemplu mesajul corespunzător
valorii EACCESS a lui errno poate fi recuperat cu ajutorul expresiei
sys_errlist[EACCESS].
Întrebare: Care este fisierul header ce trebuie inclus pentru a accesa variabila errno?
Răspuns: errno.h
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 28
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 29
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Răspuns: Este acelasi cu exit (k) daca main nu este apelata recursiv.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 30
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Răspunsurile
la test se vor
da în spaţiul
liber din
chenar, în
continuarea
enunţurilor
2. Cum se lansează un nou program în UNIX?
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 31
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Testul 2.
3. Terminarea functiei main sau apel explicit la una din functiile exit sau
_exit
Indicaţii de redactare:
- scrieţi comentarii
- se vor testa toate codurile de eroare posibile
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 32
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
3.1. Generalităţi
Aşa cum am arătat în capitolul precedent, noţiunea de fişier are, în UNIX, un înţeles
mai larg decât în alte sisteme de operare. În UNIX prin fişier se înţelege o resursă sistem,
care poate să corespundă unui periferic cum ar fi: disc, cititor de bandă, terminal sau
imprimantă sau unui fişier pe suport magnetic, în sensul obişnuit al cuvântului. Fişierul
este identificat unic de numărul de disc şi de numărul de i-nod, şi poate să fie înregistrat
sub diverse nume, în diverse directoare (cataloage).
O caracteristică a fişierelor UNIX este tipul acestora, care poate fi:
ajutorul comenzii:
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 33
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
În acest caz, se creează un i-nod nou, care, însă, nu are propria sa zonă de date.
Adresa corespunzătoare zonei de date din i-nod conţine adresa i-nodului corespunzător
fişierului sursă. La exploatarea unei legături simbolice, se acţionează asupra informaţiilor
fişierului sursă.
Cu ajutorul comenzii :
$mount nume_masina:nume_director_sursa nume_ director_destinatie
ls –l.
#include <sys/types.h>
#include <sys/stat.h>
struct stat {
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 35
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
caracteristicilor i-nodului */
};
Exploatarea acestui câmp se face cu ajutorul unor măşti definite prin constante
simbolice şi anume:
Valorile tuturor celor trei biţi corespunzători drepturilor proprietarului pot fi recuperate cu
ajutorul măştii S_IRWXU.
Mai avem la dispoziţie două măşti pentru recuperarea, din acelaşi câmp a altor
două caracterisitici pe bit : set-uid (masca S_ISUID) şi set-gid (masca S_ISGID).
Semnificaţiile acestor două informaţii le vom explica ulterior.
sau
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 36
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Testul pentru tipul de fişier se poate face atât cu ajutorul unor măşti dar şi
cu ajutorul unor expresii macro-definite (macro-uri), ce reprezinta expresii ce dau ca
rezultat 1 sau 0, în funcţie de faptul daca fişierul este de tipul respective sau nu.
Exemplu
…………
if (S_ISDIR(st.st_mode) {
S_IRGRP | S_IXGRP) {
citire şi scriere */
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 37
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Apelurile cu care se pot încărca datele referitoare la un i-nod într-o structură de tipul
stat sunt
#include <sys/types.h>
#include <sys/stat.h>
Ambele apeluri încarcă în structura a cărei adresă este dată în al doilea parametru,
caracteristicile i-nodului fişierului desemnat de primul parametru. Diferenţa între cele două
apeluri este că la primul fişierul este referit prin nume, pe când la al doilea fişierul este
referit prin numărul unei intrări valide din tabela de descriptori. Pentru succesul acestor
apeluri, nu sunt necesare drepturi asupra fişierului, ci numai drepturile de rigoare asupra
directoarelor ce intervin în localizarea fişierului invocat prin nume. În caz că aceste drepturi
nu există, variabila errno va lua valoarea EACCESS. O altă cauză posibilă a insuccesului
acestor doua apeluri este inexistenţa fişierului referit. În acest caz, errno va lua valoarea
ENOENT.
#include <unistd.h>
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 38
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Reamintim că un fişier este efectiv şters dacă numărul de legături fizice ajunge 0 şi nici un
proces nu mai are un descriptor deschis corespunzător acestui fişier.
Redenumirea unui fişier, sau ca să fim mai precişi, a numelui unei legături fizice,
altfel decât cu ajutorul comenzii mv, se poate realiza cu ajutorul apelului:
#include <unistd.h>
#include <sys/types.h>
Ca şi în cazul funcţiei stat, cele două apeluri diferă prin modul de referire al fişierului
specificat de primul argument (nume sau descriptor). Al doilea argument reprezintă noile
drepturi de acces şi este construit prin disjuncţie dintre constantele prezentate la
descrierea câmpului st_mode al structurii stat.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 39
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <unistd.h>
Ca şi în cazul funcţiei stat, cele două apeluri diferă prin modul de referire
al fişierului specificat de primul argument (nume sau descriptor). În caz de apel reuşit,
indicatorii set-uid şi set-gid sunt resetaţi automat.
Menţionăm faptul că apelurile pentru schimbarea atributelor unui i-nod nu reuşesc
decăt dacă proprietarul procesului este acelaşi cu proprietarul fişierului sau dacă
proprietarul procesului este root.
Anumite apeluri sistem pot avea drept efect lateral crearea de noi i-noduri.
Enumerăm unele dintre acestea, în funcţie de tipul noului i-nod creat:
- fişiere de tip obişnuit : open (în anumite circumstanţe) şi creat.
- director : mkdir.
- tuburi: pipe si mkfifo.
- legături simbolice : symlink.
Pentru crearea de noi i-noduri generice mai avem la
dispoziţie apelul:
#include <sys/stat.h>
int mknod(char *nume_fisier, mode_t drepturi_acces);
Al doilea argument descrie tipul şi drepturile de acces şi este este construit prin disjuncţie
dintre constantele prezentate la descrierea câmpului st_mode al structurii stat.
Întrebare: Cum se numeste campul structurii stat care memoreaza tipul fisierului si
drepturile de acces asupra acestuia
Răspuns: st_mode
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
Acest apel este recomandabil, în special, pentru fişierele de tip obişnuit. Pentru alte
tipuri de fişiere există apeluri specifice pentru deschidere.
obişnuit, ce are drept proprietar şi grup proprietar, proprietarul şi, respectiv grupul
proprietar al procesului, şi, ca drepturi de acces cele menţionate în al treilea parametru
filtrate prin masca de drepturi implicite pentru crearea fişierelor. Această mască se poate
vizualiza sau modifica cu ajutorul comenzii SHELL umask .
acest caz existenţa prealabilă a fişierului conduce la eşecul apelului (se doreşte exclusiv
creare de fişier nou şi nicidecum deschiderea unui fişier existent). Ca apelul sa fie
încununat de succes trebuie să existe drepturi de scriere în director.
apel fără a se mai aştepta înlăturarea cauzei blocajului, se furnizează cod de retur –1
către apelant, iar errno ia valoarea EAGAIN. Citirile şi scriereile ulterioare se tratează, de
asemenea, în mod neblocant.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 41
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
disc.
apel fără a se mai aştepta înlăturarea cauzei blocajului, se furnizează cod de retur –1
către apelant, iar errno ia valoarea EAGAIN.
În caz de eroare se returnează –1, iar în caz de succes se crează o nouă intrare în
tabela de descriptori şi se returnează indexul acesteia. Un cod de retur ne-negativ
înseamnă succes şi reprezintă descriptorul ce va fi folosit la operaţiile următoare. În plus:
fişierului deschis, numărul de intrari din tabela de fişiere deschise ce pointează către
aceasta este incrementat.
situaţia în care s-a cerut în mod expres închiderea sa prin apel la primitiva close.
Observaţii.
descriptorul deschis, acesta rămâne valid în ciuda unor modificări ulterioare ale drepturilor
de acces efectuate de alt proces.
mai mic descriptor nefolosit, chiar dacă experimental se poate constata acest fapt.
deschideri, una în mod O_RDONLY şi alta în mod O_WRONLY, deoarece în acest din
urmă caz cei doi descriptori gestionează fiecare propria sa poziţie curentă.
Apelul urmator :
#include <sys/types.h>
#include <sys/stat.h>
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 42
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <fcntl.h>
este echivalent cu
#include <unistd.h>
unde argumentul reprezintă descriptorul recuperat cu open. Efectele acestui apel sunt
următoarele:
dacă numărul de mai sus devine nul, se eliberează intrarea din tabela
de i-noduri din memorie şi în caz că numărul de legături fizice ale fişierului este, de
asemenea, nul, fişierul este şters.
#include <unistd.h>
octeţi, pe care îi va depune în memorie la adresa desemnată de ptr. Pentru fişiere de tip
obişnuit, procedura de citire se desfăşoară astfel:
numărul de octeţi de la poziţia curentă până la sfărşitul fişierului. Se vor citi k = min(nr_oct,
rest_oct). Codul de retur va fi k, adică numărul de octeţi efectiv citiţi. Poziţia curentă se va
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 43
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Exemplu
typedef struct {
char a[20];
long b;
int c;
} MY_STR;
int nr;
MY_STR *ptr;
…..
unde nr împarţit la sizeof (MY_STR) reprezintă numărul de structuri efectiv citite. În caz că
acesta este mai mic decât 40, în mod sigur, la următorul apel codul de retur va fi 0,
deoarece s-a ajuns la sfârşitul fişierului.
#include <unistd.h>
citiţi din memorie de la adresa desemnată de ptr. Pentru fişiere de tip obişnuit, procedura
de scriere se desfăşoară astfel:
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 44
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
vor scrie în fişier, la sfârşitul acestuia sau la poziţia curentă, în funcţie de faptul dacă
indicatorul O_APPEND a fost poziţionat sau nu. Funcţia returnează numărul de octeţi
scrişi. În caz că se returnează mai puţin de nr_oct, aceasta semnifică eroare (disc plin, de
exemplu). Scrierea octeţilor nu se face direct pe disc, ci prin intermediul memoriei cache,
mai puţin situaţia în care s-a folosit la deschidere opţiunea O_SYNC.
Exemplu
MY_STRUCT vec[40];
……
……
40*sizeof(MY_STRUCT)) {
/* caz de eroare */
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 45
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <unistd.h>
int dup(int desc);
Paremetrul reprezintă valoarea unui descriptor valid, iar apelul returnează, în caz de
succes, valoarea noului descriptor ce duplică pe primul. Acest apel garantează duplicarea
în indexul cel mai mic corespunzător unei intrări libere în tabela de descriptori ai procesului.
Apelul:
#include <unistd.h>
int dup2(int desc1, int desc2);
duplică descriptorul valid desc1, în valoarea desc2, dacă aceasta corespunde unui
descriptor liber. În caz că această condiţie nu este realizată, se va închide fişierul
corespunzător lui desc2, în prealabil.
Răspuns: Este garantata duplicarea descriptorului in cel mai mic descriptor liber.
Întrebare: Care este unul din avantajele duplicarii unui descriptor, fata de o deschidere
obisnuita?
Altă categorie de comenzi este aceea pentru citirea sau modificarea modului de
deschidere. Se pot seta sau reseta numai indicatorii O_APPEND, O_NONBLOCK,
O_NDELAY si O_SYNC. Atragem atenţia asupra faptului că aceste caracteristici sunt ale
tabelei de fişiere deschise şi, în consecinţă, orice modificare a acestora afectează toţi
descriptorii ce pointează spre intrarea respectivă din tabela de fişiere deschise. Comanda
F_GETFL, determină returnarea modului de deschidere sub forma unui întreg, a cărui
valoare este interpretată pe biţi. Comanda F_SETFL, determină instalarea noului mod de
deschidere, caz în care este necesară pasarea celui de al treilea parametru, de tip int,
care conţine valoarea de instalat. Exemplificăm, în continuare, modul de setare şi resetare
al atributului O_APPEND.
int desc, atribut;
…….
atribut = fcntl(desc, F_GETFL); /* citire valoare */
atribut |= O_APPEND; /* setare */
fcntl(desc, F_SETFL, atribut); /* instalare */
……..
atribut = fcntl(desc, F_GETFL); /* citire valoare */
atribut &= ~O_APPEND; /* resetare */
fcntl(desc, F_SETFL, atribut); /* instalare */
……..
Comanda F_DUPFD, duplică descriptorul în intrarea liberă cu cel mai mic indice
din tabela de descriptori, care este mai mare sau egal cu valoarea celui de al treilea
parametru, care este de tip int.
Una din funcţionalităţile principale ale apelului fcntl este aceea de a gestiona
blocajele asupra fişierelor. Blocajele se înregistrează la nivelul tabelei de i-noduri din
memorie, şi este proprietatea exclusivă a procesului care l-a pus. În consecinţă, numai
acesta poate ridica blocajul. Dacă nu o face, blocajul va persista până la terminarea
procesului respectiv.
Caracteristicile unui blocaj sunt:
- zona blocată, ce poate fi între două poziţii din fişier sau de la
o poziţie din fişier până la sfârşitul fişierului.
- tipul blocajului: partajat sau exclusiv. Un blocaj partajat poate
coexista cu alte blocaje partajate, pe când existenţa unui blocaj exclusiv, împiedică
instalarea altor blocaje până la deblocare.
- modul de operare : consultativ, fără să conducă efectiv la blocarea
operaţiilor de citire/scriere, dar împiedică punerea de alte blocaje incompatibile, şi
imperativ ce duce la blocarea operaţiilor de citire/scriere în zona blocată astfel: un blocaj
imperativ partajat blochează operaţiile de scriere, iar un blocaj imperativ exclusiv
blochează atât operaţiile de scriere cât şi de citire din zona blocată.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 47
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 48
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Întrebare: Care este valoarea variabilei errno in urma unui apel sistem esuat din cauza
unui deadlock?
Răspuns: EDEADLCK.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 49
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <unistd.h>
int symlink (char *sursa, char *destinatie);
ce are acelaşi efect ca şi comanda shell:
$ln –s sursa destinatie
Consultarea unui fişier de tip legatură simbolică cu ajutorul apelului stat va conduce
la furnizarea caracteristicilor i-nodului către care pointează legătura, şi, în consecinţă, tipul
de fişier obţinut nu va fi niciodată legătură simbolică. Pentru a obţine caracteristicile i-
nodului de tip legătură simbolică se poate folosi apelul :
#include <sys/stat.h>
int lstat (char *nume_fisier, struct stat *ptr);
ce are acelaşi efect cu stat, mai puţin cazul unui fişier de tip legătură simbolică, în care
obţinem caracteristicile legăturii simbolice, şi nu ale fişierului spre care pointează aceasta .
În cazul unei legături simbolice putem determina numele legăturii fizice (fişierului)
spre care pointează aceasta, cu ajutorul apelului:
#include <symlink.h>
ssize_t readlink(char *nume_fisier, char *ptr, size_t dim);
unde nume_fisier este numele unei legături simbolice. Apelul încarcă la adresa ptr primele
cel mult dim caracterele din numele fişierului spre care pointează legătura simbolică, şi
returnează numărul de caractere scrise la această adresă. Menţionăm că şirul scris nu
este terminat cu caracterul „\0‟;
3.8 Gestiunea directoarelor
Fişierele speciale de tip director pot fi deschise cu open si citite cu read. Acest mod
de abordare a problemei citirii unui director este, însă, complet contraindicată deoarece
structura acestor fişiere se poate modifica de la o versiune la alta a sistemului de operare.
Singurul lucru pe care îl ştim cu certitudine este acela că directorul realizează
corespondenţa între i-nod şi numele unei legături fizice.
Fişierul header dirent.h, conţine, printre altele, definiţia tipului de date DIR, ce
descrie caracteristicile directorului, precum şi a structurii dirent, ce conţine caracteristicile
unei intrări in director, printre care numele în câmpul char d_name[NAME_MAX];
Atenţie! Este uşor de ghicit faptul că tipul de date DIR este o structură. Cu toate
acestea, programatorului trebuie să-i fie transparent acest lucru. Dimpotrivă, acesta poate
folosi, de exemplu, câmpurile structurilor stat, flock, dirent,… descrise ca atare în
documentaţie.
Deschiderea unui fişier de tip director se poate face prin intermediul apelului:
#include <dirent.h>
DIR *opendir (char *nume_dir);
ce deschide directorul având numele dat in argument. În caz de eroare se returnează
NULL. Codul de retur va fi folosit, în continuare, pentru exploatarea directorului.
Citirea intrării următoare din director se poate face cu apelul:
#include <dirent.h>
struct dirent *readdir(DIR *p);
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 50
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Acest apel va furniza, dupa deschidere, rând pe rând, intrările din director, inclusiv cele
numite “.” şi “..”. Codul de retur NULL, semnifică sfârşitul parcurgerii directorului sau o
eroare.
Apelul:
#include <dirent.h>
void rewidndir (DIR *p);
repoziţionează intrarea într-un director deschis, la începutul acestuia.
Închiderea unui fişier de tip director se va realiza cu ajutorul apelului:
#include <dirent.h>
int closedir (DIR *p);
Nu există apeluri speciale pentru scrierea intr-un director. Această operaţiune este
un efect lateral al apelurilor: creat, open, link, mkfifo (creare de fişier de tip tub), mknod
sau unlink.
Crearea unui nou director se face cu apelul:
#include <sys/types.h>
int mkdir (char *nume_dir, mode_t drepturi);
Se creează astfel un nou director având numele specificat de primul parametru şi
drepturile de acces specificate de cel de al doilea. Acest director va conţine doar două
legături şi anume “.” şi “..”.
Ştergerea unui director se face cu ajutorul apelului:
#include <unistd.h>
int rmdir (char *nume_dir);
Acest apel reuşeşte doar dacă directorul având numele specificat de parametru conţine
doar legăturile “.” şi “..”.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 51
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
conţine, printre altele, adresa buffer-ului tampon si descriptorul de fişier. Pentru un fişier
deschis cu biblioteca standard, descriptorul se poate recupera cu ajutorul apelului:
#include <stdio.h>
int fileno (FILE *fp);
- Macro definiţiile de tip FILE * ale fişierelor standard: stdin, stdout,
stderr.
- Macro definiţiile _IOFBF, _IOLBF şi _IONBF ce descriu modul de
gestionare al buffer-ului tampon, etc.
Deschiderea fişierelor se face cu ajutorul apelului:
#include <stdio.h>
FILE *fopen (char *nume_fisier, char *mod);
ce deschide fişierul cu numele dat de primul parametru în modul descris de al doilea.
Acesta din urmă poate lua valorile:
“r” – citire (corespunzător lui O_RDONLY la un apel echivalent al lui open), “w” –
scriere, precedată de trunchiere (O_WRONLY | O_CREAT | O_TRUNC), “a” – scriere
exclusiv la sfârsit (O_WRONLY | O_CREAT | O_APPEND), “r+” (O_RDWR), “w+”
(O_RDWR | O_CREAT | O_TRUNC), “a+” (O_RDWR| O_CREAT| O_APPEND). Pe
diferite versiuni, parametrul mod mai poate avea diverse alte valori cum ar fi: “r+b”, “w+b”
sau “a+b” corespunzătoare operaţiilor asupra fişierelor de tip binar.
Un fişier deschis cu open, poate fi exploatat in continuare cu ajutorul bibliotecii
standard în urma apelului:
#include <stdio.h>
FILE *fdopen (int desc, char *mod);
unde primul argument reprezintă descriptorul, iar al doilea modul de deschidere ce poate
lua aceleaşi valori ca şi la fopen , numai că acestea trebuie să fie compatibile cu modul
iniţial de deschidere al fişierului cu open, pentru reuşita apelului.
Apelul următor permite redirectarea unei intrări din tabela de fişiere deschise către
un alt fişier:
#include <stdio.h>
FILE *freopen(char *nume_fisier, char *mod, FILE *fp);
De exemplu, următorul apel redirectează intrarea standard către fişierul cu numele
iii
freopen (―iii”, ―r‖, stdin);
pe când :
freopen (―ooo”, ―a‖, stdout);
redirectează ieşirea standard către fişierul cu numele ooo.
Deschiderea unui fişier temporar se face cu apelul:
#include <stdio.h>
FILE *tmpfile();
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 52
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Fişierul temporar este plasat într-un director special, va fi şters la apelul lui exit, şi va avea
un nume generat aleator de sistem. Un nume de asemenea fişier, fără crearea acestuia,
poate fi obţinut cu apelul:
#include <stdio.h>
char *tmpnam(char *ptr);
unde zona alocată pentru parametru trebuie să fie de cel puţin L_tmpnam caractere.
Numele este furnizat în codul de retur şi scris la adresa dată de parametru în caz că
acesta nu are valoarea NULL.
Închiderea unui fişier se face cu apelul:
#include <stdio.h>
int fclose(FILE *fp);
Funcţia returnează 0 în caz de succes şi EOF în caz de eroare.
Scrierea unui caracter într-un fişier se poate face fie cu ajutorul macro-ului
#include <stdio.h>
int putc (int c, FILE *fp);
sau prin apelul funcţiei:
int fputc (int c, FILE *fp);
putchar(c);
este echivalent cu putc(c, stdout);
Scrierea unui şir de caractere se face cu apelul:
#include <stdio.h>
int fputs(char *sir, FILE *fp);
Subliniem faptul că terminatorul de şir „\0‟ nu este scris în fişier.
Scrierea unui vector de elemente de un tip oarecare se face cu apelul:
#include <stdio.h>
int fwrite(void *p, size_t dim, size_t nr_elem, FILE *fp);
unde primul parametru reprezintă adresa primului element, al doilea dimensiunea fiecărui
element, iar al treilea numărul de elemente. Codul de retur reprezintă numărul de
elemente scrise.
Apelul :
#include <stdio.h>
int fprintf (FILE *fp, char *format, …);
este un apel cu număr variabil de argumente, primele două menţionate fiind obligatorii.
Alcătuirea şirului desemnat de al doilea argument determină numărul şi tipul argumentelor
din zona variabilă. Aceste argumente vor fi convertite din format intern în format text şi
scrise în fişierul desemnat de primul argument. Apelul
printf (fp, format, …); \
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 53
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
este echivalent cu
fprintf (stdout, format, …);
Caracterele din şirul desemnat de al doilea argument vor fi scrise ca atare în fişier, mai
puţin situaţia în care apare caracterul special %. În acest caz, unul sau mai multe
caractere ce-I urmează sunt interpretate drept specificator de conversie care determină,
printre altele, tipul următorului argument din zona variabilă. Se scrie atunci rezultatul
conversiei în fişier, după care se continua parcurgerea şirului format. Caracterul ce
urmează după % determină, de regulă, tipul conversiei şi anume:
%i, %d - întreg în reprezentare zecimală
%u - întreg fără semn în reprezentare zecimală
%o - întreg fără semn în reprezentare octală
%x - întreg fără semn în reprezentare hexazecimală (cu cifre 0..9 şi a..f)
%X - întreg fără semn în reprezentare hexazecimală (cu cifre 0..9 şi A..F)
%f - float, double în reprezentare zecimală cu virgulă fixă.
%e, %E - float, double în reprezentare zecimală cu virgulă mobilă.
%g, %G - float, double în reprezentare zecimală cu virgulă fixă sau mobilă în
funcţie de valoarea exponentului.
%c - unsigned char
%s - char *, şir tipărit până la întâlnirea terminatorului de şir, „\0‟.
%p - pointer de orice tip în format hexazecimal.
%n - int *, parametru de ieşire, unde se scrie numărul de caractere scrise
până în acel moment, fără a scrie informaţie în fişier.
Între caracterul % şi cel ce determină tipul conversiei mai pot fi
intercalate alte caractere speciale ce dau informaţii suplimentare asupra modului în care
trebuie realizată conversia şi anume:
- ―-’’ implică alinierea rezultatului conversiei la stânga zonei atribuite,
altfel alinierea se face la dreapta.
- ―+‖ implică precedarea rezulatului conversiei de semn, chiar dacă
numărul convertit este pozitiv.
- blanc are acelaţi efect ca şi “+” cu deosebirea că rezultatul conversiei
unui număr pozitiv este precedat de blanc şi nu de + ca semn.
- n1, număr ce indică numărul de poziţii rezervate conversiei. Prin
lipsă, numărul de caractere ocupat de rezultatul conversiei este cel rezultat în urma
operaţiunii
- n1.n2, unde n1 are aceeaşi semnificaţie ca mai sus dar în cazul
conversiei unui număr real, n2 specifică numărul de cifre de după punctul zecimal.
- unul din caracterele h (pentru short), l (long), sau L (long double) ce
determină un tip special al argumentului de convertit.
Funcţia returnează numărul de caractere scrise în caz de succes şi –1 în
caz de eroare.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 54
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Citirea unui caracter din fişier se poate efectua fie cu ajutorul macro-ului:
#include <stdio.h>
int getc (FILE *fp);
sau prin apelul funcţiei:
int fgetc (FILE *fp);
getchar();
este echivalent cu getc(stdin);
Se obţine astfel, drept cod de retur, următorul caracter din fişier. Valoarea de retur
EOF înseamnă, fie eroare, fie faptul că s-a ajuns la sfârşitul fişierului.
Apelul:
#include <stdio.h>
int ungetc (int car, FILE *fp);
are drept efect diminuarea poziţiei curente cu o unitate si depunerea caracterului specificat
de primul argument in zona tampon pentru a fi citit la următoarea operaţiune de citire. Nu
se produc modificări la nivelul fişierului propriu-zis, ci numai în memorie. Se returnează
valoarea caracaterului in caz de succes şi EOF în caz contrar. Se garantează, însă,
succesul unui singur apel înaintea unei operaţii de citire.
Citirea unui şir de caractere se face cu ajutorul apelului:
#include <stdio.h>
char *fgets(char *ptr, int dim, FILE *fp);
ce realizează depunerea la adresa desemnată de primul parametru a maxim dim-1
caractere citite din fişier. Spunem maxim deoarece citirea se poate opri înainte de a citi
dim caractere, daca se întâlneşte sfârşitul fişierului sau un caracter sfârşit de linie, „\n‟, caz
în care acest caracter este depus şi el în zona rezultat. Se returnează valoarea ptr în caz
de succes şi NULL, în caz contrar.
Apelul:
#include <stdio.h>
char *gets(char *ptr);
citeşte un şir de caractere de la intrarea standard pâna la întâlnirea unui sfârşit de linie,
depunând rezultatul citirii la adresa desemnată de parametru. Acest apel este puţin fiabil,
deoarece nu se specifică nici o dimensiune limită pentru rezultatul citirii.
Citirea unui vector de elemente se face cu ajutorul apelului:
#include <stdio.h>
int fread(void *p, size_t dim, size_t nr_elem, FILE *fp);
unde primul argument este adresa primului element al vectorului unde se vor depune
rezultatele, al doilea dimensiunea fiecărui element, iar al treilea numărul de elemente din
vector. Codul de retur reprezintă numărul de elemente citite. Nu se garantează rezultatul
în care nu se citeşte un numar întreg de elemente.
Apelul :
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 55
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <stdio.h>
int fscanf (FILE *fp, char *format, …);
este un apel cu număr variabil de argumente, primele două menţionate fiind obligatorii.
Alcătuirea şirului desemnat de al doilea argument determină numărul şi tipul argumentelor
din zona variabilă. Aceste argumente reprezintă adrese unde se vor depune rezultatul
conversiilor din format text in format intern. Numărul, tipul conversiilor şi, implicit, numărul
şi tipul pointerilor din zona variabilă este dat de al doilea argument. Apelul
scanf (format, …);
este echivalent cu
fscanf (stdin, format, …);
Interpretarea celui de al doilea argument este asemănătoare, până la un punct, cu
cea de la apelul fscanf. Numai elementele ce determină tipul conversiei au o semnificaţie
aici. Aceste elemente sunt începute de caracterul special % urmat de alt caracter ce
determină tipul conversiei. Aceste caractere sunt:
%c - un caracter.
%s - şir de caractere, citirea terminându-se la
intâlnirea unui blanc sau a unui sfârşit de linie. Caracterul de
sfârşit de şir, „\0‟, este depus la sfârşitul şirului citit.
%p - pointer în format hexazecimal fără semn.
%d - întreg reprezentat zecimal
%u - întreg fară semn reprezentat zecimal
%o - întreg reprezentat octal
%x - întreg reprezentat hexazecimal
%e, %f, %g - float, double
%n - întreg reprezentând numarul de caractere citite până în acel
moment
%[…] - se omit caracterele din mulţimea delimitată de [ şi ].
%[^…] - se omit caracterele din complementara mulţimii delimitată [ şi ].
Între caracterul procent şi cel ce determină tipul conversiei, pot fi
intercalate diferite alte caractere ce furnizează informaţii suplimentare asupra modului de
citire. În lipsa acestora, citirea datelor se face până la întâlnirea primului caracter
incompatibil cu tipul de conversie specificat. Aceste caractere sunt:
- * indică faptul că valoarea citită nu se atribuie unei variabile şi, in
consecinţă, argumentul corespondent din zona variabilă va lipsi.
- Un întreg n, ce indică lungimea maximă pentru citirea câmpului
- unul din caracterele h (pentru short), l (long), sau L (long double) ce
determină un tip special al argumentului de convertit.
Modificarea poziţiei curente din fişier, fără a efectua operaţii de citire sau
scriere, se poate realiza cu ajutorul apelului:
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 56
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <stdio.h>
int fseek (FILE *fp, long offset, int orig);
unde al doilea parametru are ca valoare una din constantele simbolice SEEK_SET,
SEEK_CUR sau SEEK_END, a căror semnificaţie a fost explicată cu ocazia apelului lseek.
Noua poziţie curentă va fi calculată cu ajutorul formulei offset+orig.
Testarea faptului dacă poziţia curentă a ajuns la sfârşitul fişierului, se face cu
apelul:
#include <stdio.h>
int feof (FILE *fp);
ce returnează 0 dacă nu s-a atins sfârşitul fişierului şi 1 altfel.
Aşa cum am mai spus, dupa deschidere, avem un buffer tampon de dimensiune
BUFSIZ. Se poate schimba, ori adresa acestui buffer, ori dimensiunea acestuia sau modul
de utilizare prin intermediul apelului:
#include <stdio.h>
int setvbuf (FILE *fp, char *buff, int mode, size_t dim);
Parametrul al doilea reprezintă adresa noului buffer, iar al patrulea noua sa
dimensiune. Parametrul al treilea reprezinta modul de golire al bufferului pe disc şi este
specificat cu ajutorul unor constante simbolice, şi anume:
_IOFBF - golire la umplere
_IOLBF - golire la umplere sau la scrierea completă a unei linii
_IONBF - golire dupa fiecare caracter scris.
Implicit, pe fişierele de tip obişnuit, bufferul se goleşte în mod _IOFBF, adică la
umplere.
Există şi o formă simplificată a acestui apel, şi anume:
#include <stdio.h>
int setbuf (FILE *fp, char *buff);
Apelul
setbuf (fp, buff);
este echivalent cu
setvbuf (fp, buff, mode, BUFSIZ);
unde modul de golire al buffer-ului este cel dinainte, cu exceptia cazului când buff are
valoarea NULL, fapt ce reprezintă păstrarea vechiului buffer, cu aceeaşi dimensiune, dar
cu modificarea modului de golire la _IONBF, adică golire imediată.
Se poate cere golirea imediată a bufferului, chiar dacă nu sunt îndeplinite condiţiile
pentru aceasta, cu ajutorul apelului:
#include <stdio.h>
int fflush (FILE *fp);
Valoarea NULL a argumentului semnifică golirea imediată a bufferelor pentru toate
fişierele deschise.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 57
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 58
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Răspunsurile
la test se vor
da în spaţiul
liber din
chenar, în
continuarea
enunţurilor
2. Care este informatia principala continuta de o intrare din tabela
de fisiere deschise in memorie ?
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 59
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Indicaţii de redactare:
- scrieţi comentarii
- scrieti o varianta ce foloseste biblioteca sistem si alta care foloseste
biblioteca C standard
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 60
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
4. GESTIUNEA PROCESELOR
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 61
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <unistd.h>
pid_t getpid();
Cu excepţia procesului având pid 0, orice proces are un proces părinte al cărui pid
poate fi citit, pentru procesul curent, cu apelul:
#include <unistd.h>
pid_t getppid();
b. Raporturile cu utilizatorii.
#include <unistd.h>
pid_t getuid();
excepţia cazului când fişierul executabil corespunzător procesului are indicatorul set-uid
setat. În acest caz, proprietarul efectiv este proprietarul fişierului executabil. Drepturile de
acces la diferite obiecte ale sistemului (fişiere, etc.) sunt cele ale proprietarului efectiv, nu
cele ale proprietarului real. De exemplu, comanda SHELL passwd, modifică fişierul
/etc/passwd, asupra căruia nici un utilizator, cu excepţia lui root, nu are drept de scriere.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 62
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
În mod normal, deschiderea acestui fişier în scriere ar trebui sa eşueze. În cazul comenzii
passwd, acest lucru nu se intâmplă deoarece fişierul executabil passwd, are setat
indicatorul set-uid şi pe root ca proprietar. Prin urmare, root este proprietarul efectiv al
procesului. Deci orice utilizator poate modifica fişierul /etc/passwd, prin intermediul
acestei comenzi, dar numai linia aferentă lui. Pentru procesul curent, această
caracteristică poate fi citită cu apelul:
#include <unistd.h>
pid_t geteuid();
Un proces având drept proprietar pe root, poate modifica, în acelaşi timp, proprietarul real
şi efectiv prin apelul:
#include <unistd.h>
#include <unistd.h>
pid_t getgid();
#include <unistd.h>
pid_t getegid();
De asemenea, un proces având drept proprietar pe root, poate modifica, în acelaşi timp,
grupul proprietar real şi efectiv prin apelul:
#include <unistd.h>
c. Directorul curent
O altă caracteristică a unui proces este directorul său curent. Prin raport la acesta
se face adresarea fişierelor în mod relativ. Procesul moşteneşte, de asemenea, această
caracteristică de la procesul părinte, dar poate să o modifice prin apelul:
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 63
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <unistd.h>
unde argumentul desemnează numele noului director curent. Procesul curent poate citi
această caracteristică prin apelul:
#include <unistd.h>
unde primul argument desemnează bufferul unde se va depune numele directorului curent,
care va fi furnizat şi drept valoare returnată, iar al doilea argument dimensiunea bufferului.
Se poate pasa primul argument cu valoarea NULL, caz în care se returnează o adresă
dintr-o zonă statică. În caz că numele directorului are mai multe caractere decât s-a
menţionat în al doilea parametru, apelul eşuează, iar errno va lua valoarea ERANGE.
e. Data de creare.
care va depune la adresa desemnată în argument informaţiile cerute. Structura de tip tms
conţine, printre altele, următoarele câmpuri:
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 64
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <sys/stat.h>
unde argumentul reprezintă noua mască, vechea mască fiind obţinută în codul de retur.
Argumentul şi valoarea de retur sunt interpretate ca disjuncţie bit cu bit a constantelor
prezentate cu ocazia apelului stat. Prin acest apel nu pot fi specificate decât cele 9
drepturi reprezentând accesul în citire, scriere şi execuţie pentru proprietar, grup proprietar
şi ceilalţi. Nu se pot specifica drepturile speciale de set-uid şi set-gid. La un apel open
sau creat, drepturile de acces la noul fişier creat se vor obţine făcând o conjuncţie bit cu
bit între această mască şi argumentul care reprezintă drepturile de acces.
i. Starea procesului
- 3-4 Se iese dintr-un apel sistem sau s-a terminat tratarea unui semnal
sau s-a apelat funcţia (handler) ce tratează un semnal.
- 4-3 S-a facut un apel sistem sau s-a primit un semnal sau s-a ieşit din funcţia
(handler) ce tratează un semnal.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 65
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
- 3-7 Execuţia procesului s-a terminat, dar intrarea aferentă din tabela
de procese active persistă până când procesul tată ia cunoştiinţă de acest fapt..
m. Prioritatea.
#include <unistd.h>
n. Stiva procesului.
o. Valori limită.
#include <ulimit.h>
unde primul argument specifică, prin intermediul constantelor simbolice valoarea limită, iar
al doilea, dacă este cazul să existe, noua valoare. Primul argument poate lua valorile:
următor.
Întrebare: Care este apelul prin care putem gasi pid-ul procesului curent?
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 66
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Răspuns: getpid.
- datele statice. Aici sunt plasate toate variabilele globale şi cele locale statice.
Aceste variabile au o adresă fixă pe toată durata execuţiei procesului.
variabilelor create dinamic pe parcursul execuţiei procesului prin apeluri la funcţiile malloc,
calloc sau realloc.
(adrese cuprinse între 0x00000000 şi 0x3fffffff), zona de date în al doilea cadran (adrese
cuprinse între 0x40000000 şi 0x7fffffff), zona de cod pentru bibliotecile partajate în al
treilea cadran (adrese cuprinse între 0x80000000 şi 0xbfffffff) şi zona rezervată
segmentelor de memorie partajată între procese în al patrulea cadran (adrese cuprinse
între 0xc0000000 şi 0xffffffff).
În zona de date, adresele din stivă scad de la valori mari către valori mici. Punctul
care delimitează stiva de heap se numeşte punct de ruptură sau breakpoint. Utilizatorul
poate modifica, între anumite limite, această valoare cu ajutorul apelului:
#include <unistd.h>
unde valoarea argumentului reprezintă incrementul, iar codul de retur valoarea noului
punct de ruptura.
Adresele sunt alocate în zona dinamică (heap) prin intermediul unor apeluri care
returnează pointeri. Trebuie remarcat faptul că pointerii returnaţi sunt aliniaţi perfect, în
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 67
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
sensul că la adresele obţinute pot fi depuse orice fel de date indifferent de tipul acestora.
Un prim apel de acest tip este:
#include <stdlib.h>
#include <stdlib.h>
ce alocă spaţiu unui vector de nr elemente de dimensiune dim. Un apel calloc(x, z) este
echivalent cu malloc (x*z) cu singura deosebire că apelul calloc face iniţializarea zonei de
memorie alocate cu toţi biţii având valoarea zero, pe când malloc nu face nici un fel de
iniţializare a zonei alocate.
#include <stdlib.h>
Primul argument este un pointer obţinut în prealabil printr-un apel la malloc, calloc sau
realloc. Al doilea argument reprezintă dimensiunea nouă a zonei. Se garantează, în caz
de modificare a adresei zonei de memorie, copierea datelor iniţiale în noul spaţiu alocat.
Apelul cu primul argument având valoarea NULL este echivalent cu un apel la malloc.
Atenţie! Primul argument trebuie să fie un pointer obţinut ca rezultat al unui apel
malloc, calloc, realloc sau NULL.
Secventa urmatoare :
int x;
……
va fi compilată, executată, însă va strica datele statice privind gestiunea de memorie ale
procesului, efectul acestei erori riscând a se manifesta la alte apeluri ulterioare ce privesc
alocarea sau eliberarea spaţiilor de memorie.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 68
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Eliberarea unei zone de memorie alocată în prealabil cu malloc, calloc sau realloc
se face cu apelul
#include <stdlib.h>
Atenţie! Argumentul trebuie să fie un pointer obţinut ca rezultat al unui apel malloc,
calloc, realloc sau să aibe valoarea NULL. Dacă argumentul este NULL, apelul este fără
efect. Efectul apelului cu o altă valoare a argumentului poate duce la erori în urma unui
apel ulterior la una din funcţiile de gestiune a memoriei.
Este recomandat să fie eliberate zonele de memorie cât mai repede, în ciuda
faptului ca acestă operaţie se face automat la terminarea procesului.
#include <unistd.h>
În urma acestui apel ia naştere procesul fiu care execută acelaşi cod obiect ca şi procesul
tată, din acelasi punct şi primind drept date o copie a datelor procesului tată. Singurul lucru
care deosebeşte cele două procese este valoarea codului de retur al apelului fork.
Procesul tată va primi un cod de retur strict pozitiv ce reprezintă pid-ul procesului fiu nou
creat, iar procesul fiu va primi valoarea 0 drept cod de retur al apelului fork. O valoare
egală cu -1 a codului de retur indică eşecul apelului, o cauză posibilă fiind umplerea
tabelei de procese a sistemului. Un exemplu ar fi:
pid_t pid;
switch (pid = fork()) {
case -1:
………
/* eroare */
………
break;
case 0:
………
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 69
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
/* procesul fiu */
………
break;
default:
………
/* procesul tata, pid are valoarea pid-ului procesului fiu */
………
- Identificatorul pid;
- Identificatorul procesului părinte
- Timpii de execuţie care sunt initializaţi cu valoarea nulă.
- Tabela de descriptori pentru fişierele deschise este moştenita de la
procesul tată. Trebuie menţionat că descriptorii ambelor procese pointează către aceeaşi
intrare în tabela de fişiere deschise, deci orice modificare a poziţiei curente de către unul
din procese în urma unei operaţii de scriere sau citire se va repercuta şi asupra celuilalt
proces.
- codul de retur
- timpii de execuţie în mod nucleu şi utilizator
- pid-ul său şi pid-ul procesului tată
Procesul părinte poate executa următorul apel pentru a lua cunostiinţă
despre terminarea unui fiu şi modul în care s-a produs acest lucru:
#include <sys/types.h>
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 70
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <sys/wait.h>
În situaţia în care procesul nu are nici un proces fiu, codul de retur este –1, iar errno
ia valoarea ECHILD. Dacă procesul are cel puţin un fiu zombi, codul de retur este pid-ul
fiului zombi, care va dispare din tabela de procese a sistemului, iar la adresa pasată drept
parametru, daca aceasta nu are valoarea NULL, se vor depune informaţii despre modul in
care s-a terminat procesul fiu. Modul de interpretare a acestei valori va fi descris mai jos.
În caz că procesul are fii, dar nici unul nu este zombi, se va aştepta până când unul
dintre fii ajunge zombi sau până când apelul wait va fi întrerupt de primirea unui semnal,
caz în care codul de retur este –1, iar errno ia valoarea EINTR.
Alt apel care realizează aproape acelaşi lucru ca şi wait, dar care permite
utilizatorului, printre altele, să specifice pid-ul procesului aşteptat să se termine este:
#include <sys/types.h>
#include <sys/wait.h>
Primul argument indică procesul, sau mai bine zis mulţimea de procese aşteptate pentru a
se termina, şi anume:
- Valoare strict mai mică decât –1 înseamnă toate procesele din grupul
având drept identificator valoarea absolută a lui pid.
- WNOHANG, apel neblocant în cazul în care există fii, dar nici unul
zombi
Codul de retur poate fi –1 în caz de eroare, 0 în caz ca există procesele, dar nici
unul zombi (sau stopat, după caz) în mod neblocant. Un cod de retur strict pozitiv
reprezintă pid-ul unui fiu intrat în stare zombi sau stopat, după caz.
Interpretarea valorii depuse la adresa stare se va face cu ajutorul unor
macrodefiniţii, ce primesc drept argument o expresie întreagă şi calculează valorile cerute.
Aceste macro-uri sunt:
- WIFEXITED – rezultat 1 daca procesul s-a terminat normal, 0 altfel.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 71
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Întrebare: Care sunt ajantajele folosirii apelului waitpid in locul lui wait?
Următorul apel este foarte asemănător cu cel descris mai sus, cu deosebirea că
fişierele executabile nu mai trebuie, în mod obligatoriu, specificate prin cale absolută,
deoarece căutarea acestora se face prin intermediul variabilei de mediu PATH:
int execlp (char *executabil, char *arg, …, NULL);
Exemplul anterior se va putea realiza astfel:
execlp (“ls”, “ls”, “-l”, “/”, NULL);
Apelul următor:
int execle (char *executabil, char *arg, …, NULL, char **arge);
este similar cu execl, cu deosebirea că are un argument în plus, ultimul, ce reprezintă
lista noilor variabile de mediu şi valorile acestora sub forma unei liste de adrese de şiruri
de caractere ce au sintaxa nume=valoare, terminat cu valoarea NULL. Acest ultim
argument va fi al treilea argument al functiei main pentru noul executabil.
Urmeaza trei apeluri cu numar fix de argumente. Primul este:
int execv (char *executabil, char **arg);
ce are acelaşi efect cu execl, cu deosebirea că argumentele se pasează sub forma unei
liste de adrese de şiruri de caractere terminate cu valoarea NULL. Exemplul de mai sus se
va realiza astfel:
char *argv[4];
argv[0]=”ls”;
argv[1]=”-l”;
argv[2]=”/”;
argv[3]=NULL;
execv (“/bin/ls”, argv);
Apelul următor este similar:
int execvp (char *executabil, char **arg);
cu execv, cu deosebirea că se caută executabilul prin intermediul variabilei de mediu
PATH.
Ultimul apel transmite, în plus, lista noilor variabile de mediu şi valorile lor, ca în
cazul lui excle:
int execve (char *executabil, char **arg, char **arge);
Numărul maxim de argumente ce poate fi transmis este limitat de constanta
ARG_MAX definită în headerul limits.h.
În urma apelului uneia din primitivele din familia exec se pot schimba următoarele
caracteristici ale procesului:
- proprietarul efectiv, în funcţie de faptul dacă fişierul executabil are
poziţionat sau nu indicatorul set-uid.
- grupul proprietar efectiv în funcţie de faptul dacă fişierul executabil
are poziţionat sau nu indicatorul set-gid.
- funcţiile de tratare a semnalelor devin cele implicite.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 73
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
2. Care este apelul sistem UNIX prin care se poate lansa un nou
Răspunsurile proces ?
la test se vor
da în spaţiul
liber din
chenar, în 3. Care este familia de apeluri sistem UNIX prin care se poate lansa in
continuarea executie un nou fisier executabil in cadrul aceluiasi proces ?
enunţurilor
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 74
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Testul 4.
2. fork
Indicaţii de redactare:
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 75
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
5. GESTIONAREA SEMNALELOR
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 76
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
5.1. Introducere
Un proces poate primi un semnal, ceea ce este echivalent cu întreruperea sa,
urmată de trecerea în mod nucleu, de unde se apelează o funcţie ce tratează semnalul,
numită handler de semnal, iar dacă aceasta se termină cu return, se va reveni in mod
nucleu, de unde se va reveni, dacă este cazul, în mod utilizator, continuându-se, astfel,
programul din punctul în care a fost intrerupt.
Un semnal poate fi primit, fie prin apăsarea unor taste speciale de la terminalul de
control (intr, quit, stop), fie prin transmiterea de către sistemul de operare în cazul unei
erori (tentativă de a scrie la adrese ilegale, de exemplu), fie transmise de alte procese.
#define SIGKILL 9
defineşte semnalul cu sufixul KILL având valoarea 9. Din SHELL unui proces, având pid-ul
pid, i se poate transmite un semnal fie cu ajutorul comenzii:
(unde primul argument reprezintă numărul semnalului, care însă poate să fie diferit de la
un sistem la altul) sau
$kill –sufix pid
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 77
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
sau
Orice semnal este primit, fie în urma unei comenzi kill din SHELL, fie a apelului
sistem cu acelaşi nume efectuat de alt proces, fie în urma producerii unui eveniment
specific, extern procesului, cum ar fi tastarea unor caractere speciale de la terminalul de
control, erori de program, etc. Un proces nu poate transmite, cu succes, un semnal către
alt proces, decât dacă acesta are acelaşi proprietar real cu acesta sau dacă procesul ce
transmite semnalul are drept proprietar pe root.
1. terminare
2. terminare cu producerea unui fişier cu numele core ce conţine
informaţii utile pentru o depanare ulterioară
3. nici un efect
4. suspendarea procesului
5. trezirea procesului suspendat
În continuare furnizăm lista semnalelor existente pe marea majoritate a
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 78
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
SIGUSR1 Terminare
SIGUSR2 Terminare
SIGSTOP Suspendare,
nemodificabil
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 79
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <unistd.h>
unde argumentul reprezintă numărul de secunde după care procesul curent va primi
semnalul SIGALARM. Aşadar, este vorba de un semnal care este primit de la acelaşi
unde primul argument reprezintă procesele către care se transmite semnalul, iar al doilea
identificatorul semnalului trimis.
Apelul se încheie cu succes (cod de retur 0) doar dacă procesele către care se
emite semnalul au acelaşi proprietar cu procesul curent, excepţie situaţia în care procesul
curent are drept proprietar pe root.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 80
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
funcţii, în caz ca aceasta se termină cu return, procesul reintră in mod nucleu, iar sistemul
constanta generică SIG_DFL. Utilizatorul poate instala alt handler pentru anumite semnale,
cu excepţia lui SIGKILL, SIGCONT şi SIGSTOP, cu ajutorul unor apeluri pe care le vom
descrie mai jos. O altă constantă generică pentru identificarea handlerelor este SIG_IGN
aşteptare (pending) până la recepţia sa de către proces. Semnalele blocate rămân în stare
pending până la deblocare. În caz că se execută un apel sistem, semnalul rămâne în stare
pending până la terminarea apelului, adică apelurile sistem sunt neintreruptibile. Excepţie
fac, în anumite condiţii, apelurile: pause, sigsuspend, wait, waitpid, fcntl, read, send,
Un apel sistem întrerupt returnează codul de eroare –1, iar variabila errno ia
valoarea EINTR.
Un proces intrat în starea stopat, în urma primirii unuia din semnalele SIGSTOP sau
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 81
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
procesului (mai puţin situaţia în care SIGTERM are specificat un handler care se termină
Trimiterea unui semnal către un proces aflat în stare zombi nu are nici un efect.
SIGSTOP si SIGCONT.
headerul signal.h. Pentru mânuirea variabilelor de acest tip pot fi folosite apelurile:
vide.
de al doilea parametru.
de al doilea parametru.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 82
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
returnează 1 dacă semnalul desemnat de al doilea argument face parte din mulţimea de
#include <signal.h>
unde ultimul parametru este de ieşire şi va conţine, dacă este diferit de NULL, mulţimea
semnalelor blocate înaintea apelului. Primul argument poate lua una din următoarele valori,
doilea argument.
deja blocate. Cu alte cuvinte se adaugă, eventual, elemente noi la mulţimea de semnale
blocate.
următorului apel:
#include <signal.h>
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 83
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
struct sigaction {
void (*sa_handler)();
sigset_t sa_mask;
int sa_flags;
};
Primul câmp semnifică un handler pentru tratarea unui semnal. Poate avea drept valoare
semnale deja blocate pe durata execuţiei handlerului. Semnalul în curs de tratare este, în
Al treilea câmp este construit dintr-o disjuncţie bit cu bit a următoarelor constante
simbolice:
starea zombi în aşteptarea unui apel wait sau waitpid efectuat de procesul tată. Primirea
Instalarea unui nou handler de tratare a unui semnal se face cu ajutorul apelului:
#include <signal.h>
unde primul argument reprezintă semnalul căruia vrem să-l modificăm comportamentul, al
doilea argument descrie noul comportament, pe când al treilea argument este de ieşire şi
va conţine, dacă este diferit de NULL, detalii despre modul de tratare a semnalului înainte
Instalarea unui nou handler pentru tratarea unui semnal poate fi făcută şi cu ajutorul
apelului următor, moştenit de la versiunile mai vechi ale sistemului, şi care este mai puţin
flexibil:
#include <signal.h>
unde primul argument desemnează semnalul căruia vrem să-i modificăm comportamentul,
iar al doilea un pointer către noul handler (poate lua şi valorile SIG_DFL şi SIG_IGN).
Valoarea de retur este un pointer către vechiul handler de tratare a semnalului. Instalarea
noului handler are efect decât pentru prima apariţie a semnalului, după care va fi reinstalat
handlerul implicit. Dacă vrem ca instalarea handlerului să aibă caracter permanent putem
După efectuarea cu succes a unui apel din familia exec se vor reinstala automat
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 85
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <unistd.h>
int pause();
un proces este trecut în starea adormit (sleeping) până la recepţionarea unui semnal. Este
Primitiva următoare:
#include <signal.h>
unui semnal ce nu este blocat. Ieşirea din acest apel se va face, deci, doar în urma primirii
dinaintea apelului.
Din fericire, sistemul ne pune la dispoziţie apeluri prin care putem rezolva această
problemă.
#include <setjmp.h>
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 86
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
unde primul parametru este de ieşire şi va memora contextul procesului din momentul
apelului. Este evident că această variabilă, trebuie să fie, în principiu, globală. Al doilea
argument, dacă este diferit de zero, indică faptul că se salvează şi mulţimea de semnale
blocate. Codul de retur al acestui apel va fi explicat mai târziu. Reţinem, totuşi, faptul că un
apel normal, efectuat în scopul memorării contextului procesului, va returna totdeauna
valoarea 0.
După aceea, dacă, dintr-un punct ulterior al procesului, dorim să revenim în punctul
în care am salvat contextul, putem folosi apelul:
#include <setjmp.h>
unde primul parametru este cel salvat cu ajutorul apelului sigsetjmp. Efectul acestui apel
este un salt imediat după apelul lui sigsetjmp corespunzător. Al doilea argument al lui
siglongjmp este codul de retur al lui sigsetjmp, care, de obicei, se transmite cu o valoare
diferită de zero. În felul acesta, după un apel la sigsetjmp, se poate face diferenţierea
dacă s-a ieşit dintr-un apel normal, sau în urma unui apel la siglongjmp.
Atragem atenţia asupra faptului că în urma apelului siglongjmp se poate face salt
doar într-o funcţie apelantă, în mod direct sau indirect, nicidecum într-una apelată, în
mod direct sau indirect.
Un mod de tratare moştenit de la versiunile mai vechi foloseşte tipul de date
jmp_buf, folosind apelurile :
#include <setjmp.h>
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 87
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Răspunsurile 2. Care sunt apelurile sistem UNIX prin care se pot instala rutine de
la test se vor tratare a semnalelor?
da în spaţiul
liber din
chenar, în
continuarea 3. Care este numele comenzii SHELL si a apelului sistem UNIX
enunţurilor prin care se pot emite semnale catre un proces ?
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 88
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Testul 5.
2. signal, sigaction
3. kill
Indicaţii de redactare:
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 89
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 90
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
că descriptorii deschişi corespunzători acestor fişiere sunt fie în mod citire, fie în mod
scriere. În consecinţă nu se pot deschide aceste fişiere în mod citire şi scriere simultan.
În plus operaţia de citire din astfel de fişiere este distructivă, în sensul că, informaţia
odată citită nu poate fi recuperată a doua oară din fişier. Informaţia este citită în mod fifo
(primul intrat, primul ieşit), în sensul că octeţii sunt citiţi în ordinea în care au fost scrişi.
Orice tub are o capacitate finită. Mai precis dacă se scrie informaţie fără a fi citită,
se poate ajunge la umplerea tubului, caz în care procesul care scrie va fi blocat la un apel
write, până cand un alt proces va efectua un apel de citire, golind astfel tubul.
#include <unistd.h>
unde argumentul este parametru de ieşire şi corespunde unui vector de minim doi întregi,
alocat în prealabil. În caz că apelul a fost încununat de succes, p[0] va conţine un
descriptor în mod citire, iar p[1] va conţine un descriptor în scriere aferent noului fişier
creat. Spre deosebire de descriptori deschişi pentru fişiere de tip obişnuit, anumite operaţii
sunt ilegale asupra acestor descriptori cum ar fi lseek sau ioctl. În schimb, cu ajutorul
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 91
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
………..
char buf[DIM_BUF];
int nr;
int p[2];
char ch;
pipe (p);
Apelul este blocant deorece există descriptorul deschis în scriere. Un alt exemplu este
char ch;
pipe (p1);
pipe (p2);
if (fork() == 0){
…………………….
else {
…………………….
Pentru a se evita situaţii de genul celor descrise mai sus, drept o masură de igiena
programării, se recomandă închiderea tuturor descriptorilor inutili.
………..
char buf[DIM_BUF];
…………………..
int nr;
Înainte de toate scoatem în evidenţa faptul că, pentru un tub obişnuit, dacă s-au
închis toţi descriptorii de un fel, citire sau scriere, nu avem nici o şansă să recuperăm
descriptorul.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 93
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Dacă numărul de descriptori deschişi în citire este nul, procesul va primi semnalul
SIG_PIPE. Altfel:
$ls –l | wc -l
Reamintim că apelul dup duplică descriptorul dat drept argument în cel mai mic descriptor
liber.
int p[2];
pipe (p);
if (fork() == 0) {
else {
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 94
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
director.
unde parametrul opţional –p cere crearea tuturor directoarelor care intervin, dacă este
necesar, iar argumentul care urmează parametrului opţional –n indică drepturile de acces
asupra fişierului nou creat, exceptând pe cele implicite ( a se vedea comanda umask). Din
#include <sys/types.h>
#include <sys/stat.h>
unde primul argument reprezintă numele fişierului de tip tub nou creat, iar al doilea
argument drepturile de acces exprimate prin disjuncţie bit cu bit între constantele
este echivalent cu
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 95
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Deschiderea unui fişier de tip tub “cu nume” se face cu ajutorul primitivei open. În
lipsa opţiunilor O_NONBLOCK sau O_NDELAY, aceste apeluri sunt blocante în sensul ca
poziţionării unuia din indicatorii O_NONBLOCK sau O_NDELAY care fac apelul de
Ca la orice fişier, tuburile “cu nume” dispar dacă numărul de legături fizice devine
nul şi nici un proces nu posedă un descriptor asupra lui. Facem observaţia că, atunci când
numărul de legături fizice devine nul, dar există încă descriptori deschişi asupra lui, fişierul
Operaţiile de citire şi, respectiv, scriere se realizează cu ajutorul primitivelor read şi,
respectiv, write. Atragem atenţia asupra faptului că atunci când un fişier de tip tub dispare
(are zero legături fizice şi nici un proces nu posedă un descriptor deschis asupra lui)
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 96
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
2. Care este apelul sistem prin care se creeaza un tub fara nume?
Răspunsurile
la test se vor
da în spaţiul
liber din 3. Ce optiune a apelului sistem open este invalida la deschiderea
chenar, în unui tub cu nume ?
continuarea
enunţurilor
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 97
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Testul 6.
1. mkfifo, creat.
2. pipe
3. O_RDWR
Indicaţii de redactare:
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 98
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 99
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
7.1 Introducere
Noţiunea de IPC (Inter Process Communication) reprezintă obiecte cu ajutorul
cărora procesele comunică între ele. Aceste obiecte nu sunt tratate ca fişiere. Ele sunt
memorie partajată. Fiecare din aceste obiecte sunt tratate în mod specific în funcţie de
tipul acestora. Totuşi, aceste obiecte posedă şi caracteristici comune pe care le vom
Toate aceste obiecte sunt identificate la nivelul sistemului de operare prin tip şi un
identificator numeric intern. Utilizatorul le poate invoca prin intermediul unui identificator
extern. În program, identificatorii externi sunt de tipul key_t, tip de date definit în headerul
<sys/types.h>.
struct ipc_perm {
};
O cheie externă este unic determinată de un fişier existent (mai precis de numărul
de disc şi de i-nod al acestuia) şi de un număr ales de utilizator. Cheia se obţine cu
ajutorul primitivei:
#include <sys/ipc.h>
Observăm că pentru apelul prezentat mai sus nu are importanţă conţinutul fişierului ci
numai numărul de disc şi de i-nod. Este garantat faptul că acelaşi apel, cu aceleaşi
argumente, dă acelaşi rezultat, mai puţin situaţia în care fişierul este şters şi, apoi, creat
din nou cu acelaşi nume, deoarece nu ar mai avea acelaşi număr de i-nod. Apelul eşuează
dacă fişierul desemnat de primul argument nu există.
$ipcs
Fără nici o altă opţiune, se afişează IPC-urile, indiferent de tipul acestora sub forma unui
tabel ale cărui rubrici sunt după cum urmează:
–l precedată de două caractere ale căror interpretare se face în funcţie de tipul IPC-ului:
a. Cozi de mesaje :
i. S există cel puţin un proces blocat în timpul
tentativei de a scrie un mesaj (coadă plină)
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 101
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
urilor afişate în funcţie de tipul acestora: cozi de mesaje, vectori de semafoare şi, respectiv
zone de memorie partajată.
Alte opţiuni conduc la afişarea unor informaţii suplimentare care sunt dependente
de tipul IPC-ului. Acestea sunt:
urmată de opţiuni şi argumente ce indică IPC-ul şters. Opţiunile –q, -s şi, respectiv, -m
permit ştergerea IPC-urilor desemnate prin identificatorul intern, în funcţie de tipul
acestora: cozi de mesaje, vectori de semafoare şi, respectiv zone de memorie partajată.
Opţiunile –Q, -S şi, respectiv, -M permit ştergerea IPC-urilor desemnate prin cheia externă
în funcţie de tipul acestora: cozi de mesaje, vectori de semafoare şi, respectiv zone de
memorie partajată. De exemplu, comanda
şterge segmentul de memorie partajată având indicatorul intern 100 şi coada de mesaje
acestuia.
struct msqid_ds {
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 103
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
pid_t msg_lspid;
pid_t msg_lrpid;
time_t msg_stime;
time_t msg_rtime;
time_t msg_ctime;
};
Mesajele se transmit prin intermediul unor structuri definite de utilizator al căror prim
câmp este, în mod obligatoriu, de tip long , ce desemnează tipul mesajului. Acest câmp
trebuie să fie, obligatoriu, strict pozitiv. Celelalte câmpuri din structură reprezintă
conţinutul mesajului. Asemenea tip de structuri le vom numi în continuare de tip mesaj.
#include <sys/msg.h>
2. Altfel :
2.1 Dacă IPC-ul invocat de primul argument nu există, atunci
3.1 Dacă IPC-ul invocat de primul argument există, apelul reuşeşte, mai
puţin situaţia în care ambii indicatori IPC_CREAT şi IPC_EXCL sunt poziţionaţi, caz în
care codul de retur este -1, iar variabila globală errno ia valoarea EEXIST.
#include <sys/msg.h>
int msgsnd (int id, void *p, int lungime, int optiuni);
#include <sys/msg.h>
int msgrcv (int id, void *p, int lungime, long typ, int optiuni);
- lungimea mesajului recepţionat este mai mare decât cea desemnată de al treilea
argument şi indicatorul MSG_NOERROR nu este poziţionat, caz în care errno ia valoarea
E2BIG.
- tipul mesajului incorect (valoare mai mică sau egală cu 0), caz în care
errno ia valoarea EINVAL.
#include <sys/msg.h>
unde primul argument reprezintă identificatorul intern al IPC-ului, iar al doilea este dat prin
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 106
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
struct __sem {
struct semid_ds {
time_t sem_otime;
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 107
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
time_t sem_ctime;
};
struct sembuf {
short sem_op;
/* tipul operaţiei */
short sem_flg;
};
#include <sys/sem.h>
care întoarce, în caz de succes, indicatorul intern al IPC-ului. Primul şi al treilea argument
sunt interpretate la fel ca primul, şi respectiv, al doilea argument al primitivei msgget. Al
doilea argument reprezintă numărul de semafoare atomice folosite, ce nu trebuie sa
depaseasca numărul de semafoare atomice declarat la creare.
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 108
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
#include <sys/sem.h>
unde primul argument reprezintă indicatorul intern al IPC-ului, al doilea o lista de operaţiuni
asupra diverselor semafoare individuale din vector, iar al treilea numărul de elemente din
lista de operaţiuni.
ajutorul apelului:
#include <sys/msg.h>
unde primul argument reprezintă identificatorul intern al IPC-ului, al doilea numărul unui
semafor, al treilea tipul operaţiunii, iar prezenta şi tipul celui de al patrulea argument este
struct shmid_ds {
int shm_segsz;
pid_t shm_lpid;
pid_t shm_cpid;
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 110
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
/* numărul de ataşări */
time_t shm_atime;
time_t shm_dtime;
time_t shm_ctime;
};
# include <sys/shm.h>
# include <sys/shm.h>
care întoarce adresa de start a zonei de memorie partajată. Primul argument este
identificatorul intern al IPC-ului, iar al doilea adresa la care dorim să plasăm zona de
memorie. În caz că acest argument are valoarea NULL, adresa este aleasă de sistem. Din
aceste considerente, este recomandabil folosirea valorii NULL pentru acest argument. În
caz că valoarea acestui argument este diferită de NULL, utilizatorul trebuie să fie sigur că
nu intră în conflict cu alte adrese. Prezenţa opţiunii SHM_RND în al treilea argument duce
la ajustarea valorii precizate de al doilea argument cu constanta SHMLBA.
Al treilea argument este alcătuit din disjuncţie bit cu bit între diverse constante
simbolice. Pe lângă SHMLBA, menţionăm valoarea SHM_RDONLY, ce reprezintă
opţiunea de folosire a zonei de memorie doar pentru citire. În această situaţie, orice
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 111
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
# include <sys/shm.h>
#include <sys/shm.h>
unde primul argument reprezintă identificatorul intern al IPC-ului, iar al doilea este dat prin
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 112
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 113
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
Testul 7.
1. ftok.
2. Semop.
Indicaţii de redactare:
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 114
Programarea aplicatiilor sub sistemul de operare LINUX
-----------------------------------------------------------------------------------------------------------------------
8. BIBLIOGRAFIE
Bibliografie:
[1]. Tanenbaum A. S. Modern Operating Systems, Prentice Hall, 2002
[2]. Tanenbaum A. S. Operating Systems: design an implementation,
Prentice Hall, 1987
[3]. Baranga A, Programarea aplicatiilor C/C++ sub sistemul de operare
UNIX, Editura Albastra, 2004
--------------------------------------------------------------------------------------------------------------------
Andrei Baranga 115