C programozás

Az első C program a Fork System Call használatával

Az első C program a Fork System Call használatával
Alapértelmezés szerint a C programoknak nincs párhuzamossága vagy párhuzamossága, egyszerre csak egy feladat történik, minden kódsor egymás után olvasható. De néha el kell olvasnia egy fájlt vagy - még a legrosszabb is - egy távoli számítógéphez csatlakoztatott aljzatot, és ez valóban sok időt vesz igénybe egy számítógép számára. Ez általában kevesebb, mint egy másodpercig tart, de ne feledje, hogy egyetlen CPU-mag képes 1 vagy 2 milliárd végrehajtása az utasítások alatt.

Így, mint jó fejlesztő, kísértésbe eshet, hogy utasítsa a C programját arra, hogy várakozás közben tegyen valami hasznosat. Itt van a párhuzamos programozás a mentéshez - és boldogtalanná teszi a számítógépet, mert többet kell működnie.

Itt megmutatom a Linux fork rendszerhívását, amely az egyidejű programozás egyik legbiztonságosabb módja.

Az egyidejű programozás nem biztonságos?

Igen, tud. Például van egy másik módja is a hívásnak többszálas. Előnye, hogy könnyebb, de képes igazán téved, ha helytelenül használja. Ha a program tévedésből olvas be egy változót, és írjon a ugyanaz a változó ugyanakkor a program inkoherenssé válik, és szinte észrevehetetlen - az egyik legrosszabb fejlesztői rémálom.

Amint az alább látható lesz, a villa lemásolja a memóriát, így nem lehet ilyen probléma a változókkal. Ezenkívül a villa független folyamatot készít minden egyidejű feladatra. Ezeknek a biztonsági intézkedéseknek köszönhetően körülbelül ötször lassabb egy új párhuzamos feladat indítása villával, mint a többszálas szálakkal. Mint láthatja, ez nem sok az általa nyújtott előnyökhöz.

Elég magyarázat, itt az ideje tesztelni az első C programot villahívással.

A Linux villa példája

Itt van a kód:

#include
#include
#include
#include
#include
int main ()
pid_t forkStatus;
forkStatus = villa ();
/ * Gyermek… * /
if (forkStatus == 0)
printf ("A gyermek fut, feldolgozza.\ n ");
alvás (5);
printf ("A gyermek kész, kilépés.\ n ");
/ * Szülő… * /
else if (forkStatus != -1)
printf ("A szülő vár ... \ n");
várakozás (NULL);
printf ("A szülő kilép ... \ n");
más
perror ("Hiba a villa funkció meghívásakor");

visszatér 0;

Meghívom Önt, hogy tesztelje, fordítsa le és hajtsa végre a fenti kódot, de ha meg szeretné tudni, hogy nézne ki a kimenet, és túl lusta vagy a fordításhoz - végül is talán egy fáradt fejlesztő vagy, aki egész nap C programokat állított össze - az alábbi C program kimenetét megtalálja a fordításhoz használt paranccsal együtt:

$ gcc -std = c89 -Wpedantic -Wall forkSleep.c -o villa Alvás -O2
$ ./ forkSleep
A szülő vár ..
A gyermek fut, feldolgozza.
Gyermek kész, kilép.
A szülő kilép…

Kérjük, ne féljen, ha a kimenet nem 100% -ban megegyezik a fenti kimenettel. Ne feledje, hogy a dolgok egyidejű futtatása azt jelenti, hogy a feladatok kifutnak a sorrendjéből, nincs előre definiált sorrend. Ebben a példában láthatja, hogy a gyermek fut előtt szülő vár, és nincs ezzel semmi baj. Általában a sorrend a kernel verziójától, a CPU magok számától, a számítógépen jelenleg futó programoktól stb.

OK, térjen vissza a kódhoz. A fork () sor előtt ez a C program teljesen normális: egyszerre 1 sor fut, ennek a programnak csak egy folyamata van (ha a villa előtt volt egy kis késés, akkor ezt a feladatkezelőben is megerősítheti).

A villa () után most 2 folyamat futhat párhuzamosan. Először is van egy gyermeki folyamat. Ezt a folyamatot hozták létre a villán (). Ez a gyermekfolyamat különleges: nem hajtotta végre a villával () a sor fölötti kódsorokat. A fő funkció keresése helyett inkább a fork () sort futtatja.

Mi a helyzet a villa előtt deklarált változókkal??

Nos, a Linux fork () érdekes, mert okosan válaszol erre a kérdésre. A változókat és valójában a C programok összes memóriáját átmásolják a gyermek folyamatába.

Hadd határozzam meg, mit csinál a villa néhány szóval: létrehozza a klón annak a folyamatnak a hívása. A 2 folyamat szinte azonos: az összes változó ugyanazokat az értékeket fogja tartalmazni, és mindkét folyamat végrehajtja a sort közvetlenül a fork () után. A klónozási folyamat után azonban, szét vannak választva. Ha az egyik folyamatban frissít egy változót, a másik folyamatot szokás frissítse a változóját. Ez valóban egy klón, egy másolat, a folyamatok szinte semmit sem osztanak meg. Nagyon hasznos: rengeteg adatot készíthet elő, majd elágazhat (), és felhasználhatja ezeket az adatokat minden klónban.

Az elválasztás akkor kezdődik, amikor a fork () visszaad egy értéket. Az eredeti folyamat (az úgynevezett a szülői folyamat) megkapja a klónozott folyamat azonosítóját. A másik oldalon a klónozott folyamat (ezt nevezzük a gyermek folyamata) megkapja a 0 számot. Most kezdje el megérteni, hogy miért tettem az if / else if utasításokat a villa () sor után. A visszatérési érték használatával utasíthatja a gyermeket, hogy tegyen valami mást, mint a szülő - és hidd el, hasznos.

Az egyik oldalon a fenti példakódban a gyermek 5 másodpercig tartó feladatot végez, és üzenetet nyomtat. Egy hosszú ideig tartó folyamat utánzásához használom az alvás funkciót. Ezután a gyermek sikeresen kilép.

A másik oldalon a szülő üzenetet nyomtat, várja meg, amíg a gyermek kilép, és végül kinyomtat egy újabb üzenetet. Fontos, hogy a szülő megvárja gyermekét. Például a szülő ez idő nagy részében arra vár, hogy megvárja gyermekét. De utasíthattam volna a szülőt, hogy végezzen bármilyen hosszú ideje tartó feladatot, mielőtt azt mondanám neki, hogy várjon. Így hasznos feladatokat végzett volna várakozás helyett - végül is ezért használjuk villa (), nem?

Azonban, amint fentebb mondtam, nagyon fontos, hogy szülő várja gyermekeit. És ez miatt fontos zombi folyamatok.

Mennyire fontos a várakozás

A szülők általában tudni akarják, hogy a gyermekek befejezték-e a feldolgozásukat. Például párhuzamosan szeretné futtatni a feladatokat, de biztosan nem akarod a szülőt, hogy lépjen ki, mielőtt a gyerekek befejeződnének, mert ha ez megtörténne, a shell visszaadna egy üzenetet, miközben a gyerekek még nem fejezték be - ami furcsa.

A várakozási funkció lehetővé teszi a várakozást, amíg az egyik gyermek folyamat leáll. Ha egy szülő tízszer hívja a villát (), akkor tízszer kell hívnia a várakozást (), minden gyermeknek egyszer létre.

De mi történik, ha a szülők hívják a várakozási funkciót, amíg az összes gyerek rendelkezik már kilépett? Itt kellenek a zombi folyamatok.

Amikor egy gyermek kilép, mielőtt a szülő hívás várakozik (), a Linux kernel engedi a gyermeket kilépni de jegyet fog tartani mondván, hogy a gyermek kilépett. Ezután, amikor a szülő hívja a wait () -t, megtalálja a jegyet, törli a jegyet, és a wait () funkció visszatér azonnal mert tudja, hogy a szülőnek tudnia kell, amikor a gyermek befejezte. Ezt a jegyet a zombi folyamat.

Ezért fontos, hogy a szülő hívja a wait () parancsot: ha ezt nem teszi meg, akkor a zombi folyamatok megmaradnak a memóriában és a Linux kernelben nem lehet sok zombi folyamatot tartson a memóriában. A korlát elérése után a számítógép is nem képes új folyamatot létrehozni és így lesz egy nagyon rossz alak: még egy folyamat megöléséhez szükség lehet egy új folyamat létrehozására. Például, ha meg akarja nyitni a feladatkezelőt egy folyamat megsemmisítéséhez, akkor nem teheti meg, mert a feladatkezelőjének új folyamatra lesz szüksége. Még a legrosszabb is, nem tudsz megöl egy zombi folyamatot.

Ezért fontos a várakozás hívása: lehetővé teszi a kernelt megtisztítani a gyermekfolyamat, ahelyett, hogy folyamatosan felhalmozódna a lezárt folyamatok listájával. És mi van, ha a szülő úgy lép ki, hogy soha nem hív várjon()?

Szerencsére, mivel a szülő megszűnik, senki más nem hívhatja a wait () -t ezekre a gyerekekre, így van ok nélkül hogy megtartsák ezeket a zombi folyamatokat. Ezért amikor egy szülő kilép, mind megmaradt zombi folyamatok ehhez a szülőhöz kapcsolódik eltávolításra kerülnek. A zombi folyamatok igazán csak akkor hasznos, ha a szülői folyamatok lehetővé teszik annak megállapítását, hogy egy gyermek a szülő előtt befejezte a várakozást ().

Előfordulhat, hogy inkább szeretne ismerni néhány biztonsági intézkedést, hogy problémamentesen a lehető legjobban tudja használni a villát.

Egyszerű szabályok a villa rendeltetésszerű működéséhez

Először is, ha ismeri a többszálas szálat, kérjük, ne forkoljon egy programot szálakkal. Tulajdonképpen kerülje általában a több egyidejűség technológiájának keverését. a fork feltételezi, hogy normál C programokban működik, csak egy párhuzamos feladatot szándékozik klónozni, többet nem.

Másodszor kerülje a fájlok megnyitását vagy megnyitását a fork () előtt. A fájlok az egyetlen dolog megosztva és nem klónozott szülő és gyermek között. Ha szülőben 16 bájtot olvasol, akkor az előbbre fogja vinni az olvasásmutatót 16 bájttal mindkét a szülőben és a gyermekben. Legrosszabb, ha gyermek és szülő bájtokat ír az ugyanaz a fájl ugyanakkor a szülő bájtjai lehetnek vegyes a gyermek bájtjaival!

Az egyértelműség kedvéért az STDIN, STDOUT és STDERR mellett nem akarsz semmilyen megnyitott fájlt megosztani klónokkal.

Harmadszor, legyen óvatos az aljzatokkal kapcsolatban. Aljzatok vannak is megosztott szülő és gyermek között. Ez hasznos egy port meghallgatásához, majd több gyermekmunkás készségessé tételéhez, hogy készen álljon egy új kliens kapcsolat kezelésére. azonban, ha helytelenül használja, akkor bajba kerül.

Negyedszer, ha a fork () ciklust akarja meghívni, akkor ezt tegye meg rendkívüli körültekintéssel. Vegyük ezt a kódot:

/ * EZT NE Tegye össze * /
const int targetFork = 4;
pid_t forkResult
 
mert (int i = 0; i < targetFork; i++)
forkResult = villa ();
/ *… * /
 

Ha elolvassa a kódot, akkor várható, hogy 4 gyermeket hoz létre. De inkább teremt 16 gyerek. Ez azért van, mert a gyerekeknek lesz is végrehajtja a ciklust, így a childs viszont hívja a villát (). Ha a hurok végtelen, a-nak hívjuk villabomba és a Linux rendszer lelassításának egyik módja annyira, hogy már nem működik és újra kell indítania. Dióhéjban ne feledje, hogy a Klónok háborúja nem csak a Csillagok háborújában veszélyes!

Most látta, hogyan tévedhet el egy egyszerű hurok, hogyan használhatja a hurkok villával ()? Ha hurokra van szüksége, mindig ellenőrizze a villa visszatérési értékét:

const int targetFork = 4;
pid_t forkResult;
int i = 0;
csináld
forkResult = villa ();
/ *… * /
i ++;
while ((forkResult != 0 && forkResult != -1) && (i < targetFork));

Következtetés

Itt az ideje, hogy saját kísérleteket végezzen a villával ()! Próbáljon ki új módszereket az idő optimalizálására úgy, hogy több CPU-magon keresztül végez feladatokat, vagy végezzen valamilyen háttérfeldolgozást, miközben egy fájl olvasására vár!

Ne habozzon elolvasni a kézi oldalakat a man paranccsal. Megtudhatja, hogyan működik pontosan a fork (), milyen hibákat kaphat, stb. És élvezze az egyidejűséget!

A Doom telepítése és lejátszása Linuxon
Bevezetés a Doom-ba A Doom sorozat a 90-es években keletkezett az eredeti Doom megjelenése után. Azonnali sláger volt, és ettől kezdve a játéksorozat ...
Vulkan Linux felhasználók számára
A grafikus kártyák minden új generációjával azt látjuk, hogy a játékfejlesztők átlépik a grafikus hűség határait, és egy lépéssel közelebb kerülnek a ...
OpenTTD vs Simutrans
Saját közlekedési szimuláció létrehozása szórakoztató, pihentető és rendkívül csábító lehet. Ezért meg kell győződnie arról, hogy a lehető legtöbb ját...