Uvod v operacijske sisteme
Pozdrav, Habr! Rad bi predstavil serijo člankov-prevedb ene zanimive literature — OSTEP. V tem gradivu se poglobljeno ukvarja z delovanjem unix-podobnih operacijskih sistemov, zlasti z obvladovanjem procesov, različnimi načrtovalniki, pomnilnikom in drugimi podobnimi sestavinami, ki sestavljajo sodobni OS. Izvirnik vseh gradiv si lahko ogledate tukaj . Prosimo, upoštevajte, da je prevod izveden nepoklicno (dokaj prosto), a upam, da sem ohranil splošen smisel.
Laboratorijske vaje za to temo lahko najdete tukaj:
— izvirnik:
— izvirnik:
— moja osebna prilagoditev:
Lahko tudi obiščete moj kanal v =)
Delovanje programa
Kaj se zgodi, ko deluje neka programska oprema? Zagnana programska oprema izvaja eno preprosto nalogo — izvaja ukaze. Vsako sekundo milijoni in morda celo milijarde ukazov procesor izloči iz delovnega pomnilnika, nato jih dekodira (na primer, prepozna, kateremu tipu ukazov pripadajo) in izvede. To so lahko seštevanje dveh števil, dostop do pomnilnika, preverjanje pogoja, prehod na funkcijo in tako naprej. Po končanem izvajanju enega ukaza procesor preide na izvajanje drugega. In tako, ukaz za ukazom, se izvršujejo, dokler program ne konča.
Ta primer je seveda predstavljen poenostavljeno — v resnici sodobna strojna oprema omogoča, da procesor izvaja ukaze izven vrste, predvideva možne rezultate, izvršuje ukaze hkrati in podobne trike za pospešitev delovanja.
Von Neumannova modela računanja
Poenostavljena oblika delovanja, ki smo jo opisali, je podobna Von Neumannovemu modelu računanja. Von Neumann je eden izmed pionirjev računalniških sistemov, prav tako je eden izmed avtorjev teorije iger. Med delovanjem programa se odvija še vrsta drugih dogodkov, deluje veliko drugih procesov in zunanje logike, katerih osnovni cilj je poenostaviti zagon, delovanje in vzdrževanje sistema.
Obstaja nabor programske opreme, ki je odgovoren za enostavno zagon programov (ali celo omogoča hkratno zagon več programov), omogoča programom deljenje istega spomina ter interakcijo z različnimi napravami. Ta nabor programske opreme pravzaprav imenujemo operacijski sistem, katerega naloge vključujejo spremljanje, da sistem deluje pravilno in učinkovito, ter zagotavljanje enostavne upravljivosti tega sistema.
Operacijski sistem
Operacijski sistem, okrajšano OS, je kompleks medsebojno povezanih programov, namenjenih upravljanju virov računalnika in organizaciji interakcije uporabnika z računalnikom..
OS dosega svojo učinkovitost predvsem preko tehnike, ki je najpomembnejša — tehnike virtualizacije.. OS komunicira s fizičnimi viri (procesorjem, pomnilnikom, diskom in podobno) ter jih preoblikuje v bolj splošno, z večjimi možnostmi in lažjo obliko za uporabo. Zato lahko za splošno razumevanje operacijski sistem grobo primerjamo z virtualnim strojem.
Da omogoči uporabnikom, da dajejo ukaze operacijskemu sistemu in s tem uporabljajo možnosti virtualnega stroja (kot so: zagon programa, dodeljevanje pomnilnika, dostop do datotek itd.), operacijski sistem zagotavlja nekakšen vmesnik, imenovan API (sistem za programiranje aplikacij) do katerega je mogoče opraviti klice (call). Tipičen operacijski sistem omogoča izvajanje stotin sistemskih klicev.
In nazadnje, ker virtualizacija omogoča, da več programov deluje (tako sočasno uporabljajo CPU) in hkrati dostopajo do njihovih navodil in podatkov (s tem delijo pomnilnik), ter dostopajo do diskov (tako sočasno uporabljajo naprave za vnose in izhode), operacijski sistem imenujemo tudi upravljalec virov. Vsak procesor, disk in pomnilnik sta vir sistema, in tako postane ena od nalog operacijskega sistema upravljanje teh virov, kar počne učinkovito, pošteno ali, nasprotno, odvisno od naloge, za katero je ta operacijski sistem razvit.
Virtualizacija CPU
Razmotrimo sledeći program:
(https://www.youtube.com/watch?v=zDwT5fUcki4&feature=youtu.be)

On ne vrši nikakve posebne radnje, suštinski sve što radi je pozivanje funkcije spin(), čija je svrha ciklička provera vremena i vraćanje, nakon što prođe jedna sekunda. Tako ona beskonačno ponavlja niz koji je korisnik prosledio kao argument.
Pokrenimo ovaj program i prosledimo mu kao argument simbol “A”. Rezultat nije naročito zanimljiv — sistem jednostavno izvršava program koji periodično prikazuje simbol “A”.
Sada probajmo varijantu gde je pokrenuto više instanci istog programa, ali prikazuju različite pojedinačne znakove, kako bi bilo jasnije. U tom slučaju, rezultat će biti nešto drugačiji. Iako imamo jedan procesor, program se izvršava istovremeno. Kako to funkcioniše? Funkcioniše tako što operativni sistem, uz pomoć mogućnosti hardvera, stvara iluziju. Iluziju da u sistemu postoji nekoliko virtuelnih procesora, pretvarajući jedan fizički procesor u teoretski beskonačan broj i omogućavajući programima da deluju kao da se izvršavaju istovremeno. Takvu iluziju nazivamo Virtualizacija CPU.
Ovakva slika postavlja mnoga pitanja, na primer, ako nekoliko programa želi da se pokrene istovremeno, koji program će biti pokrenut? Na to pitanje odgovaraju "politike" operativnog sistema. Politike se koriste na mnogo mesta u operativnom sistemu i odgovaraju na slična pitanja, a takođe predstavljaju osnovne mehanizme koje operativni sistem implementira. Otuda i uloga operativnog sistema kao menadžera resursa.
Virtualizacija memorije
Sada da razmotrimo memoriju. Fizički model memorije u savremenim sistemima predstavlja se kao niz bajtova. Za čitanje iz memorije potrebno je navesti adresu ćelije, kako bi se do nje pristupilo. Da bismo zapisali ili ažurirali podatke, takođe je potrebno navesti podatke i adresu ćelije u koju ćemo ih zapisati.
Pristupi memoriji se vrše neprekidno tokom rada programa. Program čuva u memoriji celu svoju strukturu podataka i obraća se njoj izvršavajući različite instrukcije. Instrukcije se takođe čuvaju u memoriji, tako da se pristup njima vrši na svaki zahtev za sledeću instrukciju.
Klic malloc()
Oglejmo si naslednji program, ki dodeljuje pomnilniški prostor z uporabo klica malloc() (https://youtu.be/jnlKRnoT1m0):

Program izvaja več nalog. Najprej dodeli določen volumen pomnilnika (vrsta 7), nato izpisuje naslov dodeljenega bloka (vrsta 9), zapiše nič v prvo mesto dodeljenega pomnilnika. Nato program vstopi v zanko, ki inkrementira vrednost, zapisano v pomnilniku na naslovu spremenljivke “p”. Prav tako izpisuje identifikator samega procesa. Identifikator procesa je edinstven za vsak zagnan proces. Če zaženemo več kopij, se srečamo z zanimivim rezultatom: V prvem primeru, če ne storimo ničesar in preprosto zaženemo več kopij, bodo naslovi različni. To pa ne ustreza naši teoriji! Prav, ker je v sodobnih distribucijah privzeto vključena funkcija naključne razporeditve pomnilnika. Če jo izklopimo, dobimo pričakovani rezultat — naslovi pomnilnika pri dveh hkrati delujočih programih se bodo ujemali.

Na koncu to pomeni, da dve neodvisni programi delujeta s svojimi lastnimi zasebnimi naslovi, ki jih operacijski sistem prikaže v fizičnem pomnilniku.Zato uporaba naslovov pomnilnika v okviru enega programa ne bo nikakor vplivala na druge, in vsakemu programu se zdi, da ima svoj kos fizičnega pomnilnika, celotno dodeljenega v njegovem upravljanju. Resničnost pa je taka, da je fizični pomnilnik skupen vir, ki ga upravlja operacijski sistem.
Konsistenca
Še ena pomembna tema v okviru operacijskih sistemov je konsistenca. Ta izraz se uporablja, ko govorimo o težavah v sistemu, ki se lahko pojavijo pri delu z več stvarmi hkrati znotraj enega programa. Težave s konsistenco se pojavijo tudi v samem operacijskem sistemu. V prejšnjih primerih z virtualizacijo pomnilnika in procesorja smo ugotovili, da operacijski sistem upravlja z več stvarmi hkrati — zažene prvi proces, nato drugega in tako naprej. Izkazalo se je, da lahko takšno vedenje povzroči nekatere težave. Na primer, sodobni večnitni programi se soočajo s takšnimi težavami.
Razmotrimo sledeći program:

Program v glavni funkciji ustvari dva toka, s pomočjo klica Pthread_create(). V tem primeru lahko o toku razmišljamo kot o funkciji, ki se izvaja v enem pomnilniškem prostoru skupaj z drugimi funkcijami, pri čemer je število hkrati zagnanih funkcij očitno več kot ena. V tem primeru vsak tok začne in izvaja funkcijo worker(), ki preprosto povečuje spremenljivko,.
. Zagnali bomo to program s parameter 1000. Kot ste morda že uganili, bi moral biti rezultat 2000, saj vsak tok poveča spremenljivko 1000-krat. Vendar stvar ni tako enostavna. Poskusimo zagnati program s številom ponovitev za red več.

Ko kot vhodno število damo, na primer, 100000, pričakujemo, da bomo na izhodu videli število 200000. Vendar, ko večkrat zagnamo številko 100000, ne zgolj da ne bomo videli pravilnega odgovora, temveč bomo dobili tudi različne nepravilne odgovore. Razlaga tiči v tem, da za povečanje števila potrebujemo tri operacije - nalaganje števila iz pomnilnika, povečanje in nato zapis števila nazaj. Ker te instrukcije niso izvedene atomarno (vse hkrati), se lahko dogajajo takšne čudne stvari. Ta problem se imenuje v programiranju race condition — stanje dirke. Ko lahko neznane sile v neznanem trenutku vplivajo na izvajanje katere koli od vaših operacij.
Izvor: habr.com
