C Programovanie

Váš prvý program C s využitím systémového volania vidlice

Váš prvý program C s využitím systémového volania vidlice
Programy C predvolene nemajú súbežnosť ani paralelnosť, naraz sa deje iba jedna úloha, každý riadok kódu sa číta postupne. Ale niekedy musíte prečítať súbor alebo - ešte najhoršie - zásuvka pripojená k vzdialenému počítaču a počítač to trvá naozaj dlho. Trvá to zvyčajne menej ako sekundu, nezabudnite však, že jedno jadro procesora to dokáže vykonať 1 alebo 2 miliardy pokynov počas tejto doby.

Takže, ako dobrý vývojár, budete v pokušení nariadiť svojmu programu C, aby počas čakania urobil niečo užitočnejšie. To je miesto, kde je tu súbežné programovanie pre vašu záchranu - a urobí váš počítač nešťastným, pretože musí viac pracovať.

Tu vám ukážem systémové volanie Linux fork, jeden z najbezpečnejších spôsobov, ako robiť súčasné programovanie.

Súbežné programovanie môže byť nebezpečné?

Áno, môže. Existuje napríklad aj iný spôsob volania multithreading. Výhodou je, že je ľahší, ale môže naozaj pokaziť, ak ho používate nesprávne. Ak váš program omylom načíta premennú a zapíše do rovnaká premenná zároveň sa váš program stane nesúrodým a je takmer nezistiteľný - jedna z najhorších nočných môr vývojárov.

Ako uvidíte ďalej, vidlica kopíruje pamäť, takže s premennými nie je možné mať také problémy. Vidlica tiež vytvára nezávislý proces pre každú súbežnú úlohu. Kvôli týmto bezpečnostným opatreniam je približne päťkrát pomalšie spustenie novej súbežnej úlohy pomocou vidlice ako pri použití viacerých vlákien. Ako vidíte, na výhody, ktoré prináša, to nie je veľa.

Teraz, dosť vysvetlení, je čas otestovať svoj prvý program C pomocou fork call.

Napríklad Linux fork

Tu je kód:

#include
#include
#include
#include
#include
int main ()
pid_t forkStatus;
forkStatus = vidlica ();
/ * Dieťa… * /
if (forkStatus == 0)
printf ("Dieťa je v prevádzke, spracovanie.\ n ");
spánok (5);
printf ("Dieťa je hotové, vystupuje sa.\ n ");
/ * Rodič ... * /
else if (forkStatus != -1)
printf ("Rodič čaká ... \ n");
počkaj (NULL);
printf ("Rodič končí ... \ n");
else
perror ("Chyba pri volaní funkcie vidlice");

návrat 0;

Pozývam vás na testovanie, kompiláciu a vykonanie vyššie uvedeného kódu, ale ak chcete vidieť, ako by vyzeral výstup, ste príliš „leniví“ na jeho kompiláciu - koniec koncov ste možno unavený vývojár, ktorý kompiloval programy C celý deň - nižšie nájdete výstup programu C spolu s príkazom, ktorý som použil na jeho zostavenie:

$ gcc -std = c89 -Wpedantic -Wall forkSleep.c -o vidlicaSleep -O2
$ ./ forkSleep
Rodič čaká ..
Dieťa beží, spracováva sa.
Dieťa je hotové, vystupuje.
Rodič odchádza ..

Nebojte sa, ak výstup nie je stopercentne identický s mojím výstupom vyššie. Pamätajte, že súčasné spúšťanie vecí znamená, že úlohy dochádzajú z poradia, neexistuje žiadne preddefinované objednávanie. V tomto príklade môžete vidieť, že dieťa beží predtým rodič čaká, a nie je na tom nič zlé. Poradie vo všeobecnosti závisí od verzie jadra, počtu jadier CPU, programov, ktoré sú momentálne spustené vo vašom počítači, atď.

Dobre, vráťte sa ku kódu. Pred riadkom s fork () je tento program C úplne normálny: 1 riadok sa vykonáva naraz, pre tento program existuje iba jeden proces (ak by pred vidlicou došlo k malému oneskoreniu, môžete to potvrdiť vo svojom správcovi úloh).

Po fork () sú teraz 2 procesy, ktoré môžu bežať paralelne. Najprv je tu detský proces. Tento proces je proces, ktorý bol vytvorený pomocou forku (). Tento podradený proces je zvláštny: nevykonal žiadny z riadkov kódu nad riadkom s fork (). Namiesto hľadania hlavnej funkcie radšej spustí riadok fork ().

Čo s premennými deklarovanými pred forkom?

Linuxová vidlica () je zaujímavá, pretože inteligentne odpovedá na túto otázku. Premenné a vlastne všetka pamäť v programoch C sa skopíruje do podradeného procesu.

Dovoľte mi niekoľkými slovami definovať, čo robí vidličku: vytvorí sa klon procesu, ktorý to volá. Dva procesy sú takmer identické: všetky premenné budú obsahovať rovnaké hodnoty a oba procesy vykonajú riadok hneď za fork (). Avšak po procese klonovania, sú oddelené. Ak aktualizujete premennú v jednom procese, v druhom procese nebude mať svoju premennú aktualizovanú. Je to naozaj klon, kópia, procesy nezdieľajú takmer nič. Je to naozaj užitočné: môžete pripraviť veľa údajov, potom použiť vidličku () a použiť tieto údaje vo všetkých klonoch.

Oddelenie sa spustí, keď funkcia fork () vráti hodnotu. Pôvodný proces (nazýva sa materský proces) získa ID procesu klonovaného procesu. Na druhej strane je to klonovaný proces (tento sa nazýva detský proces) dostane číslo 0. Teraz by ste mali začať chápať, prečo som za riadok fork () vložil výroky if / else if. Pomocou návratovej hodnoty môžete dieťaťu nariadiť, aby urobilo niečo iné, čo robí rodič - a verte mi, je to užitočné.

Na jednej strane vo vyššie uvedenom príklade kódu robí dieťa úlohu, ktorá trvá 5 sekúnd, a vytlačí správu. Na napodobnenie procesu, ktorý trvá dlho, používam funkciu spánku. Potom dieťa úspešne vystúpi.

Na druhej strane rodič vytlačí správu, počká, kým dieťa vystúpi, a nakoniec vytlačí ďalšiu správu. Skutočnosť, že rodič čaká na svoje dieťa, je dôležitá. Ako príklad uvádzam, že rodič väčšinu času čaká na svoje dieťa. Mohol som však rodiča požiadať, aby vykonal akékoľvek dlhodobé úlohy, skôr ako mu poviem, aby počkal. Týmto spôsobom by namiesto čakania urobil užitočné úlohy - koniec koncov, to je dôvod, prečo používame vidlica (), č?

Ako som už uviedol vyššie, je to však skutočne dôležité rodič čaká na svoje dieťa. A je to dôležité kvôli zombie procesy.

Aké dôležité je čakanie

Rodičia všeobecne chcú vedieť, či deti dokončili svoje spracovanie. Napríklad chcete spúšťať úlohy paralelne, ale určite nechceš rodič by mal odísť skôr, ako budú deti hotové, pretože ak by sa to stalo, shell by vrátil výzvu, zatiaľ čo deti ešte nedokončili - čo je čudné.

Funkcia čakania umožňuje čakať na ukončenie jedného z podradených procesov. Ak rodič zavolá 10-krát fork (), bude tiež musieť volať 10-krát čakať (), raz za každé dieťa vytvorené.

Čo sa však stane, ak rodič zavolá funkciu čakania, kým majú všetky deti vystúpil? Tam sú potrebné procesy zombie.

Keď dieťa vystúpi skôr, ako rodič zavolá wait (), jadro Linuxu nechá dieťa opustiť sa ale lístok si nechá povedať dieťaťu, že vystúpil. Keď potom rodič zavolá wait (), nájde lístok, vymaže tento lístok a vráti sa funkcia wait () okamžite pretože vie, že rodič musí vedieť, kedy dieťa skončilo. Tento lístok sa nazýva a zombie proces.

Preto je dôležité, aby nadradené volania wait (): ak tak neurobí, zombie procesy zostanú v pamäti a jadre Linuxu nemôže majte veľa zombie procesov v pamäti. Po dosiahnutí limitu váš počítač inie je možné vytvoriť žiadny nový proces a tak budeš v a veľmi zlý tvar: dokonca na zabitie procesu bude možno potrebné vytvoriť nový postup. Napríklad, ak chcete otvoriť správcu úloh a zabiť proces, nemôžete, pretože váš správca úloh bude potrebovať nový proces. Dokonca najhoršie, nemôžeš zabiť zombie proces.

Preto je volanie wait dôležité: umožňuje jadru vyčistiť podradený proces namiesto toho, aby sa hromadil zoznam ukončených procesov. A čo ak rodič vystúpi bez toho, aby niekedy volal počkaj ()?

Pretože rodič je ukončený, našťastie nemôže nikto iný volať wait () na tieto deti, takže existuje žiadny dôvod aby sme udržali tieto zombie procesy. Preto keď rodič vyjde, všetky zostávajúce zombie procesy prepojené s týmto rodičom sú odstránené. Zombie procesy sú naozaj užitočné iba vtedy, keď rodičom umožníte zistiť, že dieťa bolo ukončené skôr, ako rodič zavolal wait ().

Teraz možno budete radšej poznať niektoré bezpečnostné opatrenia, ktoré vám umožnia najlepšie využitie vidlice bez akýchkoľvek problémov.

Jednoduché pravidlá, aby vidlica fungovala podľa plánu

Po prvé, ak viete viacvláknové spracovanie, nerozdeľujte program pomocou vlákien. Všeobecne sa vyhnite zmiešaniu viacerých technológií súbežnosti. fork predpokladá, že pracuje v normálnych programoch C, má v úmysle naklonovať iba jednu paralelnú úlohu, nie viac.

Po druhé, vyhnite sa otvoreniu alebo otvoreniu súborov pred fork (). Súbory sú jednou z mála vecí zdieľané a nie naklonovaný medzi rodičom a dieťaťom. Ak prečítate 16 bajtov ako rodič, posunie to kurzor čítania o 16 bajtov dopredu oboje u rodiča a u dieťaťa. Najhoršie, ak dieťa a rodič zapisujú bajty do súboru rovnaký spis súčasne môžu byť bajty rodiča zmiešané s bajtmi dieťaťa!

Aby bolo jasné, mimo STDIN, STDOUT a STDERR naozaj nechcete zdieľať žiadne otvorené súbory s klonmi.

Po tretie, dávajte pozor na zásuvky. Zásuvky sú tiež zdieľané medzi rodičom a deťmi. Je to užitočné, ak chcete počúvať port a potom nechať viac detských pracovníkov pripravených zvládnuť nové pripojenie klienta. Avšak, ak ho použijete nesprávne, dostanete sa do problémov.

Po štvrté, ak chcete volať v rámci slučky fork (), urobte to pomocou maximálna starostlivosť. Zoberme si tento kód:

/ * TOTO NEKOMPILUJTE * /
const int targetFork = 4;
pid_t forkResult
 
pre (int i = 0; i < targetFork; i++)
forkResult = vidlica ();
/ *… * /
 

Ak si prečítate kód, môžete očakávať, že z neho vzniknú 4 deti. Ale skôr vytvorí 16 detí. Je to preto, že deti budú tiež vykonajte slučku, takže deti následne zavolajú vidličku (). Keď je slučka nekonečná, nazýva sa to a vidlicová bomba a je jedným zo spôsobov, ako spomaliť systém Linux toľko, že to už nefunguje a bude potrebné reštartovať počítač. Stručne povedané, majte na pamäti, že Clone Wars nie sú nebezpečné iba v Hviezdnych vojnách!

Teraz ste videli, ako sa jednoduchá slučka môže pokaziť, ako používať slučky pomocou vidlice ()? Ak potrebujete slučku, vždy skontrolujte návratnú hodnotu vidlice:

const int targetFork = 4;
pid_t forkResult;
int i = 0;
robiť
forkResult = vidlica ();
/ *… * /
i ++;
while ((forkResult != 0 && forkResult != -1) && (t.j < targetFork));

Záver

Teraz je čas, aby ste vykonali svoje vlastné experimenty s fork ()! Vyskúšajte nové spôsoby optimalizácie času vykonávaním úloh na viacerých jadrách procesora alebo vykonajte nejaké spracovanie na pozadí, keď čakáte na načítanie súboru!

Neváhajte a prečítajte si manuálne stránky pomocou príkazu man. Dozviete sa, ako presne funguje fork (), aké chyby sa môžu vyskytnúť atď. A užívajte si súbežnosť!

Ako používať Xdotool na stimuláciu kliknutí a stlačenia myši v systéme Linux
Xdotool je bezplatný a otvorený nástroj príkazového riadku na simuláciu kliknutí a stlačenia klávesov myši. Tento článok sa bude týkať stručného sprie...
Top 5 ergonomických produktov pre počítačové myši pre Linux
Spôsobuje dlhodobé používanie počítača bolesť zápästia alebo prstov? Trpíte stuhnutými kĺbmi a neustále si musíte podávať ruky? Cítite pálivú bolesť z...
Ako zmeniť nastavenie myši a touchpadu pomocou systému Xinput v systéme Linux
Väčšina distribúcií systému Linux sa štandardne dodáva s knižnicou „libinput“ na spracovanie vstupných udalostí v systéme. Dokáže spracovať vstupné ud...