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:
Egyéb részek:
Emellett látogathattok el a csatornámra a =)
Riasztás! Ehhez az előadáshoz van egy labor! Nézd meg
Folyamat API
Nézzük meg egy folyamat létrehozásának példáját UNIX rendszeren. Ez két rendszerhíváson keresztül történik. fork() és exec().
A fork() hívás

Nézzük meg a programot, amely végrehajtja a fork() hívást. Az eredménye a következő lesz.

Először belépünk a main() függvénybe, és végrehajtjuk a sorozat kiírását a képernyőre. A sorozat tartalmazza a folyamat azonosítóját, amely az eredetiben így nevezzük PID vagy process azonosító. Ez az azonosító a UNIX rendszerben a folyamat elérésére szolgál. A következő parancs a fork() hívása lesz. Ekkor szinte pontos másolat készül a folyamatról. Az operációs rendszer számára úgy tűnik, hogy a rendszerben mintha 2 példánya lenne ugyanannak a programnak, amelyek saját maguk fognak kilépni a fork() függvény végrehajtásából. Az újonnan létrehozott utód-processz (a létrehozó szülőfolyathoz képest) már nem fog végrehajtódni a main() függvényből kezdődően. Emlékezzünk arra, hogy az utód-processz nem pontos másolata a szülőprocessznek, például saját címtartománya, saját regiszterei, saját utasítás-kimutatói stb. vannak. Így a fork() függvény hívónak visszaadott értéke eltérő lesz. Különösen a szülőprocessz a gyermekfolyamat PID-értékét kapja vissza, míg a gyermek értéke 0-t kap. Ezen visszatérési kódok alapján a továbbiakban már lehetőség nyílik a folyamatok elkülönítésére, így mindenik saját munkáját el tudja végezni. A program végrehajtása azonban nem szigorúan meghatározott. A jelentkezés után a rendszer kezdi követni a 2 folyamatot, és megtervezi azok működését. Egy magos processzor esetén egy folyamat, ebben az esetben a szülő, folytatja a működést, majd a vezérlés átkerül az utód-processzhez. Az újraindításkor a helyzet másképp alakulhat.
wait() hívás

Nézzük a következő programot. Ebben a programban a hívás révén, wait() a szülőfolyamat mindig meg fog várni, amíg a gyermekfolyamat befejezi a munkáját. Ekkor a képernyőn szigorúan meghatározott kimenetet kapunk.

exec() hívás

Nézzük meg az exec()hívást. Ez a rendszerhívás hasznos, amikor egy egészen más programot szeretnénk elindítani. Itt az execvp() A wc program, which is a word counting utility, is initiated. What happens when exec() is called? The name of the executable file and some parameters are passed as arguments to this call. The code and static data from the executable file are then loaded, overwriting the current code segment. Other memory areas, such as the stack and heap, are reinitialized. After that, the OS simply executes the program, providing it with a set of arguments. Thus, we haven't created a new process; we have merely transformed the currently running program into another running program. After executing exec(), it seems to the child process that the original program was never launched at all.
Such complexity in launching is absolutely normal for Unix shell, allowing it to execute code after the call fork(), but before the call exec(). An example of such code could be adapting the shell environment to meet the needs of the launched program before its actual startup.
Shell is just a user program. It displays a prompt and waits for you to type something. In most cases, when you enter the name of a program, the shell finds its location, calls fork(), and then to create a new process, it calls one of the exec() types and waits for it to execute with wait(). When the child process finishes, the shell returns from wait() and shows the prompt again, waiting for the next command input.
The separation of fork() & exec() allows the shell to do several things, for example:
wc file > new_file.
In this example, the output of the wc program is redirected to a file. The method by which the shell achieves this is quite simple — when creating a child process before the call to exec(), the shell closes the standard output stream and opens the file new_file, thus all output from the next launched program wc will be redirected to the file instead of the screen.
Unix pipes are implemented in a similar way, with the difference that they use the pipe() call. In this case, the output stream of the process will be connected to a pipe queue located in the kernel, which will also be linked to the input stream of another process.
Forrás: habr.com
