CATEGORII DOCUMENTE |
Un proces poate mari sau micsora dimensiunea regiunii sale de date prin utilizarea apelului sistem brk. Sintaxa pentru apelul sistem este:
brk(endds);
unde endds devine valoarea celei mai mari adrese virtuale a regiunii de date a procesului (denumita valoare de intrerupere). Ca o alternativa, utilizatorul poate apela:
oldendds=sbrk(increment);
unde increment modifica valoarea curenta de intrerupere cu numarul de biti specificat si oldennds este valoarea de intrerupere inainte de apel. Sbrk este o subrutina din biblioteca C care apeleaza brk. Daca spatiul de date al procesului creste in urma apelului, noul spatiu de date alocat este virtual contiguu in vechiul spatiu de date. Nucleul verifica daca noua dimensiune a procesului este mai mica decat dimensiunea maxima permisa de sistem si daca noua regiune de date nu se suprapune peste spatiul de adrese virtual asignat anterior (Figura 8.26). Daca toate verificarile sunt executate cu succes, nucleul apeleaza growreg pentru a aloca memorie auxiliara (de exemplu, tabele de pagini) pentru regiunea de date si incrementeaza campul dimensiune proces. Pe un sistem cu swapping, el incearca de asemenea sa aloce memorie pentru noul spatiu si initializeaza continutul acesteia cu 0; daca nu exista spatiu de memorie, el evacueaza procesul pe disc pentru a obtine spatiul necesar (se va explica in detaliu in capitolul 11). Daca procesul apeleaza brk pentru a elibera spatiul alocat anterior, nucleul elibereaza memoria; daca procesul acceseaza adrese virtuale in spatiul de adrese al paginilor eliberate, se produce o intrerupere de memorie.
algoritmul brk
intrari: noua valoare de intrerupere
iesiri: vechea valoare de intrerupere
schimba dimensiunea regiunii (algoritmul growreg);
initializeaza la 0 adresele din noul spatiu de date;
deblocheaza regiunea de date a procesului;
}
Figura 8.26 Algoritmul pentru schimbarea dimensiunii unui proces
Figura 8.27 prezinta un program care utilizeaza algoritmul brk si datele de iesire rezultate in urma rularii pe un calculator AT&T 3B20. Dupa ce se aranjeaza interceptarea semnalului de violare a segmentarii prin apelul semnal signal, procesul apeleaza sbrk si tipareste valoarea sa initiala de intrerupere. Apoi el intra intr-un ciclu, incrementand un pointer catre caractere si scriindu-si continutul pana cand se incearca scrierea la o adresa in afara regiunii de date, provocand semnalul de violare a segmentarii. Interceptand semnalul, functia catcher apeleaza sbrk pentru a aloca alti 256 de octeti in regiunea de date; procesul continua de unde a fost intrerupt in ciclu, scriind in noul spatiu de adrese alocat. Cand se cicleaza din nou in afara regiunii de date, se repeta intreaga procedura. Un fenomen interesant apare la masinile a caror memorie este alocata prin pagini, asa cum este masina 3B20. O pagina este cea mai mica unitate de memorie care este protejata prin hardware si deci, prin hardware nu se poate detecta cand un proces scrie la adrese care sunt dincolo de valoarea de intrerupere. Acest lucru este aratat in figura 8.27: primul apel sbrk returneaza valoarea 140924, ceea ce inseamna ca sunt 388 octeti lasati in pagina, care contine 2Ko pe o masina 3B20.
#include <signal.h> char *cp; int callno; main() catcher (signo) int signo; Date de iesire: valoarea initiala returnata de apelul brk 140924 s-a interceptat semnalul 11 primul la adresa 141312 s-a interceptat semnalul 11 al 2-lea la adresa 141312 s-a interceptat semnalul 11 al 3-lea la adresa 143360 (unele adrese sunt tiparite la al 10-lea apel) s-a interceptat semnalul 11 al 10-lea la adresa 143360 s-a interceptat semnalul 11 al 11-lea la adresa 145408 (unele adrese sunt tiparite la al 18-lea apel) s-a interceptat semnalul 11 al 18-lea la adresa 145408 s-a interceptat semnalul 11 al 19-lea la adresa 145408 |
Figura 8.27 Exemplu de utilizare a algoritmului brk
Procesul va fi intrerupt doar cand se adreseaza pagina urmatoare, la adresa 141312.Functia catcher adauga 256 de octeti la valoarea de intrerupere, facand-o 141180, valoare inca mica pentru a fi adresa in pagina urmatoare. De aceea, procesul se intrerupe imediat, tiparind aceeasi adresa, 141312. Dupa urmatorul apel sbrk, nucleul aloca o noua pagina de memorie, asa ca nucleul poate accesa alti 2ko, la adresa 143360, chiar daca valoarea de intrerupere nu este asa mare. La urmatoarea intrerupere, procesul va apela sbrk de 8 ori (pana cand acesta poate continua). Astfel, un proces poate uneori sa se execute dincolo de adresa sa de intrerupere, cu toate ca acesta este un stil de programare necorespunzator.
Nucleul extinde automat dimensiunea stivei utilizator cand aceasta este depasita, urmand un algoritm similar algoritmului brk. Initial un proces contine destul spatiu pentru stiva utilizator pentru a pastra parametrii apelului exec, dar poate aparea o depasire a acestui spatiu in timp ce se depun date in stiva de-a lungul executiei procesului. Cand se depaseste stiva proprie, masina genereaza o intrerupere de memorie, deoarece procesul incearca sa acceseze o locatie in afara spatiului sau de adrese. Nucleul determina ca motivul pentru care a avul loc intreruperea de memorie a fost depasirea stivei prin compararea valorii indicatorului de stiva cu marimea regiunii de stiva. Nucleul aloca spatiu nou pentru regiunea de stiva asa cum aloca spatiu in algoritmul brk. Cand revine din intrerupere procesul are spatiu de stiva necesar pentru a continua.
Acest capitol a acoperit suficiente notiuni pentru a explica cum lucreaza shell-ul. Figura 8.28 arata ciclul principal al shell-lui si demonstreaza executia asincrona, redirectarea iesirii si pipe-urile.
/* citeste linia de comanda pana la aparitia caracterului 'sfarsit de fisier' */
while (read(stdin, buffer, numchars))
if(/* cerere de redirectare catre o un fisier pipe */
/* a doua componenta a liniei de comanda */
close(stdin); dup(fildes[0]);
close(fildes[0]); close(fildes[1]);
/* intrarea standard este acum redirectata catre un pipe */ }
execv(command2, command2, 0); }
/* procesul parinte continua de aici
asteapta terminarea procesului fiu daca este nevoie*/
if(amper==0) retid=wait(&status);
Figura 8.28 Ciclul principal al shell-lui
Shell-ul citeste o linie de la intrarea sa standard si o interpreteaza dupa un set fixat de reguli. Descriptorii fisierelor standard de intrate si de iesire pentru shell-ul de logare in sistem sunt de obicei ai terminalului pe care utilizatorul ii foloseste asa cum s-a aratat in capitolul 6. Daca shell-ul recunoaste sirul de intrare ca fiind o comanda interna (de exemplu, comenzile cd, for, while si altele), el executa comanda intern fara a mai crea procese; altfel, presupune ca aceasta este numele unui fisier executabil.
Cea mai simpla linie de comanda contine un nume de program si cativa parametri cum ar fi:
who
grep -n include *.c
ls -1
Shell-ul executa un apel fork si creeaza un proces fiu care executa programul pe care utilizatorul l-a specificat in linia de comanda. Procesul parinte, shell-ul care este folosit de utilizator, asteapta pana cand procesul fiu termina comanda si apoi revine in ciclu pentru a citi comanda urmatoare.
Pentru a rula un proces asincron (in fundal), ca de exemplu
nroff -mm bigdocument &
shell-ul seteaza o variabila interna amper cand analizeaza caracterul &. Daca gaseste variabila setata la sfarsitul ciclului, el executa apelul wait dar reia imediat ciclul si citeste urmatoarea linie de comanda.
Figura arata ca procesul fiu are acces la copia liniei de comanda dupa executarea apelului fork. Pentru a redirecta iesirea standard catre un fisier, ca in
nroff -mm bigdocument>output
procesul fiu creeaza fisierul de iesire specificat in linia de comanda; daca apelul creat nu se incheie cu succes (de exemplu, la crearea fisierului intr-un director fara drept de acces ), procesul fiu ar trebui sa se termine imediat. Daca apelul creat se incheie cu succes, procesul fiu inchide fisierul de iesire standard anterior si duplica descriptorul de fisier al noului fisier de iesire. Descriptorul fisierului de iesire standard refera acum fisierul de iesire redirectat. Procesul fiu inchide descriptorul de fisier obtinut prin apelul creat pentru a conserva descriptorii de fisier pentru programul executat. Shell-ul redirecteaza fisierele standard de intrare si eroare intr-un mod similar.
Figura 8.29 Relatiile intre procese pentru linia de comanda ls -l|wc
Figura 8.29 arata cum trateaza shell-ul o linie de comanda ls-l|wc cu un singur pipe.
Dupa ce procesul parinte apeleaza fork si creeaza un proces fiu, fiul creeaza un pipe. Procesul fiu executa la randul sau un apel fork; el si fiul sau trateaza fiecare cate o componenta a liniei de comanda. Procesul fiu ( " nepotul " ) creat prin al doilea apel fork executa prima componenta a comenzii (ls): el scrie in pipe, asa ca inchide descriptorul fisierului sau standard de iesire, duplica descriptorul de scriere in pipe si inchide descriptorul initial de scriere in pipe pentru ca acesta nu mai este necesar. Procesul parinte (wc) al ultimului proces fiu (ls) este fiul procesului shell (vezi Figura 8.29). Acest proces (wc) inchide propriul fisier standard de intrare si duplica descriptorul de citire din pipe facandu-l sa devina propriul descriptor de fisier standard de intrare. Apoi inchide descriptorul initial de citire din pipe pentru ca nu mai are nevoie de el si executa a doua componenta a comenzii din linia de comanda. Cele doua procese care executa linia de comanda, se executa asincron, si iesirea unui proces devine intrare pentru celalalt proces. Procesul shell intre timp asteapta pentru ca procesul sau fiu (wc) sa se termine, apoi procedeaza in mod uzual: intreaga linie de comanda este executata complet atunci cand procesul care executa comanda wc se termina. Shell-ul reintra in ciclu si citeste urmatoarea comanda.
Politica de confidentialitate | Termeni si conditii de utilizare |
Vizualizari: 1659
Importanta:
Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved