Scrigroup - Documente si articole

     

HomeDocumenteUploadResurseAlte limbi doc
AccessAdobe photoshopAlgoritmiAutocadBaze de dateCC sharp
CalculatoareCorel drawDot netExcelFox proFrontpageHardware
HtmlInternetJavaLinuxMatlabMs dosPascal
PhpPower pointRetele calculatoareSqlTutorialsWebdesignWindows
WordXml


Apelul altor programe - UNIX

linux



+ Font mai mare | - Font mai mic



Apelul altor programe - UNIX

Apelul sistem exec apeleaza un alt program suprapunand peste spatiul de memorie al unui proces o copie a unui fisier executabil. Continutul contextului de nivel utilizator care a existat inaintea apelului exec nu mai este accesibil pentru mult timp cu exceptia parametrilor apelului exec pe care nucleul ii copiaza din vechiul spatiu de adresa in cel nou. Sintaxa apelului sistem este:



execve(numeIfisier, argv, envp)

unde numeIfisier este numele fisierului executabil apelat, argv este un sir de pointeri catre caractere care sunt parametrii programului executabil iar envp este un sir de pointeri catre caractere care reprezinta mediul programului executat. Exista mai multe functii de biblioteca care apeleaza exec cum ar fi execl, execv, execle, etc. Cand un program foloseste parametrii in linia de comanda, cum ar fi

main(argc,argv)

parametrul argv este o copie a parametrului argv pentru apelul exec.

copiaza parametrii apelului exec in noua regiune de stiva a

utilizatorului;

procesare speciala pentru programele de tip setuid, si pentru

executia pas cu pas;

elibereaza inodul fisierului (algoritmul iput);

}

Figura 8.19. Algoritmul pentru apelul altor programe

Figura 8.20 prezinta formatul logic al unui fisier executabil, asa cum exista in sistemul de fisiere, generat de obicei de asamblor sau incarcator. Acesta contine 4 parti:

Antetul principal care precizeaza cate sectiuni sunt in fisier, adresa de start pentru executia procesului si 'numarul magic' care da tipul fisierului executabil.

Antetele de sectiuni care descriu fiecare sectiune din fisier, dand marimea sectiunii, adresele virtuale pe care ar trebui sa le ocupe sectiunea atunci cand ruleaza in sistem si alte informatii.

Sectiunile de date (text) care sunt initial incarcate in spatiul de adrese al procesului.

Diferite sectiuni care pot contine tabele de simboluri si alte date utile pentru depanare.

Formatele specifice au evoluat de-a lungul anilor dar toate fisierele executabile au continut un antet principal cu un 'numar magic'.

Numarul magic este un intreg pe 8 biti care identifica fisierul ca un modul incarcabil si da nucleului posibilitatea sa distinga diferite caracteristici de rulare ale sale. De exemplu, folosirea unui anumit numar magic pe un PDP 11/70 informeaza nucleul ca procesul poate utiliza pana la 128 ko de memorie in loc de 64 ko; numarul magic joaca, de asemenea, un rol important in sistemele cu paginare, dupa cum se va vedea in capitolul 12.

Figura 8.20. Imaginea unui fisier executabil

In acest moment, nucleul a accesat inodul fisierului executabil si a verificat daca il poate executa. Nucleul este pe punctul de a elibera resursele de memorie care formeaza contextul de nivel utilizator al procesului. Dar, deoarece parametrii pentru noul program sunt continuti in spatiul de memorie care trebuie eliberat, nucleul copiaza parametrii din vechiul spatiu de memorie care trebuie eliberat intr-un buffer temporar, pana cand vor fi atasate regiunile noului spatiu de memorie. Deoarece parametrii apelului exec sunt adresele utilizator ale unor campuri care contin siruri de caractere, nucleul copiaza adresele campurilor si apoi sirurile de caractere in spatiul nucleului pentru fiecare camp in parte. El ar putea alege diferite locuri pentru a pastra sirurile de caractere, in functie de implementare. Cele mai obisnuite locuri sunt stiva nucleu, zonele nealocate (cum ar fi paginile) de memorie care pot fi imprumutate temporar sau memoria secundara, cum ar fi un dispozitiv de swapping.

Cea mai simpla implementare pentru copierea parametrilor in noul context de nivel utilizator este folosirea stivei nucleu. Dar, deoarece configurarea sistemului impune in mod obisnuit o limita de marime pentru stiva nucleu si deoarece parametrii apelului exec pot avea o lungime variabila, aceasta implementare trebuie sa fie combinata cu altele. Din celelalte variante, implementarile utilizeaza cea mai rapida metoda. Daca este usor sa se aloce pagini de memorie, o astfel de metoda este preferabila deoarece accesul la memoria primara este mai rapid decat la cea secundara (cum ar fi un dispozitiv de swapping).

Dupa copierea parametrilor apelului exec in locul de pastrare din nucleu, nucleul detaseaza vechile regiuni ale procesului folosind algoritmul detachreg. Tratamentul special pentru regiunile de text va fi discutat mai tarziu in acest paragraf. In acest moment procesul nu are context de nivel utilizator asa incat erorile care apar de acum inainte duc la terminarea sa, cauzata de un semnal. Astfel de erori pot fi: rularea in afara spatiului din tabela de regiuni a nucleului, incercarea de a incarca un program a carui marime depaseste limita sistemului, incercarea de a incarca un program ale carui adrese de regiuni se suprapun, si altele. Nucleul aloca si ataseaza regiuni pentru text si date, incarcand continutul fisierului executabil in memoria principala (algoritmii allocreg, attachreg si loadreg). Regiunea de date a unui proces este initial impartita in doua: date initializate in momentul compilarii si date care nu se initializeaza in momentul compilarii ('bss'). Alocarea si atasarea initiala a regiunilor de date se face pentru datele initializate. Nucleul mareste apoi regiunea de date folosind algoritmul growreg pentru datele 'bss' si initializeaza valoarea acestora cu 0.

In final, aloca o regiune pentru stiva procesului, o ataseaza procesului si aloca memorie pentru pastrarea parametrilor apelului exec. Daca nucleul a salvat parametrii apelului exec in pagini de memorie, acesta poate utiliza acele pagini pentru stiva. In caz contrar, el copiaza parametrii apelului exec in stiva utilizatorului.

Nucleul sterge adresele interceptorilor de semnale utilizator din u area, deoarece acele adrese nu mai au sens in noul context de nivel utilizator. Semnalele care au fost ignorate raman ignorate si in noul context. Nucleul seteaza apoi contextul registru salvat pentru modul utlizator, setand valorile initiale ale indicatorului de stiva si numaratorului program: incarcatorul a scris numaratorul de program initial in antetul fisierului. Nucleul executa actiuni speciale pentru programele de tip setuid si pentru executia proceselor pas cu pas. In final, el invoca algoritmul iput, eliberand inodul care a fost alocat initial prin algoritmul namei la inceputul apelului exec. Utilizarea algoritmilor namei si iput in apelul exec corespunde deschiderii si inchiderii unui fisier; starea unui fisier in timpul apelului exec este asemanatoare celei a unui fisier deschis cu exceptia absentei unei intrari in tabela de fisiere. Cand procesul revine din apelul exec, el executa codul noului program. Totusi, este acelasi proces de dinainte de apelul exec: identificatorul sau de proces nu s-a schimbat, si nu s-a schimbat nici pozitia sa in ierarhia de procese. Numai contextul de nivel utilizator s-a schimbat.

main()

Figura 8.21. Utilizarea apelului sistem exec

De exemplu, programul din figura 8.21 creeaza un proces fiu care apeleaza exec. Imediat dupa ce procesul parinte si procesul fiu revin din apelul fork acestea executa copii independente ale programului. Cand procesul fiu este pe cale de a apela exec, regiunea sa de cod (text) consta din instructiunile programului, regiunea sa de date contine sirurile '/bin/date', si 'date', iar stiva sa contine cadrele stiva pe care procesul le-a introdus. Nucleul gaseste fisierul '/bin/date' in sistemul de fisiere, observa ca toti utilizatorii il pot executa si determina daca este un modul incarcabil executabil. Prin conventie primul parametru al listei de parametri argv pentru apelul exec este calea catre numele fisierului executabil. Procesul are astfel acces la numele programului de nivel utilizator, ceea ce uneori reprezinta o caracteristica utila.

Nucleul copiaza apoi sirurile '/bin/date' si 'date' intr-o zona interna de pastrare si elibereaza regiunile de cod, date si stiva ocupate de proces. El aloca procesului regiuni noi de cod, date si stiva, copiaza sectiunea de text a fisierului '/bin/date' in regiunea de text si sectiunea de date a fisierului in regiunea de date. Nucleul reconstruieste lista initiala de parametri (aici sirul de caractere 'date') si o pune in regiunea de stiva. Dupa apelul exec, procesul fiu nu mai executa vechiul program ci executa programul 'date'. Cand programul 'date' se termina, procesul parinte primeste starea de terminare a acestuia din propriul apel wait.

#include <signal.h>

main()

f()

sigcatch(n)

int n;

Figura 8.22 Exemplu de program care scrie in regiunea proprie de text

Pana acum, am presupus ca, textul si datele procesului ocupa sectiuni separate in programul executabil si, din acest motiv, regiuni separate la rularea programului. Sunt doua avantaje pentru pastrarea textului si a datelor programului separat: protectie si folosire in comun .

Daca textul si datele s-ar afla in aceeasi regiune, sistemul nu ar putea impiedica un proces sa-si suprascrie zona de text, pentru ca nu ar sti care adrese contin instructiuni si care date. Dar daca textul si datele sunt in regiuni separate, nucleul poate activa mecanismul de protectie hardware pentru a impiedica procesele sa-si suprascrie zona de text. Daca procesul incearca din greseala sa-si suprascrie spatiul sau de text, se genereaza o intrerupere de protectie care in mod obisnuit duce la terminarea procesului.

De exemplu, programul din figura 8.22 atribuie pointerului ip adresa functiei f si apoi aranjeaza sa fie interceptate toate semnalele. Daca programul este compilat astfel incat codul si datele sunt in regiuni separate, procesul care executa programul genereaza o intrerupere de protectie cand se incearca sa se scrie continutul lui ip, deoarece se incearca scrierea in regiunea de cod care este protejata la scriere. Procesul intercepteaza semnalul si se termina fara a executa instructiunea printf din functia main. Totusi, daca programul a fost compilat astfel incat codul si datele sa fie in aceeasi regiune (regiunea de date), nucleul nu isi da seama ca un proces a suprascris adresa functiei f. Adresa functiei f contine valoarea 1! Procesul executa instructiunea printf dar executa o instructiune nepermisa cand se apeleaza functia f. Nucleul trimite semnalul SIGILL si procesul se termina. Pastrarea datelor si instructiunilor in regiuni separate face usoara protectia impotriva erorilor de adresare. Versiunile anterioare de UNIX permiteau codului si datelor sa se afle in aceeasi regiune din cauza limitarii lungimii proceselor impusa de calculatoarele PDP: programele erau mai mici si necesitau mai putini registri de 'segmentare' daca codul si datele ocupau aceeasi regiune. Versiunea curenta a sistemului nu are o astfel de limitare a marimii proceselor si viitoarele compilatoare nu vor accepta optiunea de incarcare a codului si datelor in aceeasi regiune.

Al doilea avantaj al scrierii in regiuni separate a codului si datelor este ca se permite partajarea regiunilor. Daca procesul nu poate scrie in regiunea sa de cod, codul nu se poate schimba in timpul ce nucleul il incarca din fisierul executabil. Daca mai multe procese executa un fisier, ele pot sa partajeze regiunea de cod, economisind memorie. Astfel, cand nucleul aloca o regiune de cod pentru un proces in apelul sistem exec, acesta verifica daca fisierul executabil permite ca regiunea sa de text sa fie partajata, indicatie data de numarul magic.

 

algoritmul xalloc /* aloca si initializeaza regiunea de cod */

intrari: inodul fisierului executabil

iesiri: niciuna

ataseaza regiunea la proces (algoritmul attachreg);

deblocheaza regiunea;

return;

}

/* nu exista o regiune de codcreeaza una */

aloca o regiune de cod (algoritmul allocreg);

/* regiunea este blocata */

if( inodul are bitul sticky setat)

comuta pe 0 indicatorul sticky al regiunii;

ataseaza regiunea la adresa virtuala indicata de antetul indicat de

inod (algoritmul attachreg);

if(fisierul este formatat special pentru un sistem cu paginare)

/* Capitolul 12 va prezenta acest caz */

else /* nu este formatat pentru un sistem cu paginare */

citeste codul fisierului in regiune (algoritmul loadreg);

schimba protectia regiunii in tabela privata de regiuni in read

only;

deblocheaza regiunea; }

}

Figura 8.23 Algoritmul pentru alocarea regiunilor de cod

Daca fisierul executabil permite partajarea regiunii sale de text, nucleul urmeaza algoritmul xalloc pentru a gasi o regiune existenta pentru codul fisierului sau pentru a asigna una noua (vezi figura 8.23).

In algoritmul xalloc, nucleul cauta in lista regiunilor active regiunea de cod a fisierului, identificand-o pe aceea al carei pointer indica inodul fisierului executabil. Daca o asemenea regiune nu exista, nucleul aloca o regiune noua (algoritmul alloreg), o ataseaza procesului (algoritmul attachreg), o incarca in memorie (algoritmul loadreg) si ii schimba protectia in read-only. Ultimul pas provoaca o intrerupere de protectie a memoriei daca un proces incearca sa scrie in regiunea de cod. Daca, in cautarea prin lista regiunilor active, nucleul localizeaza o regiune care contine codul fisierului, se asigura ca regiunea este incarcata in memorie (altfel asteapta) si o ataseaza procesului.

Nucleul deblocheaza regiunea la sfarsitul algoritmului xalloc si decrementeaza contorul regiunii mai tarziu, cand acesta executa algoritmul detachreg, in timpul apelului exit sau exec. Implementarea traditionala a sistemului contine o tabela de text pe care nucleul o manipuleaza in modul deja descris pentru regiunile de cod. Setul regiunilor de text poate fi astfel vazut ca o versiune moderna a vechii tabele de text.

Sa ne amintim ca atunci cand se aloca o regiune pentru prima data prin algoritmul allocreg (Paragraful 6.5.2), nucleul incrementeaza contorul de referinte al inodului asociat cu regiunea, dupa ce acesta fusese incrementat in algoritmul namei (apelul iget) la inceputul apelului exec. Pentru ca nucleul decrementeaza contorul de referinte o singura data in algoritmul iput, la sfarsitul apelului exec, contorul de referinte al inodului fisierului care este executat este cel putin egal cu 1: de aceea, daca un proces face un apel unlink pentru un fisier, continutul lui ramane intact. Nucleul nu mai are nevoie de fisier dupa incarcarea sa in memorie, dar are nevoie de un pointer catre inodul fisierului, pointer situat intr-un camp din intrarea in tabela de regiuni, pentru a identifica fisierul care corespunde regiunii respective. Daca contorul de referinte ar fi fost redus la 0, nucleul ar fi putut realoca inodul din memoria interna altui fisier, compromitand semnificatia pointerului catre inod: daca utilizatorul ar fi executat un fisier nou, nucleul ar fi gasit regiunea de cod a vechiului fisier cu erori. Nucleul evita aceasta problema incrementand contorul de referinte al inodului in algoritmul allocreg, prevenind reasignarea inodului din memoria interna. Cand procesul detaseaza regiunea de cod in timpul apelurilor exit sau exec, nucleul decrementeaza contorul de referinte in algoritmul freereg, mai putin in cazul in care inodul are bitul sticky setat, asa cum se va vedea.

Figura 8.24 Relatiile intre tabela de inoduri si tabela de regiuni

pentru partajarea codului

De exemplu, reconsideram executia fisierului '/bin/date' din figura 8.21 si presupunem ca acesta are zone de cod si date separate. Prima data procesul executa '/bin/date', apoi nucleul aloca o intrare pentru cod in tabela regiunilor (Figura 8.24) si lasa contorul de referinte al inodului la valoarea 1 (dupa executia completa a apelului exec). Cand '/bin/date' se termina, nucleul parcurge algoritmii detachreg si freereg, decrementand contorul de referinte al inodului la 0. Totusi, daca nucleul nu ar fi incrementat contorul de referinte pentru inodul fisierului '/bin/date' prima data cand s-a apelat exec, contorul de referinte ar fi 0 si inodul s-ar gasi in lista libera pe timpul executiei procesului. Sa presupunem ca un alt proces executa fisierul '/bin/who' si nucleul aloca inodul din memoria interna folosit anterior pentru fisierul '/bin/date' fisierului '/bin/who'. Nucleul cauta in tabela regiunilor inodul fisierul '/bin/who', dar gaseste inodul fisierului '/bin/date'. Presupunand[DV1] [DV2] ca regiunea contine codul fisierului '/bin/who', nucleul poate executa un program gresit. In consecinta, contorul de referinte al inodului fisierului care are zona de cod folosita in comun este cel putin 1, asa ca nucleul nu poate realoca inodul.

Posibilitatea de partajare a regiunii de cod permite nucleului sa scada timpul de incepere a executiei unui program prin folosirea bitului sticky. Administratorul de sistem poate seta bitul sticky cu apelul sistem chmod pentru fisierelor executabile utilizate frecvent. Cand un proces executa un fisier care are setat bitul sticky, nucleul nu poate elibera memoria alocata pentru cod cand, mai tarziu, este detasata regiunea in timpul apelurilor exit sau exec, chiar daca contorul de referinte scade la 0.

Nucleul lasa regiunea de cod intacta, cu contorul de referinte al inodului egal cu 1, chiar daca aceasta nu mai este atasata altui proces. Cand alt proces executa fisierul, gaseste intrare in tabela de regiuni pentru codul fisierului. Timpul de start al procesului este mic, pentru ca el nu trebuie sa citeasca codul fisierului din sistemul de fisiere: daca codul se afla inca in memorie, nucleul nu face nici o operatiune de I/O pentru citirea codului; daca nucleul a evacuat codul in zona de swap, acesta incarca mai repede codul de pe dispozitivul de swap decat din sistemul de fisiere, asa cum se va vedea in capitolul 11.

Nucleul sterge intrarile pentru regiunile de cod in modul cu bitul sticky setat in urmatoarele cazuri:

Daca un proces deschide fisierul pentru scriere, operatiile de scriere vor schimba continutul fisierului, invalidand continutul regiunii.

Daca un proces schimba modul de acces la fisier (chmod) astfel incat bitul sticky nu mai este setat, fisierul nu trebuie sa mai ramana in tabela de regiuni.

Daca un proces executa un apel unlink pentru fisier, nici un proces nu va mai putea sa-l execute in continuare pentru ca fisierul nu mai are intrare in sistemul de fisiere; din acest motiv nici un proces nou nu va accesa intrarea fisierului din tabela de regiuni. Pentru ca nu este nevoie de regiunea de cod, nucleul o poate sterge pentru a elibera unele resurse.

Daca un proces 'demonteaza' sistemul de fisiere, fisierul nu va mai fi accesibil si nici un proces nu-l va mai putea executa,la fel ca in cazul precedent.

Daca nucleul ruleaza in afara spatiului de memorie pe dispozitivul swap, el incearca sa elibereze spatiul disponibil prin eliberarea regiunilor cu bitul sticky setat care sunt neutilizate in momentul respectiv. Desi alte procese pot avea nevoie curand de o astfel de regiune de cod, nucleul are mai multa nevoie de spatiu de memorie, imediat.

Regiunea de cod cu bitul sticky setat trebuie sa fie stearsa in primele doua cazuri pentru ca ea nu mai reflecta starea curenta a fisierului. Nucleul sterge intrarile regiunilor cu bitul sticky setat in ultimele trei cazuri, pentru ca acest lucru este mult mai practic.

Desigur, nucleul elibereaza regiunile doar daca nici un proces nu le utilizeaza in momentul respectiv (contorul lor de referinte este 0); altfel apelurile sistem open, unlink, unmount (cazurile 1,3 si 4) esueaza.

Scenariul pentru apelul sistem exec este putin mai complicat daca procesul se executa pe el insusi. Daca utilizatorul tipareste:

sh script

shell-ul creeaza un proces fiu si acesta executa shell-ul si comenzile din fisierul 'script'. Daca un proces se executa singur si permite partajarea propriei regiuni de cod, nucleul trebuie sa evite situatiile de blocare a inodurilor si a regiunilor. De aceea, nucleul nu poate bloca vechea regiune de cod,nu o poate mentine blocata, si apoi nu poate incerca sa blocheze noua regiune de cod, deoarece regiunile (veche si noua) sunt una si aceeasi. in schimb, nucleul paraseste vechea regiune de cod atasata la proces, pentru ca aceasta nu va fi utilizata niciodata.

Procesele apeleaza de obicei exec dupa fork; astfel procesul fiu copiaza spatiul de adrese al procesului parinte in timpul apelului sistem fork, suprascrie acest spatiu in timpul apelului exec si executa o imagine program diferita de cea a procesului parinte. Nu ar fi mai natural sa combinam cele doua apeluri intr-unul singur pentru a apela programul si a-l rula ca pe un proces nou? Ritchie presupune ca apelurile sistem fork si exec sunt apeluri sistem diferite pentru ca atunci cand s-a proiectat sistemul UNIX, el si Thomson au putut sa adauge apelul sistem fork fara a face prea multe schimbari asupra codului din nucleul existent.Separarea apelurilor fork si exec este importanta din punct de vedere functional, pentru ca procesele pot manipula proprii descriptori de fisiere standard de intrare si iesire independent pentru a initializa pipe-urile mai elegant decat daca ambele apeluri ar fi combinate intr-unul singur. in exemplul de shell din paragraful 8.8. se va vedea aceasta trasatura.


 [DV1]

 [DV2]



Politica de confidentialitate | Termeni si conditii de utilizare



DISTRIBUIE DOCUMENTUL

Comentarii


Vizualizari: 950
Importanta: rank

Comenteaza documentul:

Te rugam sa te autentifici sau sa iti faci cont pentru a putea comenta

Creaza cont nou

Termeni si conditii de utilizare | Contact
© SCRIGROUP 2024 . All rights reserved