Azaz hogyan tud két folyamat egymás közt információt cserélni...
Névvel ellátott csővezeték (FIFO)
Osztott memória (shared memory)
Amint már láttuk, folyamatnak nevezzünk egy futó programot. Unixban minden folyamatot – kivéve a 0 és az 1 folyamatokat, amelyek a rendszer indulásakor töltődnek be – egy másik folyamat indít el a fork rendszerfüggvény meghívásával. Az újonnan elindított folyamatot fiúnak (gyereknek), azt a folyamatot pedig amelyik őt elindította szülőnek (apának) nevezzük. (Előfordulhat, hogy a szülőfolyamat hamarabb befejeződik mint a gyerek, ekkor a gyerek kap egy új szülőt: az init (1) folyamatot.)
Egy multitasking rendszerben ugyanazt a programot egyidőben akár több folyamat is futtathatja, és bármelyik folyamat végrehajthat egy másik programot.
A Unixban minden folyamatnak egyedi azonosítója van. Ezt a pozitív egész számot nevezzük folyamatazonosítónak (PID). A számot a rendszer automatikusan adja minden új folyamat létrehozásakor. Egy folyamat lekérdezheti a saját PID-jét a getpid rendszerhívás segítségével. Mivel minden folyamat PID-je egyedi, nem lehet azt megváltoztatni, de a szám újra felhasználható, amint a folyamat befejeződött.
A 0 PID-del rendelkező folyamat mindig a swapper, az 1 azonosítójú pedig az init folyamat. Ezeket a rendszer induláskor automatikusan betölti, s mindvégig a memóriában maradnak.
Amikor a rendszer egy folyamatot elindít létrehoz számára bizonyos adatszerkezeteket:
-
kódszegmens (amennyiben a folyamatnak még nem volt futó példánya létrehoz
egy új kódszegmenset, ha már volt, a létezőt fogja használni),
- adatszegmens (tartalmazza a statikus és dinamikus változókat is; lefele nő),
- veremszegmens (felfele nő).
Az adat- és veremszegmens folyamathoz kapcsolt, azaz annyi példányban jön létre, ahány futó példánya van a folyamatnak.
Egy folyamat több attribútummal is rendelkezik, amelyeket a megfelelő függvényekkel le is kérdezhetünk; ezek közül a legfontosabbak:
-
folyamatazonosító (PID vagy ID),
- szülőfolyamat folyamatazonosítója (azé a folyamaté, amely a fork függvényhívást
kiadta),
- a terminál, amelyről a folyamatot elindítottuk,
- a felhasználó azonosítója,
- a felhasználó csoportjának azonosítója,
- S jogosultság (a folyamat elindítása során megkaphatja a tulajdonosa összes
jogait),
- állomány maximális mérete (milyen nagy állományt tud létrehozni),
- adatszegmens maximális mérete,
- nice érték (0 és 39 közötti szám, amelyet a folyamat prioritásának a
kiszámításánál használ).
Egy folyamatnak a létrehozásától számítva több különböző állapota is lehet, attól függően, hogy éppen fut-e, várakozik-e valamilyen erőforrásra, van-e elegendő memória stb. Az állapotok közti átmeneteket az alábbi ábra szemlélteti:
|
Egy folyamat a fork függvényhívás után CREAT állapotba kerül. Ekkor a szülő szegmensei gyakorlatilag megduplázódnak. Ha van elegendő memória a folyamat számára átkerül READY (végrehajtható) állapotba, különben a háttértárolóra kerül és READY swapped állapotban várakozik. Amennyiben a folyamat elegendő prioritással rendelkezik READY állapotból átkerülhet RUN (futó) állapotba. Ebből az állapotból csak akkor kerül ki, ha valamilyen erőforrásra kell várakoznia (WAIT). Ha ezután fogy el a memória szintén a háttértárolóra kerül WAIT swapped állapotba. A folyamat feladata befejeztével TERMINATE (zombie) állapotba kerül, ami azt jelenti, hogy a folyamattáblában még megmarad amíg a szülője fut.
Unix alatt egy felhasználó akár több folyamatot is elindíthat – lényegében minden futó program külön folyamatként fut. Egyszerre azonban csak egy folyamattal tudunk kommunikálni (az egy darab billentyűzet miatt). A háttérben futó folyamatok is kommunikálhatnak egymással.
A folyamatok közti kommunikáció (IPC, Inter-process communication) többféleképpen is megvalósulhat:
-
jelzések segítségével,
- állományokon keresztül,
- csővezetékekkel (pipe),
- névvel ellátott csővezetékeken keresztül (FIFO),
- szemaforok alkalmazásával (semaphores),
- üzenetsorokkal (message queues),
- osztott memória segítségével (shared memory),
- socket,
- stream.
Az utolsó két módszert főként olyankor használjuk, amikor a két folyamat nem ugyanazon a gazdagépen fut.
A jelzéseket főként más folyamatok figyelmeztetésére, riasztására, vagy kivételek kezelésére használjuk. Ezen üzenetek nagyon kevés információt hordoznak, mivel véges számú jelből állhatnak. A jelzés hatására a folyamat megszakad és értelmezi az adott jelet. Alkalmazásuk nem túl hatékony, főleg folyamatok befejezésére használjuk.
Az állományok segítségével a folyamatok viszonylag könnyen tudnak adatokat cserélni. Például az egyik folyamat ír egy fájlba, a másik folyamat pedig olvassa azt, visszafele pedig épp fordítva – természetesen egy másik állományt használva. Enne a módszernek két nagy hátránya van:
-
ha a két folyamat egyidőben dolgozik, nem tudnak megfelelően összehangolódni
(például az egyik folyamat a fájl végét hibásan érzékeli, s a kommunikáció
megszakad),
- ha a kommunikáció hosszú időn keresztül tart, az állomány mérete
jelentősen megnőhet.
A csővezetékek (pipe-ok) már megoldják a fájloknál fellépő szinkronizációs problémákat. A pipe a rendszer által létrehozott (név nélküli) speciális állomány. Maximális hossza 10 blokk (5120 byte). Feladata: két folyamat közötti kommunikáció megvalósítása (az egyik folyamat ír a standard kimenetre, a másik pedig olvas a standard bemenetről). A pipe FILO szerkezetű. Két mutatója van: egyik az író folyamaté, a másik az olvasóé. Így az írás/olvasás cirkulárisan történik (ha a pipe megtelik az írónak kell várnia, ha kiürül az olvasónak).
Pipe-okat a parancssorban is használhatunk. Például:
$ ls | more
A pipe-on keresztül történő kommunikációnak is vannak hátrányai:
-
a kommunikációban résztvevő folyamatok kötelezően apa-fiú
kapcsolatban kell legyenek, vagy kell legyen egy közös ősük,
- a régebbi verziók nem engedik meg az elemi szinten történő írást és
olvasást, több író/olvasó folyamat esetén,
- lassú működés.
Névvel ellátott csővezeték (FIFO)
A névvel ellátott csővezetékek nagyon hasonlítanak a hagyományos pipe-okhoz. Egy FIFO állománynak azonban van neve és minden olyan folyamat hozzáférhet, amelynek joga van hozzá. A FIFO a második problémát is megoldotta, hiszen itt már értelmezettek az atomi műveletek is. Egyetlen hátrány maradt, az alacsony sebesség.
Az üzenetek olyan kis adatcsomagok, amelyeket üzenetként küldünk el. Itt természetesen meg kell mondanunk a címzett (folyamat) azonosítóját. Az üzenetek többfélék lehetnek. Bármely folyamat – amelynek van joga – kaphat üzenetet a hálózaton keresztül.
A folyamat választhat a bejövő üzenetek közül: lehet mindig az első, az első egy bizonyos típusúból vagy az első egy típuscsoportból.
A szemafor egy olyan jel (például egy változó), amely megmutatja, hogy egy folyamat végrehajthat-e egy bizonyos utasítást, vagy várakoznia kell.
Osztott memória (shared memory)
Az osztott memória a leggyorsabb kommunikációt eredményezi. Alapötlete: ugyanazt a memóriarészt használja az összes összeköttetésben levő folyamat. Minél gyorsabban beírja az egyik folyamat az információt a memóriába, annál gyorsabban tudja kiolvasni azt egy másik. Ebben az esetben is felhasználhatunk egy szemafort vagy küldhetünk üzenetet, azért, hogy a folyamatok írását/olvasását összehangoljuk.
Copyright (C) Buzogány László, 2002