Sunteți pe pagina 1din 114

Curs de

SISTEME DE OPERARE

Autor :
prep. ing. Győrödi Robert

1995-1996
Curs de SISTEME DE OPERARE

CUPRINS

1. Introducere.............................................................................................................................5
1.1 Prezentare generală...........................................................................................................5
1.2 Responsabilităţile unui S.O...............................................................................................6
1.3 Dezvoltarea istorică a S.O................................................................................................7
1.3.1 Istoria timpurie a S.O.................................................................................................7
1.3.2 Sisteme cu prelucrare în loturi şi multiprogramare...................................................8
1.3.3 Sisteme cu divizarea timpului....................................................................................8
1.3.4 Sisteme cu memorie virtuală......................................................................................9
1.3.5 S.O. pentru minicalculatoare...................................................................................10
1.3.6 S.O. pentru microprocesoare şi calculatoare personale...........................................10
1.3.7 Reţele de calculatoare şi sisteme distribuite............................................................10
1.3.8 Perspective...............................................................................................................11
2. Structura Sistemelor de Operare..........................................................................................12
2.1 Organizarea memoriei.....................................................................................................12
2.2 Structuri de execuţie.......................................................................................................14
2.3 Interacţiunea componentelor...........................................................................................16
2.4 Adaptabilitatea la configuraţii hardware.........................................................................17
3. Sistemul de Operare MS-DOS. Probleme generale.............................................................18
3.1 Structura S.O. MS-DOS. Încărcarea sistemului.............................................................19
3.2 Utilizarea şi organizarea spaţiului pe discuri..................................................................19
3.3 Hard discuri.....................................................................................................................20
3.3.1 Floppy discuri..........................................................................................................21
3.3.2 Formatarea...............................................................................................................21
3.3.3 Partiţii.......................................................................................................................22
3.3.4 MBR-ul, sectoare boot şi tabele de partiţii..............................................................22
3.3.5 Partiţii extinse şi logice............................................................................................22
3.4 Organizarea spaţiului pe discuri......................................................................................23
3.4.1 Caracteristicile fizice ale discului............................................................................23
3.4.2 Organizarea logică a discurilor................................................................................24
3.4.3 Partiţionarea discurilor fixe.....................................................................................27
3.5 Sistemul de întreruperi în MS-DOS...............................................................................28
3.5.1 Întreruperi hardware................................................................................................29
3.5.2 Întreruperi BIOS......................................................................................................30
3.5.3 Întreruperi DOS.......................................................................................................30
3.6 Formatul fişierelor executabile. Prefixul segmentului de program................................31
3.6.1 Structura fişierelor .COM........................................................................................31
3.6.2 Structura fişierelor .EXE.........................................................................................32
4. Interfaţa MS-DOS cu utilizatorul.........................................................................................35
4.1 Generalităţi......................................................................................................................35
4.2 Servicii oferite de MS-DOS. Serviciile sistem...............................................................38
4.2.1 Funcţii pentru gestionarea fişierelor........................................................................38
4.2.2 Funcţii pentru gestionarea cataloagelor...................................................................41
4.2.3 Funcţii pentru I/E cu dispozitive standard orientate pe caractere............................42

-2-
Curs de SISTEME DE OPERARE
4.2.4 Funcţii pentru gestionarea perifericelor...................................................................43
4.2.5 Funcţii pentru gestionarea memoriei şi a proceselor...............................................45
4.2.6 Alte funcţii sistem....................................................................................................48
4.3 Funcţii de bibliotecă C pentru apelul sistemului MS-DOS............................................49
4.3.1 Funcţii pentru gestionarea fişierelor........................................................................49
4.3.2 Funcţii pentru gestionarea cataloagelor...................................................................50
4.3.3 Funcţii pentru gestionarea perifericelor...................................................................52
4.3.4 Funcţii pentru gestionarea memoriei.......................................................................55
4.3.5 Funcţii pentru gestionarea proceselor......................................................................56
4.3.6 Alte funcţii sistem....................................................................................................62
5. Gestionarea memoriei..........................................................................................................65
5.1 Memoria convenţională..................................................................................................65
5.2 Blocurile din Memoria Superioară (UMB).....................................................................65
5.3 Structuri de date folosite de MS-DOS în gestionarea memoriei....................................66
5.4 Procesarea liniei A20. Memoria înaltă...........................................................................67
6. Sistemul de Operare UNIX. Probleme generale..................................................................69
6.1 Introducere......................................................................................................................69
6.1.1 Cauzele răspândirii sistemului de operare UNIX....................................................69
6.1.2 Considerente de realizare a S.O. UNIX...................................................................70
6.1.3 Caracteristici generale al sistemelor UNIX.............................................................71
6.2 Principii de utilizare ale sistemului UNIX......................................................................71
6.3 Principalele comenzi UNIX............................................................................................73
6.4 Elemente ale limbajului de comandă UNIX (Shell).......................................................77
6.5 Proceduri shell (Shell scripts).........................................................................................80
6.5.1 Crearea şi executarea unui fişier de comenzi...........................................................80
6.5.2 Variabile şi parametri care pot fi prevăzuţi în pseudo-programe shell....................81
6.5.3 Intrarea şi ieşirea standard.......................................................................................82
6.5.4 Structuri de control în proceduri shell.....................................................................82
6.5.5 Comentarii................................................................................................................85
6.5.6 Substituţia comenzilor.............................................................................................85
6.5.7 Ordinea evenimentelor.............................................................................................85
6.5.8 Opţiunile shell-ului..................................................................................................86
6.5.9 Redirectarea întreruperilor.......................................................................................86
6.5.10 Ambianţa sau mediul de lucru...............................................................................86
6.5.11 Lansarea shell-ului din login..................................................................................87
6.5.12 Un exemplu de shell script.....................................................................................88
6.6 Vedere generală asupra sistemului..................................................................................90
6.6.1 Arhitectura sistemului UNIX...................................................................................90
6.6.2 Mediul de procesare.................................................................................................91
6.6.3 Servicii ale sistemelor de operare............................................................................92
6.6.4 Restricţii hardware...................................................................................................92
6.6.5 Întreruperi şi excepţii...............................................................................................93
6.6.6 Nivele de execuţie ale procesorului.........................................................................93
6.6.7 Gestionarea memoriei..............................................................................................93
7. Arhitectura Sistemului de Operare UNIX............................................................................95
7.1 Introducere......................................................................................................................95
7.2 Subsistemul de fişiere.....................................................................................................96
7.3 Subsistemul de procese...................................................................................................99
7.3.1 Contextul unui proces............................................................................................101
7.3.2 Stările şi tranziţiile unui proces.............................................................................102
7.4 Controlul proceselor......................................................................................................105

-3-
Curs de SISTEME DE OPERARE
7.4.1 Crearea proceselor.................................................................................................105
7.4.2 Semnale..................................................................................................................108
7.4.3 Terminarea proceselor...........................................................................................112
7.4.4 Aşteptarea terminării proceselor............................................................................112
7.4.5 Executarea altor programe.....................................................................................113

-4-
Curs de SISTEME DE OPERARE

1. Introducere.

1.1 Prezentare generală.

Prin sistem de calcul vom înţelege un ansamblu de echipamente fizice (hardware) şi


programe şi date (software).
Programele se consideră împărţite în două clase foarte largi :
• programe de aplicaţie - sunt cele care rezolvă probleme pentru un utilizator sau o
clasă de utilizatori.
• programe de sistem - sunt destinate administrării sistemelor de calcul, utilizând
resursele acelui sistem.

Programele de sistem se divid în :


• programe utilitare
• sistem de operare

În categoria programelor utilitare intră :


• translatoare de limbaj, compilatoare
• interpretoare de comenzi
• utilitare de întreţinere
• interpretoare

Sistemul de operare este responsabil pentru inclusiv unele forme de comunicare cu


utilizatorii.

Reprezentarea schematică a unui sistem de calcul :

Calculul Proiectarea Programe de


salariilor circuitelor digitale aplicaţie

}
Editor de Interpretor
Compilator
texte de comenzi Programe
SISTEM DE sistem
OPERARE

}
Limbaj maşină
Hardware
Echipamente fizice

Fig. 1.1-1
O posibilă definiţie a S.O. : Vom spune că un S.O. este un set de programe care are
două obiective :
• asigură gestionarea resurselor unui sistem de calcul, implementând algoritmi care să
realizeze performanţe cât mai bune;
• realizează o interfaţă între utilizatori şi sistemul de calcul, extinzând dar şi
simplificând setul de operaţii disponibile.

-5-
Curs de SISTEME DE OPERARE

Se consideră că rolul primar al unui S.O. este administrarea resurselor sistemului de


calcul, care pot fi resurse logice sau resurse fizice.
Considerăm resurse fizice : echipamentele unui sistem de calcul (procesorul, memoria
internă, memoria externă, echipamentele de introducere/extragere şi echipamente interne).
Principalele resurse logice ar fi : fişierele, procesele (activităţi legate de execuţia unor
programe), unităţile de lucru, job-uri sau sesiuni de lucru.
Din punctul de vedere al unui administrator al sistemului de calcul, S.O. trebuie să
asigure un raport cost/performanţă cât mai bun în utilizarea resurselor, aceasta ducând adesea
la utilizarea în comun a unor resurse. Din punctul de vedere al utilizatorului unui sistem de
calcul este important ca accesul la sistem să fie făcut într-un mod cât mai simplu, fără a fi
necesară cunoaşterea de particularităţi constructive ale echipamentelor, sau de detalii de
implementare ale programelor. Utilizatorul doreşte să obţină cât mai rapid serviciile
solicitate. Scopurile administratorului şi utilizatorului sunt în multe cazuri divergente.

Există cel puţin trei clase de persoane care fac uz de sistemul de calcul :
• utilizatori obişnuiţi - aceştia folosesc programele scrise de alţii, ei nu ştiu să
programeze, dar trebuie să aibe cunoştinţe despre modul de utilizare al sistemului de
calcul.
• programatori de aplicaţie - ei ştiu să programeze, lucrează în limbaje de nivel înalt şi
din aceste limbaje fac apel indirect la o serie de servicii oferite de S.O.
• programatori de sistem - care programează în limbaje de nivel înalt sau în limbaj de
asamblare şi fac uz mai direct de serviciile S.O., folosind un mecanism de
declanşare al serviciilor, diferit de cel aflat la dispoziţia programatorilor de aplicaţii.

1.2 Responsabilităţile unui S.O.

a) Interfaţa cu utilizatorul.
Există cel puţin două componente distincte ale interfeţei :
• interfaţa prin comenzi
• interfaţa prin apeluri sistem

Interfaţa prin comenzi - este realizat prin introducerea de la un dispozitiv de intrare a


unor comenzi care sunt prelucrate de interpretorul de comenzi al sistemului.
Apelurile sistem - se folosesc în programe şi se declanşează în timpul execuţiei acestor
programe. Ca formă ele se apropie de apelurile de funcţie dar au un alt mecanism de execuţie.
Interfaţa la nivel de comenzi este cea care determină în bună măsură dacă S.O. este
atractiv pentru utilizator sau nu. În noile S.O., se insistă pe crearea de interfeţe “user-
friendly”.

b) Gestionarea fişierelor.
Suportul cel mai folosit al fişierelor este discul magnetic, dar există şi alte medii de
stocare (de ex. discuri compacte).
În componenta de S.O. care realizează gestionarea de fişiere sunt prevăzute servicii
pentru crearea, distrugerea, citirea, scrierea, organizarea şi controlul accesului la informaţiile
din fişiere.

c) Gestionarea perifericelor.
Datorită varietăţii echipamentelor periferice care se pot conecta într-un sistem de
calcul se impune ca S.O. să preia detaliile legate de lucrul cu aceste periferice. În modulul de
gestionare a perifericelor sunt incluse toate aspectele specifice pentru fiecare periferic, astfel
-6-
Curs de SISTEME DE OPERARE
încât spre utilizatorii acestui modul funcţionarea tuturor perifericelor să apară standardizată.
De regulă modulul de gestionare a perifericelor funcţionează pe baza unor interacţiuni cu
mecanismul de întreruperi.

d) Gestionarea memoriei.
Din memoria internă o parte este întotdeauna rezervată S.O., iar restul memoriei este
disponibilă pentru unul sau mai mulţi utilizatori. Dacă S.O. permite existenţa mai multor
programe utilizator în memoria internă se pune problema controlului accesului acestor
programe la diverse zone de memorie. În sistemele cu memorie virtuală gestionarea memoriei
include şi o parte a memoriei externe, care serveşte ca suport pentru memoria virtuală.

e) Gestionarea proceselor.
Numim proces un program în execuţie sub controlul unui S.O. Majoritatea S.O.
prevăd posibilitatea existenţei simultane a mai multor procese, de unde rezultă necesitatea de
a controla interacţiunea acestora. Este vorba în primul rând de accesul proceselor la unitatea
centrală dar şi de mecanisme de sincronizare între procese.

f) Tratarea erorilor.
S.O. trebuie să fie pregătit pentru a reacţiona la o mare diversitate de erori cu cauze
atât în hardware cât şi în software. Erorile trebuie detectate şi în măsura posibilului trebuie
asigurată revenirea din erori şi continuarea lucrului.

g) Gestionarea sistemului.
Instalarea unui S.O. pe un nou echipament, culegerea de date statistice despre
comportarea sistemului pentru ajustarea funcţionării sale în viitor şi în special ţinerea
evidenţei resurselor consumate de fiecare utilizator al sistemului.

În prezent se tinde spre standardizarea S.O. în special spre standardizarea interfeţelor.

1.3 Dezvoltarea istorică a S.O.

1.3.1 Istoria timpurie a S.O.

La primele calculatoare programarea se făcea în cod maşină, cu instrucţiuni, dacă nu


şi date, introduse prin chei de la panoul de comandă al calculatorului. În aceste condiţii
utilizarea unui calculator cerea cunoştinţe profesionale foarte bune din partea
programatorului, care avea control complet asupra calculatorului. O primă îmbunătăţire apare
prin conectarea la calculator a unor dispozitive periferice care să faciliteze introducerea
programelor şi datelor şi extragerea rezultatelor. În domeniul limbajelor apare conceptul de
limbaj de asamblare şi care devine un prim limbaj de sistem, precum şi asamblorul care
constituie embrionul unui S.O.
Programe utilitare : editor de legături şi încărcătorul de programe.
Utilizarea calculatoarelor în aceste condiţii implica efectuarea unui număr mare de
operaţii manuale, situaţie răsturnată după introducerea benzilor şi discurilor magnetice. După
1950 este perioada în care numărul de calculatoare începe să crească (UNIVAC şi IBM).
Utilizatorii acestor sisteme îşi creează organizaţii proprii şi au întâlniri la care discută în
primul rând reducerea operaţiilor manuale şi simplificarea operaţiilor de
introducere/extragere. În urma acestor discuţii apare la firma General Motors primul S.O.
numit I.O.S. (Input Output System). Exista o porţiune rezidentă în memorie care conţinea
rutinele pentru operaţiile de I/O şi ele se apelau din subrutine. Se făcea salt la sfârşitul

-7-
Curs de SISTEME DE OPERARE
fiecărui program pentru ca o rutină încărcător din I.O.S. să încarce în memorie următorul
program. Apoi au apărut S.O.S. - IBM 709, mai perfecţionat, includea şi un asamblor şi apoi
sistemul FMS, tot la IBM.

1.3.2 Sisteme cu prelucrare în loturi şi multiprogramare.

Apariţia acestor sisteme a fost determinată de apariţia discurilor magnetice şi a


benzilor magnetice în sistemele de calcul. Apar sistemele T.O.S. şi D.O.S. Apare dorinţa de
utilizare a puterii de calcul sporite a hardware-ului. Utilizatorii nu mai aveau acces direct la
calculator, ci programele lor împreună cu datele necesare pentru execuţie se colectau de către
un personal specializat şi ele erau însoţite şi de cartele de comandă, prin care se specifică
serviciile dorite de la sistem. Pentru aceasta se folosea J.C.L. - Job Control Language -
convenţia pentru serviciile dorite de la sistem.
Pachetele de cartele formau loturi care erau lansate în execuţie când atingeau o
anumită dimensiune. Lucrul în loturi permite sporirea gradului de utilizare a U.C. O creştere
suplimentară a gradului de utilizare a U.C., dar şi a celorlalte echipamente dintr-un sistem de
calcul se obţin prin introducerea multiprogramării (prezenţa simultană în memoria operativă a
unui sistem de calcul a mai multor programe utilizator). Atunci când un program solicită o
operaţie periferică U.C. este dată unui alt program din memorie.

UC în UC
activitate neutilizat Timp
Progr. operaţie aşteptare
A periferică

timp

Progr.
B
Grad de
utilizare
timp

Progr.
C

timp

Fig. 1.3-1
Din punctul de vedere al utilizatorului sistemele cu prelucrare în loturi produc o
separare a utilizatorului de sistemul de calcul şi lungesc timpul dintre predarea (introducerea)
unei lucrări în sistem şi obţinerea rezultatelor.

1.3.3 Sisteme cu divizarea timpului.

Datorită introducerii în sistemele de calcul a unor echipamente numite terminale, a


fost posibilă dezvoltarea unor sisteme cu divizarea timpului. Terminalul permite introducerea
-8-
Curs de SISTEME DE OPERARE
informaţiilor în sistem (de ex. prin tastatură), precum şi extragerea rezultatelor (prin
mecanisme de imprimare pe hârtie, echipament cu tub catodic).
Folosind acest sistem este posibil ca mai mulţi utilizatori să aibă acces simultan direct
la resursele calculatorului.

Memoria Calculator
Imprimantă
externă (UC +memorie)

.....

Terminale

Fig. 1.3-2
Timpul UC este acum distribuit între terminale active în cantităţi egale numite cuante.
Toate programele active la un moment dat pot progresa. Ordinul de mărime al unei cuante era
de 100 ms. Nu toate programele active la un moment dat au loc în memoria operativă. De
aceea, în astfel de sisteme se utilizează şi un mecanism (algoritm) special pentru gestionarea
memoriei numit swapping. Aceasta înseamnă că atunci când UC este luată de la un program
activ în acel moment şi cedată la alt program, dacă programul care primeşte controlul nu este
prezent în memorie, el va fi transferat în memorie în locul unui program care a pierdut
dreptul de utilizare a UC. Acesta este transferat în memoria externă în starea în care e la
momentul respectiv (programele îşi schimbă locul).
De regulă însă algoritmul de swapping este combinat cu multiprogramarea rezultând
o creştere a gradului de utilizare a memoriei şi a UC.

Exemple :
• OS/360 - sistem de operare cu multiprogramare, scris în 1965 pentru IBM 360.
• CTSS - sistem de operare cu divizarea timpului (Compatible Time Sharing System)
pentru IBM 7094 (generaţia a II-a) scris la MIT.
• TSS - Time Sharing System scris de IBM.
• TSO - variantă a lui OS/360 care permite şi o opţiune de divizare a timpului.
• MULTICS
• proiect realizat la MIT
• S.O. bazat pe calculatorul GE635 (General Electric)
• alte concepte exploatate :
• memorie virtuală segmentată
• ierarhie de fişiere
• redirectarea operaţiilor de I/E
• acces protejat la resurse
• interfaţă extinsă cu utilizatorul
• ideea de bază : puterea de calcul să se transforme în utilitate publică

1.3.4 Sisteme cu memorie virtuală.

Exemple :
• Atlas - 1961 - conceptul de memorie cu un singur nivel
• IBM 370 :
• OS/MVS - Multiple Virtual Space
-9-
Curs de SISTEME DE OPERARE
• OS/VM - Virtual Machine - face posibilă rularea simultană a mai multor S.O.

1.3.5 S.O. pentru minicalculatoare.

Minicalculatoarele erau sisteme de calcul cu performanţe mai modeste dar cu un preţ


redus, acest preţ lărgind categoria potenţialilor cumpărători. Firma care a produs cele mai
multe sisteme de acest fel este DEC iar calculatorul produs se numeşte PDP (PDP 7, PDP 8).
După 1970 apare PDP-11 care a avut o foarte mare răspândire. Asemenea calculatoare
au fost construite şi la noi : CORAL, Independent. Pentru PDP-11 primul S.O. oferit a fost
RT11 (Real Time 11). RT11 este un sistem cu divizarea timpului. Pe modele mai
perfecţionate ale lui PDP-11 sau utilizat S.O. RSX-11 (avea un sistem de gestionare a
fişierelor mai performant precum şi un set extins de comenzi şi programe utilitare).
Tot firma DEC a dezvoltat ulterior o altă familie de calculatoare VAX-11. Pentru
aceste sisteme s-a folosit S.O. VMS (Virtual Memory System).
Alt S.O. dezvoltat pentru minicalculatoare este UNIX, care a fost scris de Thompson
1968-1969, de la Bell Labs (AT&T). Acest sistem a fost inspirat din sistemul de operare
MULTICS. Ca şi particularităţi conţine mecanisme pentru comunicare între procese, sistem
de fişiere ierarhic şi limbaj de comandă deosebit de flexibil. Sistemul a fost rescris pentru
PDP-11 de Thompson şi Ritchie, pentru aceasta folosindu-se limbajul C. În acest fel UNIX a
devenit primul S.O. scris în bună măsură în limbaj de nivel înalt, deoarece până atunci toate
S.O. erau scrise în limbaj de asamblare. Aceasta face ca sistemul să fie uşor portabil şi s-a
realizat implementarea lui şi pe alte tipuri de calculatoare.
Azi UNIX este disponibil pe toate tipurile de calculatoare. Pentru standardizarea
sistemului există un institut numit POSIX.

1.3.6 S.O. pentru microprocesoare şi calculatoare personale.

În 1971 Intel a produs primul microprocesor pe 4 biţi : 4004. Pentru 8080 a fost scris
S.O. CP/M (Digital Research), care era simplu, monoutilizator şi avea un set redus de
comenzi.
Apariţia microprocesoarelor pe 16 biţi duce la apariţia calculatoarelor personale :
8086 (8088), Motorola 6800.
Pentru familia 8086 apare MS-DOS, scris de Microsoft. Acest sistem împrumută
elemente de la UNIX dar este monoutilizator şi monoprocesor.
La apariţia µP 80286, IBM a introdus familia PS/2, pentru care a elaborat S.O. OS/2.
Baza de aplicaţii a lui OS/2 este mai redusă decât aceea pentru MS-DOS şi astfel este folosit
mai puţin.
Microprocesoarele începând cu 80386 sunt deja suficient de puternice pentru folosirea
S.O. UNIX.
Pe lângă calculatoarele personale se răspândesc staţiile de lucru (workstation), care au
putere de lucru mare, bazate pe procesoare RISC.

1.3.7 Reţele de calculatoare şi sisteme distribuite.

Reţelele de calculatoare înseamnă integrarea sistemelor de calcul cu sisteme de


comunicaţie. Ideea comunicaţiei între calculatoare a fost realizată pentru prima dată în
sistemul ARPANET, finanţată de o agenţie numită DARPA (Defence Advanced Research

- 10 -
Curs de SISTEME DE OPERARE
Project Agency). Fiecare dintre calculatoarele conectate avea sistemul propriu de operare, iar
operaţiile dintre calculatoare erau privite ca operaţii periferice.
Realizarea reţelelor a impus modificări în software şi hardware. Odată cu răspândirea
sistemelor s-a pus problema sistemelor de operare de reţea. Acest deziderat s-a putut realiza
atunci când s-au răspândit calculatoarele interconectate local (LAN - Local Area Networks),
astfel apărând MAN (Metropolitan Area Networks) şi WAN (Wide Area Networks).
Există şi încercări de realizare a unor S.O. care tratează toate resursele dintr-o reţea ca
făcând parte dintr-un sistem unic, deci nu mai lasă vizibilă utilizarea topologiei de reţea.
Acestea se numesc S.O. distribuite.

1.3.8 Perspective.

Câteva dintre tendinţele actuale ar fi : dezvoltarea reţelelor de calculatoare, realizarea


unor sisteme cu paralelism masiv, interconectarea strânsă a sute sau mii µP.
Includerea în S.O. a elementelor de gestionare pentru paralelism masiv este
considerată o sarcină dificilă. Va fi deasemenea dificil să se dezvolte limbaje de programare
şi compilatoare pentru astfel de sisteme.
O altă tendinţă este realizarea unor interfeţe cu utilizatorul cât mai evoluate, în special
integrarea şi interpretarea a cât mai multor medii de comunicare (text, grafic, voce,
imagine ...) adică o tendinţă spre sisteme multimedia.
Deasemenea o tendinţă este orientarea spre sisteme deschise, sisteme ale căror
specificaţii sunt făcute publice şi sunt standardizate. Astfel se pot realiza implementări
diferite dar care să respecte specificările standardizate. Aceasta va permite portabilitatea
aplicaţiilor şi chiar a S.O.

- 11 -
Curs de SISTEME DE OPERARE

2. Structura Sistemelor de Operare.

Sistemele de Operare sunt sisteme de complexitate mare, care pentru a fi înţelese şi


stăpânite trebuie divizate în subsisteme. O cale de divizare funcţională a S.O. ar fi
considerarea diverselor responsabilităţi ale sistemului ca module separate. Există şi alţi factori
cu importanţă în stabilirea structurii unui S.O., cum ar fi : caracteristicile hardware ale
sistemului, limbajul de implementare folosit, domeniile de aplicaţie etc. Din cauza
complexităţii un S.O. va apare diferit când este examinat din puncte de vedere diferite.
Posibile puncte de vedere ar fi :
• organizarea codului sursă al sistemului
• organizarea memoriei
• structura de execuţie
• interacţiunea dintre componente
• adaptabilitatea la configuraţii hardware

2.1 Organizarea memoriei.

Întotdeauna o parte a unui S.O. trebuie să se găsească în memorie în timpul


funcţionării sistemului. Acesta este partea rezidentă a S.O. şi constă din cod care tratează
servicii critice :
• planificarea proceselor
• tratarea erorilor
• tratarea iniţială a apelurilor sistem

Componentele utilizate cu frecvenţă redusă cum ar fi părţi din sistemul de gestionare a


sistemului sau interpretorul de comenzi, nu rămân în permanenţă în memorie ci se încarcă la
cerere de pe un suport extern. Acestea se numesc componente tranzitorii ale S.O..
Pentru partea rezidentă a S.O. se rezervă o zonă fixă a memoriei. De obicei începutul
şi sfârşitul memoriei fizice.
Pentru componentele tranzitorii ale sistemului poate fi rezervată o zonă denumită sau
acestea pot fi tratate la fel ca şi programele utilizatorului, fiind încărcate în orice porţiune de
memorie disponibilă.
În zona inferioară a memoriei găsim detalii despre întreruperi. Zona superioară a
memoriei este uneori utilizată pentru comunicare cu perifericele iar în alte arhitecturi se
foloseşte pentru S.O. din motive de simplificarea scrierii programelor.
Organizarea memoriei în S.O. este prezentată prin hărţi de memorie, unde de regulă
apar 3 aspecte distincte :
• conţinutul spaţiului virtual de adrese al unui program utilizator
• conţinutul spaţiului virtual de adrese al unui program sistem
• conţinutul memoriei fizice

- 12 -
Curs de SISTEME DE OPERARE
În CP/M nu se face distincţie între cele 3 aspecte, deci harta va reprezenta conţinutul
memoriei fizice (Fig. 2.1-1).
64K BIOS - conţine rutinele pentru tratarea la nivel fizic a
BIOS
operaţiilor periferice
BDOS BDOS - conţine elemente necesare pentru construcţia
CCP unui sistemde fişiere
BIOS +BDOS ≈ 10K
Nefolosit CCP - interpretor de comenzi al sistemului (2K), el este
încărcat la cerere. Dacă este necesar, un program
Programede de aplicaţie poate acoperi zona de memorie a CCP.
aplicaţie+date
256 Obs. : Toate programele de aplicaţie sunt generate
considerând că ele se încarcă în memorie de la
zonă comunicaţie
adresa 1000H (4K).
0

Fig. 2.1-1
În MS-DOS deosebim o hartă a memoriei fizice şi una a spaţiului virtual de adrese a
programelor (Fig. 2.1-2 şi Fig. 2.1-3).
1M zona de comunicaţii constă din tabela vectorilor de
BIOS (ROM)
întreruperi (1K) şi zona de transfer cu discul 512 octeţi.
Mem video zona rezident SO se includ rutinele pentru gestiunea
640K
Interpretor c-zi sistemului, rutinele pentru realizarea sistemului de fişiere şi
o parte din interpretorul de comenzi.
Nefolosit memoria video cuprinde zona care se pune în
corespondenţă directă cu zona consolei utilizator.
Programede
aplicaţie+date
~100K
Rezident SO
1536
zonă comunicaţie
0

Fig. 2.1-2
Din punctul de vedere al unui program spaţiul virtual de adrese se compune din 1 ÷ 4
segmente de 64 Kocteţi. În figura Fig. 2.1-3 se prezintă cazul tipic : modelul de memorie
small.
64 K 64 K
stiva aplicaţiei

cod propriuzis

dateleaplicaţiei zona decomunicaţie(PSP)


0 0
segmentul dedate(DS) segmentul decod (CS)

Fig. 2.1-3

- 13 -
Curs de SISTEME DE OPERARE
În UNIX harta fizică a memoriei va depinde de arhitectura calculatorului pe care este
implementat sistemul.
Există o separare totală între spaţiul de adrese al unui program utilizator şi spaţiul de
adrese al unui program sistem (Fig. 2.1-4). Orice comunicare între programele utilizator şi
sistem se face prin apeluri sistem (deci nu prin memorire comună).

Adrese Adrese
MARI STIVA MARI
Neutilizat

Partea rezidentă
Dateiniţiale a S.O.

Cod program
Adrese Adrese
mici mici
Program utilizator Sistemul deOperare

Fig. 2.1-4

Obs.: Comparativ, partea rezidentă a celor trei S.O. prezentate necesită următoarele
cantităţi de memorie :
• CP/M ≈ 10Kb
• MS-DOS ≈ 100Kb
• UNIX ≈ 1Mo

2.2 Structuri de execuţie.

Codul unui S.O. constă din mai multe unităţi executabile, execuţia fiecărei unităţi
fiind declanşată de evenimente specifice.
În organizarea tradiţională S.O. pot fi privite ca o colecţie de rutine, activate fiecare
prin apeluri sistem din programe utilizator, sau ca urmare a unor întreruperi.

apeluri sistem
puncte dispecer Rutine de tratare
de a apelurilor sistem
intrare
intr #1
în
sistem Rutine de tratare
intr #2 specifice

Fig. 2.2-1
Rutinele care servesc ca puncte de întrerupere în S.O. trebuie să fie în partea rezidentă
a sistemului de operare.

- 14 -
Curs de SISTEME DE OPERARE
Obs.: Majoritatea S.O. prevăd un singur punct de intrare în sistem, acesta fiind un
dispecer care va rezolva apelurile.
În timpul execuţiei acestor rutine sau altora pot apare solicitări de încărcare a unor
părţi nerezidente. Aduse în memorie ele vor avea acelaşi nivel de privilegiu cu rutinele
rezidente (există şi excepţii). Un apel sistem apare în programe cu aceeaşi sintaxă ca şi apelul
obişnuit de rutine. Traducerea lor în cod executabil este însă diferită, prevăzându-se operaţiile
necesare pentru schimbarea regimului de execuţie (se trece din regim neprivilegiat, specific
programelor utilizator, în regim privilegiat, specific S.O., adică nu mai sunt aplicate toate
elementele de protecţie aplicate programului utilizator).
Multe din rutinele S.O. revin la programul apelant numai după satisfacerea completă a
serviciilor solicitate.
Există apeluri sistem (şi deci rutine din S.O.) care declanşează activităţi ce se vor
desfăşura simultan cu desfăşurarea activităţilor U.C. fără a avea nevoie de U.C. (de exemplu
operaţiile de I/O). În aceste cazuri, programul care a făcut apelul, e de regulă suspendat până
la terminarea serviciului, iar U.C. poate fi folosit de alte programe (vezi multiprogramarea).
La terminarea unui serviciu de durată, solicitat de un program, se declanşează o întrerupere ce
va face ca U.C. să suspende execuţia programului în curs şi să treacă la execuţia rutinei de
tratare a întreruperii. Ca urmare a execuţiei acestei rutine se vor modifica diferite structuri de
date şi este posibil ca programul care a fost blocat pentru efectuarea serviciului să devină gata
de execuţie. La terminarea rutinei de tratare a întreruperii, o componentă a S.O., numită
planificator decide dacă se reia execuţia programului care a fost suspendat în momentul
apariţiei întreruperii sau se alege un alt program.
În S.O. simple toate serviciile sunt duse până la capăt fără a se reveni la programul de
aplicaţie sau altele.
În S.O. mai complexe, apar activităţi ale S.O. care trebuie să continuie pe o perioadă
mai lungă în paralel cu execuţia proceselor utilizator (tipărirea unor fişiere la imprimantă,
controlul unor linii de telecomunicaţie, o serie de activităţi periodice cum ar fi la UNIX
salvarea tampoanelor din memorie pe disc). Ele sunt organizate ca procese distincte, numite
taskuri sistem şi sunt tratate asemănător cu procesele utilizator, având însă prioritate de
planificare mai mari.
Organizarea unei părţi din S.O. ca procese este o cale de reducere a erorilor în
realizarea S.O. (pentru că trebuie controlat un volum mai mic de cod). Există o parte a
codului de S.O. care rămâne în afara proceselor sistem şi cuprinde servicii esenţiale, între ele
fiind inclusă şi planificarea proceselor, comunicarea între procese, rutine pentru gestionarea
memoriei etc.

- 15 -
Curs de SISTEME DE OPERARE

2.3 Interacţiunea componentelor.

Într-o structură fără restricţii, orice rutină a unui S.O. ar putea apela orice altă rutină
sau ar putea face acces la orice structuri de date ale sistemului. Un astfel de S.O. ar fi foarte
puţin fiabil pentru că ar fi extrem de dificil să se controleze efectele unei schimbări într-o
rutină sau într-o structură de date. Restricţii asupra interacţiunilor componentelor pot fi
impuse atât în procesul de proiectare a S.O. cât şi în timpul execuţiei rutinelor sistem. La
proiectare restricţiile se pot realiza prin intermediul compilatorului, dacă se foloseşte un
limbaj adecvat pentru implementare. La execuţie este necesar un sprijin din partea hardware-
ului, ceea ce în cele mai multe arhitecturi nu este întâlnit.
Primul S.O. proiectat structurat a fost sistemul T.H.E. (Technische Hocheschool
Eindhard), realizat de un grup condus de Dýkstra. A fost experimental, a apărut înainte de
1968 şi a fost scris pentru minicalculatoarele Philips. Era organizat pe mai multe nivele
(Fig. 2.3-1) :

5. Utilizatori

4. Programe deaplicaţie

3. Periferice

2. Consola utilizator

1. Memoria virtuală

0. Planificarea proceselor,
tratarea întreruperilor

Fig. 2.3-1
Fiecare nivel gestionează o categorie de resurse, vitualizează resursele respective. Se
realizează astfel o secvenţă de maşini abstracte (sau virtuale), fiecare bazată pe maşina de la
nivelul inferior şi fiecare oferind noi funcţii pentru nivelul inferior :
• La nivelul 0 există o colecţie de rutine, ce realizează virtualizarea procesorului.
deasupra acestui nivel este asigurat faptul că fiecare proces are propriul său procesor
virtual.
• La nivelul 1 există un proces sistem care realizează virtualizarea memoriei. În
nivelele următoare fiecare proces are propriul spaţiu de memorie virtuală. Resursele
gestionate la nivelul 1 sunt memoria internă şi un tambur magnetic, care serveşte ca
suport extern al memoriei virtuale (precursorul discului magnetic).
• Nivelul 2 conţine un proces care gestionează consola calculatorului. Prin
virtualizarea acesteia fiecare proces din nivelele superioare are propria consolă
utilizator, la care poate emite mesaje.
• La nivelul 3 se găseşte câte un proces pentru fiecare periferic, altul decât consola
sau tamburul prezent în configuraţia sistemului de calcul.
• Nivelul 4 conţine un număr oarecare de procese care funcţionează ca interpretoare
de comenzi şi deci pot controla execuţia programelor utilizatorilor.
Structurarea pe nivele a S.O. permite ca porţiunile critice ale sistemului să fie izolate
în nivelele inferioare care se construiesc şi se testează cu foarte multă rigurozitate. Numărul

- 16 -
Curs de SISTEME DE OPERARE
de nivele din structura unui sistem nu este fix, depinde de deciziile luate de proiectanţi,
funcţie de domeniul de aplicaţie, de experienţa proiectanţilor etc.
O extindere a modelului este prezentată în figura Fig. 2.3-2.

15 Shell (interpretor dec-zi)


14 Cataloage
13 Proceseutilizator Reţea (nu
12 I/E la nivel logic neapărat)

11 Periferice
10 Sistem defişiere
9 Comunicaţii interprocese
8 Mecanismedeprotecţie
7 Memorievirtuală Capabilităţi
6 Memorie secundarã localã
5 Proceseelementare Calculator
singular
4 Întreruperi
3 Mecanismepentru proceduri
Hardware
2 Setul deinstrucţiuni
1 Circuiteelectronice

Fig. 2.3-2

2.4 Adaptabilitatea la configuraţii hardware.

S.O. depinde în mare măsură de hardware-ul calculatorului, aceasta fiind una din
principalele deosebiri ale S.O. faţă de alte componente software. Este de dorit ca un S.O. să
fie proiectat avându-se în vedere şi portabilitatea. în cazul cel mai simplu este necesar ca un
S.O. să poată fi uşor adaptat la sisteme de calcul cu configuraţii diferite dar din aceeaşi
familie. Adică să se poată uşor specifica un număr diferit de periferice, cantitate diferită de
memorie etc. În cazul extrem pentru adaptarea la o nouă configuraţie ar trebui făcute
modificări în codul sursă al S.O. Modalitatea uzuală întâlnită însă este ca la iniţializarea S.O.
să fie consultată o serie de fişiere de configurare din care se culeg informaţii specifice unui
sistem de calcul dat. La MS-DOS acest fişier este config.sys, în UNIX există mai multe fişiere
de configurare pe mai multe zone funcţionale ale sistemului. Mai trebuie prevăzută şi
modalitatea de a adăuga simplu la sistem porţiuni de program numite drivere care controlează
funcţionarea unor periferice noi adăugate în configuraţia hardware.

- 17 -
Curs de SISTEME DE OPERARE

3. Sistemul de Operare MS-DOS. Probleme generale.

Sistemul există din 1981. Este primul S.O. pentru microprocesoare pe 16 biţi. În
momentul de faţă se consideră că este cel mai răspândit S.O. din lume, având vândute câteva
zeci de milioane de exemplare. Sistemul de operare constituie baza pentru câteva zeci de mii
de aplicaţii, ceea ce îl situează pe primele locuri în lume.
MS-DOS versiunea 1.0 a apărut în august 1981. Această versiune avea multe
caracteristici în comun cu S.O. CP/M (care a fost folosit ca şi model). Avea sistem de fişiere
cu un singur nivel, ceea ce s-a corectat în martie 1983, la versiunea 2.0, care conţine pentru
prima dată driver pentru discul winchester de 10Mb şi sistem de fişiere ierarhic. În versiunea
2.0 se împrumută de la UNIX modul de realizare a apelurilor sistem pentru lucrul cu fişiere.
În august 1984 apare versiunea 3.0 care este legată de introducerea calculatoarelor AT
cu Intel 80286 şi cu discuri fixe de 20Mb (arhitectura standard I.B.M.) şi discuri flexibile de
1,2Mb. Versiunea 3.0 permite definirea de discuri virtuale în RAM şi introduce posibilitatea
de protecţie a fişierelor (un fişier poate fi considerat read only).
În noiembrie 1984 apare versiunea 3.1 care permite lucrul în reţea şi posibilitatea ca în
aceeaşi reţea să se lucreze cu sisteme de fişiere diferite.
În 1986 s-a introdus versiunea 3.2 cu driver pentru discuri flexibile de 3 1/2 inch şi
câteva comenzi suplimentare între care xcopy. De asemenea introduce un driver special care
permite copierea pe aceeaşi unitate de disc.
În 1987 este lansată versiunea 3.30 care adaugă câteva funcţii cerute de utilizatori, pe
noua familie de calculatoare numită PS/2. Se permit 4 porturi de comunicare, se permit
dischete de 1.44Mb şi se permit pe discuri fixe mai multe (4) partiţii de maximum 32Mb
fiecare.
În 1988 este introdusă versiunea 4.0 care permite lucrul cu discuri fixe cu capacităţi
de până la 2Gb. Permite utilizarea de către programe a memoriei expandate şi introduce o
interfaţă grafică cu utilizatorul, realizată conform specificaţiilor T.B.M. numite C.U.A.
(Common User Access). Versiunea 4.1 lansată spre sfârşitul lui 1988 corectează unele
anomalii găsite la 4.0.
În primăvara lui 1991 apare versiunea 5.0 care extinde interfaţa grafică în sensul unei
apropieri de Windows.
Din 1993 este disponibilă versiunea MS-DOS 6.0 iar din 1994 versiunea 6.2, care
dispun de următoarele facilităţi :
• includerea unui utilitar pentru compresia datelor pe disc ce poate determina chiar
dublarea capacităţii suporţilor magnetici (DoubleSpace);
• incorporarea unui program antivirus (MSAV şi VSAFE);
• înlocuirea comenzilor Backup-Restore cu un produs mai bun şi posibilitatea
eliminării fragmentărilor de pe disc (DEFRAG);
• incorporarea unei modalităţi îmbunătăţite de gestionare a memoriei superioare
(MemMaker);
• determinarea în mod automat a programelor ce se pot executa în memoria
superioară (DOS 6.0 ocupă 200K din totalul de 384K);
• comanda MOVE care permite mutarea unui director cu întreg conţinutul;
• comanda CHOICE, ce permite citiri de la tastatură în timpul execuţiei, inclusă în
limbajul de comenzi al fişierelor batch.

- 18 -
Curs de SISTEME DE OPERARE
3.1 Structura S.O. MS-DOS. Încărcarea sistemului.

3.2 Utilizarea şi organizarea spaţiului pe discuri.

Când se instalează , sau se face upgrade la un sistem, o mare parte a lucrului implică
manipularea şi utilizarea discurilor. Pentru ca pe un disc să se poată stoca fişiere, acesta
trebuie pregătit în conformitate cu sistemul utilizat.
Principalii paşi în pregătirea discurilor sunt:
• Formatarea discului. Acest proces implică mai multe operaţii de pregătire, ca de
exemplu căutarea de sectoare defecte. În prezent cele mai multe hard discuri nu
necesită această operaţie, deoarece ele vin deja gata formatate de la producător.
Acest proces mai este cunoscut şi sub numele de Formatare Low Level.
• Partiţionarea hard discului, aceasta în cazul în care se doreşte utilizarea lui pentru
mai multe activităţi care să nu interfere unele cu altele. Un motiv pentru partiţionare
este folosirea unui singur disc pentru mai multe sisteme de operare. Un alt motiv
este de a ţine separat fişierele utilizatorilor de cele de sistem, aceasta simplificând
back-up-urile şi deasemenea ajută la protejarea fişierelor sistem.
• Crearea unui sistem de fişiere (de un tip potrivit) pe fiecare disc sau partiţie. De
obicei discurile, respectiv partiţiile nu se pot utiliza până când nu se creează pe ele
un sistem de fişiere. Acest proces mai este cunoscut şi sub numele de Formatare
High Level.
• Pentru anumite sisteme, pentru ca sistemele de fişiere create să se poată utiliza, ele
trebuiesc montate sau automat sau manual. (UNIX). De obicei sistemele de fişiere
montate manual trebuiesc demontate tot manual.

- 19 -
Curs de SISTEME DE OPERARE

3.3 Hard discuri

În figura următoare se prezintă schematic părţile importante ale unui hard disc.

Privit de sus :
Sens de rotaţie
Pistă / Track

Sector

Cap de
citire / scriere

Platan (Platter)

Feţe

Cilindru

Fig. 3.3-1
Un hard disc constă dintr-unul sau mai multe platane circulare, care au una sau ambele
feţe acoperite cu o substanţă magnetică, folosită pentru înregistrarea informaţiilorr. (Platanele
sunt de obicei construite dintr-o substanţă mai dură, de ex. aluminiu, ceea ce le dă şi numele
de discuri dure sau de masă - hard disc). Pentru fiecare suprafaţă există un cap de
citire/scriere care examinează sau alterează informaţia înregistrată. Platanele se rotesc pe o
axă comună, cu o viteză tipică de 3600 rotaţii pe minut, deşi discurile de înaltă performanţă
au o viteză de rotaţie mărită. Capetele se deplasează de-a lungul razei platanelor. Această
mişcare combinată cu rotaţia platanelor permite accesarea oricărei părţi a suprafeţei.
Procesorul şi discul comunică printr-un controller de disc. Acesta separând restul
computerului de sarcina de a controla efectiv discul.

- 20 -
Curs de SISTEME DE OPERARE
Suprafeţele, de obicei sunt divizate în inele concentrice, numite piste (tracks), iar
acestea sunt divizate în sectoare (sectors). Această împărţire este folosită pentru specificarea
unei anumite locaţii de pe disc şi pentru alocarea spaţiului pentru fişiere. Astfel pentru a găsi
un anumit loc pe disc se poate specifica : suprafaţa x, pista y, sectorul z. De obicei numărul
de sectoare este acelaşi pentru toate pistele, dar unele hard discuri au mai multe sectoare pe
pistele exterioare şi mai puţine pe pistele interioare. Tipic, un sector va conţine 512 octeţi de
date. Discul nu poate manipula o cantitate mai mică de date decât aceea a unui sector.
Fiecare suprafată este divizată în piste şi sectoare în acelaşi fel. Aceasta înseamnă că
dacă capul pentru o suprafaţă este pe o pistă, capetele pentru celelalte suprafeţe sunt
deasemenea pe pistele corespondente. Toate pistele corespondente luate împreună poartă
denumirea de cilindru. Mutarea capetelor de pe un cilindru pe altul durează un anumit timp,
astfel plasând împreună datele care sunt accesate des, astfel ca să fie situate pe acelaşi
cilindru, nu este necesară operaţia de mutare a capetelor pentru a citi toate aceste date.
Aceasta înbunătăţeşte performanţele. Din păcate nu întotdeauna este posibil să plasăm astfel
fişierele. Fişierele care sunt stocate în mai multe părţi de pe disc se numesc fragmentate.
Numărul feţelor (sau capetelor), cilindrilor şi sectoarelor variază destul de mult.
Specificarea numărului fiecăruia este numită geometria hard discului. Această geometrie de
obicei este memorată într-o memorie specială numită CMOS RAM.
Din păcate BIOS-urile mai vechi au o limitare în implementare, fiind imposibilă
specificarea unui număr de piste mai mare de 1024, în memoria CMOS, aceasta este însă prea
puţin pentru hard discurile mari. Pentru a depăşi această limitare, controllerele de disc
comunică o geometrie falsă şi translatează adresele date de calculator astfel încât să concorde
cu realitatea. De exemplu un hard disc ar putea avea 8 capete, 2048 piste şi 35 sectoare pe
pistă. În acest caz, o posibilă translatare ar fi : 16 capete, 1024 piste şi 35 sectoare pe pistă.
Această translatare ar putea afecta performanţele (vezi discuţia despre fragmentare).
Translatarea este o problemă doar pentru discurile IDE. Discurile SCSI utilizează o
numerotare secvenţială a sectoarelor.

3.3.1 Floppy discuri

Un disk floppy constă dintr-o membrană flexibilă acoperită pe una sau ambele feţe cu
o substanţă magnetică similară cu cea a hard discurilor. Un disc floppy corespunde unui
platan dintr-un hard disc, dar este îndepărtabil din unitate, pe când hard discurile constituie o
unitate indivizibilă.
Ca şi un hard disc, un floppy este divizat în piste şi sectoare (iar cele două piste
corespondente de pe feţele unui floppy formează un cilindru), dar sunt mult mai puţine decât
pe un hard disc.
O unitate de floppy discuri de obicei poate fi folosită cu mai multe tipuri de discuri,
de exemplu o unitate de 31/2 inch poate fi utilizată atât cu dischete de 720KB cât şi de
1,44MB.

3.3.2 Formatarea

Formatarea este procesul scrierii de marcaje pe mediul magnetic care sunt utilizate la
marcarea pistelor şi sectoarelor. Înainte ca un disc să fie formatat, suprafaţa sa magnetică este
un haos complet de semnale magnetice. Când este formatată, se introduce o anumită ordine în
acest haos, în principal prin ”tragerea” de linii acolo unde vor fi pistele precum şi acolo unde
acestea sunt despărţite în sectoare. Detaliile de implementare nu sunt relevante pentru acest
curs. Ceea ce este însă important, este că un disc nu poate fi folosit până când el nu este
formatat.

- 21 -
Curs de SISTEME DE OPERARE
Pentru discurile IDE şi SCSI în prezent formatarea se face de către producător, astfel
încât aceasta nu mai trebuie făcută. Mai mult s-ar putea ca unele discuri să necesite o
formatare specială, astfel o reformatare ar duce la scăderea performanţelor hard discului.
Dicurile care totuşi au nevoie de formatare necesită programe speciale care de obicei
există în BIOS, sau ca şi programe executabile sub un anumit sistem de operare.

3.3.3 Partiţii

Un hard disc poate fi împărţit în mai multe partiţii. Fiecare partiţie se comportă ca şi
cum ar fi un hard disc separat. Ideea este accea că există doar un singur hard disc şi se doreşte
instalarea a două sisteme de operare, se poate împărţi hard discul în două părţi. Fiecare sistem
de operare folosind partiţia sa cum doreşte şi ne atingându-se de alte partiţii. Fără partiţii ar
trebui ca pentru fiecare sistem de operare să existe un hard disc separat.
Discurile floppy nu sunt partiţionate. Nu există nici un impediment tehnic pentru
aceasta, dar deoarece ele sunt de capacitate relativ mică, partiţiomarea lor nu ar ajuta aproape
la nimic.

3.3.4 MBR-ul, sectoare boot şi tabele de partiţii

Informaţia cu privire la modul în care un hard disc a fost partiţionat este memorată în
primul sector al acestuia (adică în primul sector al primei piste de pe prima faţă). Primul
sector este Master Boot Record-ul discului, acesta este sectorul pe care BIOS-ul îl citeşte şi îl
lansează când sistemul este pornit pentru prima dată. MBR-ul conţine un mic program care
citeşte tabela de partiţii, caută partiţia activă (cea care este marcată ca şi bootable) şi citeşte în
memorie primul sector al acelei partiţii, adică sectorul de boot al partiţiei (MBR-ul este tot un
sector de boot, dar are un statut special). Acest sector de boot conţine un alt program care
citeşte în memorie prima parte a sistemului de operare care există pe acea partiţie şi îi dă
controlul.
Partiţionarea este doar o convenţie urmată de cele mai multe sisteme de operare.
Unele sisteme de operare suportă partiţii, dar ele ocupă o singură partiţie şi folosesc o
partiţionare internă în cadrul acelei partiţii. Aceste tipuri de sisteme de operare pot coexista
cu alte sisteme de operare, dar un sistem de operare care nu suportă partiţii nu poate coexista
pe acelaşi hard disc cu alte sisteme de operare.

3.3.5 Partiţii extinse şi logice

Schema originală de partiţionare pentru hard discurile de PC-uri admite doar patru
partiţii. Pentru a depăşi această limitare au fost introduse partiţiile extinse. Aceasta este un
truc pentru a permite partiţionarea unei partiţii primare în subpartiţii. O astfel de partiţie
primară, împărţită în subpartiţii se numeşte partiţie extinsă, iar subpartiţiile sunt partiţii
logice. Acestea din urmă se comportă ca şi partiţiile primare, dar sunt create într-un mod
diferit.
Structura de partiţionare a unui hard disc ar putea arăta ca în următoarea figură. Discul
este divizat în trei partiţii primare, a doua partiţie fiind divizată în două partiţii logice. O parte
a discului nu este partiţionată deloc, deci nu este folosită. Discul ca şi întreg şi fiecare partiţie
primară are câte un sector de boot.

- 22 -
Curs de SISTEME DE OPERARE
MBR
Boot sector

Date area
Primary partition of partition

Boot sector
Unused boot sector

Date area
Logical
Extended partition
Unused boot sector
partition
Date area
Logical
Unused boot sector
partition
Boot sector

Primary partition Date area

Observaţie: Aici ar trebui să se completeze de la 3.2.3. (din curs) tabela de partiţii şi


explicarea acesteia. După aceea ar trebui continuat cu 3.2.2. cu câteva observaţii de la 3.2.1.

3.4 Organizarea spaţiului pe discuri.

3.4.1 Caracteristicile fizice ale discului.

În configuraţia calculatoarelor personale compatibile IBM se întâlnesc două tipuri de


floppy discuri (discuri flexibile) şi hard disc (discuri fixe). Din punct de vedere fizic discurile
sunt împărţite pe fiecare faţă în piste, iar fiecare pistă în sectoare. Capacitatea standard pentru
discurile flexibile a fost o perioadă de 360 Kocteţi ceea ce înseamnă 40 piste 9 sectoare pe
fiecare faţă, dimensiunea unui sector fiind 512 octeţi. S-a folosit şi 720 Koceteţi, ceea ce a
făcut să se dubleze numărul de piste, după care dimensiunea standard pentru un disc de 5 1/4 a
fost 1,2 Mocteţi (80 de piste 15 sectoare).
Pentru discurile de 31/2 s-au folosit două capacităţi standard:
• 720 Kocteţi (80 de piste 9 sectoare).
• 1,44 Mocteţi (80 piste 18 sectoare).
Pentru discurile fixe capacităţile au fost de 10 Mocteţi (20, 40, 80, 220, 300, 600, 1
Goctet, …).
Se folosesc două numerotări ale sectoarelor, o numerotare zisă BIOS şi cealaltă DOS.
Numerotarea BIOS consideră adresele unui sector prin trei componente : cap, pistă şi sector.
Adresele DOS consideră sectoarele numerotate în continuare fără a putea considera adresa
fizică a sectorului. Numerotarea capetelor porneşte de la 0, numerotarea pistelor de la 0, iar
numerotarea sectoarelor de la 1.
În notaţia DOS se porneşte de la primul sector de pe pista 0 a feţei 0, după epuizarea
ei se continuă cu pista 0 a feţei 1. Numerotarea se face în ordinea cilindrilor.

- 23 -
Curs de SISTEME DE OPERARE
3.4.2 Organizarea logică a discurilor.

În sistemul MS-DOS se foloseşte o convenţie materializată prin comanda format care


împarte suprafaţa fizică a unui disc în patru zone :
• sectorul de încărcare
• tabela de alocare a fişierelor (FAT)
• catalogul rădăcină
• spaţiul pentru date

Sector Catalog Zona de


FAT
încãrcare rãdãcinã date

În cazul discurilor fixe există un element suplimentar şi anume tabela de partiţii.


Sectorul de încărcare pe lângă programul încărcător mai conţine informaţii despre
structura sistemului de operare.
FAT are la început informaţii generale despre structura discului şi apoi evidenţa
alocării sectoarelor pentru diferite fişiere şi a spaţiului liber. Pentru siguranţă se păstrează
două copii pentru FAT, fiecare având o dimensiune în funcţie de capacitatea discului şi este
dată de comanda FORMAT. Discurile flexibile au 4 16 sectoare pentru fiecare FAT.
Catalogul rădăcină : are o dimensiune fixă, în funcţie de capacitatea discului (la
discurile fixe este între 714 sectoare) şi este compus din intrări care descriu câte un fişier. În
catalogul rădăcină se pot descrie doar un număr limitat de fişiere. O intrare de catalog conţine
numele fişierului şi numărul intrării din tabela de alocare a fişierelor (FAT), care corespunde
primei zone de date alocate acestui fişier.
Zona de date conţine informaţii propriu-zise înregistrate în fişere, unele dintre aceste
fişiere pot să fie cataloage.

A. Sectorul de încărcare.
Primii trei octeţi ai sectorului de încărcare în MS-DOS conţin un salt necondiţionat la
adresa 2Eh, de la această adresă începe de fapt programul încărcător. Începând cu al patrulea
octet al sectorului este înregistrat aici blocul de parametri BIOS.
La sfârşitul sectorului de încărcare, ultimii doi octeţi, există o informaţie specifică
numită semnătură DOS, 55 AAh.

Deplasament Lungime Descriere


03 8 Identificatorul sistemului (DOS 4.0)
0B 2 Număr octeţi / sector
0D 1 Număr sectoare / grupă (cluster)
0E 2 Număr sectoare rezervate la începutul discului
10 1 Număr copii FAT
11 2 Număr intrări în catalogul rădăcină
13 2 Număr total de sectoare / disc
15 1 Identificator de format
16 2 Număr sectoare / FAT
18 2 Număr sectoare / pistă
1A 2 Număr feţe
1C 2 Sectoare speciale

- 24 -
Curs de SISTEME DE OPERARE
B. Tabela de alocare a fişierelor.
Există două structuri pentru FAT :
• tabele FAT în care intrarea are 12 biţi
• tabele în care intrarea are 16 biţi.
Conţinutul unei intrări în FAT este de fapt un număr de grupă. Alocarea spaţiului
pentru fişiere făcându-se la nivel de grupă de sectoare.
Dimensiunea unei grupe este 18 sectoare (se alege în funcţie de capacitatea discului).
Dacă dimensiunea intrării este 12 biţi ⇒ valoarea maximă 0FFFh (4095) ⇒ 4096 de
valori ⇒ putem descrie discuri cu capacitatea 4096 / 2 Kocteţi ≈ 20 Mocteţi.
Dacă dimensiunea intrării este de 16 biţi ⇒ 0FFFFh ⇒ 65535 valori.
Prin numerele de grupă care se înregistrează prin intrările FAT se formează liste
simplu înlănţuite ale grupelor alocate fiecărui fişier. Începutul fiecărei liste este păstrată în
intrarea de catalog a fişierului.
Există valori rezervate ale intrărilor în FAT :
0 - corespunde unei grupe libere.
F peste tot - sfârşit de listă.
(F)FF7 - marchează zone defecte pe disc.

0 1 2 3 4 5 6 7
200 000 206 000 205 000 208 206 800
208 21A 000 000 000 20D FFF 000 000
210 000 000 000 FFF 000 000 000 000
218 000 000 FFF 0 0 0 0 0

Când se şterge un fişier toate intrările FAT corespunzătoare spaţiului marcat de acel
fişier vor fi marcate ca disponibile. Nu e afectat fişierul în zona sa de date. Spaţiul alocat unui
fişier creşte odată cu creşterea dimensiunii fişierului. La crearea unui fişier nu se alocă spaţiu
peste necesarul din acel moment pentru fişier.

C. Catalogul rădăcină.
Fiecare intrare de catalog are lungimea de 32 octeţi, deci un sector de pe disc conţine
16 intrări de catalog (7 sectoare pentru un catalog ⇒ 112 fişiere). În subcataloage,
dimensiunea nu este limitată. Numărul de intrări din catalogul rădăcină este limitat la 256 (16
sectoare). O intrare are 8 câmpuri. Formatul unei intrări de catalog este :

Deplasament Dimensiunea Format Descriere


00h 8 ASCII Numele fişierului
08h 3 ASCII Extensia fişierului
0Bh 1 Binar Atributele fişierului
0Ch 10 –––– Rezervat
16h 2 Binar Timp
18h 2 Binar Data
1Ah 2 Binar Prima intrare FAT
1Ch 4 Binar Dimensiunea fişierului (în octeţi)

Numele fişierului, dacă este mai scurt de opt caractere, se completează la dreapta cu
blancuri. Între caractere nu sunt recomandate blancuri. Deasemenea nu se recomandă
utilizarea caracterelor ’*’ şi ’?’ ce au o semnificaţie specială pentru interpretorul de comenzi.
Deasemenea caracterul ’.’.
MS-DOS nu face deosebirea între litere mari şi litere mici. În primul octet al câmpului
de nume dintr-o intrare de catalog pot apărea trei combinaţii binare, care au semnificaţie
specială.
00h - că această intrare de catalog nu a fost folosită;

- 25 -
Curs de SISTEME DE OPERARE
E5h - fişierul respectiv a fost şters.
2Eh - se consideră ca şi cod special: punctul ’.’ pentru intrările speciale, şi ’..’,
care există în fiecare subcatalog, ’.’ descrie catalogul însuşi şi al doilea
catalogul părinte.
Prima intrare a catalogului rădăcină este deasemenea rezevată pentru a înregistra
eticheta de volum (care poate fi un şir de maxim 11 caractere).
Extensia se poate completa la dreapta cu blancuri. Combinaţia care se foloseşte ca
extensie dă o anumită informaţie despre conţinutul fişierului.
Octetul de atribute foloseşte fiecare bit din octet pentru a preciza unele caracteristici
ale fişierelor :
Cod hexa Semnificaţie
01 RO (pentru citire)
02 Atributul de fişier ascuns (Nu îl listează comanda DIR)
04 Fişier sistem
08 Descrie eticheta de volum
10 Intrarea subcatalog (este subdirector)
20 Arhivă
40 Rezervat
80 Rezervat

Câmpul pentru timp :

Depl 17h Depl 16h

h h m m x x

5cb 6cb 5cb


Câmpul pentru dată :
Depl 19h Depl 18h

y y m m d d
7cb 4cb 5cb
În câmpul pentru an 0 119 lună zi
pentru lună 0 12
pentru zi 0 31
Câmpurile de dată şi timp se pot folosi împreună cu un întreg lung fără semn (4 octeţi)
la operaţii de comparare. Pentru fişierele care au fost create fără a avea spaţiu alocat şi
intrarea care descrie eticheta de volum, pentru acest câmp avem 0.
Câmpul pentru dimensiunea fişerului. Valoarea lui corespunde în mod real cu
dimensiunea fişierului. La fişierele create prin editoare de texte se poate întâmpla ca valoarea
înregistrată aici să depăşească valoarea reală, deoarece acestea salvează câte un buffer odată.
Sfârşitul real al fişierului în acest caz va fi identificat prin codul ^Z (1Ah).

D. Zona de date.
Unitatea de alocare este grupa, de obicei alocarea se face la prima grupă disponibilă,
cu toate că se pot aplica algoritmi de selecţie a grupelor astfel încât spaţiul unui fişier să fie
într-o zonă cât mai restrânsă a discului. Dacă un fişier ajunge să fie fragmentat, adică ajunge
să aibă grupe din zone diferite ale discului, se reduc performanţele de transfer, deoarece
creşte numărul de deplasări ale capetelor de citire-scriere.

- 26 -
Curs de SISTEME DE OPERARE

3.4.3 Partiţionarea discurilor fixe.

Este un nivel superior de gestionare al spaţiului în sensul că înainte ca un disc să poată


fi utilizat spaţiul său este împărţit într-un număr de secţiuni logice numite partiţii, fiecare
comportându-se ca o unitate specifică distinctă. Convenţia adoptată la MS-DOS este aceea că
numărul maxim de partiţii este patru.
Este permis ca în partiţii diferite să se găsească sisteme de operare diferite cu propriile
sale organizări ale spaţiului din partiţia rezervată. Această divizare în partiţii a fost impusă şi
de imposibilitatea unor resurse MS-DOS de a gestiona spaţii mai mari de 32 Mocteţi.
Fiecare partiţie constă dintr-o zonă contiguă de cilindri şi are o dimensiune dată de
utilizator. Numărul şi dimensiunea partiţiilor se pot modifica, dar doar prin distrugerea
vechiului conţinut al partiţiei.
Primul sector al discului fix va conţine informaţii despre partiţionare, iar prima
partiţie va începe cu primul sector de pe faţa a doua a discului. Deasemenea în primul sector
se mai găseşte un program de iniţializare, care verifică tabela de partiţii, deduce care este
partiţia activă şi determină încărcarea partiţiei active.
Tabela de partiţii ocupă ultima parte a sectorului, patru intrări de câte 16 octeţi.
Această tabelă începe la octetul cu deplasamentul 1BEh, pentru că ultimii doi octeţi ai
sectorului trebuie să conţină semnătura sistemului.
O intrare a tabelei de partiţii :

Deplasament Lungime Descriere


0 1 Partiţie activă / inactivă
1 1 Adresă cap început de partiţie
2 1 Adresă sector început de partiţie
3 1 Adresă cilindru început de partiţie
4 1 Identificator sistem
5 1 Adresă cap sfârşit de partiţie
6 1 Adresă sector sfârşit de partiţie
7 1 Adresă cilindru sfârşit de partiţie
8 2 Număr sectoare care preced partiţia (LOW WORD)
A 2 Număr sectoare care preced partiţia (HIGH WORD)
C 2 Număr sectoare ocupate de partiţie (LOW WORD)
E 2 Număr sectoare ocupate de partiţie (HIGH WORD)

- 27 -
Curs de SISTEME DE OPERARE

3.5 Sistemul de întreruperi în MS-DOS.

În arhitectura calculatoarelor personale compatibile IBM semnalele de întrerupere pot


fi generate de trei categorii de surse :
• întreruperi hardware interne - sunt generate chiar în procesor, într-una din
următoarele situaţii :
1. instrucţiunea precedentă provoacă o depăşire a capacităţii registrelor
procesorului (frecvent datorită unei împărţiri cu 0).
2. în registrul de stare program este poziţionat fanionul de deviere
(TRAP), ceea ce face ca după fiecare instrucţiune să se genereze o
întrerupere.
3. instrucţiunea precedentă a fost INTO (interrupt overflow) şi este
poziţionat fanionul de depăşire OF (overflow flag).
• întreruperi hardware externe - sunt generate de surse exterioare procesorului şi ajung
direct sau prin intermediul controllerului programabil de întreruperi la pinii
corespunzători ai procesorului.
• întreruperi software - sunt generate prin instrucţiuni speciale prevăzute în programe
(INT), argumentul acestei instrucţiuni fiind nivelul pe care se generează
întreruperea. Acestea se folosesc în mod normal pentru a apela diverse servicii
oferite de S.O.

În arhitectura 80x86 sunt prevăzute 256 de nivele de întrerupere. Nivelul de


întrerupere are rolul de a permite selectarea unei anumite rutine de tratare. În procesor nu se
definesc priorităţi între aceste nivele. Adresele rutinelor de tratare (exprimate pe câte 4 octeţi)
se găsesc într-o tabelă de vectori de întreruperi, situată în primul kilooctet de RAM (0 ÷
3FF). Nivelul de întrerupere este folosit ca indice în această tabelă. Atunci când se ia în
considerare un semnal de întrerupere, procesorul execută următoarele operaţii :
1. Salvează în stivă registrul fanioanelor (registrul stare program).
2. Dezactivează temporar întreruperile hardware prin ştergerea
fanioanelor corespunzătoare din registrul de stare program (TF şi IF).
3. Salvează în stivă adresa instrucţiunii următoare din programul în curs
(CS:IP).
4. Citeşte din tabela de vectori de întrerupere adresa corespunzătoare
nivelului pe care a sosit întreruperea şi efectuează salt la această adresă.

Rutinele de tratare a întreruperilor se termină cu o instrucţiune de revenire specială


IRET, care provoacă următoarele operaţii :
1. Extrage din stivă IP şi CS.
2. Extrage din stivă registrul fanioanelor.

Astfel se provoacă implicit reluarea programului suspendat când a apărut întreruperea.


stiva

fanioane
CS
IP
SP

Fig. 3.5-1

- 28 -
Curs de SISTEME DE OPERARE
Iniţializarea conţinutului tabelei vectorilor de întrerupere se face la încărcarea
sistemului de operare, parţial de rutinele din BIOS şi parţial de rutinele de iniţializare din
IO.SYS şi MSDOS.SYS.

3.5.1 Întreruperi hardware.

Se numesc astfel nivelele de întrerupere din prima grupă de 16 : 00h ÷ 0Fh.


Utilizarea lor este următoarea :
• nivelul 00h - împărţire la 0 şi oferă posibilitatea tratării printr-o rutină a acestei
situaţii. La încărcarea S.O., vectorul pentru acest nivel este iniţializat din BIOS să
indice spre o instrucţiune IRET. Sistemul MS-DOS nu oferă implicit o tratare a
împărţirii cu 0.
• nivelul 01h - este nivelul de execuţie pas cu pas. Aici sunt dirijate întreruperile care
se generează după fiecare instrucţiune de program, când este poziţionat TF. Acest
vector este deasemenea iniţializat de BIOS spre o instrucţiune IRET.
• nivelul 02h - pe acest nivel se tratează întreruperile nemascabile (de exemplu
apariţia unei erori de paritate în memorie). Acest vector este iniţializat la fel ca cele
de mai sus.
• nivelul 03h - este nivelul pentru întreruperile de test, prin care se fixează puncte de
control (breakpoints) în program. Iniţializat la fel ca mai sus.
• nivelul 04h - pe acest nivel se semnalează întreruperea de depăşire, cea produsă de
instrucţiunea INTO; se foloseşte tot în scopuri de depanare. Iniţializat la fel ca mai
sus.
• nivelul 05h - tipărirea conţinutului ecranului (print screen). Aici există un vector
spre o rutină adevărată. Rezultatul execuţiei rutinei de întrerupere este memorat într-
un octet de stare care se găseşte la adresa de paragraf 0050:0. Dacă este : 0 atunci
tipărirea s-a efectuat corect, 1 atunc este în curs de tipărire, FF atunci a apărut o
eroare la tipărire.
• nivelul 06h - nu este folosit de MS-DOS.
• nivelul 07h - nu este folosit de MS-DOS.
• nivelul 08h - întreruperea de ceas - este o întrerupere hardware externă. Semnalele
de întrerupere pe acest nivel sosesc de la un circuit de tip 8259 de pe canalul 0.
Întreruperile de ceas la PC-uri sunt generate de circa 18,2 ori pe secundă, iar tratarea
acestor întreruperi constă în incrementarea unui contor. Contorul este situat în
memorie la adresa 0046:C. În rutina de tratarese face apel şi la rutina de tratare a
nivelului 1Ch, pe care programatorii pot include programe cu acţiune dependentă de
timp.
• nivelul 09h - întrerupere de la tastatură (se produce la apăsarea şi la eliberarea unei
taste). În esenţă rutina care tratează aceste întreruperi asigură preluarea de la
tastatură a codului de scanare a tastei apăsate. Rutina asigură transformarea codului
de scanare şi în cod ASCII şi împreună cele două coduri se memorează în tamponul
pentru tastatură, utilizat de BIOS. Acest tampon se găseşte la adresa 40:1E în aşa
zisa zonă de date ROM-BIOS. Tot această rutină mai asigură şi recunoaşterea unor
configuraţii speciale de taste, cum ar fi : CTRL-ALT-DEL, CTRL-Break.
• nivelul 0Ah - este tot pentru întreruperile de ceas, dar de pe canalul 2 al lui 8259.
• nivelul 0Bh - nu este folosit de MS-DOS.
• nivelul 0Ch - nu este folosit de MS-DOS.
• nivelul 0Dh - este nivelul pe care sosesc întreruperile externe de la unităţile de
discuri. Acestea sunt dirijate pe canalul 5 al lui 8259.
• nivelul 0Eh - nu este folosit de MS-DOS.
• nivelul 0Fh - nu este folosit de MS-DOS.

- 29 -
Curs de SISTEME DE OPERARE
3.5.2 Întreruperi BIOS.

Aceste întreruperi sunt generate prin software, cu ajutorul instrucţiunii INT xx, unde
xx este nivelul întreruperii.
Caracteristic tuturor întreruperilor generate prin software este faptul că sunt generate
sincron cu programul în execuţie. Majoritatea au rolul de a apela servicii de sistem aşa încât
întrerupera trebuie să fie însoţită de informaţii încărcate în registre care să precizeze serviciul
dorit şi eventual infomaţii necesare acelui serviciu.
Utilizarea întreruperilor BIOS este următoarea :
• nivelul 10h - serveşte la apelarea serviciilor de ecran, cum ar fi fixarea modului de
lucru, a formei cursorului, afişarea unui caracter, a unui şir, defilarea ecranului etc.
• nivelul 11h - furnizează informaţii despre configuraţia hardware a sistemului.
• nivelul 12h - serveşte pentru obţinerea dimensiunii memoriei
• nivelul 13h - serveşte pentru apelarea serviciilor de lucru la nivel fizic cu discuri :
iniţializare, citirea stării, poziţionări, citiri, scrieri etc.
• nivelul 14h - este prevăzut pentru apelarea serviciilor de lucru cu interfaţa serială :
iniţializare, emisia sau recepţia unui caracter, fixarea regimului de transmisie etc.
• nivelul 15h - acest nivel se foloseşte pentru comanda altor periferice şi pentru
raportarea evenimentelor de intrare/ieşire.
• nivelul 16h - permite realizarea de operaţii asupra tamponului tastaturii.
• nivelul 17h - permite controlul interfeţei paralele, la care de regulă este conectată o
imprimantă.
• nivelul 18h - întrerupere rezervată, folosită pentru declanşarea interpretorului de
BASIC din ROM.
• nivelul 19h - întreruperea pentru încărcarea la cald.
• nivelul 1Ah - întreruperea pentru citirea şi poziţionarea timpului.
• nivelul 1Bh - întreruperea la care se ataşează rutina de tratare a combinaţiei CTRL-
C.
• nivelul 1Ch - întreruperea care se leagă de nivelul 08h şi dă posibilitatea activării
unui program la momente determinate în timp.
• nivelul 1Dh - nu conţine o adresă de rutină, ci conţine o adresă la care se găsesc
parametri pentru iniţializarea ecranului.
• nivelul 1Eh - conţine adresa unui bloc de parametri pentru lucrul cu discurile.
• nivelul 1Fh - conţine adresa tabelei folosită pentru generatorul de caractere pentru
codurile ASCII extinse.

3.5.3 Întreruperi DOS.

Întreruperile DOS sunt cele cu nivelele 20h ÷ 3Fh. Şi acestea au evoluat faţă de
primele versiuni ale sistemului DOS, prin concentrarea majorităţii serviciilor oferite pe
nivelul 21h. Pe acest nivel sunt prevăzute şi servicii apelabile prin intermediul întreruperilor
BIOS.
Utilizarea întreruperilor DOS este următoarea (vor fi enumerate doar cele mai
folosite):
• nivelul 20h - este întreruperea pentru terminarea programului curent. Generarea
unui astfel de întreruperi face ca execuţia programului să se termine şi controlul să
revină programului care a lansat acel program.
• nivelul 21h - pe acest nivel sunt concentrate funcţiile MS-DOS.
• nivelul 22h - vectorul de pe acest nivel este adresa rutinei care se apelează la
terminarea programului în curs, după un INT 20h sau INT 27h sau după apelarea
unor funcţii sistem (ca de exemplu : 00h, 4Ch şi 31h).

- 30 -
Curs de SISTEME DE OPERARE
• nivelul 23h - conţine adresa la care se transferă controlul pentru combinaţia de taste
CTRL-Break.
• nivelul 24h - este întreruperea de tratare a erorilor critice de intrare-ieşire.
• nivelul 25h - foloseşte pentru citirea de pe disc, localizarea sectoarelor făcându-se
prin adrese DOS.
• nivelul 26h - foloseşte pentru scrierea pe disc, localizarea sectoarelor făcându-se
prin adrese DOS.
• nivelul 27h - întreruperea TSR (Terminate and Stay Resident), se termină execuţia
programului dar nu se eliberează memoria corespunzătoare lui.
• nivelul 2Fh - întreruperea pentru gestionarea proceselor. Acest serviciu este
disponibil numai de la versiunea 3.0 şi permite ca anumite programe ataşate pe acest
nivel să se desfăşoare în paralel cu programele de aplicaţie (de exemplu comanda
print).

3.6 Formatul fişierelor executabile. Prefixul segmentului de program.

În sistemul de operare MS-DOS există două formate de fişiere executabile care sunt
tratate în mod diferit. Identificarea tipului de format executabil se face prin extensia numelui
de fişier (.COM şi .EXE).

3.6.1 Structura fişierelor .COM.

Aceste fişiere au dimensiunea limitată la 64KB. Astfel într-un singur segment se


găseşte atât codul cât şi datele programului. În fişier se găseşte imaginea pură a unui program
(toate adresele din program se vor pune în legătură cu adresele fizice numai relativ la
conţinutul registrului CS). Atunci când un fişier .COM este adus în memorie, sunt identificate
două zone în porţiunea de memorie rezevată programului. O primă zonă de 256 octeţi numită
PSP care conţine informaţii generale, valabile pentru orice program şi o a doua zonă care
cuprinde codul şi datele, aduse direct din fişier. PSP-ul este construit de S.O. pentru fiecare
program executabil adus în memorie.
Încărcarea unui program are loc în două etape :
1. Găsirea unei zone de memorie şi construirea PSP.
2. Încărcarea programului imediat în continuarea PSP.

programde
Program aplicaţie
.COM
CS:100
PSP
CS:0
S.O.

Fig. 3.6-1

Structura PSP este prezentată în următorul tabel :

- 31 -
Curs de SISTEME DE OPERARE
Nr. Depl. Lung. Semnificaţie
câmp
1. 00h 2 INT 20h
2. 02h 2 Dimensiunea memoriei (în paragrafe)
3. 04h 1 nefolosit
4. 05h 5 CALL FAR
5. 0Ah 4 Vector INT 22h
6. 0Eh 4 Vector INT 23h
7. 12h 4 Vector INT 24h
8. 16h 2 CS pentru programul apelant (de ex. COMMAND.COM)
9. 18h 20 rezervat
10. 2Ch 2 Pointer spre ambianţă
11. 2Eh 4 Adresa stivei pentru funcţii DOS
12. 32h 30 rezervat
13. 50h 3 INT 21h urmat de RETF
14. 53h 9 rezervat
15. 5Ch 16 FCB1
16. 6Ch 16 FCB2
17. 7Ch 4 rezervat
18. 80h 128 DTA
Tabelul 3.6-1

În câmpul 1 este prevăzută instrucţiunea INT 20h care constituie unul din modurile de
terminare a execuţiei unui program. În program va apare un salt necondiţionat la locaţia
respectivă. O justificare a acestei soluţii ar fi că dacă într-un program care face referiri
externe rămâne o referinţă nerezolvată aceasta ar apărea ca apel la adesa 0.
Câmpul 2 arată câtă memorie este disponibilă pentru program, precizând adresa de
paragraf a sfârşitului memoriei accesibile programului.
Câmpul 4 se foloseşte la apelarea serviciilor DOS din prima versiune (00h ÷ 24h).
În câmpurile 5, 6, 7 se salvează la începutul execuţiei programului vectorii pentru
nivelele de întreruperi indicate (22h, 23h, 24h). Vectorii vor fi instalaţi la sfârşitul execuţiei.
Este posibil ca pe durata programului acesta să folosească alţi vectori pentru nivelele
respective, fără a perturba funcţionarea altor programe.
Câmpul 10 este adresa de segment care indică spre zona în care apar sub formă de
şiruri de caractere variabilele sistem şi valorile lor.
Câmpul 13 conţine instrucţiuni pentru apelarea funcţiilor sistem mai recente.
Câmpurile 15 şi 16 servesc la prelucrarea fişierelor în stilul CP/M.
Câmpul 18, în timpul execuţiei programului acest câmp serveşte ca tampon pentru
operaţiile cu discuri, dacă programul apelează funcţii care necesită un astfel de tampon şi nu
este prevăzută o zonă corespunzătoare în cadrul programului. Înainte de începerea propriuzisă
a programului, aici se găsesc parametrii din linia de comandă sub forma unui şir de caractere.
Se precizează că între parametri nu se găsesc numele programului şi eventualele informaţii de
redirectare ale intrării sau ieşirii.

3.6.2 Structura fişierelor .EXE.

Programele care se păstrează în fişiere .EXE pot avea orice dimensiune, dar nu putem
lansa în execuţie un program mai lung decât zona disponibilă de memorie RAM.

- 32 -
Curs de SISTEME DE OPERARE
Un fişier cu extensia .EXE constă din două părţi, prima conţine antetul şi tabela de
relocare iar a doua conţine programul propriu-zis şi datele aferente acestuia. Antetul începe
cu primul octet al fişierului. În antet se găsesc informaţii folosite în procesul de încărcare al
programului în memorie.

Nr. Depl. Dim. Conţinut


câmp
1. 00h 2 4Dh, 5Ah - semnătură fişier .EXE
2. 02h 2 ExtraBytes - număr octeţi din ultimul sector alocat fişierului
3. 04h 2 Pages - număr de sectoare alocate fişierului
4. 06h 2 RelocItems - număr intrări în tabela de relocare
5. 08h 2 HeaderSize - dimensiunea antetului în paragrafe
MinAlloc - numărul minim de paragrafe solicitate de
6. 0Ah 2
program dincolo de sfârşitul programului
MaxAlloc - numărul maxim de paragrafe pe care îl solicită
7. 0Ch 2
programul
8. 0Eh 2 InitSS - valoarea iniţială pentru SS
9. 10h 2 InitSP - valoarea iniţială pentru SP
CheckSum - suma de control = suma negativă a tuturor
10. 12h 2 cuvintelor din codul programului fără a lua în
considerare antetul, ci doar partea executabilă
11. 14h 2 InitIP - valoarea iniţială pentru IP
12. 16h 2 InitCS - valoarea iniţială pentru CS
RelocTable - deplasamentul tabelei de relocare faţă de
13. 18h 2
începutul fişierului (01Ch)
14. 1Ah 2 Overlay - numărul de suprapuneri
Tabelul 3.6-2

Câmpurile MinAlloc şi MaxAlloc se completează la editarea de legături. Dacă nu se


precizează, valoarea lui MaxAlloc va fi completată cu 0FFFFh ceea ce înseamnă că
programul solicită toată memoria disponibilă. Dacă ambele sunt zero atunci programul se va
încărca la capătul superior al memoriei disponibile.
Pentru a încărca un program .EXE, MS-DOS-ul mai întâi citeşte antetul fişierului
pentru a determina semnătura precum şi pentru a calcula dimensiunea imaginii programului.
După aceasta încearcă să aloce memoria necesară. Mai întâi adună dimensiunea imaginii
programului cu dimensiunea PSP-ului şi cu cantitatea de memorie specificată în câmpul
MinAlloc. Dacă această sumă depăşeşte dimensiunea celui mai mare bloc de memorie
disponibil, se opreşte încărcarea programului şi se întoarce un cod de eroare. Altfel se adună
dimensiunea imaginii programului cu dimensiunea PSP-ului şi cu cantitatea de memorie
specificată în câmpul MaxAlloc. Dacă această a doua sumă este mai mică decât dimensiunea
celui mai mare bloc de memorie disponibil, MS-DOS alocă respectiva cantitate de memorie.
Altfel se alocă cel mai mare bloc de memorie disponibil.
După alocarea memoriei, se determină adresa de segment, numită adresa segment de
start, la care se va încărca imaginea programului. Dacă ambele câmpuri MinAlloc şi
MaxAlloc sunt diferite de 0, imaginea va fi încărcată imediat în continuarea zonei rezervate
PSP-ului.
Următorul pas este citirea înregistrărilor din tabela de relocare şi ajustarea tuturor
adreselor de segment specificate de pointerii de relocare. Pentru fiecare pointer din tabela de
relocare se caută adresa segmentului relocatabil corespunzător din imaginea program şi se
adună la aceasta adresa segment de start. Odată ajustate, adresele de segment indică spre
segmentele de memorie unde sunt încărcate codul şi datele programului.

- 33 -
Curs de SISTEME DE OPERARE

Tabela de relocare conţine intrări de câte 2


cuvintede16 biţi :

I_OFF I_SEG

START_SEG - adresa deînceput al programului


(PSP+10h)
RELO_SEG =START_SEG +I_SEG

(RELO_SEG : I_OFF) +=START_SEG

Fig. 3.6-2

După acestea se construieşte PSP-ul în partea inferioară a memoriei alocate. MS-


DOS-ul foloseşte valorile din antetul fişierului pentru a seta regiştrii SP şi SS şi se ajustează
registrul SS prin adunarea la aceasta a adresei segment de start. Deasemenea se setează
regiştrii ES şi DS să indice adresa de segment a PSP-ului.
În final se citesc valorile iniţiale pentru CS şi IP, tot din antetul fişierului, ajustându-se
valoarea registrului CS la fel ca şi SS-ul, adunându-se la aceasta adresa segment de start şi se
transferă controlul programului aflat la adresa ajustată.

- 34 -
Curs de SISTEME DE OPERARE

4. Interfaţa MS-DOS cu utilizatorul.

4.1 Generalităţi.

Serviciile oferite de S.O. se apelează prin intermediul întreruperilor. Pentru DOS


nivelul de întrerupere folosit pentru apelul serviciilor sistem este 21h (INT 21h).
Ca regulă generală :
• registrul AH conţine numărul serviciului.
• ceilalţi regiştri conţin informaţii suplimentare necesare apelului respectiv.
• în urma execuţiei, rezultatele se vor găsi în diferite registre.
• cele mai multe servicii, în urma apelului, dacă a apărut o eroare, poziţionează
CARRY, iar în AX se întoarce un cod de eroare.

Exemplificare : pentru funcţia 36h, care furnizează spaţiul liber disponibil pe disc
avem:
AH <- 36h - codul serviciului
DL <- id. disc - numărul discului (0 - disc implicit, 1 - A, 2 - B etc.)
La întoarcerea din apel :
AX - conţine numărul de sectoare/cluster sau FFFFh pentru eroare.
BX - numărul de clustere disponibile.
CX - numarul de octeţi/sector.
DX - numărul total de clustere.

Ca şi secvenţă de cod în limbaj de asamblare :

mov AH,36h
mov DL,1 ; discul A
int 21h
cmp AX,0FFFFh
je err_rtn ; salt la rutina de tratare a erorii
mov sec_du,AX ; nr. sectoare/cluster
mov avl_du,BX ; nr. clustere disponibile
mov byt_sec,CX ; nr. octeţi/sector

Pentru ca funcţiile DOS să poată fi apelate din limbajul C, există câteva funcţii de
bibliotecă : bdos, bdosptr, int86, int86x, intdos, intdosx, intr, geninterrupt. Prototipul
acestor funcţii se găseşte în dos.h.
Funcţiile intdos, intdosx apelează servicii DOS prin intermediul lui INT 21h. Mai
generale sunt funcţiile int86, int86x (oferă accesul la regiştrii DS şi ES).
Structurile de date care intervin în aceste apeluri (aceste structuri sunt puse în
corespondenţă cu regiştrii microprocesorului) :

struct WORDREGS { /* regiştrii pe cuvânt */


unsigned int ax, bx, cx, dx, si, di, cflag, flags;
};
struct BYTEREGS { /* regiştrii pe octet */
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
};
struct SREGS { /* structură separată pentru regiştrii de segment */
unsigned int es, cs, ss, ds;

- 35 -
Curs de SISTEME DE OPERARE
};

union REGS { /* combină WORDREGS şi BYTEREGS */


struct WORDREGS x;
struct BYTEREGS h;
};

struct REGPACK { /* similar cu WORDREGS */


unsigned r_ax, r_bx, r_cx, r_dx;
unsigned r_bp, r_si, r_di, r_ds, r_es, r_flags;
};

Prototipurile funcţiilor sunt următoarele (toate sunt definite ca int) :

int bdos(int dosfun, unsigned dosdx, unsigned dosal);


primeşte ca argumente :
• numărul serviciului în dosfun
• conţinutul registrului DX în dosdx
• conţinutul registrului AL în dosal
valoarea returnată este conţinutul registrului AX după apelul DOS.

int bdosptr(int dosfun, void *arg, unsigned dosal);


există unele servicii care solicită o zonă de memorie (în care să se memoreze
mai multe informaţii decât încap în regiştri, de aceea se transmite un pointer).

int int86(int intno, union REGS *in, union REGS *out);


primeşte numărul întreruperii şi doi pointeri. Cade în sarcina programatorului
să completeze regiştrii (pentru apeluri DOS : intno = 21h, in.h.ah = numărul
serviciului).

int int86x(int intno, union REGS *in, union REGS *out, struct SREGS *sregs);

Funcţiile int86 şi int86x copiază conţinutul variabilei in în regiştrii procesorului, iar


după apel copiază în out conţinutul rezultat al regiştrilor.

Funcţiile intdos şi intdosx nu mai conţin numărul de întrerupere.

int intdos(union REGS *in, union REGS *out);


int intdosx(union REGS *in, union REGS *out, struct SREGS *sregs);

În caz de eroare toate returnează ca valoare conţinutul registrului AX. Informaţii


suplimentare se găsesc în variabila “globală” doserrno. Mai există şi variabila errno care este
poziţionată în urma unor funcţii mai evoluate (nu cele care lucrează direct cu întreruperi).

- 36 -
Curs de SISTEME DE OPERARE
EXEMPLU :
Se consideră următorul program, care determină spaţiul disponibil pe disc:

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <dos.h>

long free_space(int drive) /* primeste ca argument drive-ul */


{
union REGS inregs, outregs;

inregs.h.ah = 0x36;
inregs.h.dl = drive;
int86(0x21, &inregs, &outregs);

/* se face testul daca a aparut eroare */


if (outregs.x.ax == 0xFFFF)
return (-1); /* cod de eroare */
/* trebuie fortata conversia de tip : unsigned -> long */
return (long)outregs.x.ax*(long)outregs.x.bx*(long)outregs.x.cx;
}

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


{
int drive;
long space;

if (argc != 2) {
printf("Utilizare : FREE_SP <drive>\n");
exit(1);
}
drive = (int)(toupper(*argv[1]) - 'A' + 1);
if ((space = free_space(drive)) == -1)
printf("Unitate invalida !\n");
else
printf("Spatiul liber pe discul %c: este %ld octeti.\n",
*argv[1], space);
exit(0);
}

Programul se mai putea face apelând o funcţie de bibliotecă getdfree.


În funcţia free_space avem : int86(0x21, &inregs, &outregs), ar fi fost suficientă o
singură variabilă, astfel : int86(0x21, &inregs, &inregs), asta presupunând că după apel nu ne
interesează conţinutul acesteia de dinaintea apelului (folosim aceeaşi zonă de memorie).

- 37 -
Curs de SISTEME DE OPERARE

4.2 Servicii oferite de MS-DOS. Serviciile sistem.

4.2.1 Funcţii pentru gestionarea fişierelor.

În primele versiuni operaţiile asupra fişierelor se efectuau folosind FCB-uri (File


Control Block), aceasta fiind preluată de la sistemul CP/M.
În versiunile mai noi funcţiile cele mai folosite au devenit cele care lucrează cu indici
de fişier (File Handle) - fiecare fişier este identificat printr-un număr, astfel programatorul
lucrează doar cu un număr întreg, restul operaţiilor fiind preluate de S.O.
Fişierele standard au următorii indici :
• stdin = 0
• stdout= 1
• stderr = 2
• prn = 3
• aux = 4

În următoarele se vor enumera cele mai folosite funcţii :


• 3Ch : funcţia de creare a unui fişier. Crează un fişier cu numele precizat de
utilizator şi îi asignează un indice (în funcţie de câţi indici erau deja asignaţi).
• 5Bh : funcţia care crează un fişier numai dacă numele specificat este unic
(dacă fişierul există atunci returnează un cod de eroare).
• 5Ah : funcţia care crează un fişier temporar cu nume unic. Numele acestui
fişier se formează din cifre hexa oţinute din data şi ora curentă. Acest fişier
de obicei va trebui şters la terminarea programului.
• 41h : funcţia de stergere a unui fişier.
• 3Dh : funcţia de deschidere a unui fişier specificat într-un mod de asemenea
specificat (citire, scriere, citire-scriere etc.).
• 3Eh : funcţia de închidere a unui fişier specificat prin indice. Indicele
respectiv este eliberat, astfel încât va putea fi folosit pentru lucrul cu un alt
fişier.
• 3Fh : funcţia de citire dintr-un fişier. Se furnizează indicele şi numărul de
octeţi care se vor citi. Octeţii citiţi se depun la o adresă, deasemenea
specificată de programator. Se întoarce numărul de octeţi citiţi.
• 40h : funcţia de scriere într-un fişier. Se furnizează indicele şi numărul de
octeţi care se vor scrie. Deasemenea se specifică adresa de unde se vor prelua
octeţii de scris. Se întoarce numărul de octeţi scrişi.
• 42h : funcţia de poziţionare a pointerului de citire-scriere în fişier. Se
furnizează indicele, poziţia relativă la care urmează să fie poziţionat pointerul
şi locul de referinţă, faţă de care s-a comunicat poziţia relativă, care poate fi :
începutul, poziţia curentă sau sfârşitul fişierului. Pentru ca această funcţie să
se poată executa corect fişierul trebuie să fie deschis.
• 45h : funcţia care face duplicarea unui indice de fişier. Această funcţie
crează un nou indice care se referă la acelaşi canal de intrare/ieşire ca şi cel
original. Noul indice moşteneşte drepturile de acces precizate pentru primul
indice.
• 46h : funcţia de redirectare. Această funcţie face ca doi indici de fişier să se
refere la acelaşi fişier fizic, se presupune că ambii indici existau deja şi
corespundeau unor canale de intrare/ieşire diferite.

Diferenţa dintre 45h şi 46h : funcţia 45h este utilă pentru golirea bufferelor (un fel de
flush) cu indicele duplicat se poate forţa închiderea fişierului, astfel golindu-se bufferele

- 38 -
Curs de SISTEME DE OPERARE
DOS. Secvenţa este mai rapidă decât dacă am face acelaşi lucru cu un singur indice close şi
apoi open. Versiunile mai noi ale DOS au o funcţie specială de golire a bufferelor. Funcţia
46h se foloseşe la redirectarea intrărilor şi ieşirilor.

EXEMPLU :
Pentru a exemplifica folosirea funcţiilor pentru gestionarea fişierelor considerăm
următorul exemplu :

/* Programul este conceput pentru modelul de memorie SMALL ! */

#include <stdlib.h>
#include <stdio.h>
#include <dos.h>

#define SIZE 512 /* numar octeti pe care ii citim sau scriem */

int copiaza(char *srs, char *dst)


{
union REGS regs;
unsigned idsrs, iddst;
char buffer[SIZE];
unsigned nbytes; /* variabila care memoreaza cati octeti au */
/* intervenit in operatia de citire */
regs.h.ah = 0x5B;
regs.x.cx = 0x0000; /* definim fara atribute */
regs.x.dx = (unsigned)dst;
/* in DX se memoreaza adresa numelui */
/* fisierului destinatie */
/* Daca se doreste alt model decat SMALL */
/* se va completa si DS */
int86(0x21, &regs, &regs);
if (regs.x.cflag != 0)
return (-1); /* intoarce un cod de eroare */
iddst = regs.x.ax; /* memoram indicele fisierului destinatie */

regs.h.ah = 0x3D;
regs.h.al = 0x00; /* acces in citire */
regs.x.dx = (unsigned)srs;
int86(0x21, &regs, &regs);
if (regs.x.cflag != 0)
return (-1); /* intoarce un cod de eroare */
idsrs = regs.x.ax; /* memoram indicele fisierului sursa */

/* urmeaza un ciclu care citeste dintr-un fisier si scrie in celalalt fisier */


do {
/* citirea */
regs.h.ah = 0x3F;
regs.x.bx = idsrs;
regs.x.cx = (unsigned)SIZE; /* dimensiunea cat se citeste */
regs.x.dx = (unsigned)buffer;
/* adresa bufferului unde se face citirea */
int86(0x21, &regs, &regs);
nbytes = regs.x.ax;
/* functia read din C face acelasi lucru */
- 39 -
Curs de SISTEME DE OPERARE

/* scrierea */
regs.h.ah = 0x40;
regs.x.bx = iddst;
regs.x.cx = nbytes;
regs.x.dx = (unsigned)buffer;
int86(0x21, &regs, &regs);
} while (nbytes == SIZE);
/* cand s-au citit mai putini octeti decat cati s-au precizat */
/* in apel, ciclul se opreste */

/* inchidere fisiere */
regs.h.ah = 0x3E;
regs.x.bx = idsrs;
int86(0x21, &regs, &regs);
regs.h.ah = 0x3E;
regs.x.bx = iddst;
int86(0x21, &regs, &regs);
/* ca valoare de intoarcere dam valoarea indicatorului CARRY */
/* care ne spune cum s-a inchis fisierul destinatie */
return ((int)regs.x.cflag);
}

/* programul este valabil pentru cazuri pentru care nu avem nume */


/* ambigue de fisiere, adica sa nu contina * sau ? */
int main(int argc, char *argv[])
{
if (argc != 3) {
printf("Utilizare : COPIAZA <fis_srs> <fis_dst>\n");
exit(1);
}
if (copiaza(argv[1], argv[2]) == 0)
printf("Program terminat normal.\n");
else
printf("Eroare la copiere.\n");
exit(0);
}

- 40 -
Curs de SISTEME DE OPERARE

4.2.2 Funcţii pentru gestionarea cataloagelor.

În următoarele se vor enumera funcţiile folosite pentru gestionarea cataloagelor :


• 39h : funcţia de creare a unui catalog. Parametrii (cum ar fi numele
catalogului) se comunică în registre.
• 3Ah : funcţia de ştergere a unui catalog. Se admite ştergerea catalogului
numai dacă acesta este gol.
• 3Bh : funcţia de schimbarea catalogului curent.
• 43h : funcţia pentru citirea/poziţionarea atributelor unui fişier.
• 47h : funcţia de obţinere a numelui catalogului curent.
• 4Eh : funcţia pentru găsirea primului fişier care corespunde unui tipar dat.
Determină în catalogul curent numele primului fişier care se poate pune în
corespondenţă cu tiparul dat. Informaţii despre fişierul găsit (dată, atribute,
lungimea fişierului etc.) se furnizează în zona de transfer cu discul (DTA).
• 4Fh : funcţia pentru găsirea următorului fişier care corespunde unui tipar
dat. Se utilizează după un apel la 4Eh. Găseşte următorul fişier sau spune că
nu există sau nu mai există fişier corespunzător tiparului dat.
• 56h : funcţia pentru schimbarea numelui unui fişier. Prin această funcţie se
poate face şi mutarea unui fişier într-un alt catalog, dar doar pe acelaşi disc.
• 57h : funcţia pentru citirea/poziţionarea datei şi orei unui fişier.

EXEMPLU :
Programul afişează numele catalogului curent de pe o unitate logică specificată.
Dacă nu se specifică, la apelul programului, unitatea logică, se consideră unitatea
implicită. Se lucrează cu modelul de memorie SMALL.
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <dos.h>

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


{
union REGS r;
char nume[64];
unsigned drive = 0;
if (argc > 1)
drive = (unsigned)(toupper(*argv[1])-'A'+1);
r.h.ah = 0x47;
r.x.si = (unsigned)nume;
r.h.dl = drive;
int86(0x21, &r, &r);
if (r.x.cflag) {
printf("Nume de periferic invalid.\n");
exit(1);
}
printf("Catalog curent : %c%c\\%s\n",
drive ? ('A'+drive-1) : ' ', drive ? ':' : '\b', nume);
exit(0);
}

- 41 -
Curs de SISTEME DE OPERARE
Următorul program schimbă catalogul curent. În program se va ilustra şi lucrul cu
pointeri FAR.

#include <stdio.h>
#include <dos.h>

static char buffer[80];

int main(void)
{
char far* nume_cat;
union REGS r;
struct SREGS sr;
printf("Introduceti numele noului catalog : ");
gets(buffer);
nume_cat = buffer;
r.h.ah = 0x3B;
r.x.dx = FP_OFF(nume_cat); /* se preia deplasamentul bufferului */
sr.ds = FP_SEG(nume_cat); /* se preia adresa de segment */
int86x(0x21, &r, &r, &sr);
return (0);
}

4.2.3 Funcţii pentru I/E cu dispozitive standard orientate pe caractere.

Dispozitivele standard orientate pe caractere sunt : consola (tastatura şi monitorul),


imprimanta, porturile seriale, porturile paralele. Dacă într-un program se folosesc aceste
funcţii, actunci este permisă redirecţionarea intrării şi/sau ieşirii standard a acelui program.
Se recomandă utilizarea subserviciilor oferite de funcţia 44h. În rutinele pentru
tratarea erorilor critice, în driverele de periferice trebuiesc folosite funcţiile vechi (cele cu
numere mici).
În următoarele se enumeră funcţiile din această categorie :
• 01h : citire cu ecou de la tastatură. Codul caracterului citit se va găsi în AL.
• 02h : afişarea unui caracter. Codul caracterului de afişat trebuie încărcat în
DL.
• 03h : citire caracter de la intrarea auxiliară COM1. Codul caracterului citit
se va găsi în AL.
• 04h : trimiterea unui caracter la ieşirea auxiliară COM1. Codul caracterului
de trimis trebuie încărcat în DL.
• 05h : tipărirea unui caracter (LPT1). Codul caracterului de tipărit trebuie
încărcat în DL.
• 06h : intrare/ieşire directă, fără ecou de la tastatură, respectiv pe ecran. Se
preia un caracter de la STDIN dacă există un caracter pregătit şi dacă în DL
se găseşte FFh, sau se trimite un caracter la STDOUT, dacă în DL se găseşte
altceva decât FFh (adică codul unui caracter).
• 07h : simplă citire fără ecou de la tastatură. Se aşteaptă până când există un
caracter disponibil.

Obs. : Funcţiile 06h şi 07h nu tratează caracterele speciale.


• 08h : citire fără ecou de la tastatură, se deosebeşte de 06h şi 07h prin faptul
că se recunosc tastele speciale CTRL-Break etc., acestea generând o
întrerupere pe nivelul 23h.
- 42 -
Curs de SISTEME DE OPERARE
• 09h : afişarea unui şir de caractere. DS:DX trebuie să indice spre adresa
şirului de caractere terminat cu caracterul ‘$’.
• 0Ah : intrare prin zonă tampon de la tastatură. Citeşte un şir de caractere de
la STDIN până la apariţia caracterului CR (Carriage Return), şirul fiind
memorat într-o zonă tampon cu următorul format :
0 1 2 ...
LT LE caractere

LT - lungimetotală a zonei rezervate


LE - lungimeefectivă (împreună cu
CR)

Fig. 4.2-1
• 0Bh : verificarea stării intrării. Se determină dacă există sau nu un caracter
disponibil la intrare (AL = 00h <=> nu există, AL = FFh <=> există ).
• 0Ch : ştergerea zonei tampon de intrare şi citire de la tastatură. Se curăţă
zona tampon de intrare după care se apelează una din funcţiile de citire
caractere (01h, 06h, 07h, 08h, 0Ah) al cărui cod este introdus în registrul AL.

4.2.4 Funcţii pentru gestionarea perifericelor.

Considerăm în această grupă funcţiile care permit obţinerea de informaţii sau


controlul echipamentelor periferice standard, precum şi funcţia specială 44h, care permite
lucrul cu orice periferic, dacă driverele perifericului există şi respectă anumite restricţii :

• 19h : obţinerea unităţii implicite (curente). Rezultatul se întoarce în AL (0 -


A, 1 - B etc. - aceasta este o excepţie, deoarece numerotarea discurilor în
acest caz porneşte de la 0 şi nu de la 1).
• 0Eh : selectarea unităţii implicite (curente). Ca şi parametru în DL se
specifică numărul discului. La revenire funcţia furnizaează în AL numărul de
unităţi logice existente.
• 2Fh : obţinerea adresei zonei de transfer cu discul (DTA). Funcţia
furnizează în ES:BX adresa DTA.
• 1Ah : poziţionarea adresei DTA. În DS:DX se comunică adresa noului
DTA. Zona de transfer cu discul este folosită la toate accesele la fişiere prin
FCB-uri şi este implicată în funcţiile de regăsirea fişierelor.
• 1Bh : obţinerea informaţiilor despre tabela de alocare a fişierelor FAT, a
unităţii curente. Informaţiile obţinute sunt : AL - număr sectoare/cluster, CX
- număr octeţi/sector, DX - număr unităţi de alocare.
• 1Ch : obţinerea informaţiilor despre FAT, a unei unităţi specificate. Se
comportă ca şi precedentul dar cere ca parametru de intrare în DL numărul
unităţii (0 - implicit, 1 - A, 2 - B etc.).
• 36h : obţinerea spaţiului liber pe disc.
• 44h : controlul general de intrare/ieşire (IOCTL). Realizează schimbul de
informaţii dintre aplicaţii şi echipamente ne-standard. Este prevăzută cu mai
multe subfuncţii prin care se realizează următoarele :
a) Obţinerea informaţiilor despre un echipament.
b) Poziţionarea informaţiilor de comandă pentru un echipament.
c) Citire în mod caracter.
d) Scriere în mod caracter.

- 43 -
Curs de SISTEME DE OPERARE
e) Citire în mod bloc.
f) Scriere în mod bloc.
g) ... alte funcţii.

EXEMPLU :
Program care afişează numele şi lungimea tuturor fişierelor normale din catalogul
curent. Deasemenea se mai afişează lungimea totală a acestor fişiere. O formă simplificată
a comenzii DIR.

/*
In zona DTA se respecta urmatoarea conventie :
00h - 14h - rezervati
15h - atributele fisierului
16h - 17h - ora ultimei modificari
18h - 19h - data ultimei modificari
1Ah - 1Dh - lungimea fisierului
1Eh - 2Bh - numele si extensia fisierului
*/

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <dos.h>

int gata;
char ndta[256];
char *dta;

int find_first(char *tipar);


int find_next(void);
void set_dta(char dta[]);
void get_dta(void);

char *tipar = “*.*”;

void main(void)
{
long total = 0L;
long *dim;
set_dta(ndta);
get_dta();
printf(“Adresa DTA este %p\n”, dta);
dim = (long*)(ndta + 0x1A);
gata = find_first(tipar);
while (!gata) {
total += (*dim);
gata = find_next();
}
printf(“Lungimea totala fisiere = %ld\n”, total);
}

int find_first(char *tipar)


{
union REGS r;
- 44 -
Curs de SISTEME DE OPERARE
r.h.ah = 0x4E;
r.x.dx = (unsigned)tipar; /* Obs. se lucreaza cu modelul SMALL ! */
r.x.cx = 0;
int86(0x21, &r, &r);
printf(“%-20s %ld\n”, (ndta + 0x1E), *(long*)(ndta + 0x1A));
return (r.x.ax == 18);
}

int find_next(void)
{
union REGS r;
r.h.ah = 0x4F;
int86(0x21, &r, &r);
printf(“%-20s %ld\n”, (ndta + 0x1E), *(long*)(ndta + 0x1A));
return (r.x.ax == 18);
}

void set_dta(char dta[])


{
union REGS r;
r.h.ah = 0x1A;
r.x.dx = (unsigned)dta;
int86(0x21, &r, &r);
}

void get_dta(void)
{
union REGS r;
r.h.ah = 0x2F;
int86(0x21, &r, &r);
dta = (char*)r.x.bx;
}

4.2.5 Funcţii pentru gestionarea memoriei şi a proceselor.

Funcţiile pentru gestionarea memoriei şi a proceselor se prezintă împreună deoarece o


utilizare importantă a funcţiilor de gestionare a memoriei este aceea de a elibera suficientă
memorie pentru a putea rula un proces fiu :
• 48h : alocarea memoriei. Încearcă să aloce dinamic, procesului curent, un
număr de paragrafe de memorie specificat în registrul BX, dacă în BX se va
introduce 0xFFFF se va aloca toată memoria disponibilă. În caz că alocarea
reuşeşte, adresa de segment pentru memoria alocată este returnată în AX, în
caz de eroare se va poziţiona indicatorul CARRY.
• 49h : eliberarea memoriei alocate. Face disponibil un bloc de memorie care
a fost alocat anterior cu funcţia 48h. Adresa blocului va fi dată prin ES.
• 4Ah : modificarea alocării memoriei. Încearcă să modifice dimensiunea
unui bloc de memorie alocat anterior cu 48h. Noua dimensiune dorită se
încarcă în BX iar adresa de segment a zonei în ES. La lansarea normală în
execuţie a unui program, acestuia i se alocă toată memoria disponibilă, deci
pentru a putea lansa în execuţie un proces fiu, trebuie mai întâi redusă
dimensiunea blocului de memorie a cărui adresă de început este PSP-ul.

- 45 -
Curs de SISTEME DE OPERARE
• 58h : citirea/poziţionarea strategiei de alocare a memoriei. Se poate face
alocare de tipul First Fit (ceea ce înseamnă prima potrivire), Best Fit (se
examinează toată lista şi se alocă blocul cel mai mic care satisface cerinţele),
Last Fit (se parcurge lista şi se alocă ultima potrivire). Prin conţinutul
registrului AL se alege citirea (00h) sau poziţionarea (01h), iar în BX se va
găsi/transmite strategia (00h, 01h, 02h).
• 4Bh : încărcarea şi executarea unui program. Permite ca programul apelant
să încarce în memorie un alt program (AL = 03h) şi eventual să-l lanseze în
execuţie (AL = 00h). Numele fişierului executabil care conţine programul ce
va fi încărcat se precizează printr-un şir ASCIIZ a cărui adresă se încarcă în
DS:DX. Tot ca parametru de intrare în registrele ES:BX se introduce un
pointer la un bloc de parametri care se vor transmite noului program. Acest
bloc de parametri are o structură diferită după cum se cere lansarea în
execuţie sau numai încărcare.

Pentru lansarea în execuţie :

Depl. Lung. Semnificaţie


Adresa de segment a ambianţei (pentru 0 se
00h 2
copiază ambianţa părintelui).
Adresa de segment şi deplasamentul pentru o
02h 4 linie de comandă de maximum 128 octeţi,
transmisă programului.
06h 4 Adresa primului FCB.
0Ah 4 Adresa celui de-al doilea FCB.
Tabelul 4.2-1

Pentru încărcare :

Depl. Lung. Semnificaţie


00h 2 Adresa de segment la care se încarcă programul.
02h 2 Factor de relocare al programului.
Tabelul 4.2-2
Obs.: Cea mai simplă metodă de a încărca şi executa un alt program constă în
lansarea în execuţie a unei noi copii a procesorului de comenzi
command.com căreia i se transmite o linie de comandă având prevăzut
comutatorul /c. Funcţia 4Bh cu AL = 03h este folosită pentru încărcarea de
segmente ale unui program cu segmente suprapuse şi deasemenea pentru
încărcarea unor drivere.
• 4Ch : terminarea execuţiei unui proces. Un program apelează această
funcţie pentru a anunţa către sistem terminarea execuţiei şi predarea
controlului către procesul părinte. În mod uzual procesului părinte i se poate
transmite şi un cod de întoarcere de un octet. Înainte de terminare se închid
fişierele şi se reâncarcă vectorii de întrerupere pentru nivelele 22h, 23h şi
24h. Deasemenea se realizează şi eliberarea memoriei.
• 4Dh : obţinerea codului de retur al unui proces fiu. Această funcţie întoarce
codul transmis de un proces fiu care s-a terminat fie prin funcţia 4Ch fie prin
31h. Valoarea codului de retur este în AX. În AL este codul returnat de
proces, iar în AH este un alt cod, care precizează condiţiile de terminare (AH
= 0 - terminare normală, 1 - proces terminat cu CTRL-Break sau CTRL-C, 2
- eroare critică de periferic, 3 - s-a făcut terminarea prin 31h - TSR).

- 46 -
Curs de SISTEME DE OPERARE
• 62h : obţinerea adresei PSP. Adresa de segment se întoarce în BX.
• 31h : realizează revenirea în procesul părinte în aşa fel încât programul care
a apelat funcţia să-şi reţină o anumită zonă de memorie. Dimensiunea
memoriei rezidente trebuie introdusă în registrul DX (în paragrafe). Se poate
transmite şi un cod de retur prin AL. Fişierele deschise de program nu se
închid implicit. Spre deosebire de întreruperea 27h, prin această funcţie se
pot reţine zone de memorie de peste 64K.

EXEMPLU :
Utilizarea funcţiilor de gestiune a proceselor şi memoriei se ilustrează printr-un
exemplu care constă din două programe. Primul PARENT.C şi al doilea CHILD.C.
/*
Programul PARENT.C va solicita introducerea de la tastatură a numelui unui fişier
executabil (forma executabilă a lui CHILD.C) pe care îl lansează în execuţie prin
funcţia 4Bh. Programul citeşte şi afişează codul de retur furnizat la terminare de
programul lansat în execuţie.
*/
#include <stdio.h>
#include <dos.h>

static char tampon[128], param[14];


char *errmsg[] = {
"eroare necunoscuta",
"numar de functie invalid",
"fisierul nu a fost gasit",
"cale invalida",
"eroare necunoscuta",
"acces refuzat",
"eroare necunoscuta",
"eroare necunoscuta",
"memorie insuficienta",
"eroare necunoscuta",
"ambianta invalida",
"format invalid"
};

void main(void)
{
char far*numefis, far*bloc;
union REGS r;
struct SREGS sr;
printf("Dati numele programului fiu : ");
gets(tampon);
numefis = tampon;
bloc = param;
r.h.ah = 0x4B; /* Lansare in executie a unui proces */
r.h.al = 0x00; /* Incarcare si lansare in executie */
r.x.dx = FP_OFF(numefis);
sr.ds = FP_SEG(numefis);
r.x.bx = FP_OFF(bloc);
sr.es = FP_SEG(bloc);
int86x(0x21, &r, &r, &sr);
if (!r.x.cflag) {
printf("Terminare normala !\n");
- 47 -
Curs de SISTEME DE OPERARE
r.h.ah = 0x4D;
int86(0x21, &r, &r);
printf("Codul de intoarcere este : %X\n", r.h.al);
}
else printf("Terminare anormala, %s !\n",
(r.x.ax <= 11) ? errmsg[r.x.ax] : errmsg[0]);
}

/*
Programul CHILD.C va anunţa intrarea sa în execuţie printr-un mesaj, după care
alocă mai multă memorie, dacă poate, prin 48h şi se termină prin apelul funcţiei exit
(4Ch).
*/
#include <stdio.h>
#include <dos.h>

void main(void)
{
union REGS r;
printf("Hello, I'm the child !\nI need more memory !\n");
r.h.ah = 0x48;
r.x.bx = 200;
int86(0x21, &r, &r);
if (r.x.cflag)
printf("Not enough memory, AX = %x, BX = %x\n", r.x.ax, r.x.bx);
else
printf("The allocated memory starts at : %x\n", r.x.ax);
r.h.ah = 0x4c;
r.h.al = 0x5A;
printf("Tnto thy hands I deliver my soul.\n");
int86(0x21, &r, &r);
}

4.2.6 Alte funcţii sistem.

În următoarele se va menţiona o listă a funcţiilor sistem care nu au intrat în categoriile


menţionate până acum :
• 2Ah : obţinerea datei curente a sistemului. Produce ca rezultat în registre
data curentă : an, lună, zi. AL = ziua în săptămână (0 - duminica, 1 - luni, ...),
CX = anul curent, DH = luna curentă (1 ÷ 12), DL = ziua curentă (1 ÷ 31).
• 2Bh : poziţionarea datei curente.
• 2Ch : obţinerea timpului sistem curent. CH = ora curentă (0 ÷ 23), CL =
minutul curent (0 ÷ 59), DH = secunda (0 ÷ 59), DL = sutimea de secundă
(0 ÷ 99).
• 2Dh : poziţionarea timpului curent. În AH se returnează 00h dacă în registre
s-au introdus informaţii valide şi FFh dacă informaţiile sunt invalide.
• 35h : obţinerea valorii curente ale unui vector de întrerupere. Pentru apelul
funcţiei se încarcă în AL numărul nivelului de întrerupere dorit iar rezultatul
se obţine în ES:BX. Se recomandă ca asupra vectorilor de întrerupere să se
opereze întotdeauna prin funcţii sistem şi nu prin scriere directă în memorie.
• 25h : poziţionarea valorii unui vector de întrerupere. AL = numărul
nivelului de întrerupere dorit, DS:DX = pointer la rutina care va trata
întreruperea pe nivelul specificat. Se recomandă ca înainte de utilizarea
- 48 -
Curs de SISTEME DE OPERARE
acestei funcţii să se salveze vechea valoare a vectorului de întrerupere astfel
ca la sfârşitul programului curent să se poată reface vectorii de întrerupere
aşa cum au fost înaintea lansării acestui program.
• 5Ch : blocarea şi deblocarea accesului la un fişier. Interzice sau autorizează
accesul la o regiune specificată a unui fişier. Se utilizează în special în
reţelele de calculatoare. În registrul AL se precizează dacă se doreşte blocarea
(00h) sau deblocarea (01h). În registrul BX se introduce identificatorul logic
al fişierului. În CX:DX se introduce deplasamentul regiunii afectate, poziţia
în fişier unde începe zona blocată sau deblocată, iar în SI:DI lungimea zonei
afectate. Această funcţie are efect numai după ce programul SHARE a fost
instalat. Se recomandă ca o regiune de fişier să fie ţinută blocată cât mai
puţin timp. Un proces fiu nu moşteneşte drepturile de acces ale procesului
părinte. Funcţia permite şi cererea blocării la nivel de fişier, în acest caz CX
şi DX conţin valoarea 00h, iar SI şi DI conţin FFh.

4.3 Funcţii de bibliotecă C pentru apelul sistemului MS-DOS.

Majoritatea implementărilor pentru sistemul DOS prevăd funcţii de bibliotecă. Se


recomandă folosirea acestor funcţii ori de câte ori este nevoie pentru portabilitatea
programului. Aceste funcţii ne depind de proprietăţile hardware ale claculatoarelor iar unele
dintre acestea nu sunt specifice doar sistemului de operare MS-DOS. Pot apare diferenţe între
denumirile acestor funcţii de la o implementare la alta. De multe ori o funcţie există în două
variante. Vom întâlni o funcţie _read şi o altă funcţie read. Prima funcţie este specifică MS-
DOS (_read) şi în implementarea ei se apelează o funcţie MS-DOS, cealaltă este introdusă
pentru compatibilitate cu sistemul UNIX.

4.3.1 Funcţii pentru gestionarea fişierelor.

1. Crearea unui fişier (3Ch)

int _creat(const char *path, int attribute);

Returnează indicatorul fişierului (file handle) pentru succes, -1 pentru eroare. Ca şi


atribute pot fi folosite orice combinaţii între următoarele : FA_RDONLY, FA_HIDDEN,
FA_SYSTEM, FA_LABEL, FA_DIREC, FA_ARCH. Prototipul fucţiei se găseşte în fişierul
antet io.h, iar constantele simbolice pentru atribute se găsesc în dos.h. Prin variabila errno se
specifică natura erorii (ENOENT - calea nu este dată bine, ENFILE - prea multe fişiere
deschise în program, EACCES - acces interzis la fişier).

2. Deschiderea unui fişier (3Dh)

int _open(const char *filename, int mode);

Specificarea modului de deschidere se poate face cu ajutorul unor constante simbolice,


care pentru compatibilitate cu UNIX se găsesc în fcntl.h. Returnează indicatorul fişierului în
caz de succes, iar în caz de eroare returnează -1. Se pot specifica trei elemente prin mode şi
anume : care este forma de acces (O_RDONLY, O_WRONLY, ORDWR), care este forma de
partajare a accesului (O_DENYALL, O_DENYWRITE, O_DENYREAD, O_DENYNONE)
sau dacă fişierul este moştenit de procesele fiu (O_NOINHERIT).

- 49 -
Curs de SISTEME DE OPERARE

3. Închiderea unui fişier (41h)

int unlink(const char *filename);

Returnează 0 pentru succes, respectiv -1 pentru eroare.

4. Închiderea unui fişier (3Eh)

int close(int handle);

Pentru închiderea unui fişier se specifică numai descriptorul fişierului. În caz de


incident se returnează -1 iar prin variabila errno se descrie natura erorii (EBADF - descriptor
eronat).

5. Citirea dintr-un fişier cu indice specificat (3Fh)

int _read(int filehandle, void *buffer, unsigned readcount);

Se pot citi până la 64K printr-o singură operaţie. La revenire funcţia returnează
numărul de octeţi citiţi. Dacă a apărut o eroare se returnează -1.

6. Scrierea într-un fişier cu indice specificat (40h)

int _write(int filehandle, void *buffer, unsigned writecount);

Se returnează numărul de octeţi scrişi care trebuie să corespundă cu valoarea lui


writecount. Aceleaşi erori ca şi la citire.

Pentru fiecare dintre aceste 5 funcţii există şi funcţia fără liniuţă, compatibile UNIX,
diferenţa apare ca şi comportare la open, care corespunzând UNIX-ului permite o comportare
diferită la citire, după cum fişierul este deschis în mod text sau în mod binar. Mai există şi
funcţii de bibliotecă echivalente cu apeluri sistem DOS care nu conţin liniuţa :

int creatnew(const char *path, int attribute); (5Bh)


int creattemp(char *path, int attribute); (5Ah)
int dup(int handle); (45h)
int dup2(int handle1, int handle2); (46h)
long lseek(int handle, long offset, int origin); (42h)

Constantele simbolice acceptate ca şi origine sunt : SEEK_SET, SEEK_CUR şi


SEEK_END, acestea fiind definite în fişierul stdio.h.

4.3.2 Funcţii pentru gestionarea cataloagelor.

Aceste funcţii au prototipurile în fişierul antet dir.h.

1. Crearea unui catalog (39h)

int mkdir(const char *path);

- 50 -
Curs de SISTEME DE OPERARE
Returnează 0 pentru succes şi -1 pentru eroare. Ca şi pentru crearea unui fişier, se
presupune că toate numele de cale există, altfel în errno se returnează codul de eroare
corespunzător.

2. Ştergerea unui catalog (3Ah)

int rmdir(const char *path);

3. Schimbarea catalogului curent (3Bh)

int chdir(const char *path);

Prin această funcţie nu putem trece de pe o unitate de disc pe alta.

4. Obţinerea numelui complet al catalogului curent

char* getcwd(char *path, int maxchars);

Primul parametru path desemnează tamponul unde se va primi numele complet al


catalogului, maxchars indică lungimea acestui tampon. Funcţia poate fi apelată şi cu valoarea
NULL pentru path, caz în care getcwd alocă din memoria dinamică maxchars octeţi şi
valoarea returnată este adresa acestei zone de memorie.

5. Obţinerea numelui catalogului curent de pe o unitate de disc specificată (47h)

int getcurdir(int drive, char *directory);

Unitatea de disc este specificată ca un întreg după convenţia : 0 - discul implicit, 1


discul A, 2 discul B etc. Rezultatul se obţine în şirul de caractere directory, acesta fiind un
tampon.

6. Obţinerea sau poziţionarea atributelor unui fişier (43h)

int _chmod(const char *pathname, int func, ...);

Este funcţie specifică DOS. Compilatorul de C acceptă prototipuri de această formă


(...). pathname este numele fişierului care urmează să fie afectat, func este funcţia dorită (0 -
citire atribute, 1 - poziţionare atribute). Dacă func = 1 atunci vom avea şi al treilea parametru,
construit din constante atribut de fişier.

int chmod(char *path, int pmode);

Aceasta este compatibilă UNIX.

7. Găsirea primului fişier care corespunde unui tipar dat (4Eh)

int findfirst(const char *pathname, struct ffblk *fileinfo, int attribute);

Returnează 0 când este succes. În câmpul ff_name se gaseşte numele fişierului care
satisface acest tipar. La eroare returnează -1, cu semnificaţia ENOFILE (nu s-a găsit nici un
fişier corespunzător tiparului dat).

8. Găsirea unui fişier care corespunde unui tipar dat (4Fh)


- 51 -
Curs de SISTEME DE OPERARE

int findnext(struct ffblk *fileinfo);

EXEMPLU :
Dacă folosim funcţii de bibliotecă putem obţine o formă de dir astfel :

#include <stdio.h>
#include <dir.h>

void main(void)
{
long total = 0L;
int gata;
struct ffblk fb;
char *tipar = "*.*";
gata = findfirst(tipar, &fb, 0);
while (!gata)
{
total += fb.ff_size;
printf("%-20s %ld\n", fb.ff_name, fb.ff_fsize);
gata = findnext(&fb);
}
printf("Lungimea totala a fisierelor : %ld\n", total);
}

4.3.3 Funcţii pentru gestionarea perifericelor.

1. Obţinerea unităţii implicite, curente (19h)

int getdisk(void);

Prototipul funcţiei se găseşte în dir.h. Funcţia returnează 0 pentru A, 1 pentru B etc.

2. Selectarea unităţii implicite (0Eh)

int setdisk(int drive);

Prototipul funcţiei se găseşte în dir.h. Unitatea se specifică astfel : 0 - A, 1 - B etc.

3. Obţinerea adresei DTA (2Fh)

char* getdta(void);

Prototipul funcţiei se găseşte în dos.h.

4. Poziţionarea adresei DTA (1Ah)

void setdta(char far* dta);

Prototipul funcţiei se găseşte în dos.h.

- 52 -
Curs de SISTEME DE OPERARE
5. Obţinerea informaţiilor despre FAT pentru unitatea curentă (1Bh)

void getfatd(struct fatinfo *dtable);

struct fatinfo {
char fi_sclus; /* număr de sectoare pe cluster */
char fi_fatid; /* octetul de identificare FAT */
int fi_nclus; /* număr de clustere al unităţii */
int fi_bysec; /* dimensiunea în octeţi a sectorului */
};

Prototipul funcţiei se găseşte în dos.h.

EXEMPLU :
Programul afişează informaţii despre FAT.

#include <stdio.h>
#include <dir.h>
#include <dos.h>

int main(void)
{
struct fatinfo info;
getfatd(&info);
printf("Informatii FAT pentru discul implicit (%c:)\n", 'A' + getdisk());
printf("Sectoare pe cluster : %5d\n", info.fi_sclus);
printf("Identificator FAT : %5X\n", info.fi_fatid);
printf("Numar de clustere : %5u\n", info.fi_nclus);
printf("Octeti pe sector : %5d\n", info.fi_bysec);
return 0;
}

6. Obţinerea informaţiilor despre FAT pentru o unitate specificată (1Ch)

void getfat(unsigned char drive, struct fatinfo *dtable);

Prototipul funcţiei se găseşte în dos.h. Unitatea logică se dă ca şi număr : 0 - unitatea


implicită, 1 - A, 2 - B etc.

7. Obţinerea spaţiului liber pe disc (36h)

void getdfree(unsigned char drive, struct dfre *dtable);

struct dfree {
unsigned df_avail; /* număr de clustere disponibile */
unsigned df_total; /* număr total de clustere */
unsigned df_bsec; /* număr de octeţi / sector */
unsigned df_sclus; /* număr de sectoare / cluster */
};

Prototipul funcţiei se găseşte în dos.h. Unitatea logică se dă ca şi număr : 0 - unitatea


implicită, 1 - A, 2 - B etc. În caz de eroare funcţia poziţionează df_sclus pe 0FFFFh.

- 53 -
Curs de SISTEME DE OPERARE
EXEMPLU :
Programul afişează spaţiul liber pe discul curent.

#include <stdlib.h>
#include <stdio.h>
#include <dos.h>
#include <dir.h>

int main(void)
{
struct dfree free;
long avail;
int drive;
drive = getdisk();
getdfree(drive+1, &free);
if (free.df_sclus == 0xFFFF)
{
printf("Eroare getdfree()\n");
exit(1);
}
avail = (long)free.df_avail * (long)free.df_sclus * (long)free.df_bsec;
printf("Discul %c: contine %ld octeti disponibili.\n", 'A'+drive, avail);
}

8. Controlul general al intrării/ieşirii (44h)

int ioctl(int handle, int func [,void *argdx, int argcx]);

Prototipul funcţiei se găseşte în io.h. Funcţiile sunt specifice pentru o anumită


versiune DOS. Portabilitatea este redusă. Această funcţie se foloseşte pentru obţinerea de
informaţii despre periferice, starea I/O. La eroare se returnează -1, altfel valoarea returnată
este corespunzătoare fiecărei subfuncţii. errno va conţine informaţii despre eroarea respectivă
(EINVAL - argument invalid, EBADF - număr de fişier eronat, EINVDAT - date invalide).

EXEMPLU :
Programul comută modul de lucru al ecranului din stilul RAM (caracterele de
control afişate fără interpretare) în COOKED (se interpretează caracterele de control,
aceasta fiind modul normal de lucru). În modul RAM creşte foarte mult viteza de afişare
pe ecran.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <io.h>

#define RAW_BIT 0x20

int main(int argc, char **argv)


{
int rawmode, handle = 1 /* stdout */, func, argdx;

if (argc < 2) {
printf("Utilizare : RAMCOOK COOKED | RAW\n");

- 54 -
Curs de SISTEME DE OPERARE
exit(1);
}
if (!stricmp(argv[1], "RAW"))
rawmode = 1;
else
if (!stricmp(argv[1], "COOKED"))
rawmode = 0;
else {
printf("Mod necunoscut : %s\n", argv[1]);
exit(2);
}
/* citire informatie */
func = 0;
ioctl(handle, func, &argdx);
if (rawmode)
argdx |= RAW_BIT;
else
argdx &= ~RAW_BIT;
func = 1;
ioctl(handle, func, &argdx);
return 0;
}

4.3.4 Funcţii pentru gestionarea memoriei.

1. Funcţia de alocare a unei cantităţi de memorie (48h)

int allocmem(unsigned size, unsigned *segp);

Prototipul funcţiei se găseşte în dos.h. Primeşte dimensiunea în paragrafe a blocului


de memorie solicitat (16 octeţi = 1 paragraf). segp este o locaţie de memorie unde se va
memora adresa de segment a blocului alocat. Returnează -1 pentru alocare corectă - s-a putut
aloca blocul de memorie solicitat. Altfel va returna dimensiunea celui ma mare bloc
disponibil.
Deosebirea dintre allocmem şi malloc este că prima cere sistemului de operare o
porţiune de memorie, pe când a doua cere alocarea memoriei din heap - spaţiul rezervat
programului.

2. Funcţia de eliberarea memoriei alocate (49h)

int freemem(unsigned segadd);

Prototipul funcţiei se găseşte în dos.h. Codul de retur poate fi 0 pentru succes şi -1


pentru eroare. În caz de eroare se poziţionează errno pe ENOMEM, precizând că s-a transmis
o adresă de segment eronată.

3. Funcţia de modificare a alocării memoriei (4Ah)

int setblock(unsigned segadd, unsigned newsize);

Prototipul funcţiei se găseşte în dos.h. Încearcă modificarea dimensiunii unui bloc


deja alocat. Permite doar mărirea blocului, nu şi micşorarea lui. Întoarce -1 pentru succes,
altfel dimensiunea memoriei disponibile.
- 55 -
Curs de SISTEME DE OPERARE

EXEMPLU :
Programul demonstrează folosirea funcţiilor amintite. În program se mai foloseşte
funcţia movedata care copiază o zonă de memorie într-o altă zonă de memorie.

#include <stdlib.h>
#include <stdio.h>
#include <dos.h>
#include <mem.h>

#define DOS_PRTSTR 9

char str[80] = "Test allocmem()...\n\r$";

int main(void)
{
union REGS r;
struct SREGS sr;
char far *stradd;
unsigned segadd, maxsize;

stradd = (char far*)(&str[0]);


if (allocmem(1, &segadd) != -1)
{
printf("Eroare de alocare !\n");
exit(1);
}
if ((maxsize = setblock(segadd, 5)) != -1)
{
printf("Eroare setblock()...\n");
printf("Dimensiunea maxima posibila = %d paragrafe.\n", maxsize);
exit(2);
}
movedata(FP_SEG(stradd), FP_OFF(stradd), segadd, 0, 80);
sr.ds = segadd;
r.x.dx = 0;
r.h.ah = DOS_PRTSTR;
intdosx(&r, &r, &sr);
freemem(segadd);
return 0;
}

4.3.5 Funcţii pentru gestionarea proceselor.

1. Obţinerea adresei PSP (62h)

unsigned getpsp(void);

Prototipul funcţiei se găseşte în dos.h. Este util pentru citirea şirurilor de ambianţă (de
exemplu PATH-ul). Valoarea returnată reprezintă adresa de segment al PSP. Trebuie să
cunoaştem deplasamentul pentru a afla datele din PSP.

- 56 -
Curs de SISTEME DE OPERARE

EXEMPLU :
Program care afişează spaţiul ocupat de el însuşi.

#include <stdio.h>
#include <dos.h>

int main(void)
{
unsigned mypsp, lastseg;
long mem_allocated;
mypsp = getpsp();
printf("PSP incepe la %04X:0000\n", mypsp);
lastseg = *((unsigned far*)(MK_FP(mypsp, 2)));
printf("Sfarsitul memoriei alocate este la %04X:0000\n", lastseg);
mem_allocated = 16 * (long) (lastseg - mypsp);
printf("%ld octeti alocati programului.\n", mem_allocated);
return 0;
}

2. Funcţia care determină rămânerea rezidentă a unui program (31h)

void keep(unsigned char status, unsigned memsize);

Prototipul funcţiei se găseşte în dos.h. Primeşte ca parametru un cod de stare care se


transmite programului părinte. memsize reprezintă cantitatea de memorie pe care o păstrează
procesul care rămâne rezident. Este reprezentată în paragrafe. Este folosită pentru gestionarea
vectorilor de întrerupere.

3. Terminarea execuţiei unui proces (4Ch)

void _exit(int status);

Prototipul funcţiei se găseşte în stdlib.h sau process.h. Nu închide fişierele aferente


programului, face numai revenirea la procesul părinte şi nu goleşte tampoanele.
Funcţia compatibilă UNIX este :

void exit(int status);

Prototipul funcţiei se găseşte în stdlib.h sau process.h. Închide toate fişierele aferente
programului. La ambele funcţii status este codul de întoarcere pentru procesul părinte.

4. Execuţia unei comenzi DOS în interiorul unui program

int system(const char *cmd);

Prototipul funcţiei se găseşte în stdlib.h sau process.h. cmd reprezintă comanda DOS
care va fi executată. Se citeşte variabila COMSPEC pentru a localiza COMMAND.COM-ul şi
se transmite argumentul cmd. Pentru execuţia acestei comenzi, dacă este nevoie, se
inspectează şi variabila PATH. COMMAND.COM se lansează cu o singură comandă după
execuţia căreia se revine.
Dacă cmd este şirul vid se face doar verificarea faptului că există COMMAND.COM.
Dacă şirul este vid şi nu se găseşte COMMAND.COM atunci se returnează 0, altfel se
- 57 -
Curs de SISTEME DE OPERARE
returnează o valoare diferită de 0. Dacă şirul nu este vid se returnează 0 pentru execuţie
normală şi -1 pentru eşec.

EXEMPLU :
Programul următor lansează în execuţie o comandă.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

int main(void)
{
char command[128];
while (1)
{
printf("Tastati o comanda (\"quit\" pentru iesire) : ");
gets(command);
strlwr(command); /* conversie la litere mici */
if (strcmp(command, "quit") == 0)
exit(0);
if (system(command) == -1)
perror("Eroare system()");
}
}

5. Încărcarea şi execuţia unui proce fiu

Prototipul funcţiilor din această categorie se găsesc în process.h.

int spawnl(int mode, char *path, char *arg0, ..., NULL);


int spawnle(int mode, char *path, char *arg0, ..., NULL, char *envp[]);
int spawnlp(int mode, char *path, char *arg0, ..., NULL);
int spawnlpe(int mode, char *path, char *arg0, ..., NULL, char *envp[]);

int spawnv(int mode, char *path, char *argv[]);


int spawnve(int mode, char *path, char *argv[], char *envp[]);
int spawnvp(int mode, char *path, char *argv[]);
int spawnvpe(int mode, char *path, char *argv[], char *envp[]);

mode reprezintă modul de execuţie al lui spawn, modul cum trebuie tratat procesul
părinte. Valorile pe care le poate primi acest parametru sunt :
• P_WAIT - procesul părinte rămâne în aşteptare până la terminarea
procesului fiu.
• P_OVERLAY - programul procesului fiu se suprapune peste programul
procesului părinte în memorie.
• P_NOWAIT - procesul părinte îşi continuă execuţia în paralel cu procesul
fiu (această formă nu este implementată în S.O. MS-DOS).
path reprezintă numele de cale al fişierului de executat.
arg0, arg1, ... reprezintă argumentele din linia de comandă pentru procesul fiu (arg0
trebuie să fie identic cu path).
argv[] reprezintă argumentele din linia de comandă pentru procesul fiu.
envp[] reprezintă tabloul cu parametri de mediu.

Semnificaţia literelor din numele funcţiilor spawn este următoarea :


- 58 -
Curs de SISTEME DE OPERARE
• l - se transmit argumentele ca o listă.
• v - se transmit argumentele ca un tablou, ca vector de pointeri.
• p - indică faptul că pentru localizarea fişierului executabil se va consulta şi
variabila de mediu PATH.
• e - precizează faptul că se transmit variabile de mediu. În absenţa lui e
procesul fiu moşteneşte variabilele de mediu ale părintelui.

Obs:
1. Este obligatoriu ca funcţiile spawn să transmită cel puţin un argument
procesului fiu, adică cel puţin numele fişierului care conţine codul procesului.
2. Lungimea combinată a tuturor şirurilor folosite ca argumente în linia
de comandă, dacă este vorba de listă |arg0| + |arg1| + ... + |argn| sau dacă este tablou
|arg[0]| + |arg[1]| + ... + |arg[n]| nu poate depăşi 128 octeţi.
3. Fişierele care au fost deschise în procesul părinte rămân deschise şi
pentru procesul fiu.
4. Valoarea de revenire din procesul fiu are semnificaţie numai dacă
mode este P_WAIT, dacă este P_OVERLAY atunci starea de revenire din procesul
fiu va lua locul stării de revenire a procesului părinte.

În caz că nu se poate lansa în execuţie procesul fiu, atunci se returnează -1, iar pentru
variabila globală errno pot exista următoarele situaţii :
• E2BIG, aceasta însemnând că lungimea totală a argumentelor depăşeşte 128 octeţi,
sau că memoria necesară pentru variabilele de mediu ar depăşi 32K.
• EINVAL, aceasta înseamnă că pentru mode s-a folosit o valoare invalidă (de
exemplu P_NOWAIT).
• ENOENT, arată că nu se poate găsi fişierul cu cod executabil. Fie că nu există acel
fişier, fie că numele de cale este invalid.
• ENOEXEC, arată că fişierul zis de cod nu este de fapt executabil.
• ENOMEM, arată că nu există memorie suficientă pentru încărcarea şi execuţia
procesului fiu (programul este prea mare).

Aceste funcţii sunt utile dacă vrem să lansăm în execuţie, din cadrul unui program, un
alt program, care a fost compilat şi linkeditat separat. Între cele două programe (părinte şi fiu)
se pot folosi în comun zone de memorie, rezultând o funcţionare asemănătoare cu a
programelor rezidente (TSR).

EXEMPLU:
Se ilustrează utilizarea acestor funcţii prin două programe : PARENT.C şi
CHILD.C. Programul PARENT.C lansează în execuţie programul CHILD.C întrebând
utilizatorul care din variantele de spawn doreşte să le folosească. Înainte de lansarea în
execuţie a fiului se pregăteşte în PARENT.C o structură de date care se va transmite
procesului fiu prin codificarea adresei acestei structuri ca şir de caractere ce va figura ca
unul dintre argumentele din linia de comandă. În procesul fiu se vor tipări argumentele
din linia de comandă şi ambianţa transmisă. Se converteşte apoi adresa transmisă ca şir de
caractere şi se face acces la structură. Se modifică în continuare diverse câmpuri ale
structurii şi se termină programul fiu. La revenirea în programul părinte se tipăresc din
nou valorile din structura de date comună pentru a verifica dacă s-au produs modificările
executate în procesul fiu. Cele două programe vor fi în fişiere separate care se compilează
şi se linkeditează separat.

/* Fisierul PARENT.C */
#include <process.h>
#include <string.h>
#include <stdio.h>
- 59 -
Curs de SISTEME DE OPERARE
#include <alloc.h>

typedef struct TEST_DATA {


char name[20];
int n;
double x;
} TEST_DATA;

/* Construim o ambianta */
char *envp[] = { /* declaram acest tablou de pointeri spre */
/* siruri de caractere in care initializam */
/* o singura variabila de mediu */
"PARENT=SPAWN FUNCTIONS",
NULL
};

void main(void)
{
char *nargv[4]; /* tabloul in care se construiesc argumentele */
char buff[20]; /* tabloul de lucru pentru dialog cu utilizatorii */
char fname[40]; /* si altul pentru conversii */

TEST_DATA *pdata;

if ((pdata = (TEST_DATA*)malloc(sizeof(TEST_DATA))) == NULL)


abort();
pdata->n = 100; /* se pregateste structura de date comuna */
pdata->x = 1000.99;
strcpy(pdata->name, "PARENT");
nargv[0] = "child.exe";
nargv[1] = fname;
sprintf(buff, "%Fp", (void far*)pdata);
printf("PARENT : Lansare proces fiu : name = %s, n = %d, x = %f.\n",
pdata->name, pdata->n, pdata->x);
nargv[2] = buff;
nargv[3] = NULL;
printf("Ce functie se apeleaza ?\n");
gets(fname);
strlwr(fname);
if (strcmp(fname, "spawnl") == 0)
spawnl(P_WAIT, "child.exe", "child.exe", fname, buff, NULL);
if (strcmp(fname, "spawnle") == 0)
spawnle(P_WAIT, "child.exe", "child.exe", fname, buff, NULL, envp);
if (strcmp(fname, "spawnlp") == 0)
spawnlp(P_WAIT, "child.exe", "child.exe", fname, buff, NULL);
if (strcmp(fname, "spawnlpe") == 0)
spawnlpe(P_WAIT, "child.exe", "child.exe", fname, buff, NULL, envp);
if (strcmp(fname, "spawnv") == 0)
spawnv(P_WAIT, "child.exe", nargv);
if (strcmp(fname, "spawnve") == 0)
spawnve(P_WAIT, "child.exe", nargv, envp);
if (strcmp(fname, "spawnvp") == 0)
spawnvp(P_WAIT, "child.exe", nargv);
if (strcmp(fname, "spawnvpe") == 0)
- 60 -
Curs de SISTEME DE OPERARE
spawnvpe(P_WAIT, "child.exe", nargv, envp);
if (strcmp(pdata->name, "CHILD") == 0)
printf("Revenire din procesul fiu : name = %s, n = %d, x = %f.\n",
pdata->name, pdata->n, pdata->x);
else
printf("A aparut o eroare !\n");
}

/* Fisierul CHILD.C */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <dos.h>

typedef struct TEST_DATA {


char name[20];
int n;
double x;
} TEST_DATA;

static char far *myname = "CHILD";

void main(int argc, char **argv, char **envp)


{
char **p_table; /* tablou de pointeri spre siruri de caractere */
TEST_DATA far *pdata; /* tabela de parametri */
void far *p_s1;
void far *p_s2;

printf("CHILD : am primit %d argumente.\n", argc);


if (argc < 3) {
printf("Nu sunt destule argumente !\n");
exit(1);
}
printf("CHILD a fost apelat prin %s.\n", argv[1]);
printf("CHILD : ambianta contine :\n");
/* se tiparesc sirurile de ambianta */
for(p_table = envp; *p_table != NULL; p_table++)
printf("%s\n", *p_table);
/* urmeaza conversia inversa, a sirului de caractere */
sscanf(argv[2], "%Fp", (void far*)&pdata);
printf("In CHILD : name = %Fs, n = %d, x = %f.\n",
pdata->name, pdata->n, pdata->x);
p_s1 = (void far*)myname;
p_s2 = (void far*)pdata->name;
/* folosind p_s1, p_s2 putem copia ceva in structura respectiva */
movedata(FP_SEG(p_s1),FP_OFF(p_s1),FP_SEG(p_s2),FP_OFF(p_s2),6);
pdata->n = 101;
pdata->x = 999.99;
/* se termina procesul fiu si se transmite 0 ca si valoare de iesire */
exit(0);
}
- 61 -
Curs de SISTEME DE OPERARE

7. Încărcarea şi lansarea în execuţie a unui proces fiu fără revenire

int execl(char *path, char *arg0, .., NULL);


int execle(char *path, char *arg0, .., NULL, char **env);
int execlp(char *path, char *arg0, ..);
int execlpe(char *path, char *arg0, .., NULL, char **env);

int execv(char *path, char *argv[]);


int execve(char *path, char *argv[], char **env);
int execvp(char *path, char *argv[]);
int execvpe(char *path, char *argv[], char **env);

Prototipurile acestor funcţii se găsesc în fişierul antet process.h. Într-un anumit sens
acestea sunt asemănătoare cu familia de funcţii spawn la care mode este pe P_OVERLAY.
În MS-DOS utilizarea funcţiilor exec este recomandabilă în aplicaţii care presupun
înlănţuirea unor programe. În caz de eşec de lansare în execuţie şi funcţiile exec returnează
-1, iar în variabila globală errno se obţin detalii despre natura erorii. Pot apărea aceleaşi erori
ca şi la spawn, mai puţin EINVAL, care era legat de argumentul mode. În plus mai pote
apărea ENFILE, dacă sunt prea multe fişiere deschise, respectiv EACCESS, dacă un fişier
specificat în PATH este blocat, sau nu este protejabil (la MS-DOS versiunile mai mari decât
3.3).

8. Terminarea anormală a execuţiei unui program

void abort(void);

Prototipul acestei funcţii se găseşte în fişierul antet stdlib.h sau process.h. Această
funcţie determină afişarea unui mesaj de eroare : "Abnormal program termination", după care
în funcţia respectivă este prevăzut un apel la funcţia exit() şi se dă codul de retur 3.

4.3.6 Alte funcţii sistem.

Prototipurile acestor funcţii sunt în fişierul antet dos.h.

1. Funcţii pentru obţinerea datei curente

void getdate(struct date *datep);

struct date {
int da_year; /* anul dat în formă binară (deja convertită +1980) */
char da_day; /* ziua 1 ÷ 31 */
char da_month; /* luna 1 ÷ 12 */
};

2. Poziţionarea datei curente

void setdate(struct date *datep);

3. Obţinerea timpului curent al sistemului

void gettime(struct time *timep);


- 62 -
Curs de SISTEME DE OPERARE

struct time {
unsigned char ti_min;
unsigned char ti_hour;
unsigned char ti_hund;
unsigned char ti_sec;
};

4. Poziţionarea timpului curent al sistemului

void settime(struct time *timep);

5. Obţinerea valorii curente a unui vector de întrerupere

void interrupt (*getvect(int interruptno))();

Funcţia de bibliotecă este getvect. interruptno este numărul de întrerupere. Valoarea


returnată de funcţie este o adresă, valoarea curentă a vectorului de întrerupere pe nivelul de
întrerupere (dat de interruptno), acea adresă este adresa rutinei de tratare.
Funcţia se foloseşte în mod uzual pentru a putea salva valoarea vectorului de
întrerupere înainte de poziţionarea sa.

6. Poziţionarea unui vector de întrerupere

void setvect(int intno, void interrupt (*handler)());

handler este adresa noii funcţii de tratare a întreruperii de nivel intno.

EXEMPLU :
În continuare se prezintă un program în care se folosesc funcţiile getvect, setvect şi
de asemenea funcţia keep (folosită pentru a realiza programe rezidente). Programul
instalează o rutină de tratare a întreruperii pe nivelul 1Ch, aceasta va incrementa un
contor modulo 10 şi va afişa la fiecare întrerupere de timp valoarea contorului respectiv.

#include <dos.h>

#define INTR 0x1C


#define SAFETY 64
#define ATTR 0x7900

/* _heaplen - este variabila globala care specifica */


/* dimensiunea zonei de alocare dinamica */
extern unsigned _heaplen = 1024;

/* _stklen - este variabila globala care specifica */


/* dimensiunea zonei de stiva */
extern unsigned _stklen = 512;

void interrupt (*oldhandler)(...);

void interrupt handler(...)


{
/* tablou pentru a pastra un rand al ecranului */
unsigned int (far *screen)[80];
- 63 -
Curs de SISTEME DE OPERARE
static int count;

/* screen trebuie sa indice in memoria video */


(void far*)screen = MK_FP(0xB800, 0);

count++;
count %= 10;
screen[0][79] = count + '0' + ATTR;
oldhandler();
}

int main(void)
{
oldhandler = getvect(INTR);
setvect(INTR, handler);
/* se calculeaza dimensiunea pentru programul rezident */
/* _psp - este variabila globala in care se gaseste */
/* adresa prefixului segmentului de program PSP */
/* _SS - corespunde registrului de segment SS */
/* _SP - corespunde registrului pointer de stiva SP */
keep(0, (_SS + (_SP + SAFETY) / 16 - _psp));
return 0;
}

- 64 -
Curs de SISTEME DE OPERARE

5. Gestionarea memoriei

MS-DOS gestionează memoria pentru a asigura că toate programele au acces la


memoria necesară pentru o execuţie normală. Sistemul alocă memorie pentru un program în
timpul încărcării, iar acesta poate aloca la rândul lui cantităţi de memorie pe măsură ce are
nevoie de ele sau poate elibera orice cantitate de memorie pe care nu o mai foloseşte.

5.1 Memoria convenţională.

Programele alocă memorie convenţională (adresele 0000:0000 ÷ A000:0000) prin


folosirea funcţiei sistem de alocare a memoriei (Funcţia 48h). Această funcţie caută după un
bloc de memorie cel puţin atât de mare cât blocul cerut şi întoarce adresa de segment al
noului bloc. Deoarece la încărcarea unui program MS-DOS poate aloca toată memoria
convenţională disponibilă, funcţia de alocare a memoriei poate întoarce o valoare de eroare
0008h (ERROR_NOT_ENOUGH_MEMORY). În acest caz registrul BX va conţine
dimensiunea celui mai mare bloc disponibil, în paragrafe.
Dacă un program nu mai are nevoie de memoria pe care a alocat-o, el poate elibera
memoria folosind funcţia de eliberare a memoriei alocate (Funcţia 49h). După ce a fost
eliberată, memoria devine disponibilă, astfel încât ea poate fi alocată din nou, de acelaşi
program sau altul. Un program îşi poate lărgi sau micşora cantitatea de memorie dintr-un bloc
la un număr specificat de paragrafe, folosind funcţia modifică dimensiunea unui bloc de
memorie (Funcţia 4Ah).
Un program care lansează în execuţie un alt program (numit program fiu) de obicei
foloseşte funcţia de modificare a dimensiunii unui bloc de memorie, pentru a-şi reduce
propria dimensiune, făcând astfel disponibilă mai multă memorie pentru programul fiu. În
astfel de cazuri, programul părinte transmite funcţiei adresa de segment al PSP-ului său,
împreună cu noua dimensiune. Programul părinte NU trebuie să elibereze memoria care
conţine propriul său cod, date şi stivă, altfel alocări succesive ar putea distruge conţinutul
acelei memorii. Pentru a înlătura această situaţie, unele programe îşi copiază codul şi datele
pe disc şi eliberează toată memoria, cu excepţia memoriei corespunzătoare unei rutine mici,
care va reloca memoria şi va reâncărca de pe disc codul şi datele, când va fi nevoie din nou de
acestea.
Strategia curentă de alocare, stabilită prin funcţia de setare a strategiei de alocare
(Funcţia 5801h), determină modul cum funcţia de alocare a memoriei va căuta pentru un bloc
de memorie. Căutarea poate începe sau de la începutul sau de la sfârşitul memoriei
convenţionale şi sfârşeşte la găsirea primului bloc care satisface cerinţele sau, dacă nu există
nici un bloc disponoibil, se întoarce blocul cel mai apropiat. Tot strategia de alocare
determină dacă funcţia va căuta în memoria convenţională sau în zona superioară a memoriei
(UMB). Un program poate determina strategia curentă de alocare prin folosirea funcţiei de
citire a strategiei de alocare (Funcţia 5800h).
Obs.: Dacă un program schimbă strategia de alocare, el ar trebui să salveze strategia
originală şi să o restaureze înainte de terminare.

5.2 Blocurile din Memoria Superioară (UMB).

Un bloc din memoria superioară (UMB) este o zonă RAM din memoria superioară
care este disponibilă programelor. Zona de memorie superioară (adresele A000:0000 ÷
FFFF:0000) este rezervată în principal pentru memorii ROM şi dispozitive mapate în

- 65 -
Curs de SISTEME DE OPERARE
memorie, dar MS-DOS poate mapa RAM la orice adresă din această zonă, care nu este
utilizată de ROM sau alte dispozitive.
Un program poate aloca un bloc de memorie superioară utilizând funcţia de alocare a
memoriei. Înainte de a aloca memorie, trebuie ca programul să seteze o strategie de alocare
potrivită şi să facă legătura cu zona de memorie superioară. Ca şi la memoria convenţională,
un program foloseşte pentru funcţia de stabilire a strategiei de alocare funcţia 5801h. O
strategie de alocare ca de exemplu FIRST_FIT_HIGH (0080h) determină ca funcţia de
alocare să caute memoria superioară şi să continue cu căutarea în memoria convenţională,
dacă nu a găsit bloc disponibil.
Funcţia de alocare nu poate căuta în memoria superioară doar dacă aceasta este legată
de restul memoriei sistem. Un program poate stabili legarea memoriei superioare prin
folosirea funcţiei de stabilire a legăturii cu memoria superioară (5803h), sau poate determina
dacă această legătură există folosind citirea stării legăturii cu memoria superioară (Funcţia
5802h).
Obs. Dacă un program schimbă legătura cu memoria superioară el ar trebui să salveze
starea anterioară şi să o refacă înainte de terminare.
Un program poate utiliza funcţia de eliberare a memoriei şi pentru eliberarea
blocurilor din memoria superioară. Deasemenea poate utiliza şi funcţia de redimensionare a
blocurilor pentru a reduce sau mări dimensiunea blocului alocat.
Dacă un program a fost lansat folosind comanda LOADHIGH, sistemul încarcă acel
program în memorie alocată din zona de memorie superioară. Deşi un program poate fi în
memoria superioară, memoria pe care o alocă depinde de strategia de alocare.
Blocurile de memorie superioară nu sunt accesibile prin funcţii MS-DOS doar dacă
comanda dos = umb este inclusă în fişierul CONFIG.SYS şi driverul HIMEM.SYS şi
software-ul de gestionare a memoriei, de ex. EMM386.EXE sunt încărcate. Dacă dos = umb
nu este specificat în CONFIG.SYS, dar software-ul de gestionarea memoriei este încărcat,
programele pot accesa zona de memorie suprioară folosind apeluri directe la software-ul de
gestionare a memoriei.

5.3 Structuri de date folosite de MS-DOS în gestionarea memoriei.

MS-DOS-ul ţine evidenţa blocurilor de memorie prin crearea unei liste înlănţuite de
structuri ARENA, care definesc dimensiunea şi proprietarul blocurilor de memorie.

Structura ARENA are următoarea componenţă:

ARENA STRUC
arenaSignature db ? ;4dh=valid, 5ah=ultim
arenaOwner dw ? ;proprietarul acestui bloc
arenaSize dw ? ;dimensiunea blocului în paragrafe
arenaReserveddb 3 dup(?) ;rezervat
arenaName db 8 dup(?) ;numele de fişier al proprietarului
ARENA ENDS

La încărcare MS-DOS-ul crează astfel de structuri pentru memoria disponibilă. El


crează alte astfel de structuri pe măsură ce este nevoie de ele, când încarcă programe şi
drivere sau când programele alocă memorie. Numărul, dimensiunea şi poziţia structurilor
ARENA depind de dimensiunea blocurilor de memorie alocate.
Programele trebuie să aibă grijă să nu altereze structurile ARENA. MS-DOS-ul nu
poate reface structurile pe care programele le-au suprascris sau le-au modificat. Dacă o
structură ARENA a fost alterată, funcţiile ca alocare de memorie şi eliberare de memorie
eşuează, dând ca şi cod de eroare valoarea 0007h (ERROR_ARENA_TRASHED).
- 66 -
Curs de SISTEME DE OPERARE
Câmpurile structură ARENA :

arenaSignature - specifică dacă structura este validă. Acest câmp trebuie să


conţină 4Dh sau 5Ah. Valoarea 5Ah indică faptul că structura
este ultima din listă.
arenaOwner - specifică proprietarul blocului. Acest câmp conţine adresa
de segment al PSP-ului programului proprietar. Conţine zero
dacă blocul este liber.
arenaSize - specifică dimensiunea blocului, în paragrafe. Blocul începe
imediat după structura ARENA.
arenaReserved - rezervat
arenaName - conţine un şir terminat cu 0, specificând numele fişierului
programului care deţine blocul. Nume ca SC şi SD sunt
utilizate de MS-DOS pentru a reprezenta cod sistem
(programe) respectiv date sistem.
Tabelul 5.3-1

Comentarii :
Fiecare structră ARENA este urmată imediat de un bloc continuu de memorie.
Următoarea structură ARENA din listă este în continuarea respectivului bloc. Aceasta
înseamnă că adresa segment a următoarei structuri din listă este egală cu adresa de segment a
blocului de memorie curent plus dimensiunea sa.
MS-DOS-ul completează câmpul arenaName pentru un bloc când încarcă un program
în acest bloc. Structurile ARENA pentru memoria alocată de programe prin funcţia de alocare
nu sunt completate în acelaşi fel.

5.4 Procesarea liniei A20. Memoria înaltă.

Pentru calculatoarele bazate pe procesoare 80286, 80386 şi mai recente, cea de-a 21
linie de adresă a procesorului (A20) controlează accesul la un spaţiu suplimentar de 64K,
numit zonă de memorie înaltă (HMA). Producătorii de calculatoare de obicei includ un circuit
pentru a dezactiva linia A20 când procesorul rulează în mod real. Acesta asigură ca mediul de
operare să fie identic cu acela al unui 8086, în care adresele ca şi FFFF:0010 se suprapun
peste începutul memoriei. Când linia A20 este activă, adresele care s-ar repeta (adresele în
intervalul FFFF:0010 ÷ FFFF:FFFF) asigură accesul la HMA.
Dacă un calculator oferă suport RAM pentru HMA, atunci MS-DOS poate activa linia
A20 şi să realoce codul sistem în HMA, astfel eliberând memoria convenţională pentru alte
programe. MS-DOS-ul se relocatează în HMA doar dacă comanda dos=high este în
CONFIG.SYS iar HIMEM.SYS este încărcat. Acest driver conţine codul care permite
activarea şi dezactivarea liniei A20.
Pentru a suporta programele care se aşteaptă ca adresele să se repete, MS-DOS-ul
dezactivează linia A20 întotdeauna când încarcă şi execută un program. În timp ce linia A20
este dezactivată, partea MS-DOS din HMA nu este accesibilă direct, deşi programele pot
totuşi să apeleze funcţii sistem MS-DOS. Pentru a realiza acest lucru, MS-DOS-ul
redirectează toate apelurile sistem la un “stub” din memoria convenţională, care activează
linia A20 şi face salt la funcţia MS-DOS solicitată. Odată activată, linia A20 rămîne activată
chiar după ce se revine din apelul funcţiei sistem.
Programele nu trebuie să folosească HMA dacă MS-DOS-ul a fost realocat aici. Un
program poate determina dacă MS-DOS-ul este sau nu în HMA folosind funcţia care întoarce

- 67 -
Curs de SISTEME DE OPERARE
versiunea MS-DOS-ului. Această funcţie setează bitul 4 din registrul DH dacă MS-DOS-ul
este în HMA.

- 68 -
Curs de SISTEME DE OPERARE

6. Sistemul de Operare UNIX. Probleme generale.

6.1 Introducere.

Sistemul UNIX, de la începuturile sale şi până astăzi, a devenit destul de popular,


rulând pe maşini cu puteri de procesare variate, de la microprocesoare şi până la mainframe-
uri, oferind astfel o ambianţă comună de execuţie pe toate acestea. Sistemul este împărţit în
două mari părţi. Prima parte constă din programe şi servicii care au făcut din sistemul UNIX
un sistem aşa de popular, aceasta fiind partea cu care iau contactul, de obicei, utilizatorii,
incluzând programe ca shell-ul, mail-ul, pachete pentru procesare de text, sisteme de control
al codului sursă etc. Cea de-a doua parte constă din sistemul de operare care suportă aceste
programe şi servicii.
Între anii 1965-1969 laboratoarele Bell ale firmei Western Electric (filială a lui
AT&T) împreună cu General Electric şi MIT au participat la dezvoltarea sistemului de
operare MULTICS. Pe măsură ce lucrările progresau a devenit evident că deşi era foarte
probabil că MULTICS va putea satisface o mare varietate de servicii, el devenea peste măsură
de voluminos şi de scump. Din acest motiv, laboratoarele Bell se retrag din acest proiect, dar
unii membri ai echipei, conduşi de Ken Thompson continuă să lucreze la un proiect mai
puţin ambiţios. Au apărut astfel elementele definitorii pentru un nou sistem de operare,
UNIX, a cărui primă versiune experimentală a fost scrisă în 1969 în limbaj de asamblare.
În anul 1972 a fost realizat primul compilator pentru limbajul C, iar sistemul UNIX a
fost rescris în acest limbaj. Acest sistem s-a impus în anii '80 ca principala soluţie de
standardiyare în domeniul sistemelor de operare, reprezentând de asemenea o modalitate de
realizare a sistemelor deschise.
În figura Fig. 6.1-1 se poate urmări evoluţia sistemului de operare UNIX.

6.1.1 Cauzele răspândirii sistemului de operare UNIX.

S.O. UNIX cunoaşte o largă răspândire datorită mai multor motive :


• este agreat de utilizatori, posedând o gamă bogată de instrumente software ce se pot
utiliza cu uşurinţă, putându-se combina pentru a se obţine noi posibilităţi. Deoarece
dispune de sute de programe utilitare diferite facilităţile oferite de UNIX acoperă o
gamă largă de aplicaţii : baze de date, reţele, grafică, inteligenţă artificială, simulare,
probleme de gestiune, statistică, instruire asistată de calculator;
• sistemul UNIX este conceput ca sistem multiuser şi multitasking. Apare deci
posibilitatea exploatării eficiente a capacităţii de prelucrare, pusă la dispoziţie de
calculatoarele actuale;
• S.O. UNIX este portabil, fiind scris în limbajul C, sistemul funcţionând aproape
identic pe mainframe-uri, mini sau microcalculatoare. Se poate realiza astfel
dezvoltarea de pachete de programe funcţionale pe o gamă largă de maşini, preţul
produsului fiind mai scăzut şi programatorilor fiindu-le transparent hardware-ul de
care dispun.
• UNIX este foarte mult folosit în universităţi.

- 69 -
Curs de SISTEME DE OPERARE

UNIX - 1969

Versiunea 3 -1973

Versiunea 6 -1975

1978 Versiunea 7 -1978


1981 PWB UNIX 1981
BSD 4.1 XENIX
1982
SystemIII
1983
BSD 4.2
1984
1985 SystemV Rel. 3.0 1985
BSD 4.3 XENIX V

1986 1986
SunOS 4.0 S.O. Mach

1988
1988
NeXT
SystemV Rel. 4.0
multiprocessor

1989
AIX IBM
1990
SCO ODT Rel. 1.1

1992 1992 1992 1992


SOLARIS AIX 3.2 UnixWare SCO ODT Rel. 2.0
USL+Novell
1993
SystemV Rel. 5.0

Fig. 6.1-1

6.1.2 Considerente de realizare a S.O. UNIX.

Implementarea S.O. UNIX are la bază următoarele considerente :


• necesitatea standardizării şi unificării mediilor de operare şi în general a interfeţei cu
utilizatorul;

- 70 -
Curs de SISTEME DE OPERARE
• transportabilitatea fişierelor între diverse sisteme de calcul, menţinând identică
structura volumelor şi fişierelor;
• asigurarea unor niveluri superioare de portabilitate a produselor program;
• folosirea unei game largi de arhitecturi de calcul, prin interconectarea
calculatoarelor de tipuri şi puteri diferite, funcţionând sub acelaşi sistem de operare;
• transparenţa produselor software faţă de evoluţia hardware-ului.

6.1.3 Caracteristici generale al sistemelor UNIX.

Printre caracteristicile generale ale S.O. UNIX care au contribuit la succesul acestui
sistem de operare, putem menţiona :
• UNIX este un sistem de operare de tip time-sharing, multitasking şi multiutilizator;
• Este asigurată protecţia fişierelor şi a modului de execuţie prin existenţa unor parole
şi drepturi de acces;
• S.O. promovează modularitatea;
• Operaţiile de intrare/ieşire sunt integrate în sistemul de fişiere, realizându-se aşa
numitele intrări/ieşiri generalizate;
• Există un sistem de gestiune a proceselor reentrante şi asincrone multiple, care se
pot sincroniza prin intermediul unui sistem de întreruperi logice;
• Gestiunea memoriei se face printr-un mecanism ce permite schimbul de pagini între
memoria RAM şi cea externă, gestionându-se spaţiul afectat execuţiei proceselor şi
controlându-se timpul de acces la procesele în aşteptare;
• S-a realizat o interfaţă simplă prin intermediul componentei SHELL, care nu este
integrată în nucleul (KERNEL) sistemului de operare, asigurându-se totodată o
identitate a sintaxei tuturor comenzilor;
• Prin scrierea S.O. în limbajul C, s-a obţinut o portabilitate atât a sistemului UNIX
propriu-zis, cât şi a software-ului de aplicaţie dezvoltat sub acest sistem, realizându-
se astfel şi premisele dezideratului de sistem deschis. Sub aceste S.O. se pot astfel
dezvolta pachete de programe funcţionale pe o gamă largă de maşini.

6.2 Principii de utilizare ale sistemului UNIX.

Fiind un sistem de operare multitasking şi multiutilizator, UNIX necesită efectuarea


unei serii de operaţii relativ complexe atât la lansare cât şi înainte de scoaterea calculatorului
de sub tensiune.
Lansarea sistemului UNIX are de obicei loc în patru etape, ale căror efecte se pot
suprapune în timp :
• La punerea calculatorului sub tensiune controlul este preluat de un program înscris
în memoria permanentă. Acest program examinează configuraţia sistemului de
calcul şi efectuează o serie de teste de bună funcţionare, după care se examinează
perifericele în căutarea unui candidat pentru lansarea sistemului, se verifică
existenţa unui sistem de fişiere UNIX pe discul ales după care se citeşte şi se încarcă
în memorie nucleul sistemului de operare, aflat într-un fişier obişnuit, direct
executabil (/vmlinuz pentru S.O. Linux).
• Nucleul S.O. primeşte controlul şi determină montarea sistemului de fişiere de pe
discul de pornire. Rădăcina acestui sistem de fişiere devine rădăcina întregii ierarhii
de cataloage accesibile unui utilizator. În acest moment există un singur proces, care
ulterior va deveni procesul fictiv 0 (însărcinat de obicei cu gestiunea memoriei
virtuale). Nucleul creează procesul init (prin încărcarea programului /etc/init),

- 71 -
Curs de SISTEME DE OPERARE
acesta este singurul proces care nu este creat prin mecanismul uzual de duplicare
(fork()). Controlul este preluat de init, care continuă activităţile legate de lansarea
sistemului. La sfârşitul acestei etape iniţializarea nucleului a luat sfârşit, sistemul
funcţionând normal, unicul proces utilizator prezent fiind init, care rulează cu
privilegiile superutilizatorului.
• init lansează un interpretor de comenzi (/bin/sh de exemplu), căruia îi revine
sarcina citirii şi executării fişierului de comenzi indirecte de configurare /etc/rc,
care conţine comenzi care asigură:
1. verificarea (cu ajutorul comenzii fsck) a corectitudinii sistemului de
fişiere rădăcină, şi eventual corectarea automată a eventualelor erori minore.
Dacă fsck eşuează, atunci un interpretor de comenzi este lansat cu intenţia ca
operatorul să refacă manual consistenţa sistemului de fişiere. În acest timp
sistemul lucrează şi operatorul primeşte privilegiile superutilizatorului;
2. montarea (cu ajutorul comenzii mount) a celorlalte discuri din
configuraţie;
3. iniţializarea altor echipamente periferice (interfeţe de reţea de
exemplu);
4. lansarea în fundal a unui număr de procese (demoni). De exemplu,
sistemele din familia BSD lansează demonul inetd, destinat lansării automate
a proceselor înregistrate ca servicii la distanţă (ftp, rlogin etc.).
• init creează (prin duplicare) câte un proces getty pentru fiecare terminal. Fiecare
getty aşteaptă introducerea unui nume de utilizator la terminalul respectiv, afişând
un prompt (de exemplu login:); această condiţie fiind satisfăcută, getty se
transformă în login, pentru a accepta utilizatorul în sistem.

Uneori ultimele două etape se desfăşoară în paralel; procesele lansate de init în ultima
etapă pot fi sau înscrise în codul binar al lui init, sau listate într-un fişier destinat acestui scop,
în general /etc/inittab. Toate aceste activităţi fiind îndeplinite, init (procesul 1) trece în
aşteptare. Din această stare el va ieşi temporar la semnalarea morţii unui interpretor de
comenzi principal.

Procesul login asociat terminalului în cauză, afişează un al doilea prompt (de exemplu
password:) şi aşteaptă introducerea unei parole. Parola citită este cifrată, apoi login caută
perechea (nume_utilizator, parolă_cifrată) în fişierul /etc/passwd. Dacă este găsită perechea
respectivă, atunci din acelaşi /etc/passwd se citesc :
• numărul de utilizator (uid-ul) utilizatorului şi numărul grupului (gid-ul) din care
face parte. login, care până acum rulase cu privilegiile superutilizatorului, îşi
schimbă apartenenţa, de acum înainte rulând sub uid-ul şi gid-ul utilizatorului în
curs de acceptare.
• catalogul de referinţă al utilizatorului. login schimbă catalogul curent în catalogul de
referinţă al utilizatorului. Dacă schimbarea nu reuşeşte, catalogul curent este stabilit
în catalogul rădăcină.
• numele interpretorului de comenzi (de exemplu /bin/sh sau /bin/bash).

login introduce în mediu o serie de valori (de exemplu HOME=catalogul de referinţă,


SHELL=numele absolut al interpretorului) şi se autoânlocuieşte (prin exec()) cu interpretorul
de comenzi al cărui nume a fost citit. La începerea execuţiei, interpretorul de comenzi
primeşte un prim argument (argumentul zero) începând cu un minus (de exemplu -sh în loc
de sh), pentru a afla astfel că este un interpretor de comenzi principal. Interpretorul de
comenzi efectuează o serie de iniţializări, apoi afişează un prompt (de exemplu $).
Introducerea unui caracter sfârşit de fişier (<CONTROL> D) sau a comenzii exit sau
logout determină terminarea execuţiei interpretorului de comenzi. Acest lucru poate avea loc
şi ca urmare a recepţionării de către interpretorul de comenzi a unui semnal imperativ de
oprire, emis fie ca urmare a începerii operaţiilor de oprire a sistemului (comanda shutdown),
- 72 -
Curs de SISTEME DE OPERARE
fie din iniţiativa superutilizatorului (prin comanda kill -KILL). Moartea interpretorului de
comenzi determină înregistrarea într-un fişier datele referitoare la sesiunea proaspăt încheiată,
trimite un semnal de rupere a legăturii către toate procesele legate de terminalul respectiv,
apoi lansează (prin duplicare) un nou getty pentru acel terminal, astfel o nouă sesiune de
lucru poate începe.
La oprirea S.O. trebuie terminate toate procesele active, înregistrate pe disc toate
informaţiile prezente în zonele tampon de scriere, demontate toate sistemele de fişiere şi
terminate toate comunicaţiile în reţea. Aceste activităţi sunt executate automat de comanda
shutdown, accesibilă numai superutilizatorului. Scoaterea calculatorului de sub tensiune fără
a da înainte comanda shutdown (sau fără a aştepta mesajul prin care sistemul anunţă că poate
fi scos de sub tensiune) produce întotdeauna o serie de erori în sistemul de fişiere. De multe
ori aceste inconsistenţe sau pot fi corectate automat, sau pot fi rezolvate manual (cu eventuale
pierderi de date), nu de puţine ori însă, sistemul de fişiere este grav compromis.

6.3 Principalele comenzi UNIX.

În general există patru categorii de comenzi :


• comenzi de informare
• comenzi de lucru cu fişiere
• comenzi de lucru cu procese
• comenzi speciale

1. Listarea conţinutului unui catalog :

ls [-optiuni] [catalog sau fisier]

Forma cea mai simplă : $ ls <CR>, $ fiind proptul pentru utilizatori, furnizează o listă
a numelor fişierelor şi cataloagelor din catalogul curent, mai puţin acele cataloage şi fişiere
ale căror nume începe cu caracterul '.', considerate invizibile (. şi .. în primul rând).
Opţiuni :

-l - obţinem informaţii mai detaliate despre fiecare intrare în catalog (long)

Ex: pentru catalogul rădăcină :


Nr. Proprie- Dimensi- Data Ora
Atribute Nume
legături tar une actualiz. actualiz.
drwxr-xr-x 2 root 1024 Apr 13 20:12 bin
drwxr-xr-x 2 root 1024 Feb 19 16:56 dev
... ... ... ... ... ... ...
-rwxr--r-- 1 root 402876 Aug 27 18:09 vmlinuz
Tabelul 6.3-1
Obs.: Pentru cataloage numărul de legături este minimum 2 (au şi legătură spre
părinte)
-a - determină afişarea şi a fişierelor ascunse, este folosită de obicei în conjuncţie cu
opţiunea l : $ ls -la sau $ ls -al
-i - face ca în faţa informaţiei de tip şi protecţie să mai apară o coloană cu numărul
nodurilor index ale fiecărui fişier.
-s - pentru a afişa lungimea fişierelor în Ko.
-R - listare recursivă.

- 73 -
Curs de SISTEME DE OPERARE
2. Copiere de fişiere.

cp fissrs ... fisdst

Efectul acestei comenzi este copierea fişierului sursă (unul) în fişierul destinaţie ($ cp
fis1 fis2) sau copierea mai multor fişiere sursă într-un catalog ($ cp fis1 fis2 fis3 dir)
În unele implementări se admite opţiunea -R pentru o copiere recursivă de cataloage.

3. Afişarea conţinutului unui fişier.

cat [-optiuni] fis ...

În mod normal această comandă afişează pe ecran conţinutul fişierelor.


Obs.: Pentru concatenarea fişierelor se poate folosi utilizând redirectarea ieşirii
standard : $ cat fis1 fis2 fis3 > fisconcatenat. În acest caz fişierele fis1, fis2 şi fis3 rămân
fişiere distincte.

4. Transferarea unui fişier dintr-un bloc în altul (echivalent rename din MS-DOS).

mv [-optiuni] fis1 ... dst

Obs.: La cp se alocă noi noduri index şi noi blocuri. La mv se schimbă doar intrarea
de catalog.

5. Ştergerea unui fişier.

rm [-optiuni] fis

Între opţiuni există şi opţiunea de ştergere recursivă -R. Comanda $ rm * este o


comandă periculoasă deoarece determină ştergerea tuturor fişierelor şi cataloagelor din
catalogul curent !
Obs.: Nu avem nevoie de *.* deoarece numele se tratează ca un tot unitar, fără
extensie. Cataloagele se şterg doar dacă ele sunt goale.
Ex: $ rm .* şterge toate fişierele care încep cu '.'.

6. Ştergerea cataloagelor.

rmdir catalog

Catalogul este şters doar dacă este gol.

Obs.: Există opţiuni care determină confirmarea acţiunii de către utilizator.

7. Schimbarea modului de protecţie al fişierului.

chmod protectie fis

Protecţia se specifică fie ca număr fie ca un şir de caractere.


Drepturile de protecţie se referă la trei categorii de utilizatori :
• proprietar
• grupul proprietar
• alţii
Drepturile posibile sunt (câte 3 biţi pe fiecare categorie) :
- 74 -
Curs de SISTEME DE OPERARE
• citire : r
• scriere : w
• execuţie : x

Ex:
$ chmod 644 fis.txt
- proprietarul fişierului va avea drepturile rw
- drepturile sunt specificate prin cifre octale, corespunzătoare celor 3 biţi care codifică
drepturile pe categorii.

$ chmod o+r fisier


- dăm altora (others) dreptul de citire (u - user, adică proprietar; g - group, adică
grupul proprietar)
- specificarea prin şir de caractere se poate face folosind + (adaugă dreptul), - (şterge
dreptul) sau = (specificare directă de valoare).

8. Schimbarea proprietarului unui fişier.

chown proprietar fisier

Dreptul de a executa această comandă îl are proprietarul curent al fişierului (care va


pierde astfel dreptul de proprietate) şi superutilizatorul.

9. Schimbarea catalogului curent.

cd catalog

10. Aflarea catalogului curent (Print Working Directory).

pwd

11. Crearea unui nou catalog.

mkdir catalog

12. Afişarea numelui utilizatorului curent.

whoami

- 75 -
Curs de SISTEME DE OPERARE
13. Listarea tuturor utilizatorilor care au momentan sesiuni deschise.

who

14. Obţinerea datei şi orei curente.

date

15. Afişarea spaţiului liber pe fiecare dintre discurile montate sau specifiacte.

df [fisier_special]

Ex: $ df /dev/fd0 - obţinem spaţiul liber pe discheta din prima unitate.

16. Montarea sistemului de fişiere.

mount [fisier_special catalog]

Ex: $ mount /dev/hda1 / - de obicei această comandă o face implicit sistemul.


$ mount /dev/hda3 /mnt - dicul /dev/hda3 va fi accesibil pe catalogul /mnt.

Obs.: mount fără nici un argument afişează lista sistemelor de fişiere montate precum
şi locul în care acestea au fost montate.

17. Demontarea sistemului de fişiere.

umount nume

nume poate fi numele fişierului special care conţine un subsistem de fişiere sau
catalogul unde a fost montat acel subsistem de fişiere.

18. Afişarea mărită a unui text pe ecran.

banner text

19. Afişarea calendarului.

cal [an] [luna]

20. Manualul de utilizare on line.

man comanda

Ex: $ man df

21. Afişarea pagină cu pagină a unui text.

more

- 76 -
Curs de SISTEME DE OPERARE
22. Un alt paginator mai puternic.

less

23. Poşta electronică.

mail nume

Ex:
$ mail xxxx
Subject : Hello
Textul mesajului
.
$
Obs.: Mesajul se termină cu caracterul '.' singur pe o linie, la începutul liniei.

24. Intrarea în sesiune.

login nume

Obs.: Dacă utilizatorul nume are parolă se va cere şi parola (Password :).

25. Ieşirea din sesiune.

logout

26. Schimbarea parolei unui utilizator.

passwd

6.4 Elemente ale limbajului de comandă UNIX (Shell).

Ori de câte ori se dă o comandă din linia de comandă, aceasta este preluată de un
program numit shell, care examinează linia şi decide care program trebuie rulat pentru a
executa comanda şi ce argumente îi vor fi transmise.
Procesorul de comenzi standard este sh (Bourne Shell). Alte procesoare de comenzi :
csh (C Shell), ksh (Khorn Shell), bash (Bourne Again Shell) etc.
Procesorul implicit pentru un utilizator este specificat în descrierea sistem a
utilizatorului, adică în fişierul /etc/passwd având următoarea structură :

Parola Nr. Utilizator Nr. Nume Directorul Procesor


Utilizato codificată Grup Complet HOME comenzi
r
john yH1!2tW3 324 55 John Barry /home/stud/john /bin/sh
Tabelul 6.4-1
Obs.: Câmpurile sunt separate prin ":".

Întotdeauna când shell-ul este gata pentru a primi o comandă, el afişează un prompt
care este de obicei de forma : "$ " sau "% ", dar poate fi orice expresie.
În general toate comenzile au ca efect execuţia unor programe. Programele pot fi
programe standard sau pot alte programe. Numele programului care va fi rulat este dat de
primul cuvânt din linia de comandă.
- 77 -
Curs de SISTEME DE OPERARE
Numele programului poate fi specificat cu tot cu cale (/bin/cat) sau doar simplu
numele, în acest caz shell-ul va căuta programul într-o serie de directoare, serie numită
PATH. Pe cele mai multe sisteme PATH-ul va consta din cel puţin trei directoare : directorul
curent("."), şi cele două directoare sistem /bin şi /usr/bin.

Înainte de a rula un program, shell-ul procesează linia de comandă pentru a determina


ce argumente să transmită programului. În cel mai simplu caz argumentele vor fi formate din
secvenţa de caractere diferite de spaţiu din linia de comandă.
Ex: $ ls -l myfile hisfile
În exemplul precedent argumentele vor fi cele trei şiruri : -l, myfile şi hisfile.
Există o serie de caractere care au o semnificaţie specială pentru shell, ele numindu-se
metacaractere şi în principiu ar fi următoarele : < > | ^ & ; ? * [ ] ( ) $ ` " ' \
Dacă dorim ca vreunul din aceste caractere să fie interpretat ca şi un caracter obişnuit
într-un anumit context, el va trebui precedat de caracterul \
Ex:
$ echo ? - vor fi listate numele fişierelor care au numele format dintr-un singur
caracter.
$ echo \? - va fi afişat ?

Acesta este mecanismul de quoting (citare).

Dacă dorim să transmitem ca parametru un şir de caractere în care să apară şi spaţiul


atunci putem să includem şirul de caractere între apostroafe sau ghilimele.

Caracterele "*", "?" şi "[...]" sunt caractere care se folosesc pentru construcţia unor
tipare. Când unul din aceste caractere este întâlnit în linia de comandă, este înlocuit cu o listă
de nume de fişiere care se potrivesc cu tiparul din care face parte, urmând următoarele reguli :
* este înlocuit de zero sau mai multe caractere.
? este înlocuit cu un singur caracter.
[sir] se potriveşte cu orice caracter care este prezent în sir. Se permit şi specificarea
unor intervale : [a-z].

Excepţie de la aceste reguli sunt caracterele "/" şi ".", care nu pot fi substituite prin
aceste caractere.

Şi în UNIX este posibilă redirectarea dispozitivelor standard (intrarea standard, ieşirea


standard şi ieşirea de eroare standard). Pentru redirectare se folosesc caracterele "<" şi ">".
Ex:
$ ls -l >listing - provoacă crearea unui fişier având ca şi conţinut ieşirea produsă de
comanda ls -l.
$ cat <myfile - determină comanda cat să citească din fişierul myfile şi să afişeze
liniile citite.

În plus faţă de aceste redirectări simple există şi alte tipuri de redirectare, dintre care
cele mai folosite sunt :
>> fisier - adaugă ieşirea standard la sfârşitul fişierului specificat (dacă fişierul
nu există acesta va creat).
<<terminator - foloseşte următoarele linii ca şi intrare standard, până când se
întâlneşte linia care se potriveşte cu tterminator-ul dat. Aceasta este
folosită în fişiere de comenzi (shell scripts).
2>fisier - redirectează ieşirea de eroare standard în fişierul specificat. Aceasta
este defapt un caz special al cazului general de redirectare a unui
identificator de fişier (stderr == 2).

- 78 -
Curs de SISTEME DE OPERARE
O altă formă de redirectare este folosirea canalelor (pipelines). Într-un canal două sau
mai multe programe sunt executate împreună, ieşirea unuia fiind intrarea următorului. În
multe implementări ale UNIX-ului, programele dintr-un canal sunt executate concurent (în
paralel). Principalul avantaj pentru utilizator este că acesta permite o notaţie concisă a unei
operaţii care pe alte sisteme ar necesita fişiere temporare.
Ex:
$ ls -l > fistemp1
$ grep *.c < fistemp1 > fistemp2
$ wc -l < fistemp2

Secvenţa de mai sus ar putea fi rescrisă folosind facilitatea de canal astfel :


$ ls -l | grep *.c | wc -l

Rezultatul ambelor grupuri de comenzi ar fi un număr precizând câte fişiere având


extensia .c există în catalogul curent.

După cum rezultă din exemplul de mai sus simbolul pentru crearea unui canal este o
bară verticală "|".
Motivul pentru care canalele sunt atât de folositoare este că există multe utilitare
UNIX, cunoscute ca fiind filtre, care primesc un şir la intrare, îl prelucrează şi transmit şirul
rezultat la ieşire (Astfel de utilitare sunt grep - caută apariţia unor expresii în diferite fişiere,
şi wc - care în principiu contorizează caracterele dintr-un fişier).

Din cele prezentate până acum a reieşit faptul că sistemul UNIX este capabil de a
executa concurent mai multe lucrări, el poate gestiona simultan mai mulţi utilizatori, poate
rula mai multe comenzi grupate în canale pentru un utilizator. În plus un utilizator poate rula
explicit mai multe comenzi independente în acelaşi timp. Aceasta este posibil datorită rulării
comenzilor în background, pentru a specifica execuţia în fundal a unei comenzi, aceasta va fi
urmată de caracterul "&". Dacă execuţia este în fundal, tastatura este imediat disponibilă
pentru introducerea unei noi comenzi.
Ex:
$ ls -l /usr/bin &
[524]
$

Numărul [Nr] este identificatorul de proces al comenzii date. Acest identificator poate
fi folosit pentru manipularea acestor procese, de exemplu pentru terminarea forţată a
proceselor.
Ex:
$ kill -9 524

Pentru comenzile care se execută în fundal, trebuie procedat în aşa fel încât ieşirea ,
respectiv intrarea lor să nu fie ecranul respectiv tastatura, pentru aceasta se pot folosi
redirectările.
Ex:
$ ls -l /usr/bin > lista &
[524]
$

În plus pentru manipularea proceselor există câteva utilitare care permit triumiterea
unui proces în background (bg), respectiv aducerea lor în foreground (fg). Pentru a vedea
situaţia proceselor există comanda ps (Process Status).

- 79 -
Curs de SISTEME DE OPERARE
Pentru a putea specifica mai mult de o comandă pe o singură linie acestea se pot
despărţi prin caracterul ";"
Ex:
$ ls -l > lista ; grep *.c < lista

Caracterul ";" forţează execuţia comenzilor într-o ordine secvenţială, adică pentru a se
executa o comandă, trebuie ca toate comenzile precedente să se fi terminat.
Pentru a grupa mai multe comenzi se pot folosi parantezele "(" şi ")".
De exemplu dacă s-ar fi dorit execuţia comenzilor precedente în background atunci :

$ ls -l > lista ; grep *.c < lista &

nu ar avea efectul dorit, deoarece doar cea de a doua comandă s-ar executa în background,
prima s-ar executa tot în foreground. Dar dacă comenzile ar fi grupate astfel :

$ (ls -l > lista ; grep *.c < lista)&

atunci am avea rezultatul dorit.

6.5 Proceduri shell (Shell scripts).

Shell-ul nu este doar un interpretor simplu de comenzi ci are totodată şi un limbaj de


programare în care pot fi scrise noi comenzi, prin folosirea comenzilor existente. Un fişier de
comenzi (shell script) este un fişier care conţine o secvenţă de comenzi ale shell-ului.

6.5.1 Crearea şi executarea unui fişier de comenzi.

Un script este creat prin crearea unui fişier conţinând secvenţa dorită de comenzi. El
poate fi executat în două moduri :
• Specificându-l explicit ca şi fişier de intrare pentru shell :

$ sh myscript
sau
$ sh < myscript

• Dând numele fişierului de comenzi exact ca orice altă comandă, în linia de comandă
a shell-ului :

$ myscript
Pentru ca cea de a doua metodă să funcţioneze trebuie ca utilizatorul să aibă dreptul de
execuţie asupra respectivului fişier. Aceasta se poate realiza prin comanda :

$ chmod u+x myscript

Când shell-ul încearcă să execute o comandă el mai întâi verifică dacă utilizatorul are
dreptul de a executa acea comandă, iar dacă da atunci verifică dacă comanda este un program
executabil sau un script (folosind primii doi bytes din fişier, aceşti doi bytes constituind ceea
ce se numeşte număr magic - magic number).

- 80 -
Curs de SISTEME DE OPERARE
6.5.2 Variabile şi parametri care pot fi prevăzuţi în pseudo-programe shell.

Shell-ul permite folosirea variabilelor pentru a manipula valori numerice şi şiruri. O


variabilă poate fi creată simplu atribuindu-i o valoare, iar dacă se foloseşte o variabilă înainte
de a i se atribui o valoare, atunci va avea ca şi valoare şirul vid. Atribuirile pot apărea în felul
următor :

$ NAME=chris
$ BIRTHDAY='December 1st'

În exemplul de mai sus s-au definit două variabile NAME şi BIRTHDAY la care li s-au
atribuit şirurile "chris" şi respectiv "December 1st". Pentru a folosi valoarea unei variabile
într-o linie de comană, numele variabilei va fi precedată de caracterul "$". Având cele două
atribuiri de mai sus următoarele două comenzi sunt echivalente :

$ echo $NAME was born on $BIRTHDAY


chris was born on December 1st
$ echo chris was born on December 1st
chris was born on December 1st

Shell-ul are mai multe variabile speciale, care sunt setate automat şi au un rol special :

HOME - indică calea către directorul utilizatorului. Acesta este directorul care
devine directorul curent când se dă comanda cd fără parametri.
PATH - conţine o serie de directoare separate prin caracterul ":", acestea sunt
locurile unde shell-ul va căuta după fişierele pentru comenzile care se
doresc a fi executate.
PS1 - conţine promptul shell-ului, de obicei setat iniţial la "$ "
PS2 - este promptul de continuare, dacă comanda se extinde pe mai mult de o
singură linie, iniţial "> ".
$ - este identificatorul de proces al shell-ului curent (nu se poate
schimba !).
! - este identificatorul de proces al comenzii lansate cel mai recent în
background.
? - este starea de ieşire a comenzii precedente.
1-9 - sunt argumentele cu care a fost apelat script-ul. De exemplu :

$ myscript chris staff 'December 1st'

"$1" va fi înlocuit prin "chris", "$2" prin "staff" şi "$3" prin "December
1st".
0 - este numele prin care a fost apelat script-ul. În exemplul precedent "$0"
ar fi înlocuit prin "myscript".
* - este înlocuit printr-o listă a tuturor argumentelor scriptului. Din
exemplul precedent "$*" ar fi înlocuit prin "chris staff December 1st".
# - reprezintă numărul de argumente din linia de comandă. În exemplul
precedent este 3.

O altă modalitate de a seta o variabilă shell este utilizând comanda shell read care
citeşte o linie de la intrarea standard şi o asignează unei variabile.
Ex:
echo "What program do you want to compile ?"
read NAME

- 81 -
Curs de SISTEME DE OPERARE
cc $NAME

6.5.3 Intrarea şi ieşirea standard.

Intrarea şi ieşirea standard al unui script sunt ca şi la celelalte comenzi moştenite de la


shell-ul care l-a lansat. Deci şi pentru scripturi funcţionează aceleaşi redirectări ca şi la
comenzile obişnuite. În plus în scripturi se pot face redirectări ale comenzilor din script.

6.5.4 Structuri de control în proceduri shell.

Ca şi majoritatea limbajelor de programare, shell-ul include construcţii pentru cicluri


şi execuţii condiţionate ale comenzilor. Construcţiile corespunzătoare ciclurilor sunt : for,
while şi until. Construcţiile condiţionale sunt : if şi case.

Construcţia if foloseşte starea de ieşire (exit state) a unei comenzi. Fiecare comandă
care se execută întoarce o valoare numerică când se termină. Convenţia este că o valoare de
ieşire de 0 corespunde unei terminări normale, pe când o valoare diferită de 0 corespunde
unei terminări anormale.
Formatul instrucţiunii if este :

if commanda
then
secventa de comenzi
elif comanda
then
secventa de comenzi
...
else
secventa de comenzi
fi

Dacă prima comandă se termină cu starea 0, atunci va fi executată prima secvenţă de


comenzi, iar restul nu va fi executat. Altfel comenda din partea de elif va fi executată şi dacă
starea de ieşire a acelei comenzi este 0 secvenţa de comenzi corespunzătoare va fi executată.
Partea de else va fi executată doar dacă toate testele anterioare întorc valori diferite de 0.
Parţile elif şi else sunt opţionale.

EXEMPLU :
Următorul script va verifica apariţia valorii din variabila NAME într+un fişier
numit namelist şi va afişa mesaje diferite depinzând de faptul dacă s-a găsit sau nu acea
valoare.

if grep -s $NAME namelist


then
echo $NAME is OK
else
echo I have never heard of $NAME
fi

- 82 -
Curs de SISTEME DE OPERARE
Următorul script exemplifică folosirea programului test, care poate fi folosit pentru
a testa existenţa unui fişier, dacă acesta poate fi citit sau scris, pentru verificarea egalităţii
a două şiruri sau dacă un număr este mai mare, egal sau mai mic decât un alt număr
ş.a.m.d. Pentru opţiunile permise de programul test se poate consulta manualul online.
Acest script va adăuga conţinutul fişierului messagefile la sfârşitul fişierului summarzfile
şi goleşte fişierul messagefile.

if test -r messagefile
then
if test -w summaryfile -a -w messagefile
then
cat messagefile >> summaryfile
cp /dev/null messagefile
else
echo Cannot write to sumary or message file
fi
else
echo Cannot read messagefile
fi

Construcţia case corespunde unei instrucţiuni de selecţie multiplă, asemănătoare cu


switch din C sau case din Pascal. Formatul acesteia este :

case word in
pattern11 | pattern12 | ... )
secventa de comenzi1
;;
pattern21 | pattern22 | ... )
secventa de comenzi2
...
esac

Valoarea specificată prin word este testată pe rând cu fiecare tipar şi la prima potrivire
se va executa secvenţa de comenzi corespunzătoare. Ca şi observaţie fiecare secvenţă de
comenzi este terminată prin două caractere punct şi virgulă (';'). Tiparele corespund tiparelor
utilizate de shell pentru nume de fişiere. Se pot specifica tipare alternative pentru aceeaşi
secvenţă de comenzi, acestea se vor separa prin caracterul bară verticală ('|').

EXEMPLU :
Următorul exemplu demonstrează folosirea tiparului '*' ca şi tipar implicit dacă
nici un alt tipar nu se potriveşte, precum şi două moduri diferite pentru a alege o opţiune
indiferent de litere mari sau mici.

case $OPTION in
-p|-P)
print $FILE
;;
-[nN])
nroff $FILE
;;
*)
echo Unknown option $OPTION
;;
esac
- 83 -
Curs de SISTEME DE OPERARE

Cicluri pot fi scrise utilizând un cilcu while (sau until) care execută repetat o
comandă, testează starea de ieşire a acesteia şi dacă este 0 (diferit de 0 pentru until) execută o
secvenţă de comenzi corespunzătoare. Când starea de ieşire devine diferită de 0 (0 pentru
until) shell+ul trece la intrucţiunea de după cilcu. Formatul celor două este :

while comanda
do
secventa de comenzi
done

until comanda
do
secventa de comenzi
done

EXEMPLU :
REPLAY=yes
while test $REPLAY = yes
do
echo "Here we go again !"
echo "Do you want to go round the loop another time ?"
read REPLY
done

Ultima construcţie folosită pentru cicluri este for care are următorul format :

for nume_de_variabila in lista_de_cuvinte


do
secventa de comenzi
done

Secvenţa de comenzi este executată odată pentru fiecare cuvânt din lista_de_cuvinte.
Variabila va lua valori succesive din lista de cuvinte. Lista de cuvinte poate fi generată
folosind tipare ca şi acelea folosite pentru nume de fişiere. Deasemenea lista de variabile
poate lipsi, caz în care ca şi listă de variabile va fi considerată lista de argumente cu care a
fost lansat shell-ul (adică $*).

EXEMPLU :
for file in *
do
echo $file
mv $file $file.old
done

for argument
do
cat $argument
done

- 84 -
Curs de SISTEME DE OPERARE
6.5.5 Comentarii.

În script-uri se pot include şi comentarii, prin folosirea comenzii ":", aceasta este o
comandă care nu face nimic, astfel încât argumentele sale pot fi folosite ca şi comentarii. Dar
argumentele acestei comenzi sunt prelucrate la fel ca şi argumentele altor comenzi, deci
trebuie să satisfacă aceeaşi sintaxă ca şi cele pentru comenzile normale. O formă sigură
pentru comentariu este ca acesta să fie pus între apostroafe.

EXEMPLU :
Următorul exemplu pe lângă exemplificarea comentariilor, introduce şi o nouă
comandă exit care determină terminarea execuţiei scriptului.

: 'are there too few arguments ?'


if test $# -lt 3
then
echo Not enough arguments
exit 1

: '... or too many ?'


elif test $# -gt 5
then
echo Too many arguments
exit 2
fi

6.5.6 Substituţia comenzilor.

Este posibilă construcţia unei părţi a unei comenzi prin executarea unei alte comenzi.
Dacă o comandă care este încadrată între caractere '`' (accente grave), este incorporată într-o
linie de comandă, atunci ieşirea sa standard, cu trecerile la linie nouă înlocuite prin spaţii, va
fi introdusă în locul acesteia în linia de comandă. Comanda din interiorul accentelor grave
poate fi orice comandă validă, chiar şi un canal (pipeline).
EXEMPLU :
Următoarea comandă va afişa o listă de fişiere din directorul curent în ordine
inversă.

$ echo `ls | sort -r`

6.5.7 Ordinea evenimentelor.

Pentru a înţelege script-uri complexe este necesar să ştim în ce ordine se fac


substituţiile de către shell.
1. Se fac substituţiile de parametri şi variabileş toate apariţiile lui "$"
urmat de un nume, cifră sau una dintre numele de variabile speciale ca şi "*" sunt
substituite, în afara situaţiei în care caracterul "$" se găseşte înconjurat de apostroafe
sau este precedat de "\".
2. Se fac substituţiile comenzilor, în afara faptului când accentul grav
apare înconjurat de apostroafe sau precedat de "\". Deci ambele substituţii
menţionate până acum au loc şi în interiorul unor şiruri înconjurate cu ghilimele,
acestea sunt diferenţele dintre şiruri înconjurate de apostroafe (single quotation) şi
şiruri înconjurate de ghilimele (double quotation).

- 85 -
Curs de SISTEME DE OPERARE
3. Se fac substituţiile numelor de fişiere ("*", "?" şi "[...]"), în afara
cayului când aceste caractere speciale sunt înconjurate cu apostroafe sau ghilimele
sau precedate de "\".

6.5.8 Opţiunile shell-ului.

Când shell-ul este lansat explicit prin comanda sh este posibilă transmiterea unor
opţiuni pentru controlarea execuţiei acesteia. Cele mai folositoare opţiuni sunt Ş
-e Determină terminarea execuţiei imediat ce o comandă din script dă greş.
-v Determină tipărirea fiecărei linii pe măsură ce este citită.
-x Tipăreşte comenzile pe măsură ce ele sunt executate.

Opţiunile pot fi setate şi din interiorul unui script folosind comanda set. De exemplu :

set -vx

va determina tipărirea fiecărei linii şi tipărirea comenzilor pe măsură ce ele sunt executate.

6.5.9 Redirectarea întreruperilor.

În unele script-uri poate fi utilă posibilitatea terminării "curate", în cazul în care se


întâmplă ceva neprevăzut, ca de exemplu apăsarea de către utilizator a tastei de întrerupere
sau de terminare. Comanda de redirecatre are forma :

trap actiune lista_de_evenimente

lista_de_evenimente este o listă de întregi, corespunzătoare semnalelor UNIX. actiune


este comanda care să fie executată când apare un semnal din lista de semnale. Dacă action
este un şir gol atunci evenimentele corespunzătoare sunt ignorate.

Cele mai folositoare semnale sunt :


0 Ieşire din shell.
2 Intrerupere de la terminal.
3 Semnal de terminare de la terminal.
15 S-a recepţionat semnalul Kill.

EXEMPLU :
Următorul exemplu demonstrează folosirea redirectării întreruperilor în cazul în
care la terminarea unui script este necesară ştergerea unui fişier temporar.

trap "echo Interruptedş exit" 2 3


trap "rm -tmp-tmpfile" 0

6.5.10 Ambianţa sau mediul de lucru.

În UNIX toate procesele posedă un mediu de lucru (ENVIRONEMENT), care este o


colecţie de nume care au asociate valori şir de caractere. Când shell-ul este pornit el îşi
încarcă ambianţa iniţială, care constă din variabilele shell-ului şi valorile iniţiale ale acestora.
Mai departe el transmite acest mediu, această ambianţă, către toate comenzile pe care le
execută. De obicei noile variabile create într-un shell nu sunt incluse implicit în ambianţă.

- 86 -
Curs de SISTEME DE OPERARE
Dacă se doreşte includerea de noi variabile în ambianţă aceasta se face explicit prin comanda
export.

$ export NAME BIRTHDAY

Comanda de mai sus adaugă variabilele NAME şi BIRTHDAY la ambianţa curentă a


shell-ului şi care va fi moştenită de orice comandă pe care acesta o va executa în continuare.
Schimbarea ambianţei unui shell sau a unui program nu va avea efect asupra ambianţei shell-
ului sau programului care a lansat în execuţie acel shell sau program.
Există însă un mod de a altera ambianţa unui shell dintr-un sub-program şi anume prin
folsirea comenzii eval. Această comandă pur şi simplu evaluează argumentele sale ca şi cum
ele ar fi constituit o linie de comandă a shell-ului. Astfel dacă un program name scrie, de
exemplu, "NAME=chris" la ieşirea standard, atunci comanda :

$ eval `name`

este echivalentă cu :

$ NAME=chris

având efectul setării variabilei de mediu NAME. Un alt mod este de a determina execuţia unui
script chiar de către shell-ul curent şi nu de către un sub-shell. Acest lucru se poate face
folosind comanda ".".

$ . setup

va avea efectul includerii în fluxul de intrare a fişierului setup.


În anumite cazuri se poate ca să se dorească ca valorile anumitor variabile să nu poată
fi modificate. Acest lucru poate fi făcut prin comanda readonly :

readonly NAME BIRTHDAY

această comandă va determina raportarea unei erori dacă se va încerca modificarea valorii
acestor variabile.

6.5.11 Lansarea shell-ului din login.

Când se intră pentru prima oară în lucru în UNIX, sistemul porneşte un shell. Acest
shell înainte de a citi de la terminal, va căuta în directorul de login după un fişier numit
.profile şi dacă acesta există va citi şi executa comenzile din el. Acest lucru permite definirea
unui ambient propriu. Un fişier .profile tipic va arăta astfel :

PATH=$HOME/bin:/etc:/bin:/usr/bin
USER=jim
MAIL=/usr/spool/mail/$USER
export PATH USER MAIL

: prevent other users from writing to the terminal


mesg n
: set default file creation mask
umask 027

echo People currently logged on:


- 87 -
Curs de SISTEME DE OPERARE
who

echo "Hello chris: nice to see you again"

6.5.12 Un exemplu de shell script.

EXEMPLU :
Script-ul cptree face o copie al unui întreg arbore de directoare, pe multe sisteme
acesta este disponibil ca şi un program standard, cu mult mai multe opţiuni, dar acest
script este destinat doar exemplificării unui script mai mare.

: Copy a directory hierarchy


: With optional argument '-v' echo filenames
if test "$1" = -v
then
ECHO=echo
VERBOSE=-v
shift
: 'replaces $1 by $2, $2 by $3, etc'
else
ECHO=:
fi
if test $# -ne 2
then
echo usage: cptree from to
exit 1
fi
: if first argument is a directory copy
: all files to second argument
if test -d $1
then
if test -f $2
then
: second argument exists, but is not a directory
echo cptree: $2 is not a directory
exit 1
elif test ! -d $2
: second argument does not exist - create it
then
$ECHO mkdir $2
: Will echo the command if -v was set,
: otherwise just a comment
mkdir $2
fi
for FILE in `ls -a $1`
: To make sure we get files beginning with "."
do
if test $FILE = . -o $FILE = ..
then
: ignore this directory and
: directory
continue

- 88 -
Curs de SISTEME DE OPERARE
fi
: call cptree recursively, passing on VERBOSE option
cptree $VERBOSE $1/$FILE $2/$FILE
done
elif test -r $1
then
: just copy a non-directory
$ECHO cp $1 $2
cp $1 $2
else
echo cptree: cannot read $1
exit 1
fi

- 89 -
Curs de SISTEME DE OPERARE

6.6 Vedere generală asupra sistemului.

6.6.1 Arhitectura sistemului UNIX.

Sistemul de operare interacţionează direct cu hard-ul, oferind anumite servicii


programelor, izolându-le în acelaşi timp de partea hard. Din acest motiv programele sunt uşor
portabile pe diferitele sisteme UNIX.

ALTE PROGRAME

UTILITARE

KERNEL

HARDWARE

DE APLICAŢIE

Fig. 6.6-1
Sistemul oferă anumite facilităţi printre care se află ţi sistemul de fişiere.
Sistemul de fişiere UNIX este caracterizat printr-o structură ierarhică, tratarea
consistentă a datelor din fişiere, posibilitatea de creare şi ştergere a fişierelor, posibilitatea
creşterii dinamice a dimensiunii fişierelor, protejarea datelor din fişiere, tratarea
dispozitivelor periferice ca şi fişiere.
/

bin etc dev usr home

… comenzi … … utilizatori …
fd0 fd1 tty0 hda

Fig. 6.6-2

EXEMPLU :
- 90 -
Curs de SISTEME DE OPERARE

char buffer[4096];

main(int argc, char *argv[])


{
int fdnew, fdold;

if (argc != 3)
{
printf(“Eroare !\n”);
exit(1);
}
fdold = open(argv[1], O_RDONLY);
if (fdold == -1)
{
printf(“Eroare !\n”);
exit(1);
}
fdnew = creat(argv[2], 0666);
if (fdnew == -1)
{
printf(“Eroare !\n”);
exit(1);
}
copy(fdold, fdnew);
exit(0);
}

copy(int fdold, int fdnew)


{
int count;
while ((count = read(fdold, buffer, sizeof(buffer))) > 0)
write(fdnew, buffer, count);
}

6.6.2 Mediul de procesare.

Un program este un fişier executabil iar un proces este o instanţă a unui program în
execuţie. Se pot executa mai multe procese simultan, nefiind impusă o limită asupra
numărului de procese existente în sistem.
Există mai multe apeluri sistem care permit proceselor crearea de noi procese,
terminarea unor procese, sincronizarea execuţiei unor procese şi efectuarea unor acţiuni la
apariţia unor evenimente.

EXEMPLU :

- 91 -
Curs de SISTEME DE OPERARE
Programul exemplifică folosirea apelului sistem fork - pentru crearea unui nou
proces, precum şi a apelurilor sistem exec - pentru lansarea în execuţie a unui alt program,
wait - pentru aşteptarea terminării unui proces fiu.

main(int argc, char *argv[])


{
int pid, wpid, status;

pid = fork();
if (pid == 0)
execl(“copy”, “copy”, argv[1], argv[2]);
wpid = wait(&status);
exit(0);
}

6.6.3 Servicii ale sistemelor de operare

Cele mai obşnuite servicii oferite de nucleul sistemului de operare sunt următoarele :
• controlul execuţiei proceselor, permiţând crearea, terminarea, suspendarea şi
comunicarea interproces.
• planificarea corectă a proceselor pentru execuţie. Procesele folosesc acelaşi
procesor, astfel dacă procesorul execută un proces, nucleul suspendă acest proces
atunci când cuanta de timp a acestuia expiră şi planifică un alt proces la execuţie.
• alocarea memoriei pentru execuţia proceselor. Nucleul permite proceselor să pună în
comun porţiuni din spaţiul lor de adrese în anumite condiţii, dar protejează spaţiul
de adrese privat împotriva acceselor nepermise, de asemenea dacă sistemul, la un
moment dat, are la dispoziţie prea puţină memorie liberă, acesta eliberează memorie
prin transferarea temporară a unui proces sau porţiuni ale acestuia în memoria
secundară, care de obicei este un disc şi care se numeşte disc de swap. Există
sisteme UNIX numite sisteme cu SWAPPING şi sisteme cu PAGING. Sistemele
cu SWAPPING transferă un întreg proces din memoria principală în memoria
secundară, iar sistemele cu PAGING transferă la un moment dat doar pagini de
memorie din memoria principală în memoria secundară.
• alocarea eficientă a memoriei secundare pentru depozitarea respectiv regăsirea
eficientă a datelor utilizatorilor. Acest serviciu reprezintă chiar sistemul de fişiere.
• acordarea accesului controlat proceselor la dispozitivele periferice, cum ar fi
terminale, discuri, discuri de reţea sau alte periferice.

6.6.4 Restricţii hardware.

Execuţia unui proces utilizator în sistemele UNIX este divizată pe două nivele :
• nivelul utilizator - user
• nivelul de nucleu - kernel

Când un proces execută un apel sistem, modul de execuţie al procesului se schimbă


din mod utilizator în mod nucleu (vom folosi pentru acest mod denumirea de mod kernel).
Chiar dacă utilizatorul nu face apel explicit la serviciile sistemului de operareacestea totuşi au
loc, deoarece pentru fiecare proces utilizator trebuiesc executete anumite operaţii ca şi
tratarea întreruperilor, planificarea proceselor la execuţie, gestionarea memoriei şi altele.

- 92 -
Curs de SISTEME DE OPERARE
Multe arhitecturi de calculatoare suportă mai multe nivele decât cele două menţionate, dar
cele două moduri sunt necesare şi suficiente pentru sistemele UNIX.

Diferenţele dintre aceste două moduri sunt :


• procesele în mod utilizator pot accesa instrucţiunile şi datele proprii, dar nu pot
accesa instrucţiunile şi datele nucleului sau ale altor procese.
• procesele în mod kernel pot accesa atât adresele utilizator cât şi adresele nucleului.
• unele instrucţiuni sunt instrucţiuni privilegiate şi execuţia lor în mod utilizator duce
la semnalarea unor erori.

6.6.5 Întreruperi şi excepţii.

Sistemele UNIX permit dispozitivelor să întrerupă procesorul în mod asincron. La


apariţia unei întreruperi, nucleul îşi salvează contextul curent, determină cauza întreruprii şi
tratează întreruperea. După tratarea întreruperii, nucleul reface contextul şi continuă procesul
întrerupt.
Hardware-ul de obicei alocă priorităţi unor întreruperi, corespunzător ordinii în care ar
trebui tratate acestea. Când nucleul tratează o întrerupere el inhibă toate întreruperile cu
prioritate egală sau mai redusă decât întreruperea curentă, astfel încât în timpul tratării pot să
fie tratate doar întreruperi cu un nivel mai mare de prioritate.
O condiţie de excepţie se referă la evenimentele cauzate de procese, ca şi adresarea
ilegală a unei zone de memorie, execuţia unei instrucţiuni privilegiate, împărţirea la 0 şi
altele. Aceste excepţii sunt diferite de întreruperi şi au loc în timpul execuţiei unei
instrucţiuni, sistemul încercând reluarea acelei instrucţiuni, după tratarea excepţiei.
Sistemul UNIX foloseşte acelaşi mecanism atât la tratarea întreruperilor cât şi la
tratarea excepţiilor.

6.6.6 Nivele de execuţie ale procesorului.

Nucleul, în anumite condiţii, trebuie să nu permită apariţia de întreruperi în timpul


unei activităţi critice, care ar avea ca rezultat structuri de date corupte.
Nivelele tipice de întreruperi sunt :
• întreruperi software
• întreruperi de la terminale
• întreruperi de la dispozitive de reţea
• întreruperi de la discuri
• întreruperi de la ceasul de timp real
• întreruperi datorate erorilor hardware

6.6.7 Gestionarea memoriei.

Nucleul se află întotdeauna în memorie. Când se compilează un program,


compilatorul generează un set de adrese pentru acel program care reprezintă adresele
variabilelor şi structurilor de date. Aceste adrese sunt generate pentru o maşină virtuală,
considerând că nici un alt program nu se va executa simultan pe aceeaşi maşină fizică. Când
programul este rulat pe o maşină, nucleul va aloca spaţiu în memoria principală, dar adresele

- 93 -
Curs de SISTEME DE OPERARE
virtuale generate de compilator nu este necesar să fie identice cu adresele fizice pe care le
ocupă acesta în maşină.
Nucleul în colaborare cu hardware-ul va iniţializa o translatera a adreselor virtuale în
adrese fizice, astefl încât adresele generate de compilator vor indica adresele fizice de pe
maşina respectivă.

- 94 -
Curs de SISTEME DE OPERARE

7. Arhitectura Sistemului de Operare UNIX

7.1 Introducere.

În UNIX există două concepte generale : fişierele şi procesele.

User programs
trap
User level
libraries

Kernel level system call interface

File
Subsystem
process interprocess
communication

buffer cache control


scheduler

subsystem memory
character block management
device drivers

Kernel level hardware control

Hardware level hardware

Fig. 7.1-1

Fig. 1 arată o diagramă bloc a nucleului, ilustrând modulele şi relaţiile dintre acestea.
În particular se reprezintă subsistemul de fişiere la stânga şi subsistemul pentru controlul
proceselor în dreapta. Figura scoate în evidenţă 3 nivele : utilizator, kernel şi hardware.
Apelurile sistem şi interfaţa oferită de librării reprezintă graniţa dintre nivelele
utilizator şi kernel. Figura partiţionează apelurile sistem în cele care interacţionează cu
subsistemul de fişiere şi cele care interacţionează cu subsistemul de control al proceselor.
Subsistemul de fişiere gestionează fişierele, alocând spaţiu acestora, administrând spaţiul
liber, compunând accesele la fişiere şi regăsind datele pentru utilizator.
Procesele interacţionează cu subsistemul de fişiere printr-un set specific de apeluri
sistem, ca şi open, close, read, write, stat(se citesc atributele unui fişier), chown, chmod.

- 95 -
Curs de SISTEME DE OPERARE
Subsistemul de fişiere accesează datele din fişiere folosind un mecanism cu buffere,
care reglează fluxul de date dintre nucleu şi suporturile externe.
Mecanismul cu buffere interacţionează cu driverele de I/E pe bloc pentru a iniţia
transferul de date spre sau dinspre nucleu. Driverele sunt module din nucleu care controlează
funcţionarea dispozitivelor periferice. De asemenea există şi drivere de I/E orientate pe
caractere pentru aşa numite raw devices.
Subsistemul de control al proceselor este responsabil pentru sincronizarea proceselor,
comunicarea interprocese, gestionarea memoriei şi planificarea proceselor.
Cele două subsisteme interacţionează între ele de exemplu la încărcarea unui program
pentru execuţie.
Câteva dintre apelurile sistem pentru controlul proceselor sunt : fork(crează un nou
proces), exec(suprascrie un proces cu imaginea unui program), wait(aşteaptă terminarea unui
proces fiu), brk(controlează dimensiunea memoriei alocate unui proces), signal(controlează
răspunsul unui proces la un semnal).
Modulul de gestiune a memoriei controlează alocarea memoriei. Dacă la un anumit
moment sistemul nu are destulă memorie fizică pentru toate procesele, nucleul va face
transfer între memoria principală şi cea secundară, astfel încât toate procesele să se poată
executa. Modulul planificator alocă CPU proceselor. El planifică un proces la execuţie, acesta
se execută până când renunţă voluntar la CPU, ca urmare a unei aşteptări sau până când îi
expiră cuanta de timp alocată, moment în care nucleul îi ia procesorul. Planificatorul în acest
caz va alege pentru execu_ie procesul disponibil cu cea mai mare prioritate. Procesul original
va fi planificat din nou la execuţie când va ajunge procesul disponibil cu cea mai mare
prioritate.
Există o varietate de forme de comunicare interproces, de la semnalarea asincronă a
unor evenimente şi până la transmiterea de mesaje între procese.
Modulul de control al hardware-ului este responsabil pentru tratarea întreruperilor şi
pentru comunicarea cu maşina. Dispozitive ca discuri sau terminale pot întrerupe procesorul
în timpul execuţiei unui proces. Nucleul poate relua execuţia procesului întrerupt după
tratarea întraruperii. Întreruperile nu sunt tratate de procese speciale ci de funcţii speciale din
nucleu, apelate în contextul procesului curent.

7.2 Subsistemul de fişiere.

Reprezentarea internă a unui fişier este dată printr-un aşa numit inode (index node),
care conţine o descriere a aşezării fizice a fişierului pe disc şi alte informaţii, ca de exemplu :
proprietarul, atributele de acces, momentul la care a fost accesat. Fiecare fişier are un singur
inode, dar poate avea mai multe nume care toate sunt mapate (indică) la acelaşi inode. Fiecare
nume este o aşa numită legătură (link). Când un proces crează un nou fişier, nucleul îi atribuie
un inode neutilizat. Inodurile sunt în compoziţia sistemului de fişiere fizic, dar nucleul le
citeşte în memorie pentru a avea un acces mai rapid la fişiere. Nucleul conţine în afara
tabelului de inoduri, două alte structuri de date : tabela de fişiere şi tabela de descriptori
pentru fişierele utilizator. Tabela de fişiere este o structură globală a nucleului, iar tabela de
descriptori ai fişierelor utilizator este împărţită pe procese.

- 96 -
Curs de SISTEME DE OPERARE

User File File Table Inode Table


Descriptor Table



Fig. 7.2-1

Când un proces deschide sau crează un fişier, nucleul alocă o intrare din fiecare tabelă
corespunzătoare inodului fişierului. Intrările din cele 3 structuri de date conţin informaţii
despre starea fişierului şi permit accesul la el. Tabela de fişiere de obicei con_ine
deplasamentul în fişier unde va avea loc următoarea operaţiune de citire sau scriere, de
asemenea mai conţine şi drepturile de acces admise procesului care a deschis fişierul. Tabela
de descriptori pentru fişierele utilizator conţine date despre toate fişierele deschise de un
anumit proces.
Un sistem de fişiere are următoarea structură :

boot super inode list data blocks


block block

Fig. 7.2-2
Blocul de boot - ocupă începutul unui sistem de fişiere, de obicei primul sector şi
poate con_ine codul de bootare necesar iniţializării sistemului de operare. Deşi doar un singur
bloc de boot este necesar pentru a iniţializa un sistem, toate sistemele de fişiere au un bloc de
boot.
Superblocul - descrie starea unui sistem de fişiere (dimensiune, câte fişiere poate
conţine, unde se găseşte spaţiul liber, etc.)
Lista de inoduri - urmează superblocului şi dimensiunea acestei liste se specifică la
configurarea sistemului. Nucleul face referire la inoduri prin indecşii acestora. Unul din
aceste inoduri este inodul rădacină al sistemului de fişiere, adică este inodul prin care
structura de directoare a sistemului de fişiere va fi accesibilă după execuţia apelului sistem
mount.
Blocurile de date - de obicei încep la sfârşitul listei de inoduri şi conţin datele
fişierelor şi date administrative. Un bloc alocat poate face parte dintr-un singur fişier din
sistemul de fişiere.
Inodul - se află pe disc şi sunt citite într-o zonă de memorie. Conţin câmpurile :
• identificatorul proprietarului - proprietatea unui fişier e divizată între un singur
proprietar şi grupul proprietar şi defineşte setul de utilizatori care au acces la fişiere.
Superutilizatorul are acces la orice fişier.
• tipul fişierului
• fişiere regulare
• fişiere directoare
• fişiere speciale pe caracter

- 97 -
Curs de SISTEME DE OPERARE
• fişiere speciale pe bloc
• fişiere FIFO
• atributele de acces - sistemul protejează fişierele faţă de 3 clase :
• proprietarul
• grupul proprietarului
• alţii
Fiecare clasă poate avea drepturi de acces pentru citire, scriere şi execuţie;
• timpii de acces la fişier:
• Timpul la care fişierul a fost ultima dată modificat.
• Timpul când a fost ultima oară accesat.
• Timpul când inodul a fost ultima dată modificat;
• numărul de legături la un fişier - reprezintă numărul de nume pe care un fişier le are
în ierarhia de directoare;
• tabela de adrese pentru blocurile de date din fişier;
• dimensiunea fişierului;

Pentru o mai mare flexibilitate, nucleul alocă spaţiu pentru un fişier doar câte un bloc
până când se ajunge la spaţiul necesar, permiţând astfel ca datele dintr-un fişier să fie
împrăştiate în sistemul de fişiere. Această schemă de alocare complică regăsirea datelor.
Tabela cu adresele blocurilor ar putea consta dintr-o listă de numere de blocuri
aparţinând fişierului, dar manipularea unei liste de blocuri este destul de greoaie (consumă
timp). Dacă un bloc logic conţine 1K atunci un fişier de 10K ar necesita un index având 10
numere de blocuri. Pentru a menţine structura unui inod destul de mică dar totuşi pentru a
permite şi fişiere de dimensiuni mari, tabela de adrese dintr-un inod are forma :
Inode Data Blocks
direct 0
1
2
3
4
5
6
7
8
direct 9
single indirect
double indirect
triple indirect

Fig. 7.2-3

- 98 -
Curs de SISTEME DE OPERARE

7.3 Subsistemul de procese

Un proces este defapt un program care se află în execuţie şi constă dintr-un şir de
octeţi pe care CPU-ul le interpretează ca şi instrucţiuni maşină (numit “text”), date şi stivă.
Mai multe procese sunt executate simultan, pe unele sisteme doar în aparenţă, ordinea
execuţiei depinzând de planificarea acestora de către kernel. Deasemenea mai multe procese
pot fi instanţe ale unui acelaşi program.
Un proces se execută urmând o secvenţă strictă de instrucţiuni, conţinută în proces, şi
nu face salturi la secvenţele altor procese. El îşi citeşte şi scrie secţiunile de date şi stivă, dar
nu poate citi sau scrie datele şi stiva altui proces. Procesele comunică cu alte procese şi cu
lumea înconjurătoare doar prin apeluri sistem.
Practic un proces pe un sistem UNIX este o entitate care este creată prin apelul sistem
fork. Orice proces, cu excepţia procesului 0 este creat când un alt proces execută acest apel
sistem. Procesul care a apelat fork se numeşte proces părinte, iar procesul nou creat se
numeşte procesul fiu. Fiecare proces are doar un singur proces părinte, dar poate avea mai
multe procese fii. Nucleul identifică fiecare proces prin numărul de proces al acestuia, numit
identificator de proces (PID).
Procesul 0 este un proces special care este creat când se iniţializează sistemul, după ce
lansează un proces fiu (procesul 1), procesul 0 devine procesul swapper (cel care gestionează
memoria virtuală). Procesul 1, cunoscut sub numele de init este "strămoşul" tuturor
proceselor din sistem.
Nucleul, ca urmare a unui apel exec încarcă un fişier executabil în memorie. Procesul
încărcat constă din cel puţin trei părţi, numite regiuni : text, date şi stivă. Regiunile text şi
date corespund secţiunilor de text şi dată ale fişierului executabil, dar regiunea de stivă este
creată automat şi dimensiunea acestuia este ajustată dinamic în timpul execuţiei.
Deoarece în sistemul UNIX un proces poate rula în două moduri, kernel sau utilizator,
se folosesc stive separate pentru fiecare mod. Stiva utilizator conţine argumentele, variabilele
locale şi alte date necesare funcţiilor care se execută în mod utilizator. Stiva kernel conţine
datele necesare pentru funcţiile care se execută în mod kernel. Funcţiile şi datele din această
stivă se referă la funcţii şi date din kernel şi nu din programul utilizator. Stiva kernel a unui
proces este vidă când un proces rulează în mod utilizator.
Fiecare proces are alocată o intrare în tabela de procese a nucleului, deasemenea
fiecărui proces îi este alocată o zonă utilizator (u area), care conţine date private manipulate
doar de nucleu. Tabela de procese conţine un pointer spre o tabelă de regiuni ale procesului
care conţine intrări care indică într-o altă tabelă de regiuni, globală, care la rândul lui indică
adresele fizice din memoria fizică, corespunzătoare acelor regiuni. O regiune este o zonă
continuă a spaţiului de adresare a unui proces (de ex. text, date şi stivă). Intrările în tabelele
de regiuni descriu atributele regiunii, adică faptul că această regiune conţine text sau date,
dacă este partajată sau privată şi dacă datele din regiune sunt în memoria principală sau nu.
Cel de-al doilea nivel de indirectare (tabela de regiuni ale unui proces), permite proceselor
independente să partajeze regiuni. Când un proces execută apelul sistem exec, nucleul alocă
regiuni pentru text, date şi stivă după ce a eliberat vechile regiuni ale procesului.
Când un proces executâ apelul fork, nucleul duplică spaţiul de adrese al vechiului
proces, permitând proceselor să partajeze regiunile când este posibil, altfel făcând o copie
fizică. Când un proces apelează exit, nucleul desalocă regiunile pe care procesul le-a deţinut.

- 99 -
Curs de SISTEME DE OPERARE
Figura următoare ilustrează structurile de date relevante ale unui proces aflat în
execuţie:

per process
region table
u area region table

process table
main memory

Fig. 7.3-1

Intrarea în tabela de procese şi zona numită u area conţin informaţii de control şi stare
despre procese. Zona u area este o extensie a intrării în tabela de procese.
Câmpurile cele mai importante din tabela de procese sunt :
• câmp de stare, indicând starea procesului.
• intrarea în tabela de procese conţine câmpuri care permit nucleului localizarea
procesului şi a zonei sale u area, din memoria principală sau secundară. Nucleul
foloseşte aceste informaţii pentru a putea face schimbarea de context, la acel proces,
când acesta trece din starea Ready to Run In Memory în starea Kernel Running sau
din starea Preempted în starea User Running. În plus, nucleul mai foloseşte aceste
informaţii când transferă procesele din sau în memoria principală. Intrarea de tabelă
mai conţine un câmp care dă dimensiunea procesului, astfel încât nucleul să ştie cât
spaţiu să aloce procesului.
• identificatori de utilizatori (UID-uri), care determină diferitele privilegii ale
proceselor. De exemplu, aceste câmpuri determină setul de procese care pot trimite
semnale unele la altele.
• identificatori de procese (PID-uri), care specifică relaţiile dintre diferite procese.
Aceste câmpuri sunt iniţializate când procesul intră în starea Created, în apelul
sistem fork.
• un descriptor de evenimente, setat când procesul este suspendat (starea de somn -
sleep).
• parametrii pentru planificatorul de procese, permit nucleului să determine ordinea în
care procesele trec în stările Kernel Running şi User Running.
• un câmp pentru semnale, care enumeră semnalele trimise procesului ăi care nu au
fost încă tratate.
• mai mulţi contori pentru păstrarea timpului de execuţie, utilizarea resurselor ăi
altele, folosite pentru gestionarea ăi calcularea priorităţii de planificare. Există şi un
câmp contor setabil de utilizator, folosit la transmiterea unui proces a unui semnal
de alarmă.

Zona u area conţine informaţii care descriu procesul, dar care trebuie să fie accesibile
doar când procesul se execută efectiv. Câmpurile cele mai importante sunt :
- 100 -
Curs de SISTEME DE OPERARE
• un pointer către intrarea în tabela de procese a procesului.
• identificatorii reali şi efectivi de utilizator, care determină diferite privilegii acordate
procesului, cum ar fi drepturile de acces la fişiere.
• câmpuri contor care memorează timpul cât procesul şi descendenţii săi au petrecut
în mod utilizator respectiv în mod kernel.
• un tablou indicând modul de răspuns al procesului la diferite semnale.
• un câmp care identifică "terminalul de login" asociat cu procesul (dacă acesta
există).
• câmp de eroare care reţine codurile de eroare dintimpul unui apel sistem.
• câmp care reţine valoarea de întoarcere dintr-un apel sistem.
• parametri de I/O care descriu cantitatea de date care urmează să fie transferate,
adresa tabloului sursă (sau destinaţie), din spaţiul utilizator, deplasamente de fişiere
şi altele.
• directorul curent şi rădăcina curentă, descriu ambianţa sistemului de fişiere pentru
respectivul proces.
• tabela descriptorilor de fişier pentru fiecare fişier deschis.
• dimensiunile maxime ale unui proces şi a fişierelor pe care poate să le creeze.
• un câmp de mască de moduri de acces pentru fişierele care vor fi create de proces.

Implementarea oferă nucleului o cale uşoară de identificare a procesului curent, prin


folosirea pointerului din zona u spre intrarea în tabela de procese.

7.3.1 Contextul unui proces

Contextul unui proces este starea acestuia, cum este ea definita de textul său, de
valorile variabilelor utilizator globale şi structurilor de date, de valorile regiştrilor pe care le
foloseste, de valorile din zona din tabela de procese care îi corespunde precum şi din zona u
area şi conţinutul stivelor utilizator şi kernel. "Text"-ul sistemului de operare şi structurile
sale de date globale, sunt partajate de toate procesele, dar ele nu constituie parte a contextului
unui proces.
Când execută un proces, sistemul se spune că ruleaza în contextul procesului. Când
nucleul decide că ar trebui să execute un alt proces, el face o aşa numită schimbare de context
(context switch), astfel încât sistemul va rula în contextul noului proces. Nucleul permite o
schimbare de context doar în anumite condiţii specifice. Când face o schimbare de context,
nucleul salvează informaţie suficientă pentru a putea relua mai târziu procesul întrerurpt. La
fel când se trece din modul utilizator în kernel, nucleul salvează suficientă informaţie, astfel
încât mai tarziu să se poată întoarce în modul utilizator şi să continue execuţia din punctul în
care a rămas. Schimbarea modului de execuţie din mod utilizator în kernel şi invers NU este o
schimbare de context.
Nucleul tratează întreruperile în contextul procesului întrerupt, chiar dacă acesta nu a
provocat apariţia întreruperii. Procesul întrerupt se putea găsi sau în mod utilizator sau în mod
kernel. Nucleul salvează suficientă informaţie astfel încât mai târziu să poată relua execuţia
procesului întrerupt, după care tratează întreruperea în mod kernel. Nucleul nu lansează sau
planifică un proces special pentru tratarea întreruperilor.

- 101 -
Curs de SISTEME DE OPERARE

7.3.2 Stările şi tranziţiile unui proces

Timpul de viată al unui proces poate fi divizat într-un set de stări care descriu
procesul. Un proces poate fi într-una din următoarele stări :
1) Procesul rulează în mod utilizator.
2) Procesul rulează în mod kernel.
3) Procesul nu rulează dar este pregătit pentru execuţie (atunci când nucleul îl
planifică).
4) Procesul este adormit şi se află în memoria principală.
5) Procesul este gata de execuţie, dar swapper-ul trebuie să îl aducă în memoria
principală înainte ca nucleul să îl poată planifica pentru execuţie.
6) Procesul este adormit, şi swapper-ul a eliberat memoria ocupată de el transferându-
l în memoria secundară.
7) Procesul este pe cale de a se întoarce din modul kernel în mod utilizator, dar
nucleul îl suspendă şi face o schimbare de context pentru a planifica un alt proces
la execuţie.
8) Procesul este nou creat şi este într-o stare de tranziţie, procesul există, dar încă nu
este gata de rulare, dar nu este nici adormit. Această stare este starea de start pentru
toate procesele în afară de procesul 0.
9) Procesul a executat apelul de sistem exit şi este într-o stare de tranziţie. Procesul nu
mai există, dar lasă o înregistrare conţinând codul de ieşire şi câteva statistici
privitoare la timpii alocaţi pentru a fi colectate de procesul părinte. Această stare,
cunoscută şi sub numele de starea zombie, este starea finala a unui proces.

- 102 -
Curs de SISTEME DE OPERARE
Următoarea diagramă prezintă diagrama de tranziţii dintre stările unui proces :

User Running

sys call,
return
interrupt return
to user
interrupt,
interrupt return
Kernel
2 Running

exit preempt
9 7
reschedule
Zombie sleep process Preempted

Asleep Ready to Run


In Memory 4 3 In Memory
wakeup
enough memory

fork
swap swap swap 8
out out in
Created
not enough memory
(swapping systemonly)
6 5
wakeup
Sleep, Swapped Ready to Run, Swapped

Fig. 7.3-2

Pentru exemplificarea acestei diagrame vom considera evoluţia unui proces tipic.
Starea iniţială a procesului este starea Created, când procesul părinte execută apelul sistem
fork şi eventual trece într-o stare în care este pregătit pentru execuţie (3 sau 5). Pentru
simplificare, presupunem că procesul intră în starea Ready to Run In Memory. Planificatorul
de procese eventual va alege procesul pentru execuţie, astfel acesta intră în starea Kernel
Running, unde îşi termină partea sa din apelul sistem fork.
Când procesul îşi termină apelul sistem, poate să treacă în starea User Running, unde
se va executa în mod utilizator. După o perioadă de timp, ceasul sistem ar putea întrerupe
procesorul şi procesul intră din nou în starea Kernel Running. După tratarea întreruperii de
ceas, nucleul poate decide planificarea spre execuţie a unui alt proces, astfel încât procesul
curent intră în starea Preempted şi în continuare va fi executat celălalt proces. Starea
Preempted este defapt echivalentă cu starea Ready to Run In Memory, dar cele două stări au
fost separate pentru a arăta că unui proces, care rulează în mod kernel, îi poate fi luat

- 103 -
Curs de SISTEME DE OPERARE
procesorul doar în momentul în care acesta este pe punctul de a se întoarce în mod utilizator.
În continuare nucleul ar putea transfera procesul din starea Preempted în memoria secundară
în starea Ready to Run Swapped, dacă este necesar. Eventual planificatorul l-ar putea alege
spre execuţie, astfel procesul întorcându-se în starea User Running, deci executându-se din
nou în starea utilizator.
Când un proces execută un apel sistem, el părăseşte starea User Running şi intră în
starea Kernel Running. Presupunând că apelul sistem necesită operaţii de I/O cu discul,
procesul este nevoit să aştepte pentru terminarea acestor operaţii. El va intra în starea Asleep
In Memory, punându-se într-o stare de aşteptare până când este înştiinţat de faptul că operaţia
de I/O s-a terminat. Când operaţia de I/O s-a terminat, hard-ul întrerupe CPU-ul şi rutina de
tratare a întreruperii v-a trezi procesul, determinându-l să intre în starea Ready to Run In
Memory.
Presupunând că sistemul execută mai multe procese care nu încap toate deodată în
memoria principală ţi swapper-ul transferă din memorie procesul, pentru a face loc altui
proces care se află în starea Ready to Run Swapped. Când un proces este transferat din
memoria principală în memoria secundară, el trece în starea Ready to Run Swapped.
Eventual swapper-ul alege procesul ca fiind cel mai potrivit pentru a fi adus în memoria
principală, astfel procesul va intra din nou în starea Ready to Run In Memory. Planificatorul
eventual va alege spre rulare procesul, el intrând în starea Kernel Running şi va continua
execuţia. Când un proce se termină, el apelează apelul sistem exit, astfel intrând în starea
Kernel Running şi în final în starea Zombie.
Un proces, la nivelul utilizatorului, are control doar asupra câtorva dintre stările de
tranziţie. În primul rând un proces poate crea un alt proces. Dar starea în care ajunge din
starea Created depinde în întregime de nucleu, procesul neavând controlul asupra acelor
tranziţii. În al doilea rând, un proces poate executa apeluri sistem pentru a trece din starea
User Running în starea Kernel Running şi astfel intrând din proprie iniţiativă în nucleu.
Procesul nu are controlul asupra tranziţiilor la întoarcere din apelul sistem, evenimentele
putând determina intrarea acestuia în starea Zombie. În final, un proces se poate termina de
bună voie, executând apelul sistem exit. Toate celelalte tranziţii de stare urmăresc un algoritm
înscris în nucleu, reacţionând la evenimente într-un fel predictibil.

- 104 -
Curs de SISTEME DE OPERARE

7.4 Controlul proceselor

Pentru controlul proceselor există câteva apeluri sistem, care vor fi explicate în
continuare. În următoarele rânduri se prezintă succint apelurile sistem implicate în controlul
proceselor, urmând ca ele să fie detaliate pe parcurs. Apelul sistem fork crează un nou proxes,
apelul exit termină execuţia unui proces şi apelul wait permite procesului părinte să-şi
sincronizeze execuţia cu terminarea unui proces fiu. Semnalele informează procesele despre
evenimente asincrone. Apelul sistem exec permite proceselor lansarea în execuţie a unui nou
program, suprascriindu-se spaţiul de adrese al procesului cu imaginea executabilă a unui
fişier. Apelul sistem brk permite unui proces să aloce mai dinamic mai multă memorie,
similar, sistemul permite şi creşterea dinamică a stivei utilizator.

7.4.1 Crearea proceselor.

Unicul mod în care se pot crea procese noi în sistemul de operare UNIX este prin
apelul sistem fork. Procesul care a apelat fork este numit proces părinte, iar procesul nou
creat este numit proces fiu. Sintaxa pentru apelul sistem fork este :

pid = fork();

La întoarcerea din apelul sistem fork, cele două procese au o copie identică a
contextului lor de nivel utilizator, cu excepţia valorii de întoarcere pid. În procesul părinte,
pid va avea valoarea corespunzătoare identificatorului procesului fiu, în procesul fiu, pid va
avea valoarea 0.

Nucleul face următoarea secvenţă de acţiuni pentru fork :


1. Alocă o intrare în tabela de procese noului proces.
2. Alocă un identificator unic pentru noul proces.
3. Face o copie logică a contextului procesului părinte. Deoarece unele porţinui ale
unui proces, ca şi regiunile de text, pot fi partajate între procese, nucleul câteodată
poate să incrementeze contorul de referinţă la o regiune în loc să îl copieze într-un
nou loc în memoria fizică.
4. Incrementează contorii din tabelele de fişiere şi inoduri, pentru fişierele asociate
procesului.
5. Întoarce identificatorul procesului fiu, procesului părinte, şi valoarea 0, procesului
fiu.

- 105 -
Curs de SISTEME DE OPERARE
În următoarea figură se ilustrează într-un mod grafic modul de creare a unui proces
fiu.
Parent Process File
Table
Per Process U Area
Parent Region Table Open Files
Data Current Directory
Changed Root
Parent
User
Stack Kernel Stack

Shared
Inode
Text
Table

Per Process U Area


Child Region Table Open Files
Data Current Directory
Changed Root
Child
User
Stack Kernel Stack

Child Process

Fig. 7.4-1

EXEMPLU :
Următorul program este un exemplu în care se ilustrează modul de partajare a
accesului la fişiere după apelul sistem fork. Programul se apelează cu doi parametri,
numele unui fişier existent şi numele unui nou fişier care va fi creat. Procesul deschide
fişierul existent, crează noul fişier şi crează un proces fiu prin apelul lui fork. Intern,
nucleul efecuează copierea contextului procesului părinte pentru procesul fiu. Fiecare
proces se execută în spaţii de adresare diferite şi fiecare poate accesa copiile private ale
variabilelor globale fdrd, fdwt şi c şi copiile private ale variabilelor de pe stivă argc şi argv,
dar niciuna nu poate accesa variabilele altui proces. Deoarece nucleul a copiat zona u area
a procesului original, procesului fiu, în timpul apelului fork, procesul fiu moşteneşte
accesul la fişierele părintelui (doar la acelea care erau deschise în momentul apelului
fork), folosind aceeaşi descriptori de fişiere.
Procesele părinte şi fiu apelează funcţia rdwrt, independent, şi execută un ciclu,
citind un bzte din fişierul sursă şi scriind-ul în fişierul destinaţie. Funcţia rdwrt se termină
când apelul sistem read întâlneşte sfârşitul de fişier. Nucleul a incrementat contorul din
tabela de fişiere a fişierelor sursă şi destinaţie şi descriptorii de fişiere din ambele procese
se referă la aceleaşi intrări în tabela de fişiere. Adică descriptorii fdrd pentru ambele
procese se referă la intrarea din tabela de fişiere pentru fişierul sursă, iar descriptorii fdwr
pentru ambele procese se referă la intrarea din tabela de fişiere pentru fişierul destinaţie.

- 106 -
Curs de SISTEME DE OPERARE
Astfel, cele două procese nu vor citi sau scrie niciodată la aceleaşi valori de deplasament în
cadrul fişierelor, deoarece nucleul incrementează deplasamentul după fiecare citire
respectiv scriere. Deşi procesele aparent copiază fişierul sursă cu viteză dublă, deoarece îşi
împart lucrul, conţinutul fişierului destinaţie va depinde de ordinea în care nucleul
planifică spre execuţie procesele. Dacă planifică procesele astfel încât ei alternează
execuţia apelurilor sistem, sau chiar dacă alternează execuţia perechilor de apeluri sistem
read-write, conţinutul fişierului destinaţie ar fi identic cu cel al fişierului sursă. Dar se
poate considera şi următorul scenariu, unde procesele sunt pe cale să citească următoarea
secvenţă de două caractere "ab" din fişierul sursă. Presupunem că procesul părinte citeşte
caracterul 'a' şi nucleul face o schimbare de context pentru a executa procesul fiu, înainte
ca procesul părinte să poată scrie ce a citit. Dacă procesul fiu citeşte caracterul 'b' şi apucă
să îl şi scrie în fişierul destinaţie înainte ca procesul părinte să fi fost replanificat, atunci
fişierul destinaţie nu va conţine secvenţa "ab" ci secvenţa "ba". Nucleul nu garantează
rata relativă de execuţie a proceselor.

#include <fcntl.h>

int fdrd, fdwt;


char c;

main(int argc, char *argv[])


{
if (argc != 3)
exit(1);
if ((fdrd = open(argv[1], O_RDONLY)) == -1)
exit(1);
if ((fdwt = creat(argv[2], 0666)) == -1)
exit(1);

fork();
/* ambele procese execută acelaşi cod */
rdwrt();
exit(0);
}

rdwrt()
{
for(;;)
{
if (read(fdrd, &c, 1) != 1)
return;
write(fdwt, &c, 1);
}
}

- 107 -
Curs de SISTEME DE OPERARE

7.4.2 Semnale.

Semnalele informează procesele despre apariţia unor evenimente asincrone. Procesele


pot să-şi transmită unele altora semnale cu ajutorul apelului sistem kill, sau nucleul poate
transmite semnale intern.
Semnalele în general pot fi clasificate astfel :
• Semnale care au de a face cu terminarea unui proces, trimise când un proces se
termină sau când un proces execută apelul sistem signal cu parametrul death of
child (distrugerea unui proces fiu);
• Semnale care au de a face cu excepţiile produse de procese, cum ar fi accesarea de
către un proces a unei adrese din afara spaţiului său virtual de adresare, când
încearcă să scrie într-o zonă marcată ca read-only, sau când execută o instrucţiune
privilegiată sau pentru diferite erori hard;
• Semnale ce au de a face cu situaţii nerecuperabile din timpul unui apel sistem, cum
ar fi consumarea tuturor resurselor sistemului în timpul unui apel exec, după ce
spaţiul de adresare original a fost eliberat;
• Semnale cauzate de o eroare neaşteptată în timpul unui apel sistem, cum ar fi apelul
la o funcţie sistem inexistentă, scrierea într-o conductă care nu are un proces care să
citească din ea sau utilizarea unor valori de referinţă ilegale pentru apelul sistem
lseek;
• Semnale care provin de la un proces în mod utilizator, cum ar fi când un proces ar
dori să recepţioneze un semnal de alarmă, după o anumită perioadă sau când
procesele trimit semnale arbitrare unele altora cu ajutorul apelului sistem kill;
• Semnale care ţin de interacţionarea cu un terminal, cum ar fi întreruperea unui
terminal de către un utilizator sau când un utilizator apasă tasta "break" sau "delete";
• Semnale pentru urmărirea execuţiei unui proces.

Nucleul testează apariţia semnalelor când un proces este pe cale să revină din mod
kernel în mod utilizator şi când intră sau părăseşte starea de asleep (adormit). Nucleul tratează
semnalele doar când un proces revine din mod kernel în mod utilizator. Astfel, un semnal nu
are un efect instantaneu asupra unui proces care rulează în mod kernel. Dacă un proces
rulează în mod utilizator şi nucleul tratează o întrerupere care determină transmiterea unui
semnal procesului, nucleul va recunoaşte şi va trata semnalul când se întoarce din tratarea
întreruperii. Deci, un proces nu va executa niciodată în mod utilizator înainte de a trata
semnalele rămase.

7.4.2.1 Tratarea semnalelor.

Nucleul tratează semnalele în contextul procesului care le-a recepţionat, deci un


proces trebuie să se execute pentru a putea trata semnale. Există trei cazuri pentru tratarea
semnalelor: procesul se termină la recepţionarea unui semnal, ignoră semnalul sau execută o
funcţie particulară (utilizator) la recepţionarea unui semnal. Acţiunea implicită este apelul lui
exit în mod kernel, dar un proces poate specifica execuţia unor acţiuni speciale la
recepţionarea unor anumite semnale, cu ajutorul apelului sistem signal.
Sintaxa pentru apelul sistem signal este :

oldfunction = signal(signum, function);

- 108 -
Curs de SISTEME DE OPERARE
unde signum este numărul semnalului pentru care procesul specifică o anumită acţiune,
function este adresa funcţiei utilizator pe care procesul vrea să îl execute la recepţionarea unui
astfel de semnal, iar valoarea de întoarcere oldfunction era valoarea lui function din cel mai
recent apel al lui signal pentru semnalul signum. Procesul în locul adresei funcţiei poate
transmite valorile 1 sau 0. Dacă parametrul este 1, atunci procesul va ignora apariţiile
ulterioare ale semnalului, iar dacă este 0, procesul va ieşi prin nucleu la apariţia acelui semnal
(comportarea implicită).
Când un proces primeşte un semnal pe care a decis să îl ignore (printr-un apel anterior
la signal cu parametrul, corespunzător adresei, 1), el îşi cpntinuă execuţia ca şi când nu s-ar fi
întâmplat nimic. Deoarece nucleul nu resetează câmpul din zona u area care indică faptul că
semnalul este ignorat, procesul va ignora semnalul ori de câte ori va mai apărea. Dacă un
proces primeşte un semnal pe care a decis să îl trateze, el va executa funcţia specificată
imediat când se reîntoarce în mod utilizator, după ce nucleul face următorii paşi :
1. Nucleul accesează contextul utilizator salvat, pentru găsirea valorii regiştrilor
program counter şi stack pointer, pe care le-a salvat pentru întoarcerea la procesul
utilizator.
2. Şterge câmpul care indică rutina de tratare a semnalului din zona u area, setându-l
pe valoarea implicită.
3. Nucleul crează pe stiva utilizator un pseudo-apel la funcţia specificată, prin
punerea în stivă a regiştrilor corespunzători, acest pseudo-apel va fi simulat în
locul unde a fost făcut apelul sistem sau a apărut întreruperea.
4. Nucleul schimbă contextul salvat astfel : el resetează valoarea program counter-
ului astfel încât să indice adresa funcţiei specificate şi setează valoarea stack
pointer-ului astfel încât să reflecte creşterea corespunzătoare a stivei utilizator.

Deci după întoarcerea din mod kernel în mod utilizator, procesul va executa funcţia de
tratare a semnalului; iar la întoarcerea din acea funcţie se va întoarce în locul în care a fost
executat apelul sistem sau s-a recepţionat întreruperea, simulând întoarcerea dintr-o
întrerupere.

Deoarece nucleul resetează câmpul care indică rutina de tratare a unui semnal s-ar
putea ajunge la situaţii nedeterminate. Deoarece procesele rulează în mod utilizator, nucleul
ar putea face o schimbare de context, apărând posibilitatea ca procesul să primească un
semnal înainte de a putea să refacă legătura spre funcţia de tratare a semnalului.

EXEMPLU :
Următorul program ilustrează această stare. Procesul apelează signal pentru a
specifica execuţia funcţiei sigcatcher la apariţia semnalului SIGINT. El crează un proces
fiu, după care execută apelul sistem nice pentru a-şi micşora prioritatea, după care intră
într-un ciclu infinit. Procesul fiu îşi suspendă execuţia pentru 5 secunde, pentru a da timp
procesului părinte pentru aşi micşora prioritatea, după care procesul fiu intră într-un
ciclu, în care trimite un semnal de întrerupere procesului părinte, prin apelul sistem kill.
Dacă kill se întoarce cu un cod de eroare, probabil din cauză că procesul părinte nu mai
există, procesul fiu se termină. Idea este că procesul părinte ar trebui să apeleze de fiecare
dată funcţia de tratare a semnalului, care să tipărească un mesaj şi să apeleze din nou
signal pentru a capta următoarea apariţie a unui semnal de întrerupere (SIGINT), astfel
procesul părinte ar executa la infinit ciclul.
Dar este posibilă apariţia următoarei secvenţe de evenimente :
1. Procesul fiu trimite un semnal de întrerupere procseului părinte.
2. Procesul părinte prinde semnalul şi apelează funcţia de tratare, dar nucleul
întrerupe procesul şi face o schimbare de context, înainte ca acesta să fi executat
din nou apelul sistem signal.
3. Procesul fiu se execută din nou şi trimite un nou semnal de întrerupere procesului
părinte.
- 109 -
Curs de SISTEME DE OPERARE
4. Procesul părinte primeşte cel de-al doilea semnal de întrerupere, dar el nu a reuşit
să specifice rutina de tratare pentru acesta, astfel încât se va apela la tratarea
impicită, adică la reluarea execuţiei procesul părinte se va termina.

#include <signal.h>

sigcatcher()
{
printf(“PID %d caught one.\n”, getpid()); /* print process id */
signal(SIGINT, sigcatcher);
}

main()
{
int ppid;

signal(SIGINT, sigcatcher);

if (fork() == 0)
{
/* give enough time for both processes to set up */
sleep(5); /* library function to delay 5 seconds */
ppid = getppid(); /* get parent id */
for(;;)
if (kill(ppid, SIGINT) == -1)
exit();
}

/* lower priority, greater chance of exhibiting race */


nice(10);
for(;;)
;
}

7.4.2.2 Grupuri de procese.

Deşi procesele de pe un sistem UNIX sunt identificate printr-un identificator unic,


sistemul câteodată trebuie să identifice procesele prin "grup". De exemplu, procesele cu un
proces strămoş comun, care este un shell sunt în general legate unele de altele, de aceea toate
aceste procese vor primi semnale când un utilizator apasă tasta "delete" sau "break" sau când
terminalul nu mai este accesibil. Nucleul foloseşte identificatorul de grup al proceselor
pentru a identifica grupurile de procese care trebuie să primească un semnal comun, pentru
anumite evenimente. Procesele din acelaşi grup de procese au aceiaşi identificatori de grup.
Apelul sistem setpgrp iniţializează identificatorul de grup al unui proces şi îl setează
egal cu valoarea identificatorului de proces al procesului care l-a apelat.
Sintaxa apelului este :

grp = setpgrp();

unde grp este noul identificator de grup al procesului. Un proces fiu reţine identificatorul de
grup al procesului părinte prin fork.

- 110 -
Curs de SISTEME DE OPERARE

7.4.2.3 Trimiterea de semnale din procese.

Procesele folosesc apelul sistem kill pentru a trimite semnale. Sintaxa apelului este :

kill(pid, signum);

unde pid identifică setul de procese care vor primi semnalul, iar signum este numărul
semnalului care se doreşte a fi transmis. Următoarea listă arată corespondenţa dintre valorile
lui pid şi setul de procese :
• Dacă pid este un întreg pozitiv, nucleul trimite semnalul la procesul cu
identificatorul pid.
• Dacă pid este 0, nucleul trimite semnalul la toate procesele din grupul procesului
emiţător.
• Dacă pid este -1, nucleul trimite semnalul tuturor proceselor al căror identificator
real de utilizator corespunde cu identificatorul efectiv de utilizator al procesului
emiţător. Dacă procesul emiţător are identificatorul efectiv de utilizator al
superutilizatorului, atunci nucleul trimite semnalul tuturor proceselor cu excepţia
procesului 0 şi 1.
• Dacă pid este un întreg negativ, dar nu -1, nucleul trimite semnalul tuturor
proceselor din grupul de procese al cărui identificator de grup este egal cu valoarea
absolută a pid.

În toate cazurile, dacă procesul emiţător nu are identificatorul efectiv de utilizator al


superutilizatorului, sau identificatorul său real sau efectiv nu se potrivesc cu identificatorul
real sau efectiv al procesului destinaţie, apelul kill eşuează.

EXEMPLU :
Următorul program exemplifică noţiunea de grup de procese, împreună cu
folosirea apelurilor de sistem menţionate.

#include <signal.h>
main()
{
register int i;
setpgrp();
for(i = 0; i < 10; i++)
{
if (fork() == 0)
{ /* proces fiu */
if (i & 1)
setpgrp();
printf("pid = %d pgrp = %d\n", getpid(), getpgrp());
pause(); /* apel sistem pentru suspendarea execuţiei */
}
}
kill(0, SIGINT);
}

În precedentul program, procesul resetează identificatorul său de grup şi crează 10


procese fii. Când sunt create, fiecare proces fiu va avea acelaşi identificator de grup ca şi
procesul părinte, dar procesele create în timpul iteraţiilor impare îşi resetează şi elel

- 111 -
Curs de SISTEME DE OPERARE
identificatorul de grup. Apelurile sistem getpid şi getpgrp întorc identificatorul de proces,
respectiv identificatorul de grup al procesului aflat în execuţie, iar apelul sistem pause
suspendă execuţia procesului până când recepţionează un semnal. În final, părintele execută
apelul sistem kill şi trimite un semnal de întrerupere tuturor proceselor din grupul său.
Nucleul trimite semnalul la cele 5 procese "pare", care nu şi-au resetat identificatorul de grup,
dar celelalte 5 procese "impare" vor continua să ruleze.

7.4.3 Terminarea proceselor.

Procesele de pe un sistem UNIX se termină prin execuţia apelului sistem exit. Un


proces pe cale de terminare intră în starea de Zombie, eliberând resursele deţinute, în afara
intrării din tabela de procese. Sintaxa pentru apel este :

exit(status);

unde valoarea lui status este întoarsă procesului părinte pentru examinare. Procesele pot apela
exit explicit sau implicit la sfârşitul programului, deoarece rutina de pornire linkeditată cu
toate programele C apelează exit când programul revine din funcţia main, punctul de intrare
al tuturor programelor. Deasemenea şi nucleul poate apela exit, intern, pentru un proces, ca
răspuns la un semnal care nu a fost captat. Dacă este aşa atunci valoarea lui status indică
numărul semnalului.

7.4.4 Aşteptarea terminării proceselor.

Un proces poate să-şi sincronizeze execuţia cu terminarea unui proces fiu prin
executarea apelului sistem wait. Sintaxa apelului sistem este :

pid = wait(stat_addr);

unde pid este identificatorul de proces al unui fiu aflat în starea Zombie, iar stat_addr este
adresa unui întreg (ret_code), care va conţine codul de ieşire pentru respectivul proces fiu.

EXEMPLU :
Pentru exemplificare considerăm următorul program. Considerăm două cazuri,
primul, apelul programului fără parametri şi al doilea caz, apelul programului cu cel puţin
un parametru. În primul caz procesul părinte crează 15 procese fii care eventual se
termină cu valoarea de ieşire i, valoarea variabilei contor când procesul fiu a fost creat.
Nucleul, executând wait pentru procesul părinte, găseşte un proces fiu în starea Zombie şi
îi întoarce identificatorul de proces, este nedeterminat al cărui proces fiu va fi întors.
Codul din librăria C, pentru apelul sistem exit plasează codul de ieşire în biţii 8 - 15 ai lui
ret_code şi întoarce identificatorul de proces.
În al doilea caz, procesul părinte apelează signal pentru a ignora semnalele
datorate "morţii" proceselor fiu. Presupunând că procesul părinte adoarme înainte ca
vreun proces fiu să se termine, când un proces fiu se termină, el trimite un semnal de
"deces" procesului părinte, procesul părinte se trezeşte şi va fi planificat pentru execuţie.
Când procesul părinte ajunge să se execute, el găseşte semnalul în aşteptare, dar deoarece
el ignoră semnalele de "deces ale fiilor", nucleul îndepărtează intrarea corespunzătoare
procesului fiu în starea Zombie din tabela de procese şi continuă execuţia lui wait ca şi
cum nu s-ar fi recepţionat nici un semnal. Nucleul execută această procedură de fiecare

- 112 -
Curs de SISTEME DE OPERARE
dată când părintele primeşte un semnal de "deces al unui fiu", până când procesul părinte
nu va mai avea fii. Atunci apelul sistem wait va întoarce -1, semnalând eroare, deoarece
nu există nici un proces fiu după care să aştepte.
Diferenţa dintre cele două cazuri este că în primul caz procesul părinte aşteaptă
după terminarea oricărui proces fiu, în timp ce în al doilea caz el aşteaptă pentru
terminarea tuturor proceselor fiu.

#include <signal.h>

main(int argc, char *argv[])


{
int i, re_val, ret_code;

if (argc >= 1)
signal(SIGCLD, SIG_IGN); /* ignoră decesul fiilor */
for(i = 0; i < 15; i++)
if (fork() == 0)
{ /* procesul fiu */
printf("child proc %x\n", getpid());
exit(i);
}
ret_val = wait(&ret_code);
printf("wait ret_val %x ret_code %x\n", ret_val, ret_code);
}

7.4.5 Executarea altor programe.

Apelul sistem exec încarcă un alt program, suprascriind spaţiul de memorie al unui
proces cu o copie a fişierului executabil. Sintaxa acestui apel sistem este :

execve(filename, argv, envp);

unde filename este numele fişierului executabil care va fi încărcat, argv este un pointer spre
un tablou de pointeri spre şirurile de caractere care vor fi parametri programului executabil,
iar envp este un pointer spre un tablou de pointeri spre şiruri de caractere care constituie
ambianţa programului care va fi executat. Există mai multe funcţii de librărie, care apelează
la exec, cum ar fi execl, execv, execle şi altele.
Când un program foloseşte parametrii din linia de comandă, ca în :

main(argc, argv)

tabloul argv este o copie a parametrului argv comunicat lui exec. Şirurile de caractere din
ambianţă au următoarea formă : "name=value" şi pot conţine informaţii utile. Procesele pot
accesa ambianţa lor prin variabila globală environ, iniţializat de rutina C de start.

EXEMPLU :
Următorul fragment de program ilustează folosirea acestui apel sistem :

main()
{
int status;

- 113 -
Curs de SISTEME DE OPERARE
if (fork() == 0)
execl("/bin/date", "date", 0);
wait(&status);
}

- 114 -

S-ar putea să vă placă și