Documente Academic
Documente Profesional
Documente Cultură
CONTROLUL PROCESELOR
Ultimul capitol a definit contextul unui proces i a explicat algoritmii care l
manipuleaz; acest capitol va descrie utilizarea i implementarea apelurilor sistem
care controleaz contextul unui proces. Apelul sistem fork creaz un nou proces,
apelul exit termin executia unui proces i apelul wait permite unui proces printe si sincronizeze execuia cu terminarea (exit) unuia (sau toi) dintre procesele fii.
Semnalele informeaz procesele despre apariia evenimentelor asincrone. Deoarece
nucleul (kernel) sincronizeaz execuia apelurilor sistem wait i exit prin intermediul
semnalelor, capitolul prezint semnalele naintea apelurilor sistem wait i exit. Apelul
sistem exec permite unui proces s lanseze n execuie un "nou" program,
suprapunnd peste spaiul propriu de adrese imaginea unui fiier executabil. Apelul
sistem brk permite unui proces s aloce mai mult memorie n mod dinamic; similar,
sistemul permite stivei utilizator s creasc n mod dinamic prin alocarea unui spaiu
suplimentar cnd este necesar, folosind aceleai mecanisme ca i pentru brk. Spre
final, capitolul prezint pe scurt construcia principalelor bucle ale shell-lui i init-lui.
Figura 8.1 arat relaiile existente ntre apelurile sistem decrise n acest capitol
i algoritmii de gestiune ai memoriei descrii n ultimul capitol. Dei aproape toate
apelurile sistem utilizeaz sleep i wakeup, acest lucru nu este artat n figur. n plus,
exec interacioneaz cu algoritmii sistemului de fiiere descrii n capitolele 4 i 5.
managementul memoriei
sincronizarea
fork
exec
dupreg
detachre
attachre
allocreg
brk
growreg
exit
wai
signa
kill
setpgrp
Altele
setuid
detachre
g
attachre
g
growreg
loadreg
mapreg
Fig. 8.1. Apelurile sistem ale proceselor i relaiile cu ali algoritmi
181
Singurul mod prin care un utilizator poate crea un nou proces n UNIX este
folosirea apelului sistem fork. Procesul care apeleaz fork este numit proces printe
(parent), iar noul proces creat este numit proces fiu (child). Sintaxa pentru apelul
sistem fork este:
pid=fork();
La revenirea din apelul sistem fork, dou procese au copii identice ale contextului la
nivel utilizator, excepie fcnd valoarea de retur a pid-ului. n procesul printe, pid-ul
are valoarea identificatorului procesului fiu; n procesul fiu, pid-ul are valoarea zero.
Procesul 0, creat intern de ctre kernel cnd sistemul este iniializat este singurul
proces care nu este creat prin intermediul apelului sitem fork.
Nucleul execut urmtoarea secven de operaii la apelul sistem fork:
1.
2.
3.
Face o copie logic a contextului procesului printe. Deoarece n mod sigur
poriuni ale procesului, cum ar fi zona de cod, pot fi mprite ntre procese, kernel
poate uneori incrementa numrul de referiri al unei regiuni n schimul copierii regiunii
la o nou locaie fizic n memorie.
4.
5.
ntoarce n procesul printe numrul identificatorului atribuit procesului fiu i
valoarea zero n procesul fiu.
Implementarea apelului sistem fork nu este trivial deoarece procesul fiu pare a-i
incepe secvena de execuie dintr-un punct aflat n aer. Algoritmul pentru fork difer
puin de la sistemul cu paginare la cerere la sistemul cu swapping; discuia care
urmeaz se bazeaz pe sistemul traditional de swapping dar va sublinia locurile n care
apar schimbri pentru sistemul cu paginare la cerere. De asemenea se presupune c
sistemul are suficient memorie pentru a pstra procesul fiu. Capitolul 9 va considera
cazul n care nu exist suficient memorie pentru procesul fiu i descrie implementarea
apelului sistem fork ntr-un sistem bazat pe paginare la cerere.
algoritm fork
intrri: niciuna
ieiri: la procesul printe, identificatorul fiului (PID)
la procesul fiu, 0
{
verific resursele disponibile ale nucleului;
obine o intrare liber n tabela proceselor, un unic numr pentru
PID;
verific ca utilizatorul s nu ruleze prea multe procese;
marcheaz starea procesului ca "fiind creat";
copiaz datele din intrarea tabela procese corespunztoare procesului
printe n
lansa un proces care s foreze celelalte procese s se termine (vezi Seciunea 8.2.3
pentru apelul sistem kill).
Mai departe nucleul iniializeaz intrarea din tabela proceselor pentru procesul fiul
creat prin copierea diferitelor cmpuri din intrarea procesului printe. De exemplu,
procesul fiu motenete numerele identificatorilor utilizator efectiv i real ai procesului
printe i valoarea nice a acestuia, utilizat pentru calcularea prioritii de planificare.
Nucleul depune identificatorul procesului printe n intrarea fiului, pune fiul n structura
arborescent a proceselor i iniializeaz diferii parametri de planificare, cum ar fi
valoarea prioritii iniiale, folosirea iniial a C.P.U. i alte cmpuri de timp. Starea
iniial a procesului este "n curs de creare" (revezi Figura 6.1).
Nucleul ajusteaz acum contoarele de referin pentru fiierele cu care procesul fiu
este automat asociat. Procesul fiu se afl n directorul curent al procesului printe.
Numrul proceselor care au acces la director va fi incrementat cu 1 i, n consecin,
nucleul incrementeaz contorul de referin al inodului. Apoi, dac procesul printe
sau unul din strmoi executase apelul sistem chroot pentru a schimba rdcina,
procesul fiu motenete schimbarea rdcinii i incrementeaz contorul de referin al
inodului respectiv. n final, nucleul caut n tabela cu descriptori fiier ai utilizatorului
(UFDT) specific procesului printe, gsete intrrile pentru fiierele deschise,
cunoscute de ctre proces i incrementeaz contoarele de referine al tabelei globale
de fiire (FT) asociate fiierelor deschise. Procesul fiu nu numai c motenete
drepturile de acces la fiierele deschise, dar i mparte accesul la fiiere cu procesul
printe deoarece ambele procese manipuleaz aceleai intrri din tabela fiierelor.
Efectul apelului sistem fork este identic cu cel al dup-ului vis-a-vis de fiierele
deschise: o nou intrare n UFDT va adresa o intrare din FT pentru un fiier deschis.
Totui, pentru dup, intrrile din UFDT sunt asociate unui singur proces, pe cnd pentru
fork, ele sunt n procese diferite.
Nucleul este acum gata pentru a crea contextul nivelului utilizator al procesului fiu.
Nucleul aloc memorie pentru u. area, regiunile i tabelele de pagini (PT) auxiliare ale
procesului fiu, duplic fiecare regiune din procesul printe folosind algoritmul dupreg i
ataeaz fiecare regiune la procesul fiu folosind algoritmul attachreg. n sistemul bazat
pe swapping, acesta copiaz coninutul regiunilor care nu sunt folosite n comun ntr-o
nou zon (area) a memoriei principale. n seciunea 6.2.4 s-a artat c u. area
conine un pointer la intrarea asociat din PT. Exceptnd acest cmp, coninutul u.
area a fiului este iniial identic cu cel al u. area a printelui, dar ele pot s difere dup
terminarea apelului sistem fork. De exemplu procesul printe poate deschide un nou
fiier dup terminarea apelului sistem fork, dar procesul fiu nu poate avea automat
acces la el.
Pn n prezent, nucleul a creat poriunea static a contextului procesului fiu; acum
acesta creaz poriunea dinamic a acestuia. Nucleul copiaz nivelul 1 al contextului
printelui, coninnd contextul regitrilor salvai i structura stivei nucleului ale
apelului sistem fork. Dac implementarea este una n care stiva nucleului este parte a
u. area, nucleul creaz automat stiva nucleu a fiului cnd acesta creaz u. area a
fiului. Altfel, procesul printe trebui s copieze propria stiv nucleu ntr-o zona privat
a memoriei asociate cu procesul fiu. n ambele cazuri, stivele nucleu pentru procesele
fiu i printe sunt identice. Nucleul creaz apoi un context pe nivel dummy (2) pentru
procesul fiu, coninnd contextul regitrilor salvai pentru nivelul context (1). Acesta
seteaz contorul de program (PC) i ali regitri n contextul regitrilor salvai astfel
nct s poat reface contextul fiului, dei acesta nu a fost executat nainte i de aceea
procesul fiu se poate recunoate singur ca fiind un proces fiu cnd acesta ruleaz. De
exemplu, dac codul nucleul testeaz valoarea registrului 0 pentru a decide dac
procesul este printe sau fiu, acesta scrie valoarea corespunztoare n contextul
regitrilor salvai corespunztor fiului n nivelul 1. Mecanismul este similar cu cel
discutat pentru o comutare de context discutat n capitolul anterior.
184
Cnd contextul fiului este gata, printele termin partea sa a apelului fork prin
schimbarea strii fiului n "gata de rulare (n memorie)" i prin ntoarcerea spre
utilizator a identificatorului de proces al fiului. Nucleul programeaz mai trziu
procesul fiu pentru execuie cu ajutorul algoritmului normal de planificare i procesul
fiu i termin astfel partea sa de fork. Contextul procesului fiu a fost completat de
ctre procesul tat; n nucleu, procesul fiu pare a fi trezit dup ateptarea unei
resurse. Procesul fiu execut poriunea de cod din apelului sistem fork, n acord cu
contorul programului, pe care nucleul l-a refcut din contextul regitrilor salvai aflat n
nivelul context 2 i ntoarce un 0 din apelul sistem.
Figura 8.3 d o imagine logic a proceselor printe i fiu i a relaiilor lor cu alte
structuri de date ale nucleului, imediat dup terminarea apelului sistem fork.
Concluzionnd, ambele procese mpart fiiere pe care printele le-a deschis n timpul
apelului sistem fork i contorul de referine al FT pentru aceste fiiere crete cu 1.
Similar, procesul fiu are acelai director curent i aceeai rdcin ca a printelui i
contoarele de referine ale inodurilor aparinnd acestor directoare cresc cu valoarea
1. Procesele au copii identice ale regiunilor de text, date i stiv utilizator; tipul
regiunii i implementarea sistemului stabilete dac procesele pot mpri o copie fizic
a regiunii de text.
Considerm programul din figura 8.4, un exemplu de partajare a accesului la fiiere pe
timpul executrii apelului sistem fork. Un utilizator poate apela programul cu doi
parametri, numele unui fiier existent i numele unui nou fiier ce va fi creat. Procesul
deshide fiierul existent, creaz noul fiier i presupunnd c nu apar erori, execut
fork i creaz un proces fiu.
Intern, nucleul face o copie a contextului printelui pentru procesul fiu iar procesul
printe i procesul fiu se execut n spaii de adrese diferite. Fiecare proces poate
accesa copiile private ale variabilelor globale fdrd, fdwt i c i copiile private ale
variabilelor de stiv argc i argv, dar nici un proces nu poate accesa variabilele celuilalt
proces. Cu toate acestea, nucleul copiaz u. area a procesului original n procesul fiu n
timpul apelului sistem fork i fiul astfel motenete dreptul de acces la fiierele
printelui (acestea sunt fiierele originale pe care printele le-a deschis i creat)
folosind aceiai descriptori de fiiere.
#include <fentl.h>
int fdrd, fdwt;
char c;
main (argc, argv)
int argc;
char *argv[];
{
if (argc!=3)
185
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 accelai cod */
rdwrt ();
exit (0);
}
rdwrt ()
{
for (;;)
{
if (read (fdrd, &c,1)!=1)
return;
write (fdwt ,&c,1);
}
}
Figura 8.4. Program n care printele i fiul au acces partajat la fiiere
Procesele printe i fiu apeleaz funcia rdwrt independent, apoi execut un ciclu,
citind cte un octet din fiierul surs i scriindu-l apoi n fiierul destinaie. Funcia
rdwrt se ntoarce atunci cnd apelul sistem read ntlnete sfritul fiierului. Nucleul
incrementase contoarele fiierelor surs i destinaie din tabela de fiiere i descriptorii
fiierelor din ambele procese refer aceeai intrare din tabela fiierelor. Aa c,
descriptorii de fiier fdrd pentru ambele procese refer aceeai intrare din tabela
fiierelor pentru fiierul destinaie i descriptorii de fiier fdwr pentru ambele procese
refer aceeai intrare din tabela fiierelor pentru fiierul surs. De aceea, cele dou
procese nu vor citi sau scrie niciodat de le (la) aceleai valori de offset ale fiierului,
deoarece nucleul incrementeaz aceste valori dup fiecare apel de citire sau scriere.
Dei procesele par s copieze fiierul surs de dou ori mai repede, coninutul fiierului
surs depinde de ordinea n care nucleul a planificat procesele. Dac acesta planific
procesele astfel nct ele s alterneze n executarea propriilor apeluri sistem, sau chiar
dac alterneaz execuia perechii read-write dintr-un proces, coninutul fiierului
destinaie va fi identic cu coninutul fiierului surs. Dar s considerm urmtorul
scenariu, n care procesele sunt gata s citeasc o scecven de dou caractere "ab"
din fiierul surs. Presupunem c procesul printre citete caracterul 'a', dup care
nucleul face o comutare de context pentru a executa procesul fiu nainte ca printele
s scrie. Dac procesul fiu citete caracterul 'b' i l scrie n fiierul destinaie nainte ca
printele s fie reprogramat, fiierul destinaie nu va conine irul "ab", ci "ba". Nucleul
nu garanteaz ritmul (rata) de programare a execuiei proceselor.
Acum s considerm programul din figura 8.5, care motenete descriptorii de fiiere 0
i 1 (intrarea i ieirea standard) de la printele su. Execuia fiecrui apel sistem pipe
186
aloc n plus 2 descriptori de fiiere n irurile to_par i to_child. Procesul execut fork
i face o copie a contextului su: fiecare proces poate accesa datele proprii, ca n
exemplul anterior. Procesul printe nchide fiierul su standard de ieire (descriptorul
de fiier 1) i duplic (dup) descriptorul de scriere ntors de pipe pentru to_child.
Deoarece primul slot liber din UFDT-ul printelui este chiar acela eliberat prin apelul
sistem close, nucleul copiaz descriptorul de scriere rezultat n urma executrii
apelului sistem pipe n slotul 1 al UFDT-lui i astfel descriptorul fiierului standard de
ieire devine descriptorul de scriere al pipe-lui pentru to_child. Procesul printe face
operaii similare astfel nct descriptorul intrrii standard s devin descriptorul de
citire al pipe-lui pentru to_par. Analog, procesul fiu i nchide fiierul standard de
ieire (descriptor 0) i duplic descriptorul de citire al pipe-lui pentru to_child. ntruct
primul slot liber n UFDT este slotul fostuului fiier standard de intare, intrarea
standard a fiului devine descriptorul de citire al pipe-lui pentru to_child. Fiul face
operaii similare astfel nct descriptorul ieirii standard s devin descriptorul de
scriere al pipe-lui pentru to_par. Ambele procese nchid descriptorii de fiiere ntori de
apelarea lui pipe ( o bun metod de programare ). Ca rezultat, cnd printele scrie
la ieirea standard, acesta scrie n pipe-ul to_child i trimite date procesului fiu, care
citete pipe-ul ca pe propria intrare standard. Cnd procesul fiu scrie la ieirea
standard, acesta scrie n pipe-ul to_par i trimite datele procesului printe care citete
pipe-ul ca pe propria intrare standard. Procesele astfel schimb mesaje prin
intermediul pipe-urilor.
Rezulatele acestui exemplu sunt aceleai, indiferent de ordinea n care procesele i
execut apelurile sistem respective. Adic nu este nici o diferen, dac printele se
ntoarce din fork naintea fiului sau dup el. Similar, nu este nici o diferen fa de
ordinea relativ n care procesele i esxecut apelurile sistem pn cnd acestea intr
n ciclu: structurile nucleului sunt identice. Dac procesul fiu execut apelul sistem
read nainte ca procesul printe s fac write, procesul fiu va intra n ateptare pn
cnd procesul printe scrie pipe-ul i este trezit. Dac procesul printe scrie pipe-ul
nainte ca procesul fiu s citeasc pipe-ul, printele nu va citi de la intrarea sa
standard pn cnd fiul nu de la citete intrarea sa standard i nu scrie la ieirea sa
standard. Din acest motiv, ordinea de execuie este fixat: fiecare proces termin un
apel sistem read i write i nu poate termina urmtorul apel sistem read pn cnd
cellalt proces nu termin un apel sistem read i write.
#include <string.h>
char string[]=" hello word";
main ()
{
int count,i;
int to_par[2], to_chil[2]; /* pentru pipe-urile printelui i fiului */
char buff[256];
pipe(to_par);
pipe(to_chil);
if(fork() ==0)
{
/* procesul fiu se execut aici */
close(0);
*/
187
dup(to_chil[0]);
intrarea standard */
close(1);
*/
dup(to_par[1]);/* duplic descriptorul de scriere al pipe la ieirea
standard */
close(to_par[1]);
close(to_chil[0]);
close(to_par 0]);
close(to_chil[1]);
for(;;)
{
if (count=read(0, buf, sizeof(buf)))==0)
exit ();
write(1, buf, count);
}
}
/* procesul printe se execut aici */
close(1);
*/
dup(to_chil [1]);
close(0);
dup(to_par [0]);
close(to_chil [1]);
close(to_par [0]);
close(to_chil [0]);
close(to_par [1]);
for(i=0; i<15; i++)
{
write(1, string, strlen(string));
read(0, buf, sizeof(buf));
}
}
Figura 8.5. Utilizarea Pipe-lui, Dup i Fork
Printele face exit dup cele 15 iteraii aflate n ciclu; apoi fiul citete "sfrit de fiier"
(EOF), deoarece pipe-ul nu are procese care s scrie, i termin. Dac fiul ar fi scris n
pipe dup ce printele a fcut exit, primul ar fi recepionat un semnal care indic
188
8.2. Semnale
Semnalele informeaz procesele despre apariia evenimentelor asincrone. Procesele i
pot trimite semnale prin intermediul apelului sistem kill, sau nucleul poate trimite
intern semnale. Exist 19 semnale n versiunea de UNIX System V (Release 2) care
pot fi clasificate dup cum urmeaz (vezi descrierea semnalelor n [SVID 85]):
1.
Semnale pentru terminarea proceselor, trimise atunci cnd un proces execut
exit sau cnd un proces execut apelul sistem signal cu parametrul "terminare fii";
2.
Semnale pentru de atenionare n cazul excepiilor provocate n procese, de
exemplu cnd acesta acceseaz o adres n afara spaiului su virtual de adrese, cnd
acesta ncearc s scrie n zonele de memorie protejate la scriere (cum ar fi codul
programului), cnd acesta execut o instruciune privilegiat, sau pentru diferite erori
hard;
3.
Semnale pentru situaii fr ieire aprute n timpul apelurilor sistem, cum ar fi
lipsa resurselor n timpul execuiei apelului sistem exec dup ce spaiul iniial de
adrese fusese eliberat (vezi Paragraful 8.5);
4.
Semnale determinate de condiii de eroare neateptate aprute n timpul unui
apel sistem, cum ar fi executarea unui apel sistem inexistent (procesul a furnizat
numrul unui apel sistem care nu corespunde unui apel sistem corect), scrierea unui
pipe care nu are proces cititor, sau folosirea unei valori de "referin" ilegale pentru
apelul sistem lseek. Ar fi mult mai comod s se ntoarc o eroare n apelurile sistem n
schimbul generrii unui semnal, dar folosirea semnalelor pentru a abandona procesele
care nu se comport corespunztor este mult mai practic (folosirea semnalelor n
aceste cazuri suplinete lipsa verificrii erorilor de ctre programator la apelurile
sistem) ;
5.
Semnale provenite de la procesele n modul utilizator, de exemplu cnd un
proces dorete s recepioneze un semnal alarm dup un anumit timp, sau cnd
procesele trimit unul altuia semnale n mod arbitrar, prin intermediul apelului sistem
kill;
6.
Semnale provenite de la interaciunea cu un terminal, atunci cnd un utilizator
nchide un terminal (sau semnalul "purttor" cade din anumite motive), sau cnd un
utilizator apas tastele "break" sau "delete";
8.
Tratarea semnalelor este diferit dup cum nucleul trimite un semnal unui proces, cum
trateaz procesul un semnal i cum un proces i controleaz reacia la apariia unui
semnal. Pentru a trimte un semnal unui proces, nucleul seteaz un bit n cmpul signal
din intrarea tabelei proceselor, corespunztor tipului semnalului recepionat. Dac
procesul este n ateptare la o prioritate ntreruptibil, nucleul l trezete. Sarcina celui
care trimite (proces sau nucleu) ia sfrit. Un proces poate pstra diferite tipuri de
semnale, dar nu are memorie pentru a pstra numrul de semnale de acelai tip pe
care le recepioneaz. De exemplu, dac un proces recepioneaz un semnal hangup i
un semnal kill, acesta seteaz biii corespunztori n cmpul signal din tabela
proceselor, dar nu poate spune cte instane ale semnalelor au fost recepionate.
Nucleul verific recepionarea unui semnal cnd un proces este gata s se ntoarc din
modul nucleu n modul utilizator i cnd acesta intr sau prsete starea de sleep la o
prioritate sczut programat (vezi figura 8.6). Nucleul trateaz semnalele doar cnd
un proces se ntoarce din modul nucleu n modul utilizator. Astfel, un semnal nu are un
efect imediat ntr-un proces care ruleaz n modul nucleu. Dac un proces ruleaz n
modul utilizator i nucleul trateaz o ntrerupere care presupune trimiterea unui
semnal ctre proces, nucleul va recunoate i trata semnalul cnd se va ntoarce din
ntrerupere. Astfel, un proces niciodat nu se va executa n modul utilizator nainte de
tratarea semnalelor importante.
Figura 8.7 prezint algoritmul pe care nucleul l execut pentru a determina dac un
proces a recepionat un semnal. Cazul semnalelor de tipul "terminare fiu" va fi tratat
mai trziu. Aa cum se va vedea, un proces are posibilitatea de a ignora semnalele
primite prin intermdiul apelului sistem signal. n algoritmul issig, nucleul terge
indicatorii care marcheaz apariia semnalelor pe care procesul vrea s le ignore dar
noteaz existena semnalelor pe care acesta nu le ignor.
algoritmul issig
intrri: niciuna
ieiri: adevrat, dac procesul recepioneaz semnale pe care nu le
ignor;
fals, n celelalte cazuri
{
while( cmpul semalului recepionat n intrarea din tabela proceselor nu
este 0)
{
gsete numrul semnalului trimis ctre proces;
if( semnalul este de "terminare fiu")
{
if( se ignor semnalul de "terminare fiu")
elibereaz
intrrile
din
tabela
proceselor
}
else if(dac nu este ignorat semnalul)
return (adevrat);
terge bitul semnalului recepionat din cmpul
semnalului aflat n tabela proceselor;
}
return (fals);
}
Figura 8.8. Algoritm pentru recunoaterea semnalelor
8.2.1.
Tratarea semnalelor
unde signum este numrul semnalului pentru care procesul specific aciunea, function
este adresa funciei (utilizatorului) pe care procesul dorete s-o apeleze la recepia
semnalului i ntoarce valoarea oldfunction care a fost valoarea lui function n cel mai
recent apel al lui signal cu parametrul signum. Procesul poate apela signal cu valoarea
1 sau 0 n loc de adresa funciei: procesul va ignora viitoarele apariii ale semnalului
dac valoarea parametrului este 1 (Paragraful 8.4 trateaz cazul special pentru
ignorarea semnalului de "terminare a fiului") i se ntoarce n nucleu dac valoarea lui
este 0 (valoarea implicit). U area conine un ir de cmpuri de tratarea semnalelor,
un cmp pentru fiecare semnal definit n sistem. Nucleul stocheaz adresa funciei
definite de utilizator n cmpul care corespunde numrului semnalului. Modul de
tratare a semnalelor de un anumit tip nu are efect n tratarea semnalelor de alte tipuri.
algoritmul psig
/*
intrri: niciuna
191
ieiri: niciuna
{
obine numrul semnalului setat n intrarea din tabela proceselor;
terge numrul semnalului din intrarea tabelei proceselor;
if( utilizatorul apelase funcia sistemsignal pentru ignorarea acestui
semnal)
return;
if( utilizatorul a specificat funcia care s trateze semnalul)
{
obine adresa virtual utilizator a interceptorului de semnale
pstrat n u area;
terge intrarea din u area care a coninut adresa interceptorului de
semnal;
modific contextul nivelului utilizator:
artificial creaz structura stivei utilizator pentru a mima apelarea
funciei de interceptare a semnalului;
modific contextul nivelului sistem:
scrie adresa interceptorului de semnal n cmpul PC al
contextului regitrilor salvai;
return;
}
if (semnalul este de tipul care face ca sistemul s creeze o imagine
intern unui proces)
{
creaz fiierul numit "core" n directorul curent;
scrie coninutul contextului nivelului utilizator n fiierul "core";
}
apeleaz imediat algoritmul exit;
}
Figura 8.8. Algoritm pentru tratarea semnalelor
Cnd trateaz un semnal (figura 8.8) nucleul determin tipul semnalului i terge bitul
semnalului tratat din intrarea corespunztoare tabelei proceselor, setat cnd procesul
a recepionat semnalul. Dac funcia de tratare a semnalului are valoarea implicit,
nucleul va descrca imaginea din memorie (core) a procesului (vezi exerciiul 8.7)
pentru anumite tipuri de semnale nainte de a face exit. Descrcarea (dump) este o
facilitate pentru programatori, permind acestora s se asigure de motivele
descrcrii i, de aceea, s-i poat depana programele. Nucleul descarc imaginea
procesului din memorie pentru semnalele care sugereaz c ceva este greit n proces,
cum ar fi faptul c procesul execut o instruciune ilegal sau cnd acesta acceseaz o
adres n afara spaiului su virtual de adrese. Dar nucleul nu descarc core-ul pentru
semnale care nu implic o eroare program. De exemplu, recepionarea unui semnal de
192
ntrerupere, trimis cnd un utilizator apas tastele "delete" sau "break" la terminal,
sugereaz c utilizatorul dorete s termine prematur un proces iar recepionarea
semnalului hangup sugereaz c terminalul login nu mai este "conectat". Aceste
semnale nu implic faptul c ceva este greit cu procesul. Semnalul quit, totui,
provoac o descrcare a core-lui chiar dac este iniiat din afar. Uzual trimis prin
apsarea tastei control + bara vertical, semnalul quit permite programatorului s
obin o descrcare a core-lui a procesului n rulare, chiar dac acesta are o bucl
infinit.
Cnd un proces recepioneaz un semnal pe care anterior a decis s-l ignore, acesta
continu ca i cnd semnalul nu ar fi aprut. Deoarece nucleul nu reseteaz cmpul
din u area care arat c semnalul este ignorat, procesul va ignora semnalul i la
apariia ulterioar a acestuia. Dac un proces recepioneaz un semnal pe care acesta
se decisese anterior s-l intercepteze, acesta execut funcia de tratare a semnalului
specificat de utilizator imediat cum se ntoarce n modul utilizator, dup ce nucleul
parcurge urmtorii pai:
1.
Nucleul acceseaz contextul regitrilor salvai al utilizatorului, gsind
numrtorul programului (PC) i pointerul stivei (SP) pe care acesta i salvase pentru
ntoarcerea n procesul utilizator.
2.
Acesta terge cmpul semnalului tratat din u area, setndu-l la starea implicit.
3.
Nucleul creaz un nivel de stivei n stiva utilizator, scriind n aceasta valorile
pentru PC i SP pe care acesta le restabilete din contextul regitrilor salvai al
utilizatorului i aloc spaiu nou, dac este necesar. Stiva utilizator art ca i cum
procesul apelase o funcie la nivel utilizator (interceptorul de semnale) n punctul unde
acesta fcuse apelul sistem sau nucleul l ntrerupsese (nainte de recunoaterea
semnalului).
4.
Nucleul schimb contextul regitrilor salvai al utilizatorului: acesta reseteaz
valorile pentru PC la adresa funciei de interceptare a semnalului i seteaz valoarea
pentru SP pentru a ine evidena creterii stivei utilizatorului.
Dup ntoarcerea din nucleu n modul user, procesul va executa funcia de tratare a
semnalului; cnd acesta termin funcia de tratare a semnalului, se ntoarce n locul
din codul utilizator unde a fost gsit apelul sistem sau s-a produs ntreruperea, imitnd
o ntoarcere din apelul sistem sau ntrerupere.
De exemplu, figura 8.9 conine un program care intercepteaz semnalele de
ntrerupere (SIGINT) i i trimite un semnal de ntrerupere (rezultatul apelului kill);
figura 8.10 conine prile relevante ale unei dezasamblri a modulului ncrcat pe un
VAX 11/780. Cnd sistemul execut procesul, apelul la rutina kill se va face la adresa
ee (n hexazecimal) i rutina execut instruciunea chmk (schimbarea modului n
nucleu) la adresa 10a pentru a face apelul sistem kill. Adresa de retur din apelul
sistem este 10c. n executarea apelului sistem, nucleul trimite un semnal de
ntrerupere ctre proces. Nucleul ia n considerare semnalul de ntrerupere cnd se
ntoarce n modul utilizator, mut adresa 10c din cotextul regitrilor salvai a
utilizatorului i o plaseaz n stiva utilizator. Nucleul ia adresa funciei de interceptare
a semnalului (catcher) 104 i o pune n contextul regitrilor salvai al utilizatorului.
Figura 8.11 arat strile stivei utilizator i contextul regitrilor salvai.
Exist cteva anomalii n algoritmul descris aici pentru tratarea semnalelor. Prima i
cea mai important este c nainte ca un proces s se ntoarc n modul utilizator dup
ce acesta trateaz un semnal, nucleul terge cmpul din u area care conine adresa
funciei de tratare a semnalului utilizator. Dac procesul dorete s trateze semnalul
193
din nou, acesta trebuie s fac apel din nou la funcia sistem signal. Aceasta are din
nefericire ramificaii: o condiie de concuren rezult deoarece a doua instan a
semnalului poate sosi nainte ca procesul s aib ocazia s invoce apelul sistem.
Pentru c procesul este executat n modul utilizator, nucleul ar putea face o comutare
de context, crescnd ansa ca procesul s recepioneze semnalul naintea resetrii
interceptorului de semnal.
#include <signal.h>
main()
{
extern chatcher();
signal(SIGINT, catcher);
kill(0, SIGINT);
}
catcher()
{
}
Figura 8.9. Codul surs al programului care intercepteaz semnale
pushab
0x18(pc)
ec:
pushl
$0x2
calls
$0x2,0x23(pc)
f5:
pushl
$0x2
f7:
clrl
-(sp)
calls
100:
ret
101:
halt
102:
halt
103:
halt
$0x2,0x8(pc)
_catcher()
104:
194
106:
ret
107:
halt
_kill()
# urmtoarea linie este n nucleu
10a:
chmk
$0x25
10c:
bgequ
0x6 <0x114>
10e:
jmp
0x14(pc)
114:
clrl
r0
116:
ret
Programul din figura 8.12 ilustreaz condiia de concuren. Procesul apeleaz signal
pentru a aranja interceptarea semnalului de ntrerupere i execut funcia sigcatcher.
Acesta creaz un proces fiu, invoc apelul sistem nice pentru a-i micora prioritatea
de programare relativ la procesul fiu (vezi cap. 8) i intr ntr-un ciclu infinit. Procesul
fiu i suspend execuia pentru 5 secunde pentru a da procesului printe timp s
execute apelul sistem nice i s-i scad prioritatea. apoi procesul fiu intr ntr-un
ciclu, trimind un semnal de ntrerupere (prin kill) la procesul pinte n timpul fiecrei
iteraii. Dac kill se ntoarce datorit unei erori, probabil datorit procesului printe
care nu mai exist, procesul fiu apeleaz exit. Ideea este c procesul printe ar trebui
s invoce interceptorul de semnale de fiecare dat cnd acesta recepioneaz un
semnal de ntrerupere. Interceptorul de semnale tiprete un mesaj i apeleaz din
nou signal pentru a intercepta urmtoarea apariie a unui semnal de ntrerupere i
printele continu s execute ciclul infinit.
#include <signal.h>
sigcatcher()
{
printf("PID %d se obine \n", getpid());
signal(SIGINT, sigchatcher);
}
main()
{
int ppid;
signal(SIGINT, sigcatcher);
if(fork==0)
{
195
/* tipretePID-ul */
*/
ppid=getppid();
for(;;)
if(kill(ppid,SIGINT)== -1)
exit();
}
/* prioritate sczut, posibilitate mare de prezentare a cursei */
nice(10);
for(;;)
}
Figura 8.12. Program ce demonstreaz condiiile de concuren n
interceptarea semnalelor
2.
Procesul printe intercepteaz semnalul i apeleaz interceptorul de semnale
dar nucleul elibereaz (preempt) procesul i comut contextul naintea executrii
apelului sistem signal.
3.
Procesul fiu se execut din nou i trimite un alt semnal de ntrerupere ctre
procesul printe.
4.
Procesul printe recepioneaz al doilea semnal de ntrerupere, dar acesta nu
are fcute setrile pentru a intercepta semnalul. Cnd acesta reia execuia, iese (exit).
mai multe semnale de un anumit tip nainte ca acesta s le poat trata. n sfrit,
sistemul BSD permite unui proces s blocheze i s deblocheze recepionarea unor
semnale printr-un nou apel sistem; cnd un proces deblocheaz semnalele, nucleul
trimite semnalele care au fost blocate ctre proces. Cnd un proces recepioneaz un
semnal, nucleul blocheaz n mod automat recepionarea altor semnale pn cnd
semnalul a fost tratat complet. Aceast metod este similar modului n care nucleul
reacioneaz la ntreruperile hard: acesta blocheaz transmiterea de noi ntreruperi n
timp ce sunt prelucrate cele anterioare.
O a doua anomalie n tratarea semnalelor se refer la interceptarea semnalelor care
apar n timp ce procesul se afl ntr-un apel sistem, n ateptare (sleep) pe o prioritate
ntreruptibil. Semnalul determin procesul s execute un longjmp din starea de
ateptare, ntoarcerea n modul utilizator i apelarea funciei de tratare a semnalului.
Cnd aceast funcie se termin, procesul pare a se ntoarce dintr-un apel sistem cu o
eroare care indic faptul c apelul sistem a fost ntrerupt. Utilizatorul poate verifica
eroarea ntoars i restarta apelul sistem, dar uneori este mai convenabil dac nucleul
face automat aceast restartare, aa cum este fcut n sistemul BSD.
O a treia anomalie apare n cazul n care un proces ignor un semnal. Dac semnalul
sosete n timp ce procesul se afl n ateptare pe un nivel de prioritate intreruptibil,
procesul se va trezi (wake up) dar nu va executa un longjmp. Aceasta nseamn c
nucleul realizeaz c ignor semnalul doar dup ce l scoate din ateptare i l pune n
execuie. O politic mai bun ar fi ca procesul s fie lsat n ateptare. Cu toate
acestea, nucleul depune adresa funciei semnal n u area i u area nu poate fi accesat
atunci cnd semnalul este trimis procesului. O soluie la aceast problem ar fi
nscrierea adresei funciei semnal n intrarea din tabela proceselor, unde nucleul ar
putea verifica dac procesul trebuie trezit la recepionarea unui semnal. Ca o
alternativ, procesul ar putea imediat s se ntoarc n starea sleep (ateptare)
utiliznd algoritmul sleep, dac i d seama c nu ar fi trebuit scos din ateptare. Cu
toate acestea, procesele utilizator nu realizeaz dac primul proces a fost trezit,
deoarece nucleul nchide intrare n algoritmul sleep ntr-o bucl while (vezi capitolul 2),
punnd procesul napoi n ateptare dac evenimentul sleep nu a aprut de fapt.
n sfrit, nucleul nu trateaz semnalele de "terminare a proceselor fii" (death of child)
la fel ca pe alte semnale. n particular, cnd procesul i d seama c a primit un
semnal de "terminare a proceselor fii" , anuleaz existena semnalului n cmpul de
semnale al tabelei proceselor i n acest caz acioneaz ca i cum nici un semnal nu ar
fi fost trimis. Efectul unui semnal de "terminare a proceselor fii" este trezirea unui
proces n ateptare pe o prioritate ntreruptibil. Dac procesul intercepteaz un
semnal de "terminare proces fiu", se apeleaz funcia de tratare utilizator ca i pentru
alte semnale. Operaiile pe care le face nucleul dac procesul ignor semnalele de
"terminare proces fiu", vor fi discutate n paragraful 8.4. n sfrit, dac un proces
invoc apelului sistem signal cu parametrul "terminare proces fiu", nucleul trimite
procesului apelant un semnal de "terminare proces fiu" dac acesta are procese n
starea zombie. n paragraful 8.4. se discut conveniile pentru apelul signal cu
parametrul "terminare proces fiu".
8.2.2.
Grupe de procese
Dei procesele ntr-un sistem UNIX sunt identificate printr-un numr unic (PID),
sistemul trebuie s identifice uneori procesele dup "grup". De exemplu, procesele cu
un strmo comun care este un login shell sunt n general nrudite i de aceea toate
aceste procese primesc semnale cnd un utilizator apas tastele "Delete" sau "Break"
sau cnd linia ctre terminal este ntrerupt. Nucleul utilizeaz identificatorul de grup
al proceselor pentru a identifica grupurile de procese nrudite care trebuie s
197
grp=setpgrp();
unde grp este noul numr al grupului de procese. Un fiu reine numrul de grup de
proces al printelui n timpul apelului sistem fork. Setpgrp are, de asemenea,
importan n setarea terminalului de control al unui proces (vezi paragraful 10.3.5.).
8.2.3.
Procesele trimit semnale folosind apelului sistem kill. Sintaxa pentru aceast funcie
este:
kill (pid, signum)
unde pid identific setul de procese care primesc semnalul i signum este numrul
semnalului trimis. n lista care urmeaz se arat corespondena dintre valorile pidurilor i setul de procese.
Dac pid-ul este un ntreg pozitiv, nucleul trimite un semnal proesului care are
identificatorul de proces pid.
Dac pid-ul este 0, nucleul trimite semnal tuturor proceselor din grupul
procesului care a trimis semnalul.
Dac pid-ul este -1, nucleul trimite semnal tuturor proceselor al cror UID (Uutilizator, ID-identificator) real este egal cu UID-ul efectiv al procesului care a trimis
semnalul (paragraful 8.6. va defini UID-ul real i efectiv). Dac procesul care trimite
semnalul are UID-ul efectiv al superutilizatorului, nucleul trimite semnalul tuturor
proceselor cu excepia proceselor 0 i 1.
Dac pid-ul este un ntreg negativ diferit de -1, nucleul trimite semnalul tuturor
proceselor din grupul de procese avnd valoarea absolut a pid-lui.
n toate cazurile, dac procesul care trimite semnalul nu are UID efectiv al
superutilizatorului, sau UID-ul su efeciv sau real este diferit de UID-ul procesului
destinatar, kill eueaz.
#include <signal.h>
main()
{
register int i;
198
setpgrp();
for(i=0;i<10;i++)
{
if(fork()==0)
{
/* procesul fiu */
if(i&1)
setpgrp();
printf("pid=%d pgrp=%d pgrp=%d\n", getpid(), getpgrp());
pause();
}
}
kill(0,SIGINT);
}
Figura 8.13. Exemplu de utilizare a lui Setpgrp
n programul din figura 8.13, procesul anuleaz numrul su de grup i creaz 10
procese fii. Cnd sunt creai, fiecare proces fiu are acelai numr de grup cu al
procesului printe, dar procesele create n timpul iteraiilor impare din ciclu reseteaz
numrul de grup. Apelurile sistem getpid i getpgrp ntorc PID-ul i ID-ul grupului
(GID) ale procesului care se execut, iar apelului sistem pause suspend execuia
procesului pn cnd acesta recepioneaz un semnal. La sfrit, procesul printe
execut apelului sistem kill i trimite un semnal de ntrerupere tuturor proceselor din
grupul su de procese. Nucleul trimite semnalul ctre 5 procese "pare" care nu i-au
schimbat propriu grup de procese n timp ce, cele 5 procese "impare" continu ciclul.
exit (status)
unde valoarea status este ntoars printelui pentru examinare. Procesele ar putea
apela exit explicit sau implicit la sfritul programului: rutina de pornire existent n
toate programele C apeleaz exit cnd programul revine din funcia main care este
punctul de intrare al tuturor programelor. Ca o alternativ, nucleul poate apela exit
199
intern pentru un proces la primirea unor semnale neinterceptate, dup cum s-a
discutat anterior. n acest caz, valoarea lui status este numrul semnalului.
Sistemul nu impune limite de timp pentru execuia unui proces i astfel procesele pot
exista pentru o perioad ndelungat. De exemplu, procesele 0 (swapper-ul) i 1 (init)
exist de-a lungul ntregii funcionri a sistemului. Alte exemple sunt procesele getty,
cate urmresc o linie de terminal, ateptnd intrarea unui utilizator i procesele
administrative cu scop dedicat.
algoritm exit
intrri: codul de retur pentru procesul printe
ieiri: niciuna
{
ignor toate semnalele;
if( conductorul grupului de procese este asociat cu terminalul de
control)
{
trimite semnalul de suspendare a tuturor membrilor grului de
procese;
iniializeaz grupul proceselor pentru toi membrii la 9;
}
nchide toate fiierele (versiunea intern a algoritmului close);
elibereaz directorul curent (algoritmul iput);
elibereaz rdcina curent (schimbat) dac exist (alg. iput);
elibereaz regiunile de memorie asociate procesului (alg. freereg);
scrie nregistrarea cu informaii de contabilitate;
trece procesul n starea zombie;
atribuie PID-ului printe al tuturor fiilor ca fiind procesul init (1);
dac vreun proces fiu era n starea zombie trimite semnalul de
"terminare proces fiu" ctre init;
trimite semnal de "terminare proces fiu" procesului printe;
comut contextul;
}
mai exist procese n execuie asociate terminalului, procesul care face exit le va
trimite un semnal de suspendare. Nucleul iniializeaz, de asemenea, numrul de grup
la 0 pentru procesele din grup, deoarece este posibil ca un alt proces s ia
identificatorul procesului care tocmai a executat exit i s fie un conductor de grup.
Procesele care au aparinut vechiului grup nu vor aparine i grupului urmtor. Nucleul
parcurge descriptorii fiierelor deschise, nchizndu-le pe fiecare prin algoritmul intern
close i elibereraz inodurile care au fost folosite pentru directorul curent i rdcina
schimbat (dac exist) prin algoritmul iput.
Nucleul elibereaz apoi toat memoria utilizatorului prin tererea regiunilor
corespunztoare cu algoritmul detachreg i schimb starea procesului n zombie. El
salveaz codul de stare al lui exit i timpii de execuie utilizator i nucleu acumulai de
proces i descendenii lui n tabela de procese. Descrierea lui wait n paragraful 8.4
arat cum un proces ia datele de timp pentru procesele descendente. Nucleul mai
scrie, de asemenea, o nregistrare de contabilitate ntr-un fiier global de cont ce
conine diferite statistici de rulare cum ar fi identificatorul utilizatorului, utilizarea CPUlui i a memoriei precum i cantitatea de operaii de intrare/ieire pentru un proces.
Programele la nivel de utilizator pot citi mai trziu fiierul de contabilitate pentru a
obine diferite date statistice utile pentru urmrirea performanelor i pentru taxarea
clientului. La sfrit, nucleul deconecteaz procesul de la arborele de procese fcnd
ca procesul 1 (init) s adopte toate procesele sale fii. Aceasta presupune c procesul 1
devine printe legal al tuturor proceselor fii n execuie pe care procesul care a
executat exit le-a creat. Dac vreunul din procesele de tip fiu este n starea zombie,
procesul ce execut exit trimite ctre init semnalul de "terminare proces fiu" pentru ca
init s-l tearg din tabela proceselor (vezi 8.9); procesul care execut exit trimite i
printelui su un semnal de terminare proces fiu. ntr-un scenariu tipic, procesul
printe execut un apel sistem wait pentru a se putea sincroniza cu procesul fiu care
face exit. Procesul aflat n starea zombie face o comutare de context astfel c nucleul
poate s programeze un alt proces pentru execuie; nucleul nu va programa pentru
execuie un proces aflat n starea zombie.
n programul din figura 8.15 un proces i creaz un fiu care i tiprete propriul PID
i execut apelul sistem pause suspendndu-i execuia pn cnd primete un
semnal. Procesul printe tiprete PID-ul fiului i execut exit ntorcnd PID-ul fiului
drept cod de stare. Dac apelul exit nu ar fi prezent, rutina de lansare procese ar
apela exit la ntoarcerea programului din funcia main. Procesul fiu creat de printe
continu s existe pn cnd primete un semnal, chiar dac procesul printe s-a
terminat.
main()
{
int child;
if((chil=fork())==0)
{
printf({child PID %d\n", getpid());
pause() /* suspend execuia pn la recepionarea unui semnal*/
}
/* printe */
printf("child PID %d\n", child);
201
exit(child);
}
Figura 8.15. Exemplu de Exit
pid=wait (stat_addr);
unde pid este identificatorul de proces al fiului din starea zombie, iar stat_addr este
adresa unui ntreg din spaiul utilizator ce va conine codul de ieire al fiului.
Figura 8.16 prezint algoritmul pentru wait. Nucleul caut un fiu al procesului n starea
zombie i dac nu gsete nici unul ntoarce o eroare. Dac gsete un fiu n starea
zombie, extrage PID-ul i parametrul furnizat apelului sistem exit al fiului i ntoarce
aceste valori la terminarea apelului sistem. Un proces ce execut exit poate astfel
preciza diferite coduri de retur pentru a da sens execuiei apelului exit, dar multe
programe nu le stabilesc n mod explicit n practic. Nucleul adaug timpul acumulat
de execuia procesului fiu n modurile utilizator i nucleu la cmpurile corespunztoare
din u area a procesului printe i n final elibereaz poziia din tabela proceselor ce
fusese ocupat de procesul n stare zombie. Aceast poziie devine disponibil pentru
noi procese.
Dac procesul ce execut wait are procese de tip fiu, dar niciunul dintre acestea nu se
afl n starea zombie, el trece n ateptare la o prioritate ntreruptibil pn la sosirea
unui semnal. Nucleul nu are un apel explicit de reluare a unui proces n ateptare prin
wait: astfel de procese sunt reluate numai la primirea unor semnale. Pentru orice
semnal, cu excepia celui de "terminare proces fiu" procesul va reaciona dup cum sa descris anterior. Cu toate acestea, dac semnalul este de "terminare proces fiu",
procesul ar putea rspunde n mod diferit.
n cazul implicit el va fi reluat din wait iar sleep va invoca algoritmului issig
pentru a cuta semnalele. Issig (figura 8.7) recunoate cazul special al semnalului
"terminare proces fiu" i ntoarce fals. Ca urmare nucleul nu execut un longjmp din
sleep ci se ntoarce n wait. Nucleul va relua bucla wait, gsete un fiu n starea
zombie -cel puin unul exist, elibereaz poziia slotul fiului din tabela proceselor i se
ntoarce din apelului sistem wait.
Dac procesul ignor semnalele de "terminare proces fiu", nucleul reia bucla
wait, elibereaz sloturile din tabela proceselor corespunztoare proceselor fii aflai n
starea zombie i caut ali fii.
202
algoritm wait
intrri: adresa unei variabile de pstrare a strii procesului ce execut
exit
ieiri: identificatorul fiului, codul de ieire al fiului
{
if(procesul n ateptare nu are procese fii)
return(eroare);
for(;;)
buclei */
{
if(procesul n ateptare are un fiu n starea zombie)
{
alege un fiu aflat n starea zombie;
adaug timpul de folosire a CPU la printe;
elibereaz intrarea fiului din tabela de procese;
return(ID-ul fiului, codul de ieire al fiului);
}
if(procesul nu are fii)
return eroare;
ateapt cu o prioritate ntreruptibit pe evenimentul :(se iese din
procesul fiu);
}
}
Figura 8.16. Algoritmul pentru wait
De exemplu, un utilizator obine diferite rezultate cnd apeleaz programul din figura
8.17 cu sau fr parametri. S considerm mai nti cazul n care utilizatorul apeleaz
programul cu un parametru (argc este 1, numele programului). Procesul printe
creaz 15 procese fiu, care eventual apeleaz exit returnnd codul i, valoarea
variabilei de ciclu cnd procesele fiu au fost create. Nucleul execut apelul sistem wait
pentru procesul printe, gsete un proces fiu n starea zombie i ntoarce
identificatorul su de proces i codul lui exit. Procesul fiu care este gsit este
nedeterminat. n biblioteca C, procedura pentru pentru apelul sistem exit pstreaz
codul n biii de la 8 la 15 ai variabilei ret_code i ntoarce identificatorul procesului fiu
pentru apelul wait. Astfel variabila ret_code este egal cu 256*i, depinznd de
valoarea lui i pentru procesul fiu i ret_val este egal cu valoarea identificatorului de
proces pentru procesul fiu.
Dac utilizatorul apeleaz programul de mai sus cu parametri (argc>1), procesul
printe apeleaz signal pentru a ignora semnalele de terminare proces fiu. S
presupunem c procesul printe ateapt n wait nainte ca vreun proces s apeleze
exit: cnd procesul fiu apeleaz exit se trimite un semnal de terminare proces fiu la
printe: procesul printe prsete starea de ateptare, pentru c ateptarea sa n
wait este la o prioritate ntreruptibil. Cnd procesul printe eventual ruleaz, acesta
gsete c cel mai important semnal a fost de "terminare proces fiu"; dar pentru c se
203
ignor semnalul de "terminare proces fiu", nucleul terge intrarea procesului aflat n
starea zombie din tabela proceselor i continu execuia lui wait ca i cum nu ar fi
existat nici un semnal.
#include <signal.h>
main(argc,argv)
int argc;
char *argv[];
{
int i, ret_val, ret_code;
if(argc>=1)
signal(SIGCLD, SIG_IGN);
fiier, crend un proces fiu la fiecare citire. Cu toate acestea, procesul printe nu
ateapt terminarea vreunui proces fiu deoarece dorete expedierea ct mai rapid a
proceselor iar un proces fiu poate exista destul de mult timp (pn s fac exit). Dac
printele face n aa fel nct apelul signal s ignore semnalele de "terminare proces
fiu" , nucleul va elibera intrrile proceselor aflate n starea zombie n mod automat.
Altfel, procesele aflate n starea zombie ar putea umple, eventual la maxim, locurile
permise n tabela de procese.
#include <signal.h>
main(argc,argv)
{
char buf[256];
if(argc!=1)
signal(SIGCLD,SIG_IGN);
unde filename este numele fiierului executabil care a fost apelat, argv este un pointer
ctre un vector de pointeri la iruri care sunt parametrii programului executabil iar
envp este un pointer ctre un vector de pointeri la iruri care reprezint mediul
programului executat. Exist mai multe funcii de bibliotec care apeleaz exec cum ar
fi execl, execv, execle, etc. Toate acestea apeleaz eventual pe execve, din acest
205
motiv acesta este folosit aici pentru a specifica apelul sistem exec. Cnd un program
folosete parametrii n linia de comand, cum ar fi
main(argc,argv)
vectorul argv este o copie a parametrului argv n exec. irurile de caractere n mediu
sunt de forma "nume=valoare" i pot conine informaii utile programelor cum ar fi
directorul propriu al utilizatorului i o cale de directoare pentru a cuta programe
executabile. Procesele i pot accesa mediul prin variabila environ, iniializat de rutina
de pornire a C-ului.
algoritm exec
intrri:
ieiri: nimic
{
obine inodul fiierului (algoritmul namei);
verifi dac fiierul e executabil, dac utilizatorul are drept de
execuie;
citete header-ele de fiier, verific dac sunt module ncrcabile;
copiaz parametrii lui exec din spaiul vechi de adrese n spaiul
sistemului;
for(fiecare regiune specificat n modului ncrcat)
{
aloc regiuni noi (algoritm allocreg);
ataeaz regiunile (algoritmul attachreg);
ncarc regiunea n memorie dac este cazul (algoritmul loadreg);
}
copiaz parametrii lui exec n noua regiune de stiv a utilizatorului;
procesare special pentru programele setuid, opiune de execuie pas
cu pas;
elibereaz inodul fiierului (algoritmul iput);
}
Figura 8.19. Algoritmul pentru exec
Figura 8.19 prezint algoritmul pentru exec. n primul rnd exec acceseaz fiierul prin
algoritmul namei pentru a determina dac este executabil, obinuit (nu e director) i
pentru a determina dac utilizatorul are drept de execuie asupra programului. Nucleul
citete apoi header-ul fiierului pentru a determina mrimea fiierului executabil.
206
Figura 8.20 prezint formatul logic al unui fiier executabil, aa cum exist n sistemul
de fiiere, n mod tipic generat de asamblor sau ncrctor. Acesta conine 4 pri:
1.
Header-ul primar care precizeaz cte seciuni sunt n fiier, adresa de start
pentru execuia procesului i "numrul magic" care d tipul fiierului executabil.
2.
Header-ele de seciune descriu fiecare seciune din fiier, dnd mrimea
seciunii, adresele virtuale pe care ar trebui s le ocupe seciunea atunci cnd ruleaz
n sistem i alte informaii.
3.
Seciunile conin att zon de "date",ct i zon de text, care sunt iniial
ncrcate n spaiul de adrese al procesului.
4.
Diferite seciuni ar putea conine tabele de simboluri i alte date utile n
depanare.
Formatele specifice au evoluat de-a lungul anilor dar toate fiierele executabile au
coninut un header primar cu un "numr magic".
Numrul magic este un ntreg scurt care identific fiierul ca un modul ncrcabil i d
nucleului posibilitatea s disting diferite caracteristici de rulare ale sale. De exemplu,
folosirea unui numr magic pe un PDP 11/70 informeaz nucleul c procesul poate
utiliza pn la 128 ko de memorie n loc de 64 ko; numrul magic joac, de
asemenea, un rol important n sistemele de paginare, dup cum se va vedea n
capitolul 9.
Header-ul primar
Header-ul seciunii 1
Numrul magic
Numrul seciunilor
Valorile iniiale ale regitrilor
Tipul seciunii
Mrimea seciunii
Adresa virtual
Header-ul seciunii 2
Tipul seciunii
Mrimea seciunii
Adresa virtual
Header-ul seciunii n
Seciunea 1
:
:
Tipul seciunii
Mrimea seciunii
Adresa virtual
Date (de exemplu, text)
Seciunea 2
Date
Seciunea n
:
:
Date
Alte informaii
pstra irurile de caractere, n funcie de implementare. Cele mai obinuite locuri sunt
stiva nucleului (un vetor local n rutina nucleului), zonele nealocate (cum ar fi paginile)
de memorie care pot fi mprumutate temporar sau memoria secundar, cum ar fi un
dispozitiv de swapping.
Cea mai simpl implementare pentru copierea parametrilor n noul context la nivel de
utilizator este folosirea stivei nucleului. Dar, deoarece configurarea sistemului impune
n mod obinuit o limit de mrime pentru stiva nucleului i deoarece parametrii lui
exec pot avea o lungime neprecizat, planul trebuie s fie combinat cu altul. Din
celelalte variante, implementrile utilizeaz cea mai rapid metod. Dac este uor s
se aloce pagini de memorie, o astfel de metod este preferabil deoarece accesul la
memoria primar este mai rapid dect la cea secundar (cum ar fi un dispozitiv de
swapping).
Dup copierea parametrilor lui exec n locul de pstrare din nucleu, nucleul detaeaz
vechile regiuni ale procesului folosind algoritmul detachreg. Tratamentul special pentru
regiunile de text va fi discutat mai trziu n acest paragraf. n acest moment procesul
nu are context la nivel utilizator aa nct erorile care apar de acum nainte duc la
terminarea sa, cauzat de un semnal. Astfel de erori pot fi: rularea n afara spaiului
coninut n tabela de regiuni a nucleului, ncercarea de a ncrca un program a crui
mrime depete limita sistemului, ncercarea de a ncrca unui program ale crei
adrese de regiuni se suprapun, i altele. Nucleul aloc i ataeaz regiuni pentru text
i date, ncrcnd coninutul fiierului executabil n memoria principal (algoritmii
allocreg, attachreg i loadreg). Regiunea de date a unui proces este iniial mprit n
dou: date iniializate n momentul compilrii i date care nu se iniializeaz n
momentul compilrii ("bss"). Alocarea i ataarea iniial a regiunilor de date se face
pentru datele iniializate. Nucleul mrete atunci regiunea de date folosind
algoritumul growreg pentru datele "bss" i iniializeaz valoarea memoriei cu 0. n
final, aloc o regiune pentru stiva procesului, o ataeaz procesului i aloc memorie
pentru pstrarea parametrilor funciei exec. Dac nucleul a salvat parametrii lui exec
n pagini de memorie, acesta poate utiliza aceste pagini pentru stiv. n caz contrar, el
copiaz parametrii lui exec n stiva utilizatorului.
Nucleul terge adresele interceptorilor de semnale utilizator din u area, deoarece
aceste adrese nu mai au sens n noul context la nivel utilizator. Semnalele care au fost
ignorate rmn ignorate i n noul context. Nucleul seteaz apoi contextul regitrilor
salvai pentru modul utlizator, setnd SP-ul i PC-ul iniiale: ncrctorul a scris PC-ul
iniial n header-ul fiierului. Nucleul execut aciuni speciale pentru programele setuid
i pentru facilitile de execuie pas cu pas, aciuni descrise n capitolul 11. La sfrit,
el invoc algoritmul iput, elibernd inodul care a fost alocat iniial prin algoritmul
namei la nceputul lui exec. Utilizarea lui namei i iput n exec corespunde deschiderii
i nchiderii unui fiier; starea unui fiier n timpul apelului lui exec este asemntoare
celei a unui fiier deschis cu excepia absenei unei intrri n tabela de fiiere. Cnd
procesul execut ntoarcerea din apelul exec, el execut codul noului program.
Oricum, este acelai proces de dinainte de exec: identificatorul su de proces nu s-a
schimbat i nici poziia sa n ierarhia de procese. Numai contextul la nivel utilizator se
schimb.
main()
{
int status;
208
if(fork()==0)
exec("/bin/date","date",0);
wait(&status);
}
Figura 8.21 Utilizarea lui Exec
De exemplu, programul din figura 8.21 creaz un proces fiu care apeleaz exec.
Imediat dup ce printele i fiul se ntorc din apelul fork acestea execut copii
indepentente ale programului. Cnd procesul fiu este pe cale de a apela exec, regiunea
sa de cod (text) const din instruciunile programului, regiunea sa de date conine
irurile "/bin/date", i "date", iar stiva sa conine nivelele pe care procesul a executat
push pentru a putea apela exec. Nucleul gsete fiierul "/bin/date" n sistemul de
fiiere, observ c toi utilizatorii l pot executa i determin dac este un modul
executabil. Prin convenie primul parametru al listei de parametri argv pentru exec
este calea ctre numele fiierului executabil. Procesul are astfel acces la numele
programului la nivel de utlizator, ceea ce uneori reprezint o caracteristic util.
Nucleul copiaz apoi irurile "/bin/date" i "date" ntr-un domeniu de pstrare i
elibereaz reginile de cod, date i stiv ocupate de proces. El aloc procesului noi
regiuni de cod, date i stiv, copiaz seciunea di fiierul "/bin/date" ce conine
instruciuni n regiunea de cod (text) i copiaz seciunea de date a fiierului n regiune
de date. Nucleul reconstruiete lista original de parametri (aici irul de caractere
"date") i o pune n regiunea de stiv. Dup apelul lui exec, procesul fiu nu mai
execut vechiul program ci execut programul "date". Cnd programul "date" se
termin, procesul printe primete starea de ieire din propriul apel wait.
Pn acum, am presupus c, codul i datele procesului ocup prii separate n
programul executabil i, din acest motiv, regiuni separate la rularea programului. Sunt
dou avantaje pentru inerea codului i a datelor programului separate: protecie i
folosire n comun (sharring). Dac codul i datele s-ar afla n aceeai regiune,
sistemul nu ar putea preveni procesul la suprascrierea instruciunilor, pentru c nu ar
ti care adrese conin instruciuni i care date. Dar dac codul i datele sunt n regiuni
separate, nucleul poate activa mecanismul de protecie hard pentru a preveni
procesele la suprascriere n zona de cod. Dac procesul ncearc din greeal s
suprascrie spaiul su de cod, se creaz o ntrerupere de protecie care n mod obinuit
rezult n terminarea procesului.
#include <signal.h>
main()
{
int i, *p;
extern f(), sigcatch();
ip=(int *)f; /* asigneaz ip la adresa funciei f */
for(i=0; i<20; i++)
signal(i, sigcatch);
*ip=1;
}
f()
{
}
sigcatch(n)
int n;
{
printf("recepionat semnalul %d\n", n);
exit(1);
}
Figura 8.22 Exemplu de program care i suprascrie codul
De exemplu, programul din figura 8.22 atribuie pointerului ip adresa funciei f i apoi
aranjeaz s fie interceptate toate semnalele. Dac programul este compilat aa nct
codul i datele sunt n regiuni separate, procesul care execut programul genereaz o
ntrerupere de protecie cnd se ncearc s se scrie coninutul lui ip, deoarece se
ncearc scrierea n regiunea de cod care este cu protecie la scriere. Pe un computer
AT&T 3B20, nucleul trimite semnalul SIGBUS la proces dar n alte implementri pot fi
trimise alte semnale. Procesul intercepteaz semnalul i apeleaz exit fr a executa
instruciunea printf din main. Totui, dac programul a fost compilat astfel nct codul
i datele s fie n aceeai regiune (regiunea de date), nucleul nu ar putea realiza c un
proces a suprascris adresa funciei f. Adresa funciei f conine valoarea 1! Procesul
execut instruciunea printf n main dar execut o instruciune nepermis cnd se face
apelul lui f. Nucleul trimite semnalul SIGILL i procesul apeleaz exit.
Pstrarea de date i instruciuni n regiuni separate face uoar protecia mpotriva
erorilor de adresare. Versiune anterioare de UNIX permiteau codului i datelor s se
afle n aceeai regiune din cauza limitrii lungimii proceselor impuse de calculatoarele
PDP: programele erau mai mici i necesitau civa regitri de "segmentare" dac
codul i datele ocupau aceeai regiune. Versiunea curent a sistemului nu are o astfel
de limitare a mrimii proceselor i viitoarele compilatoare nu vor accepta opiunea de
ncrcare a codului i datelor n aceeai regiune.
Al doilea avantaj al scrierii n regiuni separate a codului i datelor este permiterea
mpririi regiunii. Dac procesul nu poate scrie n regiunea de cod, codul nu se poate
schimba n timpul ce nucleul l ncarc din fiierul executabil. Dac mai multe procese
execut un fiier, ele pot s mpart regiunea de cod, salvnd memoria. Astfel, cnd
nucleul aloc o regiune de cod pentru un proces n exec, acesta verific dac fiierul
executabil permite ca codul su s fie mprit, indicaie dat de numrul magic. Dac
se ntmpl aceasta, se parcurge algoritmul xalloc pentru a gsi o regiune existent
pentru codul fiierului sau pentru a asigna una nou (vezi figura 8.23).
n xalloc, nucleul caut n lista regiunilor active dup regiunea de cod a fiierului,
identificnd-o pe cea al crei pointer inode se potrivete cu inodul fiierului executabil.
Dac o asemenea regiune nu exis, nucleul aloc o nou regiune (algoritmul alloreg),
o ataeaz procesului (algoritmul attachreg), o ncarc n memorie (algoritmul
loadreg) i i schimb protecia n read-only. Ultimul pas provoac o ntrerupere de
protecie a memoriei dac un proces ncearc s scrie n regiunea de cod. Dac, n
210
cutarea prin lista regiunilor active, nucleul localizeaz o regiune care conine codul
fiierului, se asigur c regiunea este ncrcat n memorie (altfel ateapt) i o
ataeaz procesului.
algoritmul xalloc
211
only;
deblocheaz regiunea;
}
Figura 8.23 Algoritmul pentru alocarea regiunilor de cod
Nucleul deblocheaz regiunea la sfritul lui xalloc i decrementeaz contorul regiunii
mai trziu, cnd acesta execut detachreg, n timpul apelului exit sau exec.
Implementarea tradiional a sistemului conine o tabel de coduri (text table) pe care
nucleul o manipuleaz n modul deja descris pentru regiunile de cod. Setul regiunilor
de cod poate fi astfel vzut ca o versiune modern a vechii tabele de cod.
S ne amintim c atunci cnd se aloc o regiune pentru prima dat n allocreg
(paragraful 6.5.2), nucleul incrementeaz contorul de referin al inodului asociat cu
regiunea, dup ce a incrementat contorul de referin n namei (apelul lui iget) la
nceputul lui exec. Pentru c nucleul decrementeaz contorul de referin o singur
dat n iput, la sfritul lui exec, contorul de referin al inodului fiierului care este
executat este cel puin egal cu 1: de aceea, dac un proces parcurge algoritmul
unlinks pentru un fiier, coninutul lui rmne intact. Nucleul nu mai are nevoie de
fiier dup ncrcarea sa n memorie, dar are nevoie de un pointer la inodul imaginii
fiierului din memoria intern n tabela de regiuni pentru a identifica fiierul care
corespunde regiunii respective. Dac contorul de referin a fost redus la 0, nucleul ar
putea realoca inodul imaginii fiierului din memoria intern altui fiier, compromind
coninutul pointerului inode n tabela regiunilor: dac utilizatorul a apelat exec pentru
noul fiier, nucleul ar gsi regiunea de cod a vechiului fiier cu erori. Nucleul evit
aceast problem incrementnd contorul de referine al inodului n algoritmul allocreg,
prevenind reasignarea inodului imaginii fiierului din memoria intern. Cnd procesul
detaeaz regiunea de cod n timpul lui exit sau exec, nucleul decrementeaz contorul
de referin n freereg, mai puin n cazul n care inodul are modul sticky-bit setat, aa
cum se va vedea.
Tabela inode-lor
.
.:
.
Tabela regiunilor
scenariu posibil dac
/bin/date are numrtorul
de referine 0
imaginea inode-lui
pentru /bin/date
:
:
:
:
regiune de text
pentru /bin/date
:
:
regiune de text
pentru /bin/date
:
:
setat n
1.
Dac un proces deschide fiierul pentru scriere, operaiile de scriere vor
schimba coninutul fiierului, invalidnd coninutul regiunii.
2.
Dac un proces schimb modul de acces la fiier (chmod) astfel nct sticky-bit
nu mai este setat, fiierul nu ar mai rmne n tabela regiunilor.
3.
Dac un proces execut unlinks pentru fiier, nici un proces nu va mai putea
s-l execute n continuare pentru c fiierul nu mai are intrare n sistemul de fiiere;
din acest motiv nici un proces nou nu va accesa intrarea fiierului din tabela regiunilor.
Pentru c nu este nevoie de regiunea de cod, nucleul poate elibera unele resurse.
4.
Dac un proces "demonteaz" sistemul de fiiere, fiierul nu va mai fi accesibil
i nici un proces nu-l va mai putea executa, la fel ca n cazul precedent.
5.
Dac nucleul ruleaz n afara spaiului alocat de dispozitivul de swap, se
ncearc s se elibereze spaiul disponibil prin eliberarea regiunilor sticky-bit care sunt
n mod curent neutilizate. Dei alte procese pot avea nevoie de o regiune de cod
curnd, nucleul dorete acest lucru i mai repede.
Regiunea de cod supus aciunii lui sticky trebuie s fie tears n primele dou cazuri
pentru c ea nu mai reflect starea curent a fiierului. Nucleul terge intrrile sticky
n ultimele trei cazuri, pentru c acest lucru este mult mai practic. Desigur, nucleul
elibereaz doar regiunile pe care nici un proces nu le utilizeaz n momentul respectiv
(numrul lor de referine este 0); altfel apelurile sistem open, unlink, unmount
(cazurile 1,3 i 4)ar funciona greit.
213
Scenariul pentru exec este puin mai complicat dac procesul se execut (exec) pe el
nsui. Dac utilizatorul tiprete:
sh script
shell-ul face un fork i procesul fiu face un apel exec asupra shell-lui i execut
comenzile din fiierul "script". Dac un proces se execut singur i permite mprirea
propriei regiuni de cod, nucleul trebuie s evite situaiile de blocare a inodurilor i a
regiunilor. Adic, nucleul nu poate bloca vechea regiune de cod, s o in blocat, i
apoi s ncerce s blocheze noua regiune de cod, deoarece regiunile (veche i nou)
sunt una i aceeai. n schimb, nucleul las vechea regiune de cod ataat la proces,
din moment ce va fi reutilizat oricum.
Procesele apeleaz uzual exec dup fork; astfel procesul fiu copiaz spaiul de adrese
al printelui n timpul apelului sistem fork, scrie acest spaiu n timpul lui exec i
execut o imagine program diferit de cea a printelui. Nu ar fi mai natural s
combinm cele dou apeluri ntr-unul singur pentru a apela programul i a-l rula ca un
nou proces? Ritchie presupune c fork i exec sunt apeluri sistem diferite pentru c
atunci cnd s-a proiectat sistemul UNIX, el i Thomson au putut s adauge apelul
sistem fork fr a face prea multe schimbri de cod n nucleul existent.Separarea lui
fork de exec este funcional important, pentru c procesele pot manipula proprii
descriptori de fiiere de intrare i ieire standard independent pentru a seta pipe-urile
mai elegant dect ar face-o ambele apeluri combinate ntr-unul singur. n exemplul de
shell din paragraful 8.8. se va vedea aceast trstur.
setuid(uid)
214
unde uid este noul identificator utilizator i rezultatul su depinde de valoarea curent
a identificatorului utilizator efectiv. Dac identificatorul utilizator efectiv al procesului
apelant este acela al superutilizatorului, nucleul reseteaz cei doi identificatori
(cmpurile lor) din tabela proceselor i u area, la uid. Dac UID-ul efectiv nu este
acela al superutilizatorului, nucleul reseteaz identificatorul utilizator efectiv n u area
la uid dac uid-ul are valoarea identificatorului utilizator real sau dac are valoarea
identificatorului utilizator salvat. n celelalte cazuri, apelul sistem se ntoarce cu
eroare. n general, procesul motenete identificatorii si (real i efectiv) de la printe
n timpul apelului sistem fork i pstreaz aceste valori pe tot parcursul execuiei
apelului sistem exec.
Programul din figura 8.25 demonstreaz apelul sistem setuid. Presupunem c fiierul
executabil produs prin compilarea programului are propietar pe "mauny"
(identificatorul utilizator 8319), bitul su pentru setuid este activat i toi utilizatorii au
permisiunea s-l execute. n plus, presupunem c utilizatorii "mjb" (identificatorul
utilizator 5088) i "maury" au n proprietate fiiere cu aceleai nume i ambele fiiere
au setat dreptul de acces read-only. Utilizatorul "mjb" vede urmtoarele date la ieire
cnd execut programul:
215
brk(endds);
unde endds devine valoarea celei mai mari adrese virtuale a regiunii de date a
procesului (denumit valoare de break). Ca o alternativ, utilizatorul poate apela:
oldendds=sbrk(increment);
unde increment schimb valoarea curent de break prin specificarea numrului de bii
i oldennds este valoarea de break nainte de apel. Sbrk este o subrutin din biblioteca
C care apeleaz brk. Dac spaiul dedate al procesului crete n urma apelului, noul
spaiu de date alocat este virtual contiguu la vechiul spaiu de date; de aceea, spaiul
virtual de adrese al procesului se extinde n continuarea spaiului de date nou alocat.
Nucleul verific dac mrimea noului proces este mai mic dect maxima sistemului i
dac noua regiune de date nu se suprapune cu spaiul de adrese virtual asignat
anterior (figura 8.26). Dac toate verificrile sunt executate, nucleul apeleaz growreg
pentru a aloca memorie auxiliar (de exemplu, tabele de pagini) pentru regiunea de
date i incrementeaz cmpul de mrime a procesului. n sistemul cu swapping,
ncearc de asemenea s aloce memorie pentru noul spaiu i i se iniializeaz
coninutul cu 0; dac nu exist spaiu de memorie se face swap-out pentru a crea
spaiu (explicat n detaliu ncapitolul 9). Dac procesul apeleaz brk pentru a elibera
spaiul alocat nainte, nucleul elibereaz memoria; dac procesul acceseaz adrese
virtuale n spaiul de adrese ale paginilor eliberate, se creaz o "ntrereupere" de
memorie.
algoritmul brk
217
*cp++=1;
}
catcher (signo)
int signo;
{
callno++;
printf(" semnalul prins %d %d la adresa %u\n", signo, callno, cp);
sbrk (256);
signal ()SIGSEGV, catcher);
}
219
8.8. Shell-ul
Acest capitol a acoperit suficiente noiuni pentru a explica cum lucreaz shell-ul. Shellul este mai complex dect este descris aici, dar relaiile procesului sunt ilustrate de un
program real. Figura 8.28 arat ciclul principal al shell-lui i demonstreaz execuia
asincron, redirectarea ieirii i conductele de tip pipe.
who
grep -n include *.c
ls -1
Shell-ul face un apel fork i creaz un proces fiu care execut programul pe care
utilizatorul l specific n linia de comand. Procesul printe, shell-ul care este folosit
de utilizator, ateapt pn cnd procesul fiu iese din comanda dat i atunci revine
pentru a citi comanda urmtoare.
Pentru a rula un proces asincron (n fundal), ca de exemplu
221
shell-ul seteaz o variabil intern amper cnd analizeaz caracterul &. Dac gsete
variabila setat la sfritul buclei, nu se execut wait dar imediat rencepe ciclul i
citete urmtoarea linie de comand.
Poza arat c procesul fiu are acces la copia liniei de comand dup fork. Pentru a
putea redirecta ieirea standard a fiierului, ca de exemplu
procesul fiu creaz fiierul de ieire specificat n linia de comand; dac apelul creat nu
se ncheie cu succes (de exemplu, la crearea fiierului n director fr drept de acces ),
procesul fiu ar trebui s apeleze imediat exit. Dc apelul creat se ncheie cu succes,
procesul fiu nchide fiierul de ieire standard anterior i duplic descriptorul de fiier
al noului fiier de ieire. Descriptorul fiierului de ieire standard refer acum fiierul
de ieire redirectat. Procesul fiu nchide descriptorul de fiier obinut prin apelul creat
pentru a conserva descriptorii de fiier pentru programul executat. Shell-ul
redirecteaz fiierele de intrare i eroare standard ntr-un mod similar.
Shell
atept
iese
wc
citete
scrie
ls-1
Figura 8.29 Relaiile proceselor pentru ls -l|wc
Codul arat cum shell-ul ar trata o linie de comand cu o singur conduct (pipe), ca
de exemplu
ls -l|wc
Dup ce procesul printe apeleaz fork i creaz un proces fiu, fiul creaz o conduct
de tip pipe. Procesul fiu execut fork; el i fiecare fiu al su trateaz fiecare cte o
component a liniei de comand. Procesul fiu (mai mare) creat prin al doilea fork
execut prima component a comenzii (ls): scrie n conduct, aa c nchide
descriptorul de fiier de ieire standard, duplic descriptorul de scriere al conductei i
nchide descriptorul original de scriere al conductei n momentul cnd nu mai este
necesar. Printele (wc) ultimului proces fiu (ls) este fiul procesului shell original (vezi
figura 8.29). Acest proces (wc) nchide propriul fiier de intrare standard i duplic
222
algoritmul start
intrri: niciuna
ieiri: niciuna
{
iniializarea tuturor structurilor de date ale sistemului;
pseudo-montarea rdcinii;
pregtirea mediului pentru procesul 0;
apel fork pentru procesul 1:
{
/* procesul 1 */
aloc regiune;
ataeaz regiunea la spaiul iniial de adrese;
crete mrimea regiunii n concordan cu codul care va fi copiat
n ea;
copiaz codul din spaiul nucleu pentru a iniializa spaiul
utilizator pentru a executa init;
schimb modul din modul nucleu n modul utilizator;
/* init niciodat nu va fi aici... ca rezultat al modului schimbat
nainte, init
*/
}
/* procesul 0 continu aici */
apel fork pentru procesele nucleu;
/* procesul 0 invoc swapper-ul pentru a face management-ul alocrii
spaiului de
Acesta este
un ciclu infinit;
/* starea se potrivete */
if( fork()==0)
{
execl("procesul specificat n buffer");
exit();
}
/* procesul init nu ateapt */
/* sare la while */
}
while((id=wait((init *)0))!=-1)
{
/* aici verific dac procesul fiu lansat s-a terminat;
* consider relansarea lui */
225
227