Operációs rendszerek: Három Easy Pieces. 3. rész: Process API (fordítás)

Bevezetés az operációs rendszerekbe

Szia Habr! Szeretném figyelmébe ajánlani egy véleményem szerint érdekes irodalom - az OSTEP - cikk-fordításait. Ez az anyag meglehetősen mélyen tárgyalja a unix-szerű operációs rendszerek munkáját, nevezetesen a folyamatokkal, különféle ütemezőkkel, memóriával és más hasonló összetevőkkel való munkát, amelyek egy modern operációs rendszert alkotnak. Az összes anyag eredetijét itt tekintheti meg itt. Kérem, vegye figyelembe, hogy a fordítás szakszerűtlenül (elég szabadon) készült, de remélem, megtartottam az általános jelentést.

A témában végzett labormunkák itt találhatók:

Egyéb alkatrészek:

Megnézheted a csatornámat is a címen távirat =)

Riasztás! Ehhez az előadáshoz van egy labor! Néz github

Process API

Nézzünk egy példát egy folyamat létrehozására UNIX rendszerben. Ez két rendszerhíváson keresztül történik Villa() и végrehajt().

Call fork()

Operációs rendszerek: Három Easy Pieces. 3. rész: Process API (fordítás)

Tekintsünk egy programot, amely a fork() hívást kezdeményezi. Végrehajtásának eredménye a következő lesz.

Operációs rendszerek: Három Easy Pieces. 3. rész: Process API (fordítás)

Először is beírjuk a main() függvényt, és kiírjuk a szöveget a képernyőre. A sor tartalmazza a folyamatazonosítót, amelyet az eredetiben hívnak PID vagy folyamatazonosító. Ezt az azonosítót a UNIX-ban használják egy folyamatra. A következő parancs meghívja a fork()-ot. Ezen a ponton a folyamat szinte pontos másolata jön létre. Az OS esetében úgy tűnik, hogy ugyanannak a programnak 2 példánya fut a rendszeren, ami viszont kilép a fork() függvényből. Az újonnan létrehozott utódfolyamat (az azt létrehozó szülőfolyamathoz képest) a továbbiakban nem kerül végrehajtásra, a main() függvénytől kezdve. Emlékeztetni kell arra, hogy egy gyermekfolyamat nem a szülőfolyamat pontos másolata; különösen saját címterével, saját regisztereivel, saját mutatójával a végrehajtható utasításokra és hasonlókkal rendelkezik. Így a fork() függvény hívójának visszaadott érték más lesz. A szülőfolyamat különösen az utódfolyamat PID-értékét kapja visszatérésként, a gyermek pedig 0-val egyenlő értéket. Ezekkel a visszatérési kódokkal szétválaszthatja a folyamatokat, és mindegyiket saját munkájára kényszerítheti. . Ennek a programnak a végrehajtása azonban nincs szigorúan meghatározva. A 2 folyamatra osztás után az operációs rendszer elkezdi figyelni őket, valamint megtervezni munkájukat. Ha egymagos processzoron hajtják végre, akkor az egyik folyamat, jelen esetben a szülő, tovább működik, majd az utódfolyamat kap irányítást. Újraindításkor más lehet a helyzet.

Hívásvárakoztatás()

Operációs rendszerek: Három Easy Pieces. 3. rész: Process API (fordítás)

Fontolja meg a következő programot. Ebben a programban a hívás jelenléte miatt várjon() A szülő folyamat mindig megvárja, hogy az alárendelt folyamat befejeződjön. Ebben az esetben szigorúan meghatározott szöveges kimenetet kapunk a képernyőn

Operációs rendszerek: Három Easy Pieces. 3. rész: Process API (fordítás)

exec() hívás

Operációs rendszerek: Három Easy Pieces. 3. rész: Process API (fordítás)

Fontolja meg a kihívást végrehajt(). Ez a rendszerhívás akkor hasznos, ha egy teljesen más programot szeretnénk futtatni. Itt fogunk hívni execvp() a wc program futtatásához, amely egy szószámláló program. Mi történik az exec() meghívásakor? Ez a hívás argumentumként átadja a végrehajtható fájl nevét és néhány paramétert. Ezután betöltődik a végrehajtható fájl kódja és statikus adatai, és felülírják a kóddal ellátott saját szegmenst. A fennmaradó memóriaterületek, például a verem és a kupac újrainicializálásra kerülnek. Ezután az operációs rendszer egyszerűen végrehajtja a programot, átadva neki egy sor argumentumot. Tehát nem hoztunk létre új folyamatot, egyszerűen átalakítottuk az éppen futó programot egy másik futó programmá. A leszármazottban az exec() hívás végrehajtása után úgy tűnik, mintha az eredeti program egyáltalán nem futott volna.

Ez az indítási komplikáció teljesen normális egy Unix shell esetében, és lehetővé teszi, hogy a shell kódot hajtson végre a hívás után Villa(), de a hívás előtt végrehajt(). Ilyen kód például az, hogy a shell-környezetet az indítandó program igényeihez igazítjuk, még az indítás előtt.

Héj - csak egy felhasználói program. Megmutatja a meghívó sort, és várja, hogy írjon bele valamit. A legtöbb esetben, ha odaírja egy program nevét, a shell megtalálja a helyét, meghívja a fork() metódust, majd meghív valamilyen típusú exec() parancsot, hogy új folyamatot hozzon létre, és várja meg, amíg az befejeződik. várj() hívást. Amikor a gyermekfolyamat kilép, a shell visszatér a wait() hívásból, és újra kinyomtatja a promptot, és várja a következő parancs bevitelét.

A fork() & exec() felosztás lehetővé teszi, hogy a shell például a következőket tegye:
wc fájl > új_fájl.

Ebben a példában a wc program kimenete át van irányítva egy fájlba. A parancsértelmező ezt meglehetősen egyszerű módon éri el – a hívás előtt létrehoz egy gyermekfolyamatot végrehajt(), a shell bezárja a szabványos kimenetet, és megnyitja a fájlt új fájl, így a tovább futó program összes kimenete wc képernyő helyett fájlba lesz átirányítva.

Unix cső hasonló módon valósulnak meg, azzal a különbséggel, hogy pipe() hívást használnak. Ebben az esetben a folyamat kimeneti adatfolyama egy, a kernelben található pipe queue-hoz kapcsolódik, amelyhez egy másik folyamat bemeneti adatfolyama kapcsolódik.

Forrás: will.com

Hozzászólás