C Programiranje

Vaš prvi C program koji koristi sustav Fork System Call

Vaš prvi C program koji koristi sustav Fork System Call
Prema zadanim postavkama, programi C nemaju istodobnost ili paralelizam, istodobno se događa samo jedan zadatak, svaki redak koda čita se sekvencijalno. Ali ponekad morate pročitati datoteku ili - čak i najgore - utičnica spojena na udaljeno računalo, a računalu to stvarno treba puno vremena. Obično je potrebno manje od sekunde, ali imajte na umu da jedna CPU jezgra može izvršiti 1 ili 2 milijarde uputa za to vrijeme.

Tako, kao dobar programer, doći ćete u napast da uputite svoj program C da učini nešto korisnije dok čeka. Tu je programiranje paralelnosti za vaše spašavanje - i čini vaše računalo nesretnim jer mora više raditi.

Ovdje ću vam pokazati sistemski poziv Linux fork, jedan od najsigurnijih načina istodobnog programiranja.

Istodobno programiranje može biti nesigurno?

Da to može. Na primjer, postoji i drugi način pozivanja višenitnost. Prednost je što je lakši, ali može stvarno pogriješite ako ga koristite pogrešno. Ako vaš program greškom pročita varijablu i upiše u ista varijabla istodobno, vaš će program postati nekoherentan i gotovo ga nije moguće otkriti - jedna od najgorih noćnih mora programera.

Kao što ćete vidjeti u nastavku, fork kopira memoriju pa nije moguće imati takvih problema s varijablama. Također, fork čini neovisni postupak za svaki istodobni zadatak. Zbog ovih sigurnosnih mjera otprilike je 5 puta sporije pokretanje novog istodobnog zadatka pomoću vilice nego s multithreadingom. Kao što vidite, to nije puno zbog blagodati koje donosi.

Sad je dovoljno objašnjenja, vrijeme je da testirate svoj prvi C program pomoću fork poziva.

Primjer vilice za Linux

Evo koda:

#include
#include
#include
#include
#include
int main ()
pid_t forkStatus;
forkStatus = vilica ();
/* Dijete… */
if (forkStatus == 0)
printf ("Dijete je pokrenuto, obrađuje.\ n ");
spavanje (5);
printf ("Dijete je gotovo, izlazi.\ n ");
/ * Roditelj ... * /
else if (forkStatus != -1)
printf ("Roditelj čeka ... \ n");
pričekati (NULL);
printf ("Roditelj izlazi ... \ n");
ostalo
perror ("Pogreška tijekom poziva funkcije vilice");

return 0;

Pozivam vas da testirate, sastavite i izvršite gore navedeni kod, ali ako želite vidjeti kako bi izlaz izgledao i previše ste lijeni da biste ga kompilirali - uostalom, možda ste umorni programer koji je cijeli dan kompajlirao C programe - izlaz programa C možete pronaći u nastavku, zajedno s naredbom koju sam koristio za sastavljanje:

$ gcc -std = c89 -Wpedantic -Zidna vilicaSpava.c -o vilicaSpavaj -O2
$ ./ vilicaSpavati
Roditelj čeka ..
Dijete trči, obrađuje.
Dijete je gotovo, izlazi.
Roditelj izlazi ..

Molim vas, ne bojte se ako izlaz nije 100% identičan mom izlazu gore. Imajte na umu da istodobno pokretanje stvari znači da su zadaci istrošeni, ne postoji unaprijed definirano naručivanje. U ovom ćete primjeru možda vidjeti da dijete trči prije roditelj čeka, i u tome nema ništa loše. Općenito, redoslijed ovisi o verziji jezgre, broju CPU jezgri, programima koji su trenutno pokrenuti na vašem računalu itd.

U redu, vratite se kodu. Prije retka s fork (), ovaj je program C sasvim normalan: istodobno se izvršava 1 redak, postoji samo jedan postupak za ovaj program (ako je došlo do malog kašnjenja prije vilice, to biste mogli potvrditi u svom upravitelju zadataka).

Nakon forka (), sada postoje 2 procesa koja se mogu paralelno izvoditi. Prvo, postoji dječji postupak. Ovaj postupak je stvoren na račvanju (). Ovaj podređeni postupak je poseban: nije izvršio niti jedan redak koda iznad retka pomoću fork (). Umjesto da traži glavnu funkciju, radije će pokrenuti liniju fork ().

Što je s varijablama deklariranim prije forka?

Pa, Linux fork () je zanimljiv jer pametno odgovara na ovo pitanje. Varijable i zapravo sva memorija u C programima kopira se u podređeni proces.

Dopustite mi da definiram što radi vilicu s nekoliko riječi: stvara a klon procesa koji ga naziva. 2 procesa su gotovo identična: sve će varijable sadržavati iste vrijednosti i oba će procesa izvršiti redak odmah nakon fork (). Međutim, nakon postupka kloniranja, odvojeni su. Ako ažurirate varijablu u jednom procesu, drugom procesu navika ažurirati njegovu varijablu. To je stvarno klon, kopija, procesi gotovo ništa ne dijele. To je stvarno korisno: možete pripremiti puno podataka, a zatim fork () i koristiti te podatke u svim klonovima.

Razdvajanje započinje kada fork () vrati vrijednost. Izvorni postupak (zove se roditeljski postupak) dobit će ID procesa kloniranog procesa. S druge strane, klonirani proces (ovaj se naziva proces djeteta) dobit će broj 0. Sada biste trebali shvatiti zašto sam stavio if / else if izjave nakon retka fork (). Koristeći povratnu vrijednost, možete uputiti dijete da učini nešto drugačije od onoga što radi roditelj - i vjerujte mi, korisno je.

S jedne strane, u gornjem primjeru koda, dijete radi zadatak koji traje 5 sekundi i ispisuje poruku. Da bih oponašao postupak koji traje dugo, koristim funkciju spavanja. Tada dijete uspješno izlazi.

S druge strane, roditelj ispisuje poruku, pričeka dok dijete ne izađe i na kraju ispisuje drugu poruku. Činjenica da roditelj čeka svoje dijete je važna. Kao primjer, roditelj većinu ovog vremena čeka da pričeka svoje dijete. Ali, mogao sam roditelja uputiti na bilo kakve dugotrajne zadatke prije nego što mu kažem da pričeka. Na ovaj bi način umjesto čekanja obavljao korisne zadatke - uostalom, zato i koristimo vilica (), br?

Međutim, kao što sam gore rekao, to je zaista važno roditelj čeka svoje dijete. I važno je zbog zombi procesi.

Koliko je važno čekanje

Roditelji općenito žele znati jesu li djeca završila obradu. Na primjer, želite paralelno izvoditi zadatke, ali sigurno ne želite roditelj da izađe prije nego što djeca završe, jer ako se to dogodi, ljuska će dati upit dok djeca još nisu završila - što je čudno.

Funkcija čekanja omogućuje čekanje dok se jedan od podređenih procesa ne završi. Ako roditelj pozove 10 puta fork (), morat će nazvati i 10 puta wait (), jednom za svako dijete stvorena.

Ali što se događa ako roditelj poziva funkciju čekanja dok to imaju sva djeca već izašao? Tu su potrebni zombi procesi.

Kad dijete izađe prije nego što roditeljski pozivi wait (), Linux kernel dopušta djetetu da izađe ali zadržat će kartu govoreći da je dijete izašlo. Zatim, kad roditelj pozove wait (), pronaći će kartu, izbrisati je i vratit će se funkcija wait () odmah jer zna da roditelj mora znati kada je dijete završilo. Ova se karta naziva a zombi proces.

Zbog toga je važno da pozivi roditelja čekaju (): ako to ne učini, zombi procesi ostaju u memoriji i Linux kernelu ne mogu zadržati mnoge zombi procese u sjećanju. Jednom kada se dosegne ograničenje, vaše računalo inije u stanju stvoriti novi proces i tako ćete biti u vrlo loš oblik: čak za ubijanje procesa, možda ćete trebati izraditi novi postupak za to. Na primjer, ako želite otvoriti svoj upravitelj zadataka da biste ubili proces, ne možete, jer će vašem upravitelju zadataka trebati novi postupak. Čak i najgore, ne možeš ubiti zombi proces.

Zato je poziv čekanje važno: omogućuje jezgru počistiti dijete proces, umjesto da se nastavi gomilati popisom okončanih procesa. A što ako roditelj izađe bez da je ikad nazvao čekati()?

Srećom, budući da je roditelj prekinut, nitko drugi ne može nazvati wait () za ovu djecu, pa postoji bez razloga zadržati ove zombi procese. Stoga, kada roditelj izlazi, svi preostali zombi procesi povezano s ovim roditeljem uklanjaju se. Zombi procesi su stvarno korisno samo ako dopušta nadređenim procesima da otkriju da je dijete završeno prije nego što se roditelj zvao wait ().

Sada biste možda voljeli znati neke sigurnosne mjere koje će vam omogućiti najbolju uporabu vilice bez ikakvih problema.

Jednostavna pravila za rad vilice kako je predviđeno

Prvo, ako znate višenitnost, nemojte račvati program pomoću niti. Zapravo, izbjegavajte općenito miješanje više istodobnih tehnologija. fork pretpostavlja da radi u normalnim C programima, namjerava klonirati samo jedan paralelni zadatak, a ne više.

Drugo, izbjegavajte otvaranje ili otvaranje datoteka prije forka (). Datoteke su jedina stvar podijeljeni a ne klonirana između roditelja i djeteta. Ako pročitate 16 bajtova u roditelju, pomaknut će kursor za čitanje prema naprijed za 16 bajtova oba u roditelju i u djeteta. Najgori, ako dijete i roditelj upišu bajtove u ista datoteka istodobno, bajtovi roditelja mogu biti mješoviti s bajtovima djeteta!

Da budemo jasni, izvan STDIN, STDOUT i STDERR, doista ne želite dijeliti otvorene datoteke s klonovima.

Treće, pripazite na utičnice. Utičnice jesu također dijeli između roditelja i djece. Korisno je za preslušavanje priključka, a zatim neka više djece-radnika bude spremno za obradu nove veze s klijentom. Međutim, ako ga pogrešno upotrijebite, upast ćete u nevolju.

Četvrto, ako želite pozvati fork () unutar petlje, učinite to s krajnja briga. Uzmimo ovaj kod:

/ * NE SASTAVLJAJTE OVO * /
const int targetFork = 4;
pid_t forkResult
 
za (int i = 0; i < targetFork; i++)
forkResult = vilica ();
/ *… * /
 

Ako pročitate kôd, mogli biste očekivati ​​da će stvoriti 4 djeteta. Ali radije će stvoriti 16 djece. To je zato što će djeca također izvrši petlju i tako će djeca, zauzvrat, pozvati fork (). Kad je petlja beskonačna, naziva se a vilica bomba i jedan je od načina usporavanja Linux sustava toliko da više ne djeluje i trebat će ponovno pokretanje. Ukratko, imajte na umu da Ratovi klonova nisu opasni samo u Ratovima zvijezda!

Sad ste vidjeli kako jednostavna petlja može poći po zlu, kako koristiti petlje s fork ()? Ako trebate petlju, uvijek provjerite povratnu vrijednost forka:

const int targetFork = 4;
pid_t forkResult;
int i = 0;
čini
forkResult = vilica ();
/ *… * /
i ++;
while ((forkResult != 0 && forkResult != -1) && (i < targetFork));

Zaključak

Sada je vrijeme da sami napravite eksperimente s vilicom ()! Isprobajte nove načine za optimizaciju vremena obavljajući zadatke na više jezgri procesora ili napravite neku pozadinsku obradu dok čekate čitanje datoteke!

Ne ustručavajte se pročitati stranice priručnika putem naredbe man. Naučit ćete kako precizno radi fork (), koje pogreške možete dobiti itd. I uživajte u istodobnosti!

Kako obrnuti smjer pomicanja miša i dodirnih pločica u sustavu Windows 10
Miš i Dodirna pločaRačunarstvo ne samo da olakšava, već i učinkovitije i oduzima puno vremena. Ne možemo zamisliti život bez ovih uređaja, ali činjeni...
Kako promijeniti pokazivač miša i veličinu, boju i shemu pokazivača na sustavu Windows 10
Pokazivač i pokazivač miša u sustavu Windows 10 vrlo su važni aspekti operativnog sustava. To se može reći i za druge operativne sustave, tako da u is...
Besplatni i otvoreni kodni pokretački programi za razvoj Linux igara
Ovaj će članak pokriti popis besplatnih i open source pokretačkih igara koje se mogu koristiti za razvoj 2D i 3D igara na Linuxu. Brojni su takvi moto...