Sissejuhatus operatsioonisĂŒsteemidesse
Tere, Habr! Soovin tutvustada teile artiklite seeriat, mis on tĂ”lkinud minu arvates huvitavat kirjandust â OSTEP. Selles materjalis kĂ€sitletakse sĂŒgavalt unix-sarnaste operatsioonisĂŒsteemide toimimist, sealhulgas protsesside, erinevate planeerijate, mĂ€luhalduse ja muude sarnaste komponentide töötamise aspekte, mis moodustavad tĂ€napĂ€eva operatsioonisĂŒsteemi. Originaali kĂ”iki materjale saate vaadata siit: . Palun arvestage, et tĂ”lge on tehtud mitteprofessionaalselt (piisavalt vabalt), kuid loodan, et olen pĂ”hisisu sĂ€ilitanud.
Laboritööd selle aine kohta leiate siit:
- originaal:
- originaal:
- minu isiklik kohandamine:
Teised osad:
Lisaks vÔite minna minu kanalile =)
Vaatame kĂ”ige fundamentaalsemat abstraktsiooni, mida opsĂŒsteem kasutajatele pakub: protsess. Protsessi mÀÀratlemine on ĂŒsna lihtne â see on töötav programm. Programm on iseenesest elutu objekt, mis asub kettal â see on juhiste kogum ja vĂ”imalikud staatilise andmed, mis ootavad kĂ€ivitamise hetke. Just opsĂŒsteem vĂ”tab need baitid ja kĂ€ivitab need, muutes programmi millekski kasulikuks.
Kasutajad soovivad sageli kĂ€ivitada rohkem kui ĂŒhte programmi korraga, nĂ€iteks vĂ”ite oma sĂŒlearvutis kĂ€ivitada brauseri, mĂ€ngu, meediamĂ€ngija, tekstiredaktori ja palju muud. Tegelikult vĂ”ib tĂŒĂŒpiline sĂŒsteem kĂ€ivitada kĂŒmneid ja sadu protsesse samaaegselt. See fakt muudab sĂŒsteemi kasutamise lihtsamaks, te ei pea kunagi muretsema, kas CPU on vaba, vaid lihtsalt kĂ€ivitate programmid.
Siit tĂ”useb probleem: kuidas luua illusioon mitmest CPU-st? Kuidas opsĂŒsteem loob illusiooni praktiliselt lĂ”putust arvust CPU-dest, isegi kui teil on vaid ĂŒks fĂŒĂŒsiline CPU?
OperatsioonisĂŒsteem loob selle illusiooni CPU virtualiseerimise kaudu. KĂ€ivitades ĂŒhe protsessi, seejĂ€rel seda peatades, kĂ€ivitades teise protsessi ja nii edasi, suudab operatsioonisĂŒsteem hoida illusiooni, et eksisteerib palju virtuaalseid CPU-sid, kuigi tegelikult on see ĂŒks vĂ”i mitu fĂŒĂŒsilist protsessorit. Seda tehnikat nimetatakse CPU ressursside ajaks jaotamiseks. See tehnika vĂ”imaldab kasutajatel kĂ€ivitada nii palju samaaegseid protsesse, kui nad soovivad. Selle lahenduse hind on jĂ”udlus â kuna kui CPU-d jagavad mitu protsessi, töötleb iga protsess aeglasemalt.
CPU virtualiseerimise elluviimiseks, eriti selle hĂ€sti tegemiseks, vajab operatsioonisĂŒsteem nii madala taseme kui ka kĂ”rge taseme toetust. Madala taseme toetus on tuntud kui mehhanismid â need madalamal tasemed ja protokollid, mis implementivad vajaliku funktsionaalsuse. NĂ€iteks selline funktsionaalsus on konteksti vahetamine, mis vĂ”imaldab opsĂŒsteemil peatada ĂŒhe programmi ja kĂ€ivitada protsessoril teise programmi. Selline ajajaotus on rakendatud kĂ”igis kaasaegsetes opsĂŒsteemides.
Nende mehhanismide kohal asub opsĂŒsteemis sisseehitatud mingi loogika, mis on esitatud "poliitikatena". Poliitika â see on teatud algoritm, mis mÀÀrab, kuidas opsĂŒsteem otsuseid langetab. Sellised poliitikad otsustavad nĂ€iteks, millist programmi tuleks kĂ€ivitada (kĂ€skude loendist) esmajĂ€rjekorras. NĂ€iteks lahendab selle ĂŒlesande poliitika, mida nimetatakse planeerijaks (scheduling policy) ja otsuse tegemisel juhindub sellistest andmetest nagu: kĂ€ivitamise ajalugu (milline programm on olnud pikim aega kĂ€ivitunud), millist koormust teostatakse antud protsessi poolt (milliseid programme on kĂ€ivitatud), jĂ”udluse mÔÔdikud (kas sĂŒsteem on optimeeritud interaktiivseks suhtlemiseks vĂ”i lĂ€bilaskevĂ”imeks) ja nii edasi.
Abstraktsioon: protsess
Töötava programmi abstraktsioon, mille teostab operatsioonisĂŒsteem, on see, mida me nimetame protsess.. Nagu eelnevalt öeldud, on protsess lihtsalt töötav programm, igas ajavahemikus. Programm, millega saame kokkuvĂ”tlikku teavet erinevatest sĂŒsteemiressurssidest, ja millele see programm pöördub vĂ”i mis selles protsessis osalevad.
Et mĂ”ista protsessi komponente, tuleb mĂ”ista sĂŒsteemi olekuid: mida programm saab lugeda vĂ”i muuta oma töö kĂ€igus. Igal hetkel tuleb mĂ”ista, millised sĂŒsteemi elemendid on programmiks tĂ€itmiseks olulised.
Ăks ilmselge sĂŒsteemi olekuelement, mida protsess sisaldab, on mĂ€lumaht. Juhised asuvad mĂ€lus. Andmed, mida programm loeb vĂ”i kirjutab, asuvad samuti mĂ€lus. Seega on mĂ€lu, mida protsess saab aadressida (nn aadressiruum), osa protsessist.
Samuti on sĂŒsteemi oleku osaks registrid. Palju juhiseid on suunatud registrite vÀÀrtuse muutmisele vĂ”i nende vÀÀrtuse lugemisele, mistĂ”ttu registrid on samuti töötlusprotsessi oluline osa.
Tuleb mĂ€rkida, et masina olek koosneb ka mĂ”ningatest eriregistritest. NĂ€iteks, IP â instrutsioonide nĂ€itaja â viitab instruksioonile, mida programm hetkel tĂ€idab. Lisaks on olemas stack pointer ja sellega seotud frame pointer, mida kasutatakse funktsioonide parameetrite, lokaalsete muutujate ja tagastusadresside haldamiseks.
LĂ”puks pöördub programm sageli pĂŒsiva mĂ€lu poole (ROM). Selline teave âI/Oâ (sisendi-vĂ€ljundi) kohta peab sisaldama nimekirja failidest, mis on protsessi poolt hetkel avatud.
Protsessi API
Protsessi töö mĂ”istmise parandamiseks vaatleme sĂŒsteemikĂ”nesid, mis peaksid olema kaasatud igasse operatsioonisĂŒsteemi liidesesse. Need API-d on mingil kujul kergesti kĂ€ttesaadavad igas operatsioonisĂŒsteemis.
â Loo (loomine): OperatsioonisĂŒsteemis peab olema mingi meetod, mis vĂ”imaldab luua uusi protsesse. Kui sisestate kĂ€su terminalis vĂ”i kĂ€ivitate rakenduse ikoonile kahekordse klĂ”psuga, saadetakse pĂ€ring operatsioonisĂŒsteemile uue protsessi loomiseks ja mÀÀratud programmi kĂ€ivitamiseks.
â Kustuta: Kuna on olemas liides protsessi loomiseks, peab operatsioonisĂŒsteem samuti pakkuma vĂ”imalust protsessi sund_kustutamiseks. Enamik programme kĂ€ivitatakse ja lĂ”petatakse loomulikult nende toimimise kĂ€igus. Vastupidisel juhul soovib kasutaja neid tappa, seega protsessi peatamise liides ei ole ĂŒleliigne.
â Oota (ootamine): MĂ”nikord on kasulik oodata protsessi lĂ”petamist, seetĂ”ttu pakutakse teatud liideseid, mis vĂ”imaldavad oote_kestuse.
â Muu juhtimine (erinev juhtimine): Peale protsessi tapmise ja ootamise on olemas ka muid mitmekesiseid juhtimisviise. NĂ€iteks pakuvad enamik operatsioonisĂŒsteeme vĂ”imalust protsessi kĂŒlmutamiseks (kĂ€sitlemine mĂ”neks ajaks) ja sellele jĂ€rgnevaks jĂ€tkamiseks (tĂ€itmise jĂ€tkamine).
â Olek (olek): Erinevad liidesed vĂ”imaldavad saada teavet protsessi oleku kohta, nagu töö kestus vĂ”i kuidas see praegu seisab.

Protsessi loomine: ĂŒksikasjad
Ăks huvitav asi on see, kuidas programmid tĂ€pselt protsessideks muunduvad. Eriti kuidas opsĂŒsteem tĂ”stab ja kĂ€ivitab programmi. Kuidas protsess tĂ€pselt luuakse.
Esiteks peab opsĂŒsteem laadima programmi koodi ja staatilised andmed mĂ€llu (protsessi aadressiruumi). Programmid asuvad tavaliselt kettal vĂ”i tahkisdrivel mingis teatud tĂ€idetavas vormingus. SeetĂ”ttu nĂ”uab programmi ja staatiliste andmete mĂ€llu laadimise protsess opsĂŒsteemilt nende baitide lugemist kettalt ja paigutamist kuhugi mĂ€llu.
Varasemates opsĂŒsteemides toimus laadimisprotsess kĂ€rsitult (eagerly), mis tĂ€hendab, et kood laaditi mĂ€llu tĂ€ielikult enne programmi kĂ€ivitamist. Kaasaegsed opsĂŒsteemid teevad seda lohakalt (lazily), laadides koodilĂ”ike vĂ”i andmeid alles siis, kui need on programmi töö kĂ€igus vajalikud.
PĂ€rast koodi ja staatiliste andmete laadimist opsĂŒsteemi mĂ€llu tuleb veel paar asja teha enne protsessi kĂ€ivitamist. Teatud kogus mĂ€lu peab olema eraldatud virnale. Programmid kasutavad virna lokaalsete muutuja, funktsiooni parameetrite ja naasaddresside jaoks.. OpsĂŒsteem eraldab selle mĂ€lu ja annab selle protsessile. Virn vĂ”ib samuti olla eraldatud teatud argumentidega, tĂ€psemalt tĂ€idab see funktsiooni main() parameetreid, nĂ€iteks massiiviga argc ja argv.
OpsĂŒsteem vĂ”ib samuti eraldada teatud koguse mĂ€lu programmi hunnik (heap) jaoks. Hunnikku kasutatakse programmide poolt selgelt taotletud dĂŒnaamiliselt eraldatud andmete jaoks.. Programmid kĂŒsivad seda ruumi, kutsudes vĂ€lja funktsiooni malloc() ja puhastavad selle selgesĂ”naliselt, kutsudes vĂ€lja funktsiooni. free(). Heap on vajalik selliste andmestruktuuride jaoks nagu: seotud loendid, hash tabelid, puud ja muud. Alguses eraldatakse vĂ€ike kogus mĂ€lu, kuid aja jooksul vĂ”ib heap programmikĂ€igus kĂŒsida rohkem mĂ€lu, kasutades raamatukogu API kutset malloc(). OperatsioonisĂŒsteem on kaasatud suurema mĂ€lu eraldamise protsessi, et aidata rahuldada neid kutseid.
OperatsioonisĂŒsteem tĂ€idab samuti initsialiseerimise ĂŒlesandeid, sealhulgas neid, mis on seotud sisendi ja vĂ€ljundiga. NĂ€iteks UNIX-sĂŒsteemides on iga protsess vaikimisi avatud kolme failikirje descriptoriga, standardse sisendi, vĂ€ljundi ja vigade jaoks. Need kirjeldajad vĂ”imaldavad programmide lugeda sisendit terminalist ning vĂ€ljundit ekraanile.
Seega laadib operatsioonisĂŒsteem koodi ja staatilisi andmeid mĂ€lu, loob ja initsialiseerib kuhja ning teostab teisi sisend-vĂ€ljundiga seotud töid, valmistades ette keskkonda protsessi tĂ€itmiseks. LĂ”puks jÀÀb viimane ĂŒlesanne: kĂ€ivitada programm selle sisendpunktist, mida nimetatakse funktsiooniks main(). Funktsioonile main() liikudes edastab operatsioonisĂŒsteem kontrolli taasluu enda protsessile, millega programm hakkab tööle.
Protsessi seisund
NĂŒĂŒd, kui meil on teatud arusaam, mis on protsess ja kuidas see luuakse, loetleme protsessi seisundid, milles see vĂ”ib viibida. KĂ”ige lihtsamal kujul vĂ”ib protsess olla ĂŒhes jĂ€rgmistest seisunditest:
â Running. KĂ€ivitamis seisundis tĂ€idab protsess protsessoris kĂ€sku. See tĂ€hendab, et juhised on ootel.
â Valmis. Valmiduse seisundis on protsess valmis kĂ€ivituma, kuid mingil pĂ”hjusel ei kĂ€ivita operatsioonisĂŒsteem seda antud ajahetkel.
â Blokeeritud. Blokeeritud olekus teeb protsess teatud toiminguid, mis ei luba tal olla tĂ€itmiseks valmis, kuni ei toimu mingit sĂŒndmust. Ăks tavapĂ€rane nĂ€ide on see, kui protsess inicialiseerib IO-operatsiooni, muutub ta blokeerituks ja seega vĂ”ib mĂ”ni teine protsess kasutada protsessorit.

VĂ”ime kujutada neid olekuid graafina. Nagu pildilt nĂ€eme, vĂ”ib protsessi olek muutuda RUNNING ja READY vahel vastavalt OPS-i otsusele. Kui protsessi olek muutub READY-st RUNNING-uks, tĂ€hendab see, et protsess on kavandatud. Vastupidises suunas â on kavandamisest maha vĂ”etud. Hetkel, kui protsess muutub BLOCKED-iks, nĂ€iteks IO-operatsiooni inicialiseerimisel, hoiab OPS teda selles olekus kuni mingi sĂŒndmuse toimumiseni, nĂ€iteks IO lĂ”petamiseni. Sellel hetkel toimub ĂŒleminek olekusse READY ja vĂ”imalik, et kohe ka RUNNING-uks, kui OPS seda otsustab.
Vaatame nĂ€idet, kuidas kaks protsessi lĂ€bivad neid olekuid. Alguseks kujutame ette, et mĂ”lemad protsessid on kĂ€ivitatud ja igaĂŒks kasutab ainult CPU-d. Sel juhul nĂ€evad nende olekud vĂ€lja jĂ€rgmiselt.

JĂ€rgmises nĂ€ites kĂŒsib esimene protsess mĂ”ne tööaja pĂ€rast IO-d ja lĂ€heb BLOCKED olekusse, pakkudes teisele protsessile vĂ”imaluse kĂ€ivituda (KUV 1.4). OS nĂ€eb, et protsess 0 ei kasuta CPU-d ja kĂ€ivitab protsessi 1. Protsessi 1 kĂ€itamise ajal lĂ”ppeb IO ja protsessi 0 olek muutub READY-ks. LĂ”puks lĂ”petab protsess 1 oma töö ja pĂ€rast seda kĂ€ivitatakse protsess 0, tĂ€idetakse ja lĂ”petatakse.

Andmestruktuur
OS on ise programm ja nagu iga teine programm, on tal mĂ”ned vĂ”tme andmestruktuurid, mis jĂ€lgivad erinevaid asjakohaseid teabe osi. Iga protsessi oleku jĂ€lgimiseks hoiab OS teatud protsesside nimekiri READY seisundis olevate protsesside jaoks ja tĂ€iendav teave protsesside jĂ€lgimiseks, mis praegu kĂ€ivad. Samuti peab operatsioonisĂŒsteem jĂ€lgima blokeeritud protsesse. I/O lĂ”petamisel peab operatsioonisĂŒsteem Ă€ratama vajaliku protsessi ja viima selle kĂ€ivitamiseks valmisoleku seisundisse.
NĂ€iteks peab operatsioonisĂŒsteem salvestama protsessori registrite oleku. Protsessi peatamise hetkel salvestatakse registrite olek protsessi aadressiruumi ning protsessi töö jĂ€tkamisel taastatakse registrite vÀÀrtused, et jĂ€tkata selle protsessi tĂ€itmist.
Lisaks ready, blocked, running olekutele on olemas veel mĂ”ned muud olekud. MĂ”nikord vĂ”ib protsess loomise hetkel olla INIT olekus. LĂ”puks vĂ”ib protsess olla paigutatud FINAL olekusse, kui see on juba lĂ”petatud, kuid teave selle kohta pole veel kustutatud. UNIX-sĂŒsteemides nimetatakse seda olekuks zombie-protsess. See the benefits of this state when a parent process needs to know the return code of a child process. Typically, 0 indicates successful completion, while 1 signifies an error; however, programmers can define additional exit codes that signal different issues. When a parent process terminates, it makes a final system call, such as wait(), to wait for the child process to finish and informs the OS that it can clean up any data associated with the completed process.

Loengupunktid:
â Protsess â operatsiooni sĂŒsteemi töötava programmi peamine abstraktsioon. Igal hetkel vĂ”ib protsessi kirjeldada selle oleku kaudu: mĂ€lu sisu selle aadressiruumis, protsessori registrite sisu, sealhulgas kĂ€skude pointer ja stack pointer, samuti IO teave, nagu avatud failid, mida loetakse vĂ”i kirjutatakse.
â Protsessi API koosneb kutsetest, mida programmid saavad protsesside suhtes teha. Tavaliselt on need kutse loomine, eemaldamine vĂ”i muud.
â Protsess on ĂŒhes mitmest olekust, sealhulgas running, ready ja blocked. Erinevad sĂŒndmused, nagu planeerimine, vĂ€ljaheitmine vĂ”i ooteaeg, vĂ”ivad muuta protsessi olekut ĂŒhest teisene.
â Protsesside nimekiri annab teavet kĂ”igi sĂŒsteemis olevate protsesside kohta. Iga kirje selles nimekirjas nimetatakse protsessi juhtplokiks, mis on tegelikult struktuur, mis sisaldab kogu vajalikku teavet konkreetse protsessi kohta.
Allikas: habr.com
