CATEGORII DOCUMENTE |
Singurul mod prin care un utilizator poate crea un nou proces in UNIX este folosirea apelului sistem fork. Procesul care apeleaza fork este numit proces parinte, iar noul proces creat este numit proces fiu. Sintaxa pentru apelul sistem fork este:
pid=fork();
La revenirea din apelul sistem fork, doua procese au copii identice ale contextului de nivel utilizator, exceptie facand valoarea de retur pid. In procesul parinte, pid are valoarea identificatorului procesului fiu; in procesul fiu, pid are valoarea zero. Procesul 0, creat intern de catre nucleu cand sistemul este initializat este singurul proces care nu este creat prin intermediul apelului sitem fork.
Nucleul executa urmatoarea secventa de operatii la apelul sistem fork:
Aloca o intrare in tabela de procese penrtru noul proces.
Atribuie un identificator unic procesului fiu.
Face o copie logica a contextului procesului parinte. Deoarece in mod sigur portiuni ale procesului, cum ar fi zona de cod, pot fi partajate intre procese, nucleu poate uneori incrementa numarul de referiri al unei regiuni in schimbul copierii regiunii la o noua locatie fizica in memorie.
Incrementeaza contorii tabelei de inoduri si tabelei de fisiere asociate procesului.
Intoarce in procesul parinte numarul identificatorului atribuit procesului fiu si valoarea zero in procesul fiu.
Implementarea apelului sistem fork nu este triviala deoarece procesul fiu pare a-si incepe secventa de executie dintr-un punct aflat in "aer". Algoritmul pentru apelul fork difera putin de la sistemul cu paginare la cerere la sistemul cu swapping; discutia care urmeaza se bazeaza pe sistemul traditional cu swapping dar va sublinia locurile in care apar schimbari pentru sistemul cu paginare la cerere. De asemenea se presupune ca sistemul are suficienta memorie pentru a pastra procesul fiu.
Capitolul 12 va considera cazul in care nu exista suficienta memorie pentru procesul fiu si descrie implementarea apelului sistem fork intr-un sistem bazat pe
paginare la cerere.
algoritm fork intrari: niciuna iesiri: la procesul parinte, identificatorul procesul fiu (PID) la procesul fiu, 0 } |
Figura 8.2. Algoritmul pentru fork
Figura 8.2 prezinta algoritmul pentru apelul sistem fork. Nucleul se asigura mai intai daca are resurse disponibile pentru a termina cu succes apelul sistem fork.
Intr-un sistem bazat pe swapping, acesta are nevoie de spatiu in memorie sau pe disk pentru a pastra procesul fiu; intr-un sistem bazat pe paginare la cerere, acesta trebuie sa aloce memorie pentru tabelele auxiliare cum ar fi tabelele de pagini. Daca nu sunt resurse disponibile, apelul sistem fork esueaza. Nucleul gaseste o intrare in tabela de procese pentru a incepe constructia contextului procesului fiu si se asigura ca utilizatorul care a apelat fork nu are deja prea multe procese in curs de executie. De asemenea selecteaza un identificator unic pentru noul proces, acesta trebuind sa fie mai mare decat cel mai recent atribuit. Daca un alt proces detine deja acel numar identificator, nucleul incearca sa atribuie urmatorul numar identificator mai mare. Cand numerele identificator ajung la valoarea maxima, atribuirea acestora incepe din nou de la zero. Deoarece cele mai multe procese se executa pentru scurt timp, cele mai multe numere identificator nu sunt utilizate atunci cand atribuirea identificatorilor se reia.
Sistemul limiteaza numarul proceselor care se pot executa simultan de catre un utilizator astfel ca acesta nu poate ocupa multe intrari din tabela de procese, lucru care ar duce la impiedicarea altor utilizatori de a crea procese noi. De asemenea utilizatorii obisnuiti nu pot crea un proces care ar duce la ocuparea ultimei intrari din tabela de procese, altfel sistemul s-ar putea bloca. Deci, daca nici un proces nu se va termina in mod natural, nu se va putea crea un nou proces deoarece nu exista nici o intrare libera in tabela de procese. Pe de alta parte, un administrator de retea poate executa un numar nelimitat de procese, numarul acestora fiind limitat doar de marimea tabelei de procese si, trebuie mentionat faptul ca acesta poate ocupa ultima intrare libera din tabela de procese. Administratorul de retea are, astfel, posibilitatea de a lansa un proces care sa forteze celelalte procese sa se termine (vezi Sectiunea 8.2.3 pentru apelul sistem kill).
Mai departe nucleul initializeaza intrarea din tabela de procese pentru procesul fiul creat prin copierea diferitelor campuri din intrarea procesului parinte. De exemplu, procesul fiu mosteneste numerele identificatorilor utilizator efectiv si real ai procesului parinte si valoarea nice a acestuia, utilizata pentru calcularea prioritatii de planificare. Nucleul depune identificatorul procesului parinte in intrarea procesului fiu, pune procesul fiu in structura arborescenta a proceselor si initializeaza diferiti parametri de planificare, cum ar fi valoarea prioritatii initiale, folosirea initiala a U.C.P. si alte campuri de timp. Starea initiala a procesului este 'in curs de creare' ( Figura 7.1).
Nucleul ajusteaza acum contoarele de referinta pentru fisierele cu care procesul fiu este automat asociat.
Procesul fiu se afla in directorul curent al procesului parinte. Numarul proceselor care au acces la director va fi incrementat cu 1 si, in consecinta, nucleul incrementeaza contorul de referinta al inodului. Apoi, daca procesul parinte sau unul din stramosi executase apelul sistem chroot pentru a schimba radacina, procesul fiu mosteneste radacina schimbata si incrementeaza contorul de referinta al inodului respectiv. In final, nucleul cauta in tabela descriptorilor de fisiere utilizator specifica procesului parinte, gaseste intrarile pentru fisierele deschise, cunoscute de catre proces si incrementeaza contoarele de referinte din intrarile in tabela globala de fisiere asociate fisierelor deschise. Procesul fiu nu numai ca mosteneste drepturile de acces la fisierele deschise, dar si partajeaza accesul la fisiere cu procesul parinte deoarece ambele procese manipuleaza aceleasi intrari din tabela de fisiere. Efectul apelului sistem fork este identic cu cel al apelului dup raportat la fisierele deschise: o noua intrare in tabela descriptorilor de fisiere utilizator va adresa o intrare din tabela de fisiere pentru un fisier deschis. Totusi, pentru apelul sistem dup, intrarile din tabela descriptorilor de fisiere utilizator sunt asociate unui singur proces, pe cand pentru apelul sistem fork, ele sunt in procese diferite.
Nucleul este acum gata pentru a crea contextul de nivel utilizator al procesului fiu. Nucleul aloca memorie pentru zona u area, regiunile si tabelele de pagini (PT) auxiliare ale procesului fiu, duplica fiecare regiune din procesul parinte folosind algoritmul dupreg si ataseaza fiecare regiune la procesul fiu folosind algoritmul attachreg. In sistemul bazat pe swapping, acesta copiaza continutul regiunilor care nu sunt folosite in comun intr-o noua zona a memoriei principale. In sectiunea 7.2.4 s-a aratat ca zona u area contine un pointer catre intrarea asociata din tabela de procese. Exceptand acest camp, continutul zonei u area a fiului este initial identic cu cel al zonei u area a parintelui, dar ele pot sa difere dupa terminarea apelului sistem fork. De exemplu procesul parinte poate deschide un nou fisier dupa terminarea apelului sistem fork, dar procesul fiu nu poate avea automat acces la el.
Pana in prezent, nucleul a creat portiunea statica a contextului procesului fiu; acum acesta creeaza portiunea dinamica a acestuia. Nucleul copiaza cadrul 1 al contextului procesului parinte, continand contextul registru salvat si cadrul stivei nucleu pentru apelul sistem fork. Daca implementarea este una in care stiva nucleu este parte a zonei u area, nucleul creeaza automat stiva nucleu a procesului fiu cand acesta creeaza zona u. area a acestuia. Altfel, procesul parinte trebui sa copieze propria stiva nucleu intr-o zona privata de memoriei asociata cu procesul fiu. In ambele cazuri, stivele nucleu pentru procesele fiu si parinte sunt identice.
Nucleul creeaza apoi un cadru context identic (2) pentru procesul fiu, continand contextul registru salvat pentru cadrul context (1). Acesta seteaza numaratorul de program si alti registrii in contextul registru salvat astfel incat sa poata reface contextul procesului fiu, desi acesta nu a fost executat niciodata inainte, si astfel incat procesul fiu sa se poata recunoaste singur ca fiind un proces fiu cand acesta se va executa. De exemplu, daca codul nucleu testeaza valoarea registrului 0 pentru a decide daca procesul este parinte sau fiu, acesta scrie valoarea corespunzatoare in contextul registru salvat corespunzator fiului in cadrul context 1. Mecanismul este similar cu cel discutat pentru o comutare de context.
Cand contextul procesului fiu este gata, procesul parinte termina partea sa de apel fork prin schimbarea starii fiului in 'gata de executie)' si prin returnarea catre utilizator a identificatorului de proces al fiului. Nucleul planifica mai tarziu procesul fiu pentru executie cu ajutorul algoritmului normal de planificare si procesul fiu isi termina astfel partea sa de apel fork. Contextul procesului fiu a fost completat de catre procesul tata; in nucleu, procesul fiu pare a fi trezit dupa asteptarea eliberarii unei resurse. Procesul fiu executa portiunea de cod din apelului sistem fork, in acord cu numaratorul de program, pe care nucleul l-a refacut din contextul registru salvat aflat in cadrul context 2 si intoarce zero la revenirea din apelul sistem.
Figura 8.3 da o imagine logica a proceselor parinte si fiu si a relatiilor lor cu alte structuri de date ale nucleului, imediat dupa terminarea apelului sistem fork. Concluzionand, ambele procese partajeaza fisierele pe care procesul parinte le-a deschis in timpul apelului sistem fork si contorul de referinta din intrarile tabelei de fisiere pentru aceste fisiere creste cu 1. Similar, procesul fiu are acelasi director curent si aceeasi radacina ca a parintelui si contoarele de referinta ale inodurilor apartinand acestor directoare cresc cu valoarea 1. Procesele au copii identice ale regiunilor de text, date si stiva utilizator; tipul regiunii si implementarea sistemului stabilesc daca procesele pot partaja o copie fizica a regiunii de text.
Consideram programul din figura 8.4, un exemplu de partajare a accesului la fisiere pe timpul executarii apelului sistem fork. Un utilizator poate apela programul cu doi parametri, numele unui fisier existent si numele unui fisier nou care va fi creat. Procesul deschide fisierul existent, creeaza noul fisier si presupunand ca nu apar erori, executa apelul sistem fork si creeaza un proces fiu.
Intern, nucleul face o copie a contextului procesului parinte pentru procesul fiu iar procesul parinte si procesul fiu se executa in spatii de adrese diferite.
Fiecare proces poate accesa copiile private ale variabilelor globale fdrd, fdwt si c si copiile private ale variabilelor de stiva argc si argv, dar nici un proces nu poate accesa variabilele celuilalt proces. Cu toate acestea, nucleul copiaza zona u area a procesului parinte in procesul fiu in timpul apelului sistem fork si fiul astfel mosteneste dreptul de acces la fisierele parintelui (acestea sunt fisierele pe care parintele le-a deschis si creat) folosind aceiasi descriptori de fisiere.
Figura 8.3. Relatii intre procesul parinte si procesul fiu dupa
executarea apelului sistem fork
Procesele parinte si fiu apeleaza functia rdwrt independent, apoi executa un ciclu, citind cate un octet din fisierul sursa si scriindu-l apoi in fisierul destinatie. Functia rdwrt termina atunci cand apelul sistem read intalneste sfarsitul fisierului. Nucleul incrementase contoarele fisierelor sursa si destinatie din tabela de fisiere si descriptorii fisierelor din ambele procese refera aceeasi intrare din tabela de fisiere. Asa ca, descriptorii de fisier fdrd pentru ambele procese refera aceeasi intrare din tabela de fisiere pentru fisierul destinatie si descriptorii de fisier fdwr pentru ambele procese refera aceeasi intrare din tabela de fisiere pentru fisierul sursa.
#include <fcntl.h>
int fdrd, fdwt;
char c;
main (argc, argv)
int argc;
char *argv[ ];
rdwrt ()
}
Figura 8.4. Program in care procesele parinte si fiu partajeaza
accesul la fisiere
De aceea, cele doua procese nu vor citi sau scrie niciodata de la (la) aceleasi adrese, deoarece nucleul incrementeaza deplasamentul in fisier dupa fiecare apel de citire sau scriere. Desi procesele par sa copieze fisierul sursa de doua ori mai repede, continutul fisierului sursa depinde de ordinea in care nucleul a planificat procesele. Daca acesta planifica procesele astfel incat ele sa alterneze in executarea propriilor apeluri sistem, sau chiar daca alterneaza executia perechii de apeluri read-write dintr-un proces, continutul fisierului destinatie va fi identic cu continutul fisierului sursa. Dar sa consideram urmatorul scenariu, in care procesele sunt gata sa citeasca o secventa de doua caractere 'ab' din fisierul sursa. Presupunem ca procesul parinte citeste caracterul 'a', dupa care nucleul face o comutare de context pentru a executa procesul fiu inainte ca parintele sa scrie. Daca procesul fiu citeste caracterul 'b' si il scrie in fisierul destinatie inainte ca procesul parinte sa fie reprogramat, fisierul destinatie nu va contine sirul 'ab', ci 'ba'. Nucleul nu garanteaza rata relativa de executie a proceselor.
Acum sa consideram programul din figura 8.5, care mosteneste descriptorii de fisiere 0 si 1 (intrarea si iesirea standard) de la procesul parinte. Executia fiecarui apel sistem pipe aloca in plus doi descriptori de fisiere in sirurile toIpar si toIchild. Procesul executa apelul sistem fork si face o copie a contextului sau: fiecare proces poate accesa datele proprii, ca in exemplul anterior. Procesul parinte inchide fisierul sau standard de iesire (descriptorul de fisier 1) si duplica (dup) descriptorul de scriere intors de apelul sistem pipe in sirul toIchild. Deoarece primul slot liber din tabela descriptorilor de fisiere utilizator a procesului parinte este chiar acela eliberat prin apelul sistem close, nucleul copiaza descriptorul de scriere rezultat in urma executarii apelului sistem pipe in slotul 1 al acesteia si astfel descriptorul fisierului standard de iesire devine descriptorul de scriere al pipe-lui pentru toIchild. Procesul parinte face operatii similare astfel incat descriptorul intrarii standard sa devina descriptorul de citire al pipe-lui pentru toIpar. Analog, procesul fiu isi inchide fisierul standard de iesire (descriptor 0) si duplica descriptorul de citire al pipe-lui pentru toIchild. Intrucat primul slot liber in tabela descriptorilor de fisiere utilizator este slotul intrarii standard anterioare, intrarea standard a fiului devine descriptorul de citire al pipe-lui pentru toIchild. Fiul face operatii similare astfel incat descriptorul iesirii standard sa devina descriptorul de scriere al pipe-lui pentru toIpar. Ambele procese inchid descriptorii de fisiere intorsi de apelul pipe ( o buna metoda de programare ). Ca rezultat, cand procesul parinte scrie la iesirea standard, acesta scrie in pipe-ul toIchild si trimite date procesului fiu, care citeste pipe-ul ca pe propria intrare standard. Cand procesul fiu scrie la iesirea standard, acesta scrie in pipe-ul toIpar si trimite datele procesului parinte care citeste pipe-ul ca pe propria intrare standard. Procesele schimba astfel mesaje prin intermediul pipe-urilor.
Rezultatele acestui exemplu sunt aceleasi, indiferent de ordinea in care procesele isi executa apelurile sistem respective. Adica nu este nici o diferenta, daca procesul parinte revine din apelul sistem fork inaintea fiului sau dupa el. Similar, nu este nici o diferenta data de ordinea relativa in care procesele isi executa apelurile sistem pana cand acestea intra in propriul ciclu: structurile nucleului sunt identice. Daca procesul fiu executa apelul sistem read inainte ca procesul parinte sa execute apelul sistem write, procesul fiu va intra in asteptare pana cand procesul parinte scrie in pipe si il trezeste. Daca procesul parinte scrie pipe-ul inainte ca procesul fiu sa citeasca din pipe, procesul parinte nu va termina de citit de la intrarea sa standard pana cand fiul nu citeste de la intrarea sa standard si nu scrie la iesirea sa standard. Din acest motiv, ordinea de executie este fixata: fiecare proces termina un apel sistem read si un apel sistem write si nu poate executa urmatorul apel sistem read pana cand celalalt proces nu termina un apel sistem read si un apel sistem write.
#include <string.h>
char string[ ]=' Salut !';
main ()
}
/* procesul parinte se executa aici */
close(1); /* rearanjeaza intrarile si iesirile standard */
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++)
}
Figura 8.5. Utilizarea pipe-urilor, a apelurilor sistem dup si fork
Procesul parinte face exit dupa cele 15 iteratii din ciclu; apoi procesul fiu citeste 'sfarsit de fisier' (EOF), deoarece pipe-ul nu are procese care sa scrie, si se termina. Daca procesul fiu ar fi scris in pipe dupa ce procesul parinte s-a terminat, primul ar fi receptionat un semnal care indica scrierea intr-un pipe fara procese cititoare. Am mentionat mai devreme ca o metoda buna de programare este aceea de a inchide descriptorii de fisiere inutili. Acest lucru este adevarat din trei motive. Primul, se conserva descriptorii de fisiere in limitele impuse de sistem. Al doilea, daca un proces fiu apeleaza execs, descriptorii fisierelor raman asignati in noul context, asa cum se va vedea. Inchiderea fisierelor neesentiale inainte unui apel exec permite programelor sa ruleze intr-un mediu curat, numai cu descriptorii fisierelor standard de intrare, iesire si eroare deschisi. Al treilea, citirea dintr-un pipe intoarce 'sfarsit de fisier' doar daca nici un proces nu are pipe-ul deschis pentru scriere.
Daca procesul cititor pastreaza descriptorul de scriere al pipe-lui deschis, acesta nu va sti niciodata cand inchid pipe-ul procesele care scriu. Exemplul de mai sus nu ar lucra corect daca procesul fiu nu si-ar inchide descriptorii de scriere ai pipe-lui inaintea intrarii in ciclu.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 2331
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved