Hyrja në sistemet operative
Përshëndetje, Habr! Dua të paraqes para jush një seri artikujsh me përkthime nga një letërsi interesante sipas mendimit tim - OSTEP. Ky material shqyrton mjaft thellë funksionimin e sistemeve operativë të ngjashme me UNIX, domethënë - punën me proceset, planifikuesit e ndryshëm, memorien dhe komponentë të tjerë të ngjashëm që përbëjnë një sistem operativ modern. Origjinali i të gjithë materialeve mund ta shihni këtu . Ju lutem, merrni parasysh se përkthimi është realizuar në mënyrë jo profesionale (mjaft lirshëm), por shpresoj se kuptimi i përgjithshëm e kam ruajtur.
Për laboratorët në këtë lëndë mund të gjeni këtu:
- origjinal:
- origjinal:
- adaptimi im personal:
Pjesët e tjera:
Dhe gjithashtu mund të shikoni kanalin tim në =)
Le të shqyrtojmë abstraksionin më themelor që OF ofron përdoruesve: procesi. Përkufizimi i procesit është mjaft i thjeshtë — është një program në funksionim. Programi vetë është një gjë pa jetë, që ndodhet në diskut — një grup udhëzimesh dhe ndoshta disa të dhëna statike, që presin momentin e nisjes. Pikërisht OF merr këto bajta dhe i nis ato, duke e transformuar programin në diçka të dobishme.
Më së shpeshti, përdoruesit dëshirojnë të nisin më shumë se një program në të njëjtën kohë; për shembull, ju mund të ninit në laptopin tuaj një shfletues, një lojë, një lojtar multimedial, një redaktues teksti dhe kështu me radhë. Në fakt, një sistem tipik mund të nisë dhjetëra dhe qindra procese në të njëjtën kohë. Ky fakt e bën sistemin më të lehtë për t'u përdorur, ju keni gjithmonë mundësinë të nisni programe pa u shqetësuar se a është CPU i lirë.
Nga kjo lind problemi: si të sigurohet iluzioni i shumë CPU-ve? Si mund të krijojë OF iluzionin e një numri praktikisht të pafund CPU-sh, ndonëse ju keni vetëm një CPU fizik?
OF e krijon këtë iluzion përmes virtualizimit të CPU-ve. Duke nisur një proces, pastaj duke e ndalur atë, duke nisur një proces tjetër dhe kështu me radhë, OF mund të mbajë iluzionin se egzistojnë shumë CPU virtuale, ndonëse në të vërtetë do të ishte një ose disa procesorë fizikë. Kjo teknikë quhet ndarja e burimeve të CPU-së sipas kohës. Kjo teknikë u lejon përdoruesve të nisin aq shumë procese të njëkohshme sa të dëshirojnë. Çmimi i këtij zgjidhjeje është performanca – pasi nëse CPU-sh ndahen nga disa procese, çdo proces do të përpunojë më ngadalë.
Për të realizuar virtualizimin e CPU-ve, dhe veçanërisht për ta bërë këtë mirë, OF ka nevojë për mbështetje si në nivel të ulët ashtu edhe në nivel të lartë. Mbështetja në nivel të ulët quhet mekanizmat — këto janë metoda ose protokolle të nivelit të ulët që realizojnë pjesën e nevojshme të funksionalitetit. Një shembull i tillë funksionaliteti është kalimi i kontekstit, i cili i jep OF mundësinë të ndalë një program dhe të nisë një program tjetër në procesor. Kjo ndarje sipas kohës është realizuar në të gjitha OF-të moderne.
Në kulmin e këtyre mekanizmave ndodhet një logjikë e caktuar, e vendosur në sistemin operativ në formën e "politikave". Politika është një algoritem i caktuar për marrjen e vendimeve nga sistemi operativ. Këto politika, për shembull, vendosin se cila program duhet të fillohet (nga lista e komandave) në radhë të parë. Kështu, për shembull, ky problem do të zgjidhet nga politika e quajtur politika e planifikimit (scheduling policy) dhe kur zgjidh një vendim do të udhëhiqet nga të dhëna të tilla si: historia e nisjes (cili program është nisur për më gjatë gjatë minutës së fundit), çfarë ngarkese ka ky proces (cilat lloje programesh janë nisur), metrikat e performancës (a është optimizuar sistemi për ndërveprimin interaktiv apo për kapacitetin) e kështu me radhë.
Abstraksioni: procesi
Abstraksioni i një programi në punë, i ekzekutuar nga sistemi operativ, është ajo çka ne e quajmë një proces. Siç është thënë më parë, procesi është thjesht një program në punë, në çdo moment të caktuar të kohës. Një program me të cilin mund të marrim informacionin e përgjithshëm nga burime të ndryshme të sistemit dhe të cilave programi ka qasje apo që ndikon gjatë ekzekutimit të tij.
Për të kuptuar përbërësit e procesit, duhet të kuptojmë gjendjet e sistemit: çfarë mund të lexojë apo të modifikojë programi gjatë punës së tij. Në çdo moment të caktuar, duhet të kuptojmë se cilët elemente të sistemit janë të rëndësishëm për ekzekutimin e programit.
Një nga elementet e dukshme të gjendjes së sistemit, të cilat përfshihen në proces, është memoria. Instruksionet ndodhen në memorie. Të dhënat që programi lexon ose shkruan gjithashtu ndodhen në memorie. Kështu, memoria që procesi mund të adresojë (hapësira e quajtur adresë) është një pjesë e procesit.
Po ashtu, pjesë e gjendjes së sistemit janë regjistrat. Shumë instruksione janë të orientuara për të ndryshuar vlerën e regjistrave ose për të lexuar vlerën e tyre dhe kështu regjistrat bëhen gjithashtu një pjesë e rëndësishme e funksionimit të procesit.
Duhet theksuar se gjendja e makinës formohet gjithashtu nga disa regjistra të veçantë. Për shembull, IP — instruction pointer është treguesi i instruksionit që programi po ekzekuton në këtë moment. Ka gjithashtu stack pointer dhe i lidhur me të frame pointer, që përdoren për të menaxhuar: parametrat e funksioneve, variablat lokale dhe adresat e kthimit.
Së fundi, programet shpesh i referohen ROM-it (memoria e përhershme). Kjo informacion mbi “I/O” (hyrjen-daljen) duhet të përfshijë një listë të skedareve që procesi i ka hapur në atë moment.
API-i i Procesit
Për të përmirësuar kuptimin e funksionimit të procesit, le të shqyrtojmë shembuj të thirrjeve sistemore që duhet të përfshihen në çdo ndërfaqe të sistemit operativ. Këto API në një formë ose tjetër janë të disponueshme në çdo OS.
● Krijo (krijimi): Një OS duhet të ketë ndonjë mënyrë për të krijuar procese të reja. Kur jepni një komandë në terminal ose hapni një aplikacion me një klik të dyfishtë mbi ikonen, bëhet një kërkesë për OS-në për të krijuar një proces të ri dhe për të nisur programin e specifikuar.
● Çinstalim: Meqenëse egziston një ndërfaqe për krijimin e procesit, OS gjithashtu duhet të ofrojë mundësinë për të eliminuar me forcë një proces. Shumica e programeve natyrshëm do të fillojnë dhe përfundojnë vetë në përputhje me funksionimin e tyre. Në rast tjetër, përdoruesi do të dëshirojë të ketë mundësinë për ta përfunduar dhe kështu ndërfaqja për ndalimin e procesit do të ishte e dobishme.
● Pritja (pritja): Ndonjëherë është e dobishme të prisni për përfundimin e një procesi, prandaj ofrohen disa ndërfaqe që ofrojnë mundësinë e presjes.
● Kontrolli Misc (menaxhim i ndryshëm): Përveç vrasjes dhe pritjes së procesit, ekzistojnë edhe metoda të tjera të ndryshme kontrolluese. Për shembull, shumica e OS-ve ofrojnë mundësinë për të ngrirë një proces (ndalimi i ekzekutimit të tij për një periudhë) dhe rinisjen e mëvonshme (vazhdimi i ekzekutimit)
● Status (statusi): Ekzistojnë ndërfaqe të ndryshme për të marrë informacion mbi statusin e procesit, siç është koha e tij e punës ose në cilin gjendje ndodhet aktualisht.

Krijimi i procesit: detajet
Një nga gjërat interesante është se si programet transformohen në procese. Sidomos, si OS e ngre dhe e nis programin. Si krijohet konkretisht një proces.
Së pari, sistemi operativ duhet të ngarkojë kodin e programit dhe të dhënat statike në memorje (në hapësirën adresuese të procesit). Programet zakonisht ndodhen në disk ose në një njësi ruajtjeje të ngurtë në një format ekzekutiv. Pra, procesi i ngarkimit të programit dhe të dhënave statike në memorje kërkon që sistemi operativ të ketë aftësinë të lexojë këto byte nga disk dhe t'i vendosë ato diku në memorje.
Në sistemi operativ të hershëm, procesi i ngarkimit kryhej me ngut (eagerly), që do të thotë se kodi ngarkohej në memorje në tërësi para se programi të fillonte. Sistemet moderne operojnë në mënyrë të ngadaltë (lazily), duke ngarkuar copëza kodi ose të dhënash vetëm kur ato kërkohen nga programi gjatë ekzekutimit të tij.
Pasi kodi dhe të dhënat statike janë ngarkuar në memorje, sistemi operativ duhet të realizojë edhe disa detyra të tjera përpara se të nisë procesin. Një sasi memorjeje duhet të ndahet për stakun. Programet përdorin stakun për variabla lokalë, parametra funksionesh dhe adresa kthimi.. Sistemi operativ ndan këtë memorje dhe ia kthen procesit. Staku gjithashtu mund të ndahen me disa argumente, duke plotësuar specifikisht parametrat e funksionit main(), për shembull, me një varg argc dhe argv.
Sistemi operativ gjithashtu mund të ndanë një sasi memorjeje për heap-in e programit. Heap-i përdoret nga programet për të dhënat që kërkohen në mënyrë dinamike.. Programet kërkojnë këtë hapësirë duke thirrur funksionin malloc() dhe e pastron atë, duke thirrur funksionin free(). Heap-i është i nevojshëm për struktura të tilla si: lista të lidhura, tabela hash, pemë dhe të tjera. Në fillim, një sasi e vogël memorjeje ndahen për heap-in, por me kalimin e kohës, gjatë punës së programit, heap-i mund të kërkojë më shumë memorje përmes thirrjes së API të bibliotekës malloc(). Sistemi operativ është i përfshirë në procesin e ndarjes së më shumë memorjes për të ndihmuar në përmbushjen e këtyre thirrjeve.
Sistemi operativ gjithashtu do të kryejë detyra inizializimi, veçanërisht ato që lidhen me hyrjet dhe daljet. Për shembull, në sistemet UNIX, çdo proces përshtypëse ka 3 deshifrues të hapur të skedave për rrjedhën standarde të hyrjes, daljes dhe gabimeve. Këto deshifrues lejojnë programet të lexojnë hyrjen nga terminali dhe gjithashtu të dalin informacionin në ekran.
Kështu, duke ngarkuar kodin dhe të dhënat statike në memorie, duke krijuar dhe inicializuar stekën, si dhe duke kryer punë të tjera që lidhen me ekzekutimin e detyrave të futjes-daljes, sistemi operativ përgatit terrenin për ekzekutimin e procesit. Në fund të fundit, mbetet detyra e fundit: të iniciohet programi përmes pikës së tij të hyrjes, të quajtur funksi main(). Duke kaluar te ekzekutimi i funksionit main(), OS i kalon kontrollin CPU-së procesit të sapokrijuar, kështu që programi fillon të ekzekutohet.
Gjendja e procesit
Tani, kur kemi një farë kuptimi se çfarë është një proces dhe si krijohet ai, le të renditim gjendjet e procesit, në të cilat ai mund të ndodhet. Në formën më të thjeshtë, procesi mund të jetë në një nga këto gjendje:
● Running. Në gjendjen e ekzekutimit, procesi ekzekutohet në CPU. Kjo do të thotë se po kryhen instruksionet.
● Gati. Në gjendjen e gatishmërisë, procesi është gati të nisë, por për ndonjë arsye OS-ja nuk e ekzekuton atë në atë moment të caktuar.
● Blocked. Në gjendjen e bllokimit, procesi po kryen ndonjë operacion që nuk e lejon të jetë i gatshëm për ekzekutim derisa të ndodhi ndonjë ngjarje. Një nga shembujt e zakonshëm është kur procesi inicializon një operacion IO, ai bëhet i bllokuar dhe kështu një proces tjetër mund ta përdorë CPU-në.

Ata mund të imagjinohen këto gjendje si një graf. Siç mund ta shohim në figurë, gjendja e procesit mund të ndryshojë mes RUNNING dhe READY sipas dëshires së OS-së. Kur gjendja e procesit kalon nga READY në RUNNING, kjo do të thotë se procesi është planifikuar. Në anën tjetër - është hequr nga planifikimi. Në momentin kur procesi bëhet BLOCKED, për shembull, duke inicializuar një operacion IO, OS do ta mbajë atë në këtë gjendje deri në ndodhin e ndonjë ngjarjeje, për shembull përfundimi i IO. Në këtë moment, kalimi në gjendjen READY dhe ndoshta menjëherë në gjendjen RUNNING, nëse kjo do të vendosë OS.
Le të hedhim një vështrim në një shembull se si dy procese kalojnë përmes këtyre gjendjeve. Fillimisht imagjinoni se të dy proceset janë në ekzekutim, dhe secili përdor vetëm CPU. Në këtë rast, gjendjet e tyre do të duken si më poshtë.

Në shembullin e mëposhtëm, procesi i parë pas një kohe të caktuar kërkon IO dhe kalon në gjendjen BLOCKED, duke i dhënë mundësinë një procesi tjetër të fillojë (SHK. 1.4). OS shikon se procesi 0 nuk po përdor CPU-në dhe aktivizon procesin 1. Gjatë ekzekutimit të procesit 1, IO përfundon dhe statusi i procesit 0 ndryshon në READY. Së fundi, procesi 1 përfundon, dhe pas përfundimit të tij, procesi 0 aktivizohet, ekzekutohet dhe përfundon punën e tij.

Struktura e të dhënave
OS vetë është një program dhe ashtu si çdo program tjetër ka disa struktura kyçe të të dhënave që ndjekin informacione të ndryshme relevante. Për të ndjekur statusin e çdo procesi, OS do të mbajë një listë procesesh për të gjithë proceset në gjendjen READY dhe disa informacione shtesë për të ndjekur proceset që aktualisht po ekzekutohen. Gjithashtu, OS duhet të ndjekë edhe proceset e bllokuara. Pas përfundimit të IO, OS është e detyruar të zgjojë procesin e nevojshëm dhe ta kalojë atë në gjendjen e gatishmërisë për të filluar.
Kështu, për shembull, OS duhet të ruajë statusin e regjistrave të procesorit. Në momentin e ndalimit të procesit, gjendja e regjistrave ruhet në hapësirën adresore të procesit, dhe në momentin e vazhdimit të punës së tij, rinovohen vlerat e regjistrave dhe kështu vazhdohet realizimi i këtij procesi.
Përveç gjendjeve ready, blocked, running, ekzistojnë edhe disa gjendje të tjera. Nësë, në momentin e krijimit, procesi mund të ketë gjendjen INIT. Në fund, procesi mund të vendoset në gjendjen FINAL, kur ai ka përfunduar, por informacioni në lidhje me të nuk është pastruar ende. Në sistemet UNIX, kjo gjendje quhet proces-zombi. Kjo gjendje është e dobishme për rastet kur procesi prind dëshiron të dijë kodin e kthimit të pasardhësit, për shembull, zakonisht 0 sinjalizon përfundim të suksesshëm, dhe 1 për një gabim, megjithatë programuesit mund të krijojnë kode të tjera dalëse, duke sinjalizuar për probleme të ndryshme. Në përfundim, procesi prind bën thirrjen e fundit sistemike, për shembull wait(), për të pritur përfundimin e punës së procesit pasardhës dhe për të sinjalizuar OS-në që mund të pastrojë çdo të dhënë të lidhur me procesin e përfunduar.

Pikat kyçe të leksionit:
● Në ditën e fillimit të modulit, struktura e tij bëhet e disponueshme. Studimi në UoL përbëhet nga cikli në vijim: — abstraksioni kryesore e një programi funksional në OS. Në çdo moment, një proces mund të përshkruhet sipas gjendjes së tij: përmbajtja e memories në hapësirën e tij adresuese, përmbajtja e regjistrave të procesorit, duke përfshirë treguesin e instruksioneve dhe treguesin e stack, si dhe informacionin mbi IO, siç janë skedarët e hapur që po lexohen ose shkruhen.
● API-i i Procesit përbëhet nga thirrje që programet mund të bëjnë në lidhje me proceset. Në përgjithësi, këto janë thirrje për krijimin, fshirjen ose veprime të tjera.
● Një proces ndodhet në një nga shumë gjendje, duke përfshirë running, ready, blocked. Ngjarje të ndryshme, si planifikimi, përjashtimi nga planifikimi ose pritja, mund të transferojnë gjendjen e procesit nga njëra në tjetrën.
● Lista e proceseve përmban informacion mbi të gjitha proceset në sistem. Çdo hyrje në të quhet blloku i kontrollit të procesit, i cili në të vërtetë është një strukturë që përmban të gjitha informacionet e nevojshme mbi një proces të caktuar.
Burimi: habr.com
