Sisteme de operare: trei piese ușoare. Partea 3: Process API (traducere)

Introducere în sistemele de operare

Bună, Habr! Aș dori să vă prezint atenției o serie de articole-traduceri ale unei literaturi care este interesantă în opinia mea - OSTEP. Acest material examinează destul de profund munca sistemelor de operare asemănătoare Unix, și anume lucrul cu procese, diverse programatoare, memorie și alte componente similare care alcătuiesc un sistem de operare modern. Puteți vedea originalul tuturor materialelor aici aici. Vă rugăm să rețineți că traducerea a fost făcută neprofesionist (destul de liber), dar sper că am păstrat sensul general.

Lucrările de laborator pe acest subiect pot fi găsite aici:

Alte părți:

Puteți verifica și canalul meu la telegramă =)

Alarma! Există un laborator pentru această prelegere! Uite github

Proces API

Să ne uităm la un exemplu de creare a unui proces într-un sistem UNIX. Se întâmplă prin două apeluri de sistem furculiţă() и exec().

Apel furk()

Sisteme de operare: trei piese ușoare. Partea 3: Process API (traducere)

Luați în considerare un program care efectuează un apel fork(). Rezultatul executării sale va fi următorul.

Sisteme de operare: trei piese ușoare. Partea 3: Process API (traducere)

În primul rând, intrăm în funcția main() și imprimăm șirul pe ecran. Linia conține identificatorul de proces care în original este numit PID sau identificatorul procesului. Acest identificator este folosit în UNIX pentru a se referi la un proces. Următoarea comandă va apela fork(). În acest moment, este creată o copie aproape exactă a procesului. Pentru sistemul de operare, se pare că există 2 copii ale aceluiași program care rulează pe sistem, care la rândul său va ieși din funcția fork(). Procesul copil nou creat (în raport cu procesul părinte care l-a creat) nu va mai fi executat, pornind de la funcția main(). Trebuie amintit că un proces copil nu este o copie exactă a procesului părinte, în special, are propriul său spațiu de adrese, propriile sale registre, propriul său indicator către instrucțiuni executabile și altele asemenea; Astfel, valoarea returnată apelantului funcției fork() va fi diferită. În special, procesul părinte va primi valoarea PID a procesului copil ca retur, iar copilul va primi o valoare egală cu 0. Folosind aceste coduri de returnare, puteți apoi să separați procesele și să forțați fiecare dintre ele să-și facă propria treabă. . Cu toate acestea, execuția acestui program nu este strict definită. După împărțirea în 2 procese, sistemul de operare începe să le monitorizeze, precum și să le planifice munca. Dacă este executat pe un procesor cu un singur nucleu, unul dintre procese, în acest caz părintele, va continua să funcționeze, iar apoi procesul copil va primi controlul. La repornire, situația poate fi diferită.

Apel in asteptare()

Sisteme de operare: trei piese ușoare. Partea 3: Process API (traducere)

Luați în considerare următorul program. În acest program, datorită prezenței unui apel aștepta() Procesul părinte va aștepta întotdeauna finalizarea procesului secundar. În acest caz, vom obține o ieșire de text strict definită pe ecran

Sisteme de operare: trei piese ușoare. Partea 3: Process API (traducere)

apelul exec().

Sisteme de operare: trei piese ușoare. Partea 3: Process API (traducere)

Luați în considerare provocarea exec(). Acest apel de sistem este util atunci când dorim să rulăm un program complet diferit. Aici vom suna execvp() pentru a rula programul wc care este un program de numărare a cuvintelor. Ce se întâmplă când este apelat exec()? Acest apel este trecut numele fișierului executabil și unii parametri ca argumente. După care codul și datele statice din acest fișier executabil sunt încărcate și propriul său segment cu codul este suprascris. Zonele de memorie rămase, cum ar fi stiva și heap-ul, sunt reinițializate. După care sistemul de operare pur și simplu execută programul, trecându-i un set de argumente. Deci nu am creat un nou proces, pur și simplu am transformat programul care rulează în prezent într-un alt program care rulează. După executarea apelului exec() în descendent, se pare că programul original nu a rulat deloc.

Această complicație de pornire este complet normală pentru un shell Unix și permite ca acel shell să execute cod după apel furculiţă(), dar înainte de apel exec(). Un exemplu de astfel de cod ar fi ajustarea mediului shell la nevoile programului care este lansat, înainte de a-l lansa.

Coajă - doar un program de utilizator. Ea îți arată rândul de invitație și așteaptă să scrii ceva în ea. În cele mai multe cazuri, dacă scrieți numele unui program acolo, shell-ul își va găsi locația, va apela metoda fork() și apoi va apela un tip de exec() pentru a crea un nou proces și va aștepta ca acesta să se finalizeze folosind un astepta() apel. Când procesul copil iese, shell-ul va reveni de la apelul wait() și va tipări din nou promptul și va aștepta ca următoarea comandă să fie introdusă.

Diviziunea fork() & exec() permite shell-ului să facă următoarele lucruri, de exemplu:
wc file > new_file.

În acest exemplu, rezultatul programului wc este redirecționat către un fișier. Modul în care shell-ul realizează acest lucru este destul de simplu - prin crearea unui proces copil înainte de a apela exec(), shell-ul închide ieșirea standard și deschide fișierul fişier_nou, astfel, toate rezultatele din programul care rulează ulterior wc va fi redirecționat către un fișier în loc de un ecran.

Conducta Unix sunt implementate într-un mod similar, cu diferența că folosesc un apel pipe(). În acest caz, fluxul de ieșire al procesului va fi conectat la o coadă de conducte situată în nucleu, la care va fi conectat fluxul de intrare al altui proces.

Sursa: www.habr.com

Adauga un comentariu