Sunteți pe pagina 1din 35

Arhitectura Sistemului de Operare UNIX

Generalităţi
Subsistemul de fişiere
Subsistemul de procese
Controlul proceselor

Sisteme de Operare. Teorie şi Aplicaţii 110.1 Robert.Gyorodi@ul.ie


Diagrama bloc a nucleului UNIX
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

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
Sisteme de Operare. Teorie şi Aplicaţii 110.2 Robert.Gyorodi@ul.ie
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 – structură globală a nucleului
tabela de descriptori pentru fişierele utilizator - împărţită pe procese

Sisteme de Operare. Teorie şi Aplicaţii 110.3 Robert.Gyorodi@ul.ie


Structurile sistemului de fişiere
Când un proces deschide sau User File File Table Inode Table
creează un fişier, nucleul alocă o Descriptor
intrare din fiecare tabelă Table
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 •
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

Sisteme de Operare. Teorie şi Aplicaţii 110.4 Robert.Gyorodi@ul.ie


Sistemul de fişiere
boot super inode list data blocks
block block

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
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ădăcină 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
Ca şi o măsură de siguranţă, unele sisteme stochează mai multe copii ale
listei de inoduri, răsfirate pe tot discul
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

Sisteme de Operare. Teorie şi Aplicaţii 110.5 Robert.Gyorodi@ul.ie


Inodul
Conţine:
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
fişiere speciale pe bloc
fişiere FIFO
atributele de acces - sistemul protejează fişierele faţă de 3 clase:
proprietarul
grupul proprietarului
alţii
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

Sisteme de Operare. Teorie şi Aplicaţii 110.6 Robert.Gyorodi@ul.ie


Alocarea Spaţiului
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
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

Sisteme de Operare. Teorie şi Aplicaţii 110.7 Robert.Gyorodi@ul.ie


Subsistemul de procese
Proces
un program care se află în execuţie
constă dintr-un şir de octeţi pe care CPU-ul le interpretează ca şi:
instrucţiuni maşină (“text”)
date
stivă
Mai multe procese sunt executate simultan, ordinea execuţiei depinzând
de planificarea acestora de către kernel
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

Sisteme de Operare. Teorie şi Aplicaţii 110.8 Robert.Gyorodi@ul.ie


Procesele în UNIX
Un proces 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)
Nucleul, ca urmare a unui apel exec încarcă un fişier executabil în
memorie.
Procesul încărcat constă din cel puţin trei 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
Se folosesc stive separate pentru fiecare mod kernel / utilizator
– Stiva kernel a unui proces este vidă când un proces rulează în mod
utilizator

Sisteme de Operare. Teorie şi Aplicaţii 110.9 Robert.Gyorodi@ul.ie


Structurile de Date ale unui Proces1
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
per process
region table region table
u area

process table
main memory

Sisteme de Operare. Teorie şi Aplicaţii 110.10 Robert.Gyorodi@ul.ie


Structurile de Date ale unui Proces2
Câmpurile cele mai importante din tabela de procese sunt:
câmp de stare, indicând starea procesului
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
câmp care dă dimensiunea 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
parametri 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

Sisteme de Operare. Teorie şi Aplicaţii 110.11 Robert.Gyorodi@ul.ie


Structurile de Date ale unui Proces3
Zona u area conţine informaţii care descriu procesul, dar care trebuie să fie
accesibile doar când procesul se execută efectiv
un pointer către intrarea în tabela de procese a procesului
identificatorii reali şi efectivi de utilizator
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
câmp de eroare care reţine codurile de eroare din timpul 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

Sisteme de Operare. Teorie şi Aplicaţii 110.12 Robert.Gyorodi@ul.ie


Contextul unui Proces
Context – starea procesului, cum este ea definită de:
textul său
valorile variabilelor utilizator globale
structurilor de date
valorile regiştrilor pe care le foloseşte
valorile din zona din tabela de procese care îi corespunde precum şi din
zona u area
conţinutul stivelor utilizator şi kernel
Când execută un proces, sistemul rulează în contextul procesului
Când nucleul decide că ar trebui să execute un alt proces, el face o
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 întrerupt
Nucleul tratează întreruperile în contextul procesului întrerupt, chiar
dacă acesta nu a provocat apariţia întreruperii
Sisteme de Operare. Teorie şi Aplicaţii 110.13 Robert.Gyorodi@ul.ie
Stările şi Tranziţiile 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 system only)
6 5
wakeup

Sisteme de Operare. Teorie şi Aplicaţii Sleep, Swapped 110.14 Ready to Run, Swapped Robert.Gyorodi@ul.ie
Controlul Proceselor
Apelul sistem fork creează un nou proces
Apelul exit termină execuţia unui proces
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 dinamic mai multă
memorie, similar, sistemul permite şi creşterea dinamică a stivei
utilizator

Sisteme de Operare. Teorie şi Aplicaţii 110.15 Robert.Gyorodi@ul.ie


Crearea Proceselor1
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.

Sisteme de Operare. Teorie şi Aplicaţii 110.16 Robert.Gyorodi@ul.ie


Crearea Proceselor2
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ţiuni 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

Sisteme de Operare. Teorie şi Aplicaţii 110.17 Robert.Gyorodi@ul.ie


Crearea Proceselor3
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

Sisteme de Operare. Teorie şi Aplicaţii 110.18 Robert.Gyorodi@ul.ie


Exemplu - Crearea Proceselor1
/* 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.
*/
#include <fcntl.h>

int fdrd, fdwt;


char c;
int rdwrt();

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


if (argc != 3) {
printf("Mod de utilizare: Ex_7_4_1 <fis_srs> <fis_dst>\n");
return(1);
}
if ((fdrd = open(argv[1], O_RDONLY)) == -1) {
printf("Nu am reusit sa deschid fisierul sursa: %s\n", argv[1]);
return(2);
}
if ((fdwt = creat(argv[2], 0666)) == -1) {
printf("Nu am reusit sa creez fisierul destinatie: %s\n", argv[2]);
return(3);
}
fork();
/* ambele procese execută acelaşi cod */
rdwrt();
return(0);
}

Sisteme de Operare. Teorie şi Aplicaţii 110.19 Robert.Gyorodi@ul.ie


Exemplu - Crearea Proceselor2
int rdwrt() {
for(;;) {
if (read(fdrd, &c, 1) != 1)
return(0);
write(fdwt, &c, 1);
}
return(1);
}
/*
Procesul deschide fişierul existent, creează noul fişier şi creează un proces fiu prin apelul lui fork. Intern,
nucleul efectuează 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 byte 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. 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.
*/

Sisteme de Operare. Teorie şi Aplicaţii 110.20 Robert.Gyorodi@ul.ie


Semnale
Semnalele informează procesele despre apariţia unor
evenimente asincrone
Pentru transmiterea semnalelor se foloseşte apelul sistem kill
Clasificarea semnalelor:
Semnale care au de a face cu terminarea unui proces
Semnale care au de a face cu excepţiile produse de procese
Semnale ce au de a face cu situaţii nerecuperabile din timpul unui
apel sistem
Semnale cauzate de o eroare neaşteptată în timpul unui apel sistem
Semnale care provin de la un proces în mod utilizator
Semnale care ţin de interacţionarea cu un terminal
Semnale pentru urmărirea execuţiei unui proces

Sisteme de Operare. Teorie şi Aplicaţii 110.21 Robert.Gyorodi@ul.ie


Tratarea Semnalelor1
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
Nucleul tratează semnalele în contextul procesului care le-a recepţionat
Cazuri pentru tratarea semnalelor:
procesul se termină la recepţionarea unui semnal (implicit)
function = 0
ignoră semnalul
function = 1
execută o funcţie particulară (utilizator) la recepţionarea unui semnal – cu
ajutorul apelului sistem signal
function = adresa funcţiei utilizator

oldfunction = signal(signum, function);

Sisteme de Operare. Teorie şi Aplicaţii 110.22 Robert.Gyorodi@ul.ie


Tratarea Semnalelor2
Paşi făcuţi de nucleu la primirea unui semnal ales pentru tratare:
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
şterge câmpul care indică rutina de tratare a semnalului din zona u
area, setându-l pe valoarea implicită
creează 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
schimbă contextul salvat astfel:
resetează valoarea program counter-ului astfel încât să indice
adresa funcţiei specificate
setează valoarea stack pointer-ului astfel încât să reflecte
creşterea corespunzătoare a stivei utilizator

Sisteme de Operare. Teorie şi Aplicaţii 110.23 Robert.Gyorodi@ul.ie


Exemplu - Tratarea Semnalelor
/* Următorul program ilustrează o stare de nedeterminare. Procesul apelează signal pentru a specifica execuţia funcţiei
sigcatcher la apariţia semnalului SIGINT. El creează 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ă. Ideea 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 procesului 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.
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 implicită, adică la reluarea
execuţiei procesul părinte se va termina.
*/
#include <signal.h>

sigcatcher() {
printf(“PID %d a primit un semnal.\n”, getpid()); /* afiseaza process id */
signal(SIGINT, sigcatcher);
}

main() {
int ppid;
signal(SIGINT, sigcatcher);
if (fork() == 0) {
sleep(5); /* permite ambelor procese sa se initializeze - functie de librarie pentru a astepta 5 secunde */
ppid = getppid(); /* preia id-ul parintelui */
for(;;)
if (kill(ppid, SIGINT) == -1)
exit();
}
/* micsoram prioritatea, astfel crescand sansa unei nedeterminari */
nice(10);
for(;;)
;
}
Sisteme de Operare. Teorie şi Aplicaţii 110.24 Robert.Gyorodi@ul.ie
Grupuri de Procese
Procesele din acelaşi grup de procese au acelaşi identificatori de
grup
Nucleul foloseşte identificatorul de grup al proceselor pentru a
identifica grupurile de procese care trebuie să primească un
semnal comun, pentru anumite evenimente
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

grp = setpgrp();

Un proces fiu reţine identificatorul de grup al procesului părinte


prin fork

Sisteme de Operare. Teorie şi Aplicaţii 110.25 Robert.Gyorodi@ul.ie


Trimiterea de Semnale din Procese
Procesele folosesc apelul sistem kill pentru a trimite semnale
kill(pid, signum);
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

Sisteme de Operare. Teorie şi Aplicaţii 110.26 Robert.Gyorodi@ul.ie


Exemplu – Trimitere Semnale
/* 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 */
}
}
sleep(10);
kill(0, SIGINT);
}

Sisteme de Operare. Teorie şi Aplicaţii 110.27 Robert.Gyorodi@ul.ie


Terminarea Proceselor
Procesele de pe un sistem UNIX se termină prin execuţia
apelului sistem exit

exit(status);

Un proces pe cale de terminare intră în starea Zombie, eliberând


resursele deţinute, în afara intrării din tabela de procese
Dacă este apelat de nucleu ca răspuns la un semnal atunci
valoarea lui status indică numărul semnalului

Sisteme de Operare. Teorie şi Aplicaţii 110.28 Robert.Gyorodi@ul.ie


Aşteptarea Terminării Proceselor
Un proces poate să-şi sincronizeze execuţia cu terminarea unui
proces fiu prin executarea apelului sistem wait

pid = wait(stat_addr);

Sisteme de Operare. Teorie şi Aplicaţii 110.29 Robert.Gyorodi@ul.ie


Exemplu – Aşteptare Terminare
/* 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
creează 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 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, ret_pid, ret_code;
if (argc >= 1)
signal(SIGCLD, SIG_IGN); /* ignoră decesul fiilor */
for(i = 0; i < 15; i++)
if (fork() == 0) { /* procesul fiu */
printf("procesul fiu %x\n", getpid());
exit(i);
}
ret_pid = wait(&ret_code);
printf("wait ret_pid %d ret_code (hexa) %x\n", ret_pid, ret_code);
}

Sisteme de Operare. Teorie şi Aplicaţii 110.30 Robert.Gyorodi@ul.ie


Executarea altor Programe
Apelul sistem exec încarcă un alt program

execve(filename, argv, envp);

Există mai multe funcţii de librărie, care apelează la execve, cum


ar fi execl, execv, execle şi altele
Procesele pot accesa ambianţa lor prin variabila globală
environ, iniţializat de rutina C de start

Sisteme de Operare. Teorie şi Aplicaţii 110.31 Robert.Gyorodi@ul.ie


Exemplu – Execuţie Programe
/* Următorul fragment de program ilustează folosirea acestui apel sistem
*/

main()
{
int status;
if (fork() == 0)
execl("/bin/date", "date", 0);
wait(&status);
}

Sisteme de Operare. Teorie şi Aplicaţii 110.32 Robert.Gyorodi@ul.ie


Drepturile unui Proces
Nucleul asociază două ID-uri utilizator unui proces, independent de ID-ul
procesului:
ID-ul utilizator real (real user ID - RID)
utilizatorul responsabil pentru rularea procesului
ID-ul utilizator efectiv (effective user ID - EID)
folosit la stabilirea proprietarului noilor fişiere create, la verificarea drepturilor de
acces la fişiere şi la verificarea drepturilor de a trimite semnale altor procese prin
apelul sistem kill
nucleul permite unui proces să îşi schimbe ID-ul utilizator efectiv atunci când el
execută un program setuid sau execută explicit apelul sistem setuid

setuid(uid);

Un program setuid este un fişier executabil care are bitul setuid setat în câmpul
său de drepturi de acces.
Când un proces execută un program setuid, nucleul setează câmpul de ID
utilizator efectiv din tabela de procese şi din u_area la ID-ul proprietarului
fişierului
Pentru a distinge cele două câmpuri denumim câmpul din tabela de procese ID-ul utilizator salvat
(saved user ID - SID).
Dacă EID-ul procesului apelant este superuser, nucleul resetează RID-ul şi EID-ul din tabela de
procese şi u_area la uid
Dacă EID-ul procesului apelant nu este superuser, nucleul resetează EID-ul din u_area la uid dacă uid
are valoarea lui RID sau a lui SID
Altfel, apelul sistem întoarce o eroare
Sisteme de Operare. Teorie şi Aplicaţii 110.33 Robert.Gyorodi@ul.ie
Modificarea Dimensiunii unui Proces
Un proces poate să îşi mărească sau reducă dimensiunea
regiunii de date prin utilizarea apelului sistem brk
brk(endds);
O alternativă la această funcţie este funcţia sbrk
oldendds = sbrk(increment);
Nucleul verifică faptul ca noua dimensiune a procesului să fie
mai mică decât maximul sistemului şi ca noua regiune de date să
nu fie suprapusă peste o zonă virtuală deja asignată
Dacă procesul apelează brk pentru a elibera un spaţiu alocat
anterior, nucleul eliberează memoria respectivă
Dacă procesul accesează adrese virtuale din paginile eliberate, va
rezulta o eroare de acces la memorie (memory fault)

Sisteme de Operare. Teorie şi Aplicaţii 110.34 Robert.Gyorodi@ul.ie


Exemplu – Modificare Dimensiune
/* Următorul program exemplifică folosirea apelului brk (sbrk).
După ce prin apelul la signal se specifică modul de tratare a semnalelor SEGment Violation, procesul
apelează sbrk şi tipăreşte valoarea iniţială a valorii de break. După aceasta intră într-un ciclu în care
se incrementează un pointer la caracter şi modificând valoarea de la adresa indicată. Aceasta se repetă
fără nici un eveniment până când se încearcă o scriere la o adresă din afara regiunii de date, cauzând un
semnal de violare de segment. Interceptând semnalul, funcţia catcher apelează sbrk pentru a mai aloca 256
de octeţi pentru regiunea de date. Procesul continuă din locul în care a fost întrerupt de semnal, scriind
în noua regiune alocată. Când depăşeşte din nou regiunea de date alocată, întreaga procedură se repetă. Pe
maşinile pe care alocarea se face pe pagini apare un fenomen interesant. O pagină este cea mai mică unitate
de memorie care este protejată de hardware, astfel hardware-ul nu poate detecta dacă un proces scrie la o
adresă care depăşeşte valoarea de break, dar încă se mai găseşte în interiorul ultimei pagini alocate,
astfel fiind un acces “semilegal”.
*/
#include <signal.h>

char *cp;
int callno;

int main() {
char *sbrk();
extern void catcher(int);

signal(SIGSEGV, catcher);
cp = sbrk(0);
printf(“Valoarea brk originala : %u\n”, cp);
for(;;)
*cp++ = 1;
}

void catcher(int signo) {


callno++;
printf(“Am receptionat semnalul %d Al %d-lea apel la adresa %u\n”, signo, callno, cp);
sbrk(256);
signal(SIGSEGV, catcher);
}

Sisteme de Operare. Teorie şi Aplicaţii 110.35 Robert.Gyorodi@ul.ie

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