Sisteme de Operare: Trei Piese Ușoare. Partea 2: Abstracție: Proces (traducere)

Introducere în sistemele de operare

Salut, Habr! Vreau să vă prezint o serie de articole-traduceri dintr-o literatură care mi se pare interesantă - OSTEP. Acest material examinează în mod profund funcționarea sistemelor de operare de tip unix, și anume - gestionarea proceselor, diferitele planificatoare, memorie și alte componente asemănătoare care formează un sistem de operare modern. Puteți vizualiza originalul tuturor materialelor aici aici. Vă rog să țineți cont că traducerea a fost efectuată neprofesional (destul de liber), dar sper că am păstrat sensul general.

Lucrările de laborator pentru această materie le puteți găsi aici:

Alte părți:

De asemenea, mă puteți urmări pe canalul meu de telegramă =)

Să luăm în considerare cea mai fundamentală abstrație pe care sistemul de operare o oferă utilizatorilor: procesul. Definiția unui proces este destul de simplă — este un program în execuție. Programul, în sine, este un lucru lipsit de viață, aflat pe disc—este un set de instrucțiuni și poate câteva date statice, așteptând momentul pornirii. Anume sistemul de operare ia acești biți și îi lansează, transformând programul în ceva util.
Cel mai adesea utilizatorii doresc să ruleze mai multe programe simultan, de exemplu, puteți deschide pe laptopul dvs. un browser, un joc, un player multimedia, un editor de text și așa mai departe. De fapt, un sistem tipic poate rula zeci și sute de procese simultan. Acest lucru face ca sistemul să fie mai ușor de utilizat; nu trebuie să vă preocupați niciodată dacă CPU-ul este liber, pur și simplu porniți programele.

Din acest punct de vedere, apare problema: cum să se asigure iluzia a mai multor CPU? Cum poate sistemul de operare să creeze iluzia unui număr practic nelimitat de CPU-uri, chiar dacă aveți doar un singur CPU fizic?

Sistemul de operare creează această iluzie prin virtualizarea CPU-ului. Rulând un proces, apoi oprindu-l, apoi rulând un alt proces și așa mai departe, sistemul de operare poate menține iluzia că există numeroase CPU-uri virtuale, deși, de fapt, va fi unul sau mai multe procesoare fizice. Această tehnică se numește împărțirea resurselor CPU în timp. Această tehnică permite utilizatorilor să ruleze atâtea procese simultane cât doresc. Prețul acestei soluții este performanța — deoarece dacă CPU-ul este împărțit între mai multe procese, fiecare proces va fi procesat mai lent.
Pentru a concretiza virtualizarea CPU-ului, și în special pentru a o face bine, sistemul de operare are nevoie de suport atât la nivel scăzut, cât și la nivel înalt. Suportul la nivel scăzut se numește mecanisme — acestea sunt metode sau protocoale la un nivel inferior care implementează partea necesară a funcționalității. Un exemplu de astfel de funcționalitate este comutarea contextului, care permite sistemului de operare să oprească un program și să ruleze un alt program pe procesor. Această divizare temporară este implementată în toate sistemele de operare moderne.
Deasupra acestor mecanisme se află o logică specifică, încorporată în sistemul de operare, sub formă de „politici”. Politica — este un algoritm prin care sistemul de operare ia decizii. De exemplu, astfel de politici decid ce program trebuie să fie lansat (din lista de comenzi) în primul rând. De exemplu, această sarcină este rezolvată de o politică numită politica de planificare (scheduling policy) și, în alegerea deciziei, se va ghida după date precum: istoricul de lansare (care program a fost activ cel mai mult în ultima minută), ce sarcină generează acest proces (ce tipuri de programe au fost lansate), metricile de performanță (dacă sistemul este optimizat pentru interacțiune interactivă sau pentru capacitate de transfer) și așa mai departe.

Abstracție: procesul

Abstracția unui program în execuție, realizată de sistemul de operare, este ceea ce numim un proces. Așa cum am spus anterior, un proces este pur și simplu un program în execuție, într-un anumit interval de timp. Este programul prin care putem obține informații agregate din diverse resurse ale sistemului și la care se face referire sau pe care programul le afectează în timpul execuției sale.
Pentru a înțelege componentele procesului, trebuie să înțelegem stările sistemului: ce poate citi sau modifica programul în timpul funcționării sale. În orice moment, trebuie să știm ce elemente ale sistemului sunt importante pentru execuția programului.
Unul dintre elementele evidente ale stării sistemului incluse în proces este memoria. Instrucțiunile sunt stocate în memorie. Datele pe care programul le citește sau le scrie sunt de asemenea stocate în memorie. Astfel, memoria pe care procesul o poate adresa (așa-numitul spațiu de adresare) este o parte a procesului.
De asemenea, registrele fac parte din starea sistemului. Multe instrucțiuni sunt destinate să schimbe valoarea registrelor sau să citească valoarea acestora, astfel încât registrele devin și ele o parte importantă a funcționării procesului.
Trebuie menționat că starea mașinii este formată și din anumite registre speciale. De exemplu, IP — pointer-ul de instrucțiune — este indicativul instrucțiunii pe care programul o execută în acest moment. Mai există pointer-ul de stivă și cel asociat cu acesta, pointer-ul de cadru, care sunt utilizate pentru gestionarea: parametrilor funcțiilor, variabilelor locale și adreselor de retur.
În cele din urmă, programele apelează adesea la ROM (memorie de tip permanent). Informațiile despre „I/O” (input-output) trebuie să includă o listă a fișierelor deschise de proces în acel moment.

API-ul procesului

Pentru a îmbunătăți înțelegerea funcționării procesului, să examinăm exemple de apeluri de sistem care ar trebui să fie incluse în orice interfață a sistemului de operare. Aceste API-uri sunt disponibile într-o formă sau alta pe orice sistem de operare.

● Creează (creare): În OS ar trebui să existe o metodă prin care să se poată crea noi procese. Atunci când introduci o comandă în terminal sau deschizi o aplicație printr-un dublu click pe pictogramă, se trimite o solicitare către OS pentru a crea un nou proces și pentru a lansa programul specificat.
● Ștergere: Dacă există o interfață pentru crearea de procese, OS ar trebui să ofere și opțiunea de a forța eliminarea unui proces. Majoritatea programelor se vor lansa și se vor încheia de la sine pe parcursul execuției. În caz contrar, utilizatorul ar dori să aibă posibilitatea de a le închide, iar astfel interfața pentru oprirea procesului nu ar fi de prisos.
● Wait (așteptare): Uneori, este util să aștepți finalizarea unui proces, așa că sunt furnizate anumite interfețe care permit așteptarea.
● Control divers (diverse control): Pe lângă închiderea și așteptarea procesului, există și alte metode de control diverse. De exemplu, majoritatea sistemelor de operare oferă posibilitatea de a suspenda un proces (oprirea execuției pentru o perioadă) și apoi de a-l relua (continuarea execuției).
● Stare (stare): Există diverse interfețe pentru a obține informații despre starea procesului, cum ar fi durata de funcționare sau în ce stare se află în acest moment.

Sisteme de Operare: Trei Piese Ușoare. Partea 2: Abstracție: Proces (traducere)

Crearea procesului: detalii

Unul dintre aspectele interesante este cum programele se transformă în procese. În special, cum sistemul de operare încarcă și rulează un program. Cum este creat un proces specific.
În primul rând, sistemul de operare trebuie să încarce codul programului și datele statice în memorie (în spațiul de adresă al procesului). Programele sunt de obicei stocate pe un disc sau pe un SSD într-un anumit format executabil. Astfel, procesul de încărcare a programului și datelor statice în memorie necesită ca sistemul de operare să citească acești biți de pe disc și să îi plaseze undeva în memorie.

În sistemele de operare mai vechi, procesul de încărcare era realizat cu nerăbdare (eagerly), adică codul era încărcat în memorie în întregime înainte ca programul să fie lansat. Sistemele de operare moderne fac acest lucru în mod leneș (lazily), adică încărcând bucăți de cod sau date doar atunci când sunt necesare programului în timpul execuției sale.

După ce codul și datele statice sunt încărcate în memorie, sistemul de operare trebuie să efectueze câteva alte lucruri înainte de a lansa procesul. Un anumit spațiu de memorie trebuie să fie alocat pentru stivă. Programele folosesc stiva pentru variabile locale, parametrii funcțiilor și adresele de returnare. Sistemul de operare alocă această memorie și o oferă procesului. Stiva poate fi, de asemenea, alocată cu anumite argumente, în special, aceasta completează parametrii funcției main(), de exemplu, cu un array argc și argv.

Sistemul de operare poate, de asemenea, să aloce o anumită cantitate de memorie pentru heap-ul programului. Heap-ul este utilizat de programe pentru datele alocate dinamic la cerere. Programele solicită acest spațiu, apelând funcția malloc() și îl eliberează explicit, apelând funcția free(). O grămadă este necesară pentru structuri de date precum: liste legate, tabele de hash, arbori și altele. La început, pentru grămadă se alocă o cantitate mică de memorie, dar pe parcursul funcționării programului, grămada poate solicita o cantitate mai mare de memorie, printr-un apel de API de bibliotecă malloc(). Sistemul de operare este implicat în procesul de alocare a unei cantități mai mari de memorie pentru a ajuta la satisfacerea acestor cereri.

Sistemul de operare va efectua, de asemenea, sarcini de inițializare, în special cele legate de intrare-ieșire. De exemplu, în sistemele UNIX, fiecare proces are în mod implicit 3 descriptor de fișiere deschise, pentru fluxul standard de intrare, ieșire și erori. Aceste descriptoare permit programelor să citească intrările de la terminal și, de asemenea, să afișeze informații pe ecran.

Astfel, încărcând codul și datele statice în memorie, creând și inițializând stiva, precum și efectuând alte lucrări legate de sarcinile de intrare-ieșire, OS pregătește terenul pentru executarea procesului. În cele din urmă, rămâne ultima sarcină: a lansa programul pentru execuție prin punctul său de intrare, numit funcția main(). Trecând la executarea funcției main(), OS transferă controlul CPU-ului procesului nou creat, astfel încât programul începe să ruleze.

Starea procesului

Acum, când avem o oarecare înțelegere despre ce este un proces și cum este creat, să enumerăm stările procesului în care acesta poate fi. În cea mai simplă formă, un proces poate fi în una dintre aceste stări:
● Running. În stare de execuție, procesul rulează pe procesor. Asta înseamnă că instrucțiunile sunt executate.
● Gata. În stare de pregătire, procesul este gata să pornească, dar din diverse motive, OS nu îl execută în acel moment.
● Blocat. În stare de blocare, procesul efectuează diverse operațiuni care nu îi permit să fie gata de execuție până când nu se întâmplă un anumit eveniment. Un exemplu obișnuit este când procesul inițiază o operație IO, acesta devine blocat și astfel un alt proces poate utiliza procesorul.

Sisteme de Operare: Trei Piese Ușoare. Partea 2: Abstracție: Proces (traducere)

Imaginăm aceste stări ca pe un graf. Așa cum putem vedea în imagine, starea unui proces poate varia între RUNNING și READY, la discreția sistemului de operare. Atunci când starea procesului trece de la READY la RUNNING, înseamnă că procesul a fost programat. Invers, acesta a fost eliminat din programare. În momentul în care procesul devine BLOCKED, de exemplu, începând o operațiune IO, sistemul de operare îl va menține în această stare până la apariția unui anumit eveniment, cum ar fi finalizarea IO. În acel moment, va trece în starea READY și, eventual, imediat în starea RUNNING, dacă aceasta este decizia sistemului de operare.
Să aruncăm o privire asupra unui exemplu despre cum două procese trec prin aceste stări. Pentru început, să ne imaginăm că ambele procese sunt lansate și fiecare folosește doar CPU. În acest caz, stările lor vor arăta astfel.

Sisteme de Operare: Trei Piese Ușoare. Partea 2: Abstracție: Proces (traducere)

În exemplul următor, primul proces, după o anumită perioadă de execuție, solicită IO și trece în starea BLOCKED, oferind altui proces oportunitatea de a porni (ILUSTRAȚIE 1.4). Sistemul de operare observă că procesul 0 nu folosește CPU și pornește procesul 1. În timpul execuției procesului 1, operațiunea IO se finalizează, iar starea procesului 0 se schimbă în READY. În cele din urmă, procesul 1 se finalizează, iar odată cu încheierea sa, procesul 0 este lansat, executat și finalizează sarcina sa.

Sisteme de Operare: Trei Piese Ușoare. Partea 2: Abstracție: Proces (traducere)

Structura datelor

Sistemul de operare este, de asemenea, un program și, la fel ca orice alt program, are anumite structuri de date cheie, care monitorizează diverse piese de informații relevante. Pentru a urmări starea fiecărui proces, sistemul de operare va menține o listă de procese pentru toate procesele în starea READY și anumite informații suplimentare pentru a urmări procesele care se execută în prezent. De asemenea, sistemul de operare trebuie să monitorizeze procesele blocate. După finalizarea IO, sistemul de operare trebuie să trezească procesul corespunzător și să-l transfere în starea de pregătire pentru execuție.

Astfel, de exemplu, sistemul de operare trebuie să salveze starea registrelor procesorului. În momentul opririi procesului, starea registrelor este salvată în spațiul de adresare al procesului, iar în momentul reluării execuției acestuia, valorile registrelor sunt restaurate și, astfel, se continuă execuția acestui proces.

Pe lângă stările ready, blocked, running, există și alte stări. Uneori, în momentul creării, un proces poate avea starea INIT. În cele din urmă, un proces poate fi plasat în starea FINAL, când acesta s-a încheiat, dar informațiile despre el nu au fost încă șterse. În sistemele UNIX, această stare se numește proces-zombi. Această stare este utilă în situațiile în care procesul părinte vrea să afle codul de returnare al copilului; de exemplu, în general 0 semnalează o finalizare cu succes, iar 1 o eroare, totuși programatorii pot folosi coduri suplimentare de ieșire pentru a semnaliza diferite probleme. La finalizarea sa, procesul-părinte face ultima apelare a sistemului, de exemplu wait(), pentru a aștepta încheierea procesului copil și a semnaliza sistemului de operare că se pot șterge orice date asociate cu procesul încheiat.

Sisteme de Operare: Trei Piese Ușoare. Partea 2: Abstracție: Proces (traducere)

Punctele cheie ale prelegerii:

● Procesul — principala abstrare a unui program funcțional în sistemul de operare. În orice moment, un proces poate fi descris prin starea sa: conținutul memoriei în spațiul său de adrese, conținutul registrelor procesorului, inclusiv indicatorul de instrucțiuni și indicatorul de stivă, precum și informații despre IO, cum ar fi fișierele deschise, care sunt citite sau scrise.
● API-ul procesului constă din apeluri pe care programele le pot face în legătură cu procesele. În general, acestea sunt apeluri pentru crearea, ștergerea sau altele.
● Un proces se află într-una din numeroasele stări, inclusiv running, ready, blocked. Diferite evenimente, cum ar fi programarea, excluderea din programare sau așteptarea, pot schimba starea procesului dintr-una în alta.
● Lista proceselor conține informații despre toate procesele din sistem. Fiecare înregistrare din aceasta se numește bloc de control al procesului, care, în realitate, este o structură ce conține toată informația necesară despre un proces specific.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster