Bevezetés az operációs rendszerekbe
Helló, Habr! Szeretném bemutatni nektek egy érdekes cikk-sorozat fordítását, amely az én véleményem szerint izgalmas irodalom — OSTEP. Ebben az anyagban elég mélyen megvizsgáljuk az unix-szerű operációs rendszerek működését, különösen a folyamatokkal, különböző ütemezőkkel, memóriával és hasonló összetevőkkel foglalkozunk, amelyek alkotják a modern OS-t. Az összes anyag eredeti változatát itt láthatjátok . Kérlek vegyétek figyelembe, hogy a fordítás szakértelem nélküli (elég szabad), de remélem, az általános értelemben megmaradt.
A tantárgyhoz kapcsolódó laboratóriumi munkák itt találhatók:
- eredeti:
- eredeti:
- saját személyes adaptációm:
Egyéb részek:
Emellett látogathattok el a csatornámra a =)
Nézzük meg a legfontosabb absztrakciót, amelyet az operációs rendszer a felhasználók számára nyújt: a folyamatot. A folyamat definíciója meglehetősen egyszerű - ez egy futó program. A program önmagában élettelen dolog, amely a lemezen található - egy utasítások halmaza és esetleg néhány statikus adat, amelyek a futás pillanatára várnak. Az operációs rendszer veszi ezeket a byte-okat, és elindítja őket, átalakítva a programot valami hasznossá.
A felhasználók leggyakrabban több program futtatását szeretnék egyszerre, például elindíthatja a böngészőt, egy játékot, egy médialejátszót, egy szövegszerkesztőt és hasonlókat a laptopján. Valójában a tipikus rendszer több tucat és száz folyamatot tud egyszerre futtatni. Ez a tény egyszerűbbé teszi a rendszert: soha nem kell aggódnia, hogy a CPU szabad-e, egyszerűen csak elindítja a programokat.
Ez problémát vet fel: hogyan biztosítsuk a több CPU illúzióját? Hogyan tud az operációs rendszer olyan illúziót létrehozni, ami gyakorlatilag végtelen számú CPU-t sugall, még akkor is, ha csak egy fizikai CPU van?
Az operációs rendszer a CPU virtualizálásán keresztül teremti meg ezt az illúziót. Egy folyamatot indít el, majd leállítja, elindít egy másik folyamatot stb., így az operációs rendszer fenntarthatja annak az illúziónak a látszatát, hogy sok virtuális CPU létezik, még ha valójában csak egy vagy több fizikai processzor van. Ezt a technikát úgy hívják, hogy CPU erőforrások időbeli megosztása. Ez a technika lehetővé teszi a felhasználók számára, hogy annyi egyidejű folyamatot futtassanak, amennyit csak szeretnének. Ennek a megoldásnak az ára a teljesítmény - mivel ha a CPU-t több folyamat osztja meg, minden egyes folyamat lassabban lesz feldolgozva.
A CPU virtualizálásának megvalósításához, különösen ahhoz, hogy ezt jól végezzük, az operációs rendszer alacsony és magas szintű támogatásra is szüksége van. Az alacsony szintű támogatást mechanizmusoknak hívják - ezek alacsony szintű módszerek vagy protokollok, amelyek megvalósítják a szükséges funkciók egy részét. Egy ilyen funkció például a kontextusváltás, amely lehetővé teszi az operációs rendszer számára, hogy megállítsa az egyik programot és futtasson egy másikat a processzoron. Az ilyen időbeli megosztás minden modern operációs rendszerben megvalósítva van.
A mechanizmusok tetején található bizonyos logika, amely az operációs rendszerbe van beépítve, „szabályzat” formájában. Szabályzat — ez egy olyan algoritmus, amely az operációs rendszer döntéshozatali rendszerét képezi. Ezek a szabályzatok például meghatározzák, hogy melyik programot kell elsőként futtatni (a parancsok listájából). Így például ezt a feladatot fogja megoldani a ütemezési szabályzat (scheduling policy) és a döntésválasztás során figyelembe veszi az alábbi adatokat: futási történelem (melyik program futott a leghosszabb ideig az utolsó percben), milyen terhelést valósít meg az adott folyamat (milyen típusú programok futtak), teljesítménymetrikák (optimalizálva van-e a rendszer interaktív interakcióra vagy áteresztőképességére) és így tovább.
Absztrakció: folyamat
A működő program operációs rendszer által végrehajtott absztrakcióját nevezzük folyamat. Ahogy már említettük, a folyamat egyszerűen egy futó program bármely pillanatban. Ez a program lehetővé teszi számunkra, hogy összesített információkat szerezzünk a rendszer különböző erőforrásaiból, és amelyhez ez a program hozzáfér vagy amelyeket érint a végrehajtása során.
A folyamat összetevőinek megértéséhez ismerni kell a rendszer állapotait: hogy mit olvashat vagy változtathat a program a futása során. Bármikor meg kell érteni, hogy a rendszer mely elemei fontosak a program végrehajtásához.
A rendszer állapotának egyik nyilvánvaló eleme, amelyet a folyamat magában foglal — ez a memória. Az utasítások a memóriában helyezkednek el. Az adatok, amelyeket a program olvas vagy ír, szintén a memóriában találhatók. Így a memória, amelyet a folyamat címezhet (úgynevezett címterület) a folyamat részét képezi.
A rendszer állapotának szintén részét képezik a regiszterek. Sok utasítás irányul arra, hogy megváltoztassa a regiszterek értékét vagy olvassa le azokat, így a regiszterek is fontos részévé válnak a folyamat működésének.
Meg kell jegyezni, hogy a gép állapota néhány speciális regiszterből is áll. Például, IP — utasításmutató — mutató az utasításra, amelyet a program jelenleg végrehajt. Van még veremmutató és a hozzá kapcsolódó keretmutató, amelyeket a következők irányítására használnak: a funkciók paraméterei, helyi változók és visszatérési címek.
Végül a programok gyakran hivatkoznak az EEPROM-ra (állandó tároló). Az „I/O” (bemeneti-kimeneti) információnak tartalmaznia kell a folyamat által jelenleg megnyitott fájlok listáját.
Folyamat API
A folyamat működésének megértése érdekében vizsgáljuk meg a rendszerváltozásokat, amelyeket bármely operációs rendszer interfészébe be kell vonni. Ezek az API-k különféle formában elérhetők bármely operációs rendszeren.
● Létrehozás (létrehozás): Az operációs rendszernek tartalmaznia kell egy módszert, amely lehetővé teszi új folyamatok létrehozását. Amikor parancsot ad be a terminálba, vagy alkalmazást indít el egy ikonra kattintva, kérés érkezik az operációs rendszerhez, hogy új folyamatot hozzon létre és elindítsa a megadott programot.
● Törlés: Mivel létezik interfész a folyamat létrehozására, az operációs rendszernek lehetőséget kell biztosítania a folyamat kényszerített leállítására is. A legtöbb program természetesen saját magát indítja el és fejezi be a végrehajtás során. Ellenkező esetben a felhasználónak lehetősége kell, hogy legyen a megölésére, így a folyamat leállítására szolgáló interfész nem lenne felesleges.
● Várakozás (várakozás): Néha hasznos megvárni egy folyamat befejezését, ezért olyan interfészeket biztosítanak, amelyek lehetőséget adnak a várakozásra.
● Vegyes vezérlés (különböző vezérlés): A folyamat megölésén és várakozáson kívül még másféle vezérlési módszerek is léteznek. Például a legtöbb operációs rendszer a folyamatok felfüggesztésének (a végrehajtás megállítása egy ideig) és későbbi folytatásának lehetőségét is biztosítja.
● Állapot (állapot): Különféle interfészek léteznek a folyamat státuszával kapcsolatos információk megszerzésére, például a futási időtartama vagy az aktuális állapota.

Folyamat létrehozása: részletek
Az egyik érdekes dolog — hogyan is alakulnak a programok folyamatokká. Különösen, hogy az operációs rendszer hogyan indítja el és futtatja a programot. Hogyan jön létre konkrétan egy folyamat.
Először is az operációs rendszernek be kell töltenie a programkódot és a statikus adatokat a memóriába (a folyamat címtartományába). A programok általában lemezen vagy állandó tárolón találhatóak valamilyen végrehajtható formátumban. Így a program és a statikus adatok memóriába történő betöltéséhez az operációs rendszernek képesnek kell lennie arra, hogy ezeket a byte-okat a lemezről olvassa, és valahol elhelyezze a memóriában.
A korai operációs rendszerekben a betöltési folyamat türelmetlenül (eagerly) zajlott, ami azt jelentette, hogy a kódot teljesen betöltötték a memóriába, mielőtt a program elindult volna. A modern operációs rendszerek ezt lustán (lazily) teszik, azaz csak a kód vagy adatok darabjait tölti be, amikor a program végrehajtása során szükség van rájuk.
Miután a kód és a statikus adatok betöltődtek a memóriába, az operációs rendszernek még néhány dolgot el kell végeznie, mielőtt elindítaná a folyamatot. Egy bizonyos mennyiségű memóriát kell lefoglalni a verem számára. A programok a veremet használják helyi változók, a függvények paramétereinek és a visszatérési címek tárolására.. Az operációs rendszer ezt a memóriát lefoglalja, és átadja a folyamatnak. A verem bizonyos argumentumokkal is lefoglalható, konkrétan a main() függvény paramétereivel, például az argc és argv tömbbel.
Az operációs rendszer egy bizonyos mennyiségű memóriát is lefoglalhat a program heap-jához. A heap-et a programok a dinamikusan, kifejezetten kért adatok tárolására használják.. A programok ezt a teret a malloc() függvény hívásával kérik, és kifejezetten felszabadítják a free()függvény hívásával. A heap szükséges olyan adatszerkezetekhez, mint: láncolt listák, hashtáblák, fák és egyéb. Kezdetben egy kis mennyiségű memóriát foglalnak le a heap számára, de idővel a program működése során a heap kérhet több memóriát a malloc() könyvtári API hívásán keresztül. Az operációs rendszer részt vesz a még több memória lefoglalásában, hogy segítsen ezeknek a hívásoknak a kielégítésében.
Az operációs rendszer emellett kezdeményezési feladatokat is végrehajt, különösen azokat, amelyek a bemenetre és kimenetre vonatkoznak. Például UNIX rendszerekben minden folyamat alapértelmezés szerint 3 nyitott fájlkezelővel rendelkezik, a standard bemenetre, kimenetre és hibákra. Ezek a kezelők lehetővé teszik a programok számára, hogy a terminálból olvassanak be adatokat, és információt küldjenek a képernyőre.
Így, a kód és a statikus adatok memóriába történő betöltésével, a verem létrehozásával és inicializálásával, valamint más, a bemeneti-kimeneti feladatok végrehajtásához kapcsolódó munkával az operációs rendszer felkészíti a teret a folyamat végrehajtásához. A végén egy utolsó feladat marad: elindítani a programot a bemeneti pontján keresztül, amelyet main() funkciónak nevezünk. Amikor a main() funkció végrehajtására átmegy, az operációs rendszer átadja a CPU irányítását az újonnan létrehozott folyamatnak, ezzel elindítva a program végrehajtását.
A folyamat állapota
Most, hogy van némi elképzelésünk arról, hogy mi az a folyamat és hogyan jön létre, soroljuk fel a folyamat állapotait, amelyekben lehet. A legegyszerűbb formában a folyamat ezekben az állapotokban lehet:
● Futásban. Futtatási állapotban a folyamat a processzoron hajtódik végre. Ez azt jelenti, hogy az utasítások végrehajtása történik.
● Kész. Kész állapotban a folyamat kész arra, hogy elinduljon, de valamilyen okból az operációs rendszer nem végzi el az adott pillanatban.
● Blokkolt. Blokkolt állapotban a folyamat olyan műveleteket hajt végre, amelyek nem teszik lehetővé, hogy készen álljon a végrehajtásra, amíg meg nem történik valami esemény. Az egyik gyakori példa, amikor a folyamat inicializál egy IO műveletet, blokkolttá válik, így egy másik folyamat használhatja a processzort.

Ezeket az állapotokat gráf formájában is elképzelhetjük. Ahogy a képen látható, a folyamat állapota a RUNNING és a READY között változhat az operációs rendszer döntése alapján. Amikor a folyamat állapota READY-ből RUNNING-ba változik, az azt jelenti, hogy a folyamat ütemezésre került. Fordított irányban — eltávolítva az ütemezésből. Abban a pillanatban, amikor a folyamat BLOCKED állapotba kerül, például egy IO művelet inicializálásakor, az operációs rendszer ezen az állapotban tartja, amíg be nem következik egy esemény, például az IO befejezése. Ebben a pillanatban átkerül a READY állapotba, és esetleg azonnal a RUNNING állapotba, ha az operációs rendszer így dönt.
Nézzünk meg egy példát arra, hogy hogyan halad át két folyamat ezeken az állapotokon. Először is tegyük fel, hogy mindkét folyamat fut, és mindegyik csak a CPU-t használja. Ebben az esetben az állapotuk a következőképpen fog kinézni.

A következő példában az első folyamat egy idő után I/O-t kér, és BLOCKED állapotba kerül, lehetőséget adva egy másik folyamat számára, hogy elinduljon (1.4. ábra). Az operációs rendszer látja, hogy a 0. folyamat nem használ CPU-t, és elindítja az 1. folyamatot. Az 1. folyamat futása alatt az I/O befejeződik, és a 0. folyamat állapota READY-ra változik. Végül az 1. folyamat befejeződik, és a befejezés után a 0. folyamat elindul, végrehajtódik és befejezi a munkáját.

Adatszerkezet
Az operációs rendszer maga is egy program, és mint bármely más programnak, rendelkezik néhány kulcsfontosságú adatszerkezettel, amelyek nyomon követik a különböző releváns információkat. Az egyes folyamatok állapotának nyomon követésére az operációs rendszer fenntart egy folyamatlistát az összes READY állapotú folyamathoz, valamint néhány további információt a jelenleg futó folyamatok nyomon követésére. Az operációs rendszernek figyelnie kell a blokkolt folyamatokat is. Az I/O befejezése után az operációs rendszer köteles felébreszteni a szükséges folyamatot, és átállítani azt indításra kész állapotba.
Például az operációs rendszernek meg kell őriznie a processzor regisztereinek állapotát. A folyamat leállításakor a regiszterek állapota a folyamat címtartományában kerül mentésre, míg a folyamat folytatásakor a regiszterek értékeinek visszaállítása révén a folyamat végrehajtása folytatódhat.
A ready, blocked és running állapotokon kívül léteznek egyéb állapotok is. Néha a folyamat létrehozásakor INIT állapotban lehet. Végül a folyamat FINAL állapotba kerülhet, amikor már befejeződött, de az általa használt információk még nem lettek törölve. A UNIX rendszerekben ezt az állapotot zombi-folyamatnaknevezik. Ez az állapot hasznos, amikor a szülő folyamat szeretné tudni a gyermekének kilépési kódját, például általában a 0 a sikeres befejeződést, míg az 1 a hibás befejeződést jelzi, de a programozók további kilépési kódokat adhatnak, jelezve a különféle problémákat. A befejezéskor a szülő folyamat utolsó rendszerhívást hajt végre, például wait(), hogy megvárja a gyermek folyamat befejeződését, és jelezze az operációs rendszernek, hogy törölheti a befejezett folyamattal kapcsolatos adatokat.

A előadás kulcspontjai:
● Folyamat — a fő absztrakció egy működő program a rendszeren. Bármikor a folyamat leírható állapota alapján: a memóriában lévő tartalom az ő címtartományában, a processzor regisztereinek tartalma, beleértve az utasításmutatót és a verem mutatót, valamint az IO információt, például azokat a fájlokat, amelyeket olvasnak vagy írnak.
● Folyamat API hívásokból áll, amelyeket a programok végezhetnek a folyamatokkal kapcsolatban. Ezek általában létrehozási, törlési vagy más hívások.
● A folyamat számos állapotban lehet, beleértve a futást, készenléti állapotot és blokkolt állapotot. Különböző események, például ütemezés, ütemezésből való kivétel vagy várakozás átvihetik a folyamat állapotát egy másikba.
● Folyamatlista tartalmazza az összes folyamat információját a rendszerben. Minden bejegyzés a folyamat vezérlő blokkja, amely valójában egy struktúra, ami tartalmazza a szükséges információkat egy adott folyamatról.
Forrás: habr.com
