Hyrja në sistemet operative
Përshëndetje, Habr! Dua t'ju prezantoj një seri artikujsh përkthimesh nga një literaturë interesante sipas mendimit tim — OSTEP. Ky material shqyrton në mënyrë të thellë funksionimin e sistemeve operative të ngjashme me Unix, veçanërisht — menaxhimin e proceseve, planifikuesit e ndryshëm, memorjen dhe komponentët e tjerë të ngjashëm që përbëjnë një sistem operativ modern. Origjinali i të gjitha 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:
— origjinali:
— origjinali:
— adaptimi im personal:
Dhe gjithashtu mund të shikoni kanalin tim në =)
Funksioni i programit
Çfarë ndodh kur një program ekzekutohet? Programi i nisur kryen një veprim të thjeshtë — ai ekzekuton instruktionet. Çdo sekondë miliona dhe ndoshta miliarda instrukcionesh eksportohet nga procesori nga memorie operative, i cili në të njëjtën kohë i dekodon (p.sh., njeh se cilit lloj i përkasin këto instrukcione) dhe i ekzekuton. Këto mund të jenë shkallëzimi i dy numrave, qasje në memorie, kontrollim kushti, kalim në funksion dhe kështu me radhë. Pasi të përfundojë ekzekutimi i një instrukcioni, procesori kalon në ekzekutimin e një tjetër. Dhe kështu, instrukcioni pas instrukcioni, ato ekzekutohen derisa programi të përfundojë.
Ky shembull sigurisht është shqyrtuar në mënyrë të thjeshtuar — në të vërtetë, për të përshpejtuar punën e procesorit, hardueri modern lejon ekzekutimin e instrukcioneve jashtë rendit, llogaritjen e rezultateve të mundshme, ekzekutimin e instrukcioneve në një kohë dhe truqet e tjera të ngjashme.
Modeli i llogaritjes së Von Neumann
Forma e thjeshtuar e funksionimit që kemi përshkruar ngjan me modelin e llogaritjes së Von Neumann. Von Neumann është një nga pioneerët e sistemeve kompjuterike, gjithashtu është një nga autorët e teorisë së lojërave. Gjatë punës së programit ndodhin shumë ngjarje të tjera, punojnë shumë procese të tjera dhe logjika anësore, qëllimi kryesor i të cilave është thjeshtimi i nisjes, funksionimit dhe mirëmbajtjes së sistemit.
Ekziston një grup softuerësh që është përgjegjës për lehtësinë e nisjes së programeve (ose madje lejon ekzekutimin e disa programeve njëherësh), ai lejon që programet të ndajnë të njëjtin kujtesë dhe gjithashtu të bashkëveprojnë me pajisje të ndryshme. Ky grup softuerësh (software) quhet në thelb sistem operativ dhe detyrat e tij përfshijnë monitorimin për të siguruar që sistemi të funksionojë saktë dhe efikas, si dhe të sigurojë lehtësinë e menaxhimit të këtij sistemi.
Операционная система
Sistemi operativ, shkurt si OS — është një grup i ndërlidhur programesh që janë të destinuara për të menaxhuar burimet e kompjuterit dhe për të organizuar ndërveprimin e përdoruesit me kompjuterin..
OS arrin efektivitetin e saj kryesisht përmes teknikës më të rëndësishme — teknikës virtualizimit. OS ndërvepron me burimet fizike (procesori, kujtesa, disku dhe të ngjashme) dhe e transformon atë në një formë më të përgjithshme, me mundësi më të mëdha dhe më të lehtë për t'u përdorur. Prandaj, për të kuptuar në mënyrë të përgjithshme, mund të krahasohet në një mënyrë shumë të thjeshtë sistemin operativ me një makinë virtuale.
Për të lejuar përdoruesit të japin komandat sistemit operativ dhe kështu të shfrytëzojnë mundësitë e makinës virtuale (siç janë: ekzekutimi i programeve, shpërndarja e kujtesës, qasja në skedar dhe kështu me radhë), sistemi operativ ofron një ndërfaqe të caktuar, të quajtur API (application programming interface) e cila mund të thirret (call). Një sistem operativ tipik ofron mundësinë për të realizuar qindra thirrje sistemike.
Dhe në fund, përderisa virtualizimi lejon që shumë programe të punojnë (në këtë mënyrë, të ndajnë CPU-në), dhe njëkohësisht të kenë qasje në udhëzimet dhe të dhënat e tyre (ndërsa ndajnë kujtesën), si dhe të kenë qasje në disqet (në këtë mënyrë, të ndajnë pajisjet e hyrjes dhe daljes), sistemi operativ njihet gjithashtu si menaxher i burimeve. Çdo procesor, disk dhe kujtesë është burim i sistemit dhe kështu një nga rolet e sistemit operativ bëhet menaxhimi i këtyre burimeve, duke e bërë këtë në mënyrë efikase, të ndershme ose, përkundrazi, në varësi të detyrës për të cilën ky sistem operativ është krijuar.
Virtualizimi i CPU
Le të shqyrtojmë programin e mëposhtëm:
(https://www.youtube.com/watch?v=zDwT5fUcki4&feature=youtu.be)

Ai nuk kryen ndonjë veprim të veçantë; në thelb, gjithçka që bën është të thërrasë funksionin spin(), i cili ka si detyrë të kontrollojë koha në cikël dhe të kthehet, pasi të ketë kaluar një sekondë. Në këtë mënyrë, ai përsërit pafundësisht string-un që përdoruesi e kaloi si argument.
Të lançojmë këtë program dhe t'i kalojmë si argument simbolin “A”. Rezultati nuk është shumë interesant — sistemi thjesht ekzekuton programin, i cili periodicisht shfaq simbolin “A” në ekran.
Tani, le të provojmë një variant kur janë të lançuar shumë instanca të të njëjtit program, por që shfaqin letra të ndryshme, për të qenë më e qartë. Në këtë rast, rezultati do të jetë disi ndryshe. Megjithëse kemi një procesor, programi ekzekutohet njëkohësisht. Si është e mundur kjo? E mundshme është që sistemi operativ, me ndihmën e kapaciteteve të pajisjes, krijon iluzionin. Iluzionin se në sistem ka disa procesorë virtualë, duke e kthyer një procesor fizik në një numër teorikisht të pafund, dhe kështu duke lejuar që, programet të duken se ekzekutohen njëkohësisht. Ky iluzion quhet Virtualizimi i CPU.
Një pamje e tillë ngjall shumë pyetje, për shembull, nëse disa programe dëshirojnë të lancohen njëkohësisht, cili në të vërtetë do të lançohen? Ky pyetje zgjidhet nga “politikë” të OS. Politikat përdoren në shumë vende të OS dhe përgjigjen ndaj pyetjeve të tilla, si dhe janë mekanizma bazë që OS i realizon. Kështu që roli i OS si menaxher burimesh është i rëndësishëm.
Virtualizimi i memories
Tani le të shqyrtojmë memorien. Modeli fizik i memories në sistemet moderne paraqitet si një masiv bitësh. Për të lexuar nga memoria, duhet të specifikohet adresa e qelizës, për të aksesuar atë. Për të shkruar ose për të përditësuar të dhënat, gjithashtu duhet të specifikohet të dhënat dhe adresa e qelizës ku do të shkruhen.
Aksesi në memorie ndodh vazhdimisht gjatë punës së programit. Programi ruan në memorie të gjithë strukturën e të dhënave dhe i qaset asaj duke ekzekutuar instruksione të ndryshme. Instruksionet gjithashtu ruhen në memorie, prandaj aksesi në të ndodh gjithashtu me çdo kërkesë për instruksionin tjetër.
Thirrja malloc()
Le të shqyrtojmë programin e mëposhtëm, i cili alokon një zonë memorie duke përdorur thirrjen malloc() (https://youtu.be/jnlKRnoT1m0):

Programi kryen disa veprime. Së pari, alokon një sasi të caktuar memorjeje (rreshti 7), më pas shfaq adresën e qelisë së alokuar (rreshti 9), shkruan zero në slotin e parë të memorjes së alokuar. Më pas, programi hyn në një cikël ku inkrementon vlerën e shkruar në memorie në adresën e variablës "p". Gjithashtu, ai tregon identifikuesin e procesit të vet. Identifikuesi i procesit është unik për çdo proces të akorduar. Duke ekzekutuar disa kopje, do të ndeshni një rezultat interesant: Në rastin e parë, nëse nuk bëni asgjë dhe thjesht ngjallni disa kopje, adresat do të jenë të ndryshme. Por kjo nuk shkon me teorinë tonë! E saktë, sepse në shpërndarjet moderne është aktivizuar në mënyrë të paracaktuar funksioni i randomizimit të memorjes. Nëse e çaktivizoni, do të merrni rezultatin e pritur - adresat e memorjes për dy programe që funksionojnë njëkohësisht do të përputhen.

Siç rezulton, dy programe të pavarura punojnë me hapësira adrese private të tyre, të cilat më pas reflektohen nga sistemi operativ në memorjen fizike. Prandaj, përdorimi i adresave të memorjes brenda një programi nuk do të ndikojë në të tjerat dhe çdo program i duket se ka një copë të vetme të memorjes fizike, plotësisht në dispozitën e tij. Realiteti, megjithatë, është se memorja fizike është një burim i ndarë, i cili administrohet nga sistemi operativ.
Konsistenca
Një tjetër temë e rëndësishme në kuadër të sistemeve operative është konsistenca. Ky term përdoret kur bëhet fjalë për problemet në sistem, të cilat mund të lindin gjatë punës me shumë gjëra njëkohësisht brenda një programi. Problemet e konsistencës ndodhin edhe në vetë sistemin operativ. Në shembujt e mëparshëm me virtualizimin e memorjes dhe procesorit, kuptuam se sistemi operativ menaxhon shumë gjëra njëkohësisht - fillon procesin e parë, pastaj të dytin dhe kështu me radhë. Siç duket, ky sjellje mund të çojë në disa probleme. Për shembull, programet moderne shumëthesare përjetojnë këto vështirësi.
Le të shqyrtojmë programin e mëposhtëm:

Programi në funksionin kryesor krijon dy thread-e, duke përdorur thirrjen Pthread_create(). Në këtë shembull, mund të mendojmë për thread-in si një funksion të ekzekutuar në një hapësirë memorjeje përgjatë funksioneve të tjera, ku numri i funksioneve të ekzekutuara në të njëjtën kohë është më shumë se një. Në këtë shembull, çdo thread niset dhe realizon funksionin worker() i cili thjesht inkrementon një variabël,.
. Le të nisim këtë program me argumentin 1000. Siç e keni kuptuar, rezultati duhet të jetë 2000, pasi çdo thread inkrementoi variablin 1000 herë. Megjithatë, nuk është aq e thjeshtë. Le të provojmë të ekzekutojmë programin me një numër përsëritjesh shumë më të madh.

Duke futur një numër, për shembull, 100000, ne presim të shohim në dalje numrin 200000. Megjithatë, duke ekzekutuar numrin 100000 disa herë, jo vetëm që nuk do të shohim përgjigje të saktë, por do të marrim përgjigje të ndryshme të gabuara. Zgjidhja qëndron në faktin se për të rritur numrin kërkohen tre operacione - nxjerrja e numrit nga memoria, inkrementimi dhe pastaj shkarkimi i numrit përsëri. Duke qenë se të gjitha këto instruksione nuk realizohen në mënyrë atomike (të gjitha njëherësh), gjëra të çuditshme mund të ndodhin. Kjo problematikë quhet në programim race condition — gjendje garash. Kur forca të panjohura në një moment të panjohur mund të ndikojnë në ekzekutimin e ndonjë prej operacioneve tuaja.
Burimi: habr.com
