C Programiranje

Vaš prvi program C s sistemskim klicem Fork

Vaš prvi program C s sistemskim klicem Fork
Privzeto programi C nimajo sočasnosti ali vzporednosti, naenkrat se zgodi le ena naloga, vsaka vrstica kode se bere zaporedno. Včasih pa morate prebrati datoteko oz - celo najslabše - vtičnica, priključena na oddaljeni računalnik, kar za računalnik traja res dolgo. Običajno traja manj kot sekundo, vendar ne pozabite, da lahko eno jedro procesorja izvršiti 1 ali 2 milijardi navodil v tem času.

Torej, kot dober razvijalec, zamikali boste, da boste svojemu programu C med čakanjem naredili kaj bolj koristnega. Tukaj je za vaše reševanje sočasno programiranje - in naredi vaš računalnik nesrečen, ker mora več delati.

Tukaj vam bom pokazal sistemski klic fork for Linux, ki je eden najvarnejših načinov sočasnega programiranja.

Sočasno programiranje je lahko nevarno?

Ja, lahko. Na primer, obstaja tudi drug način klicanja večnitnost. Prednost je lažja, vendar lahko res pojdi narobe, če ga uporabljaš nepravilno. Če vaš program pomotoma prebere spremenljivko in piše v ista spremenljivka hkrati pa bo vaš program postal neskladen in ga skoraj ni mogoče zaznati - ena najhujših nočnih mor razvijalcev.

Kot boste videli spodaj, fork kopira pomnilnik, zato s spremenljivkami ni mogoče imeti takšnih težav. Poleg tega fork naredi neodvisen postopek za vsako sočasno nalogo. Zaradi teh varnostnih ukrepov je približno petkrat počasneje zagnati novo sočasno nalogo z uporabo vilic kot z večnitnim. Kot lahko vidite, to za prednosti, ki jih prinaša, ni veliko.

Zdaj, dovolj pojasnil, čas je, da preizkusite svoj prvi program C z uporabo fork klica.

Primer vilic za Linux

Tu je koda:

#include
#include
#include
#include
#include
int main ()
pid_t forkStatus;
forkStatus = fork ();
/ * Otrok ... * /
če (forkStatus == 0)
printf ("Otrok teče, obdelava.\ n ");
spanje (5);
printf ("Otrok je končan, izhod.\ n ");
/ * Starš… * /
sicer če (forkStatus != -1)
printf ("Starš čaka ... \ n");
počakajte (NULL);
printf ("Starš zapušča… \ n");
še
perror ("Napaka med klicanjem funkcije vilic");

vrnitev 0;

Vabim vas, da zgornjo kodo preizkusite, prevedete in izvedete, če pa želite videti, kako bi izgledal rezultat in ste preveč leni, da bi ga sestavili - navsezadnje ste morda utrujeni razvijalec, ki je cel dan sestavljal programe C - izhod programa C lahko najdete spodaj skupaj z ukazom, s katerim sem ga sestavil:

$ gcc -std = c89 -Wpedantic -Wall forkSleep.c -o viliceSpanje -O2
$ ./ forkSpaj
Starš čaka…
Otrok teče, obdeluje.
Otrok je končal, izstopil.
Starš zapušča…

Prosim, ne bojte se, če izhod ni 100% enak zgornjemu izhodu. Ne pozabite, da zagon stvari hkrati pomeni, da opravila niso v redu, ni vnaprej določenega razvrščanja. V tem primeru boste morda videli, da otrok teče prej starš čaka, in s tem ni nič narobe. Na splošno je vrstni red odvisen od različice jedra, števila procesorskih jeder, programov, ki se trenutno izvajajo v vašem računalniku itd.

V redu, zdaj se vrnite na kodo. Pred vrstico z fork () je ta program C povsem normalen: naenkrat se izvaja 1 vrstica, za ta program obstaja samo en postopek (če je prišlo do majhne zamude pred vilicami, lahko to potrdite v upravitelju opravil).

Po fork () sta zdaj dva procesa, ki se lahko izvajata vzporedno. Najprej gre za postopek otroka. Ta postopek je bil ustvarjen na fork (). Ta podrejeni postopek je poseben: ni izvedel nobene vrstice kode nad vrstico z fork (). Namesto da bi iskal glavno funkcijo, bo raje zagnal vrstico fork ().

Kaj pa spremenljivke, deklarirane pred fork?

No, Linux fork () je zanimiv, ker pametno odgovori na to vprašanje. Spremenljivke in pravzaprav ves pomnilnik v programih C se kopira v podrejeni postopek.

Naj v nekaj besedah ​​definiram, kaj dela vilice: ustvari a klon procesa, ki ga kliče. Dva procesa sta skoraj enaka: vse spremenljivke bodo vsebovale enake vrednosti in oba procesa bosta izvedla vrstico takoj po fork (). Vendar pa po postopku kloniranja, ločeni so. Če spremenljivko posodobite v enem, drugem postopku ne bo posodobitev spremenljivke. To je v resnici klon, kopija, procesi skoraj nič ne delijo. Res je koristno: lahko pripravite veliko podatkov in nato fork () in te podatke uporabite v vseh klonih.

Ločevanje se začne, ko fork () vrne vrednost. Prvotni postopek (imenuje se nadrejeni postopek) bo dobil ID postopka kloniranega procesa. Na drugi strani pa klonirani postopek (ta se imenuje otroški proces) bo dobil številko 0. Zdaj bi morali začeti razumeti, zakaj sem za vrstico fork () postavil stavke if / else if. Z uporabo vrnjene vrednosti lahko otroku naročite, naj stori nekaj drugačnega od tega, kar počne starš - in verjemite mi, da je koristno.

Na eni strani v zgornji zgledni kodi otrok opravi nalogo, ki traja 5 sekund, in natisne sporočilo. Za posnemanje procesa, ki traja dolgo, uporabljam funkcijo spanja. Nato otrok uspešno zapusti.

Na drugi strani starš natisne sporočilo, počaka, dokler otrok ne izstopi in končno natisne drugo sporočilo. Dejstvo, da starš čaka na svojega otroka, je pomembno. Kot primer lahko starš večino tega časa čaka na svojega otroka. Toda staršu bi lahko naročil, naj opravi kakršne koli dolgotrajne naloge, preden mu reče, naj počaka. Tako bi namesto čakanja opravil koristne naloge - navsezadnje zato uporabljamo vilice (), št?

Vendar, kot sem rekel zgoraj, je to zelo pomembno starš čaka na svoje otroke. In pomembno je zaradi zombi procesi.

Kako pomembno je čakanje

Starši na splošno želijo vedeti, ali so otroci končali obdelavo. Na primer, želite izvajati naloge vzporedno, vendar zagotovo nočete starš naj izstopi, preden konča otroke, kajti če bi se to zgodilo, bi lupina vrnila poziv, medtem ko otroki še niso končali - kar je čudno.

Funkcija čakanja omogoča čakanje, dokler se eden od podrejenih procesov ne konča. Če starš pokliče 10-krat fork (), bo moral poklicati tudi 10-krat počakati (), enkrat za vsakega otroka ustvarjena.

Kaj pa se zgodi, če starši kličejo funkcijo čakanja, medtem ko imajo vsi podrejeni otroci že izstopil? Tam so potrebni zombi procesi.

Ko otrok zapusti sistem, preden starševski klic počaka (), bo jedro Linuxa dovolilo izhod vendar bo obdržala vozovnico povedati otroku, da je izstopil. Potem, ko starš pokliče wait (), bo našel vozovnico, jo izbrisal in funkcija wait () se bo vrnila takoj ker ve, da mora starš vedeti, kdaj je otrok končal. Ta vozovnica se imenuje a zombi proces.

Zato je pomembno, da starševski klici počakajo (): če tega ne stori, zombi procesi ostanejo v pomnilniku in jedru Linuxa ne morem ohrani veliko zombi procesov v spominu. Ko je omejitev dosežena, vaš računalnik ine more ustvariti nobenega novega procesa in tako boste v zelo slaba oblika: celo za ubijanje postopka boste morda morali ustvariti nov postopek za to. Če na primer želite odpreti upravitelja opravil, da ubije postopek, tega ne morete, ker bo upravitelj opravil potreboval nov postopek. Še najslabše, ne moreš ubiti postopek zombija.

Zato je klic čakanja pomemben: omogoča jedro pospravi otroški postopek, namesto da bi se še naprej kopičil s seznamom zaključenih procesov. In kaj, če starš izstopi, ne da bi kdaj poklical počakaj ()?

Na srečo, ko je starš prekinjen, nihče drug ne more poklicati wait () za te otroke, zato obstaja brez razloga ohraniti te zombi procese. Torej, ko starš izstopi, vsi preostali zombi procesi povezan s tem staršem so odstranjeni. Zombi procesi so res uporabno samo za omogočanje nadrejenih procesov, da ugotovijo, da se je otrok končal pred nadrejenim, imenovanim wait ().

Zdaj boste morda raje vedeli nekatere varnostne ukrepe, ki vam bodo omogočili najboljšo uporabo vilic brez težav.

Preprosta pravila za pravilno delovanje vilic

Najprej, če poznate večnitnost, ne razstavljajte programa z nitmi. Pravzaprav se na splošno izogibajte mešanju več sočasnih tehnologij. fork predpostavlja, da deluje v običajnih programih C, namerava klonirati samo eno vzporedno nalogo, ne več.

Drugič, izogibajte se odpiranju ali kopiranju datotek pred fork (). Datoteke so ena edinih stvari v skupni rabi in ne klonirano med staršem in otrokom. Če preberete 16 bajtov v nadrejeni obliki, bo premaknil kurzor za branje naprej za 16 bajtov oboje v staršu in pri otroku. Najslabše, če otrok in starš napišeta bajte v isto datoteko hkrati so lahko bajti nadrejenega mešano z bajti otroka!

Če želite biti jasni, zunaj STDIN, STDOUT in STDERR res ne želite deliti odprtih datotek s kloni.

Tretjič, bodite previdni pri vtičnicah. Vtičnice so tudi v skupni rabi med starši in otroki. To je koristno, če želite poslušati vrata in nato imeti več otrok, ki so pripravljeni za obdelavo nove odjemalske povezave. Vendar, če ga boste uporabili napačno, boste imeli težave.

Četrtič, če želite v zanko poklicati fork (), to storite z izjemna skrb. Vzemimo to kodo:

/ * NE PRIPRAVI TEGA * /
const int targetFork = 4;
pid_t forkResult
 
za (int i = 0; i < targetFork; i++)
forkResult = fork ();
/ *… * /
 

Če preberete kodo, lahko pričakujete, da bo ustvarila 4 otroke. Toda raje bo ustvaril 16 otrok. To je zato, ker bodo otroci tudi izvede zanko in tako bodo otroci nato poklicali fork (). Ko je zanka neskončna, se imenuje a vilice bomba in je eden od načinov za upočasnitev sistema Linux toliko, da ne deluje več in bo potreboval ponovni zagon. Na kratko, ne pozabite, da Vojne klonov niso nevarne samo v Vojni zvezd!

Zdaj ste videli, kako lahko gre preprosta zanka narobe, kako uporabljati zanke z fork ()? Če potrebujete zanko, vedno preverite vrnjeno vrednost vilice:

const int targetFork = 4;
pid_t forkResult;
int i = 0;
naredi
forkResult = fork ();
/ *… * /
i ++;
while ((forkResult != 0 && forkResult != -1) && (tj < targetFork));

Zaključek

Zdaj je čas, da sami preizkusite vilice ()! Preizkusite nove načine za optimizacijo časa z izvajanjem nalog na več jedrih procesorja ali med čakanjem na branje datoteke v obdelavi v ozadju!

Ne oklevajte in preberite strani z navodili prek ukaza man. Spoznali boste, kako fork () natančno deluje, katere napake lahko dobite itd. In uživajte v sočasnosti!

Vadnica za bitko za Wesnoth
Bitka za Wesnoth je ena izmed najbolj priljubljenih odprtokodnih strateških iger, ki jih lahko trenutno igrate. Ne samo, da se ta igra razvija že zelo...
0 A.D. Vadnica
Od številnih strateških iger tam 0 A.D. uspe izstopati kot izčrpen naslov in zelo globoka, taktična igra, čeprav je odprtokodna. Razvoj igre deluje ze...
Vadnica za Unity3D
Uvod v Unity 3D Unity 3D je močan motor za razvoj iger. Navzkrižna platforma vam omogoča ustvarjanje iger za mobilne naprave, splet, namizje in konzol...