Ц Програмирање

Ваш први Ц програм који користи системски позив Форк

Ваш први Ц програм који користи системски позив Форк
Подразумевано, Ц програми немају истовременост или паралелизам, одједном се дешава само један задатак, сваки ред кода се чита секвенцијално. Али понекад морате прочитати датотеку или - чак и најгоре - утичница повезана на удаљени рачунар, а ово рачунару заиста треба много времена. Обично је потребно мање од секунде, али имајте на уму да једно језгро процесора може извршити 1 или 2 милијарде упутстава за то време.

Тако, као добар програмер, доћи ћете у искушење да упутите свој програм Ц да учини нешто корисније током чекања. Ту је програмирање паралелности овде за ваше спасавање - и чини ваш рачунар несрећним јер мора да ради више.

Овде ћу вам показати системски позив Линук форк, један од најсигурнијих начина истовременог програмирања.

Истовремено програмирање може бити небезбедно?

Да, може. На пример, постоји и други начин позивања мултитхреадинг. Користи је што је лакши, али може стварно погрешите ако га користите погрешно. Ако ваш програм грешком прочита променљиву и упише у иста променљива истовремено ће ваш програм постати некохерентан и готово га није могуће открити - једна од најгорих ноћних мора програмера.

Као што ћете видети доле, форк копира меморију, тако да није могуће имати таквих проблема са променљивим. Такође, форк чини независни поступак за сваки истовремени задатак. Због ових безбедносних мера отприлике је пет пута спорије покретање новог истовременог задатка користећи виљушку него код мултитхреадинга. Као што видите, то није много због користи које доноси.

Сада, довољно објашњења, време је да тестирате свој први Ц програм помоћу форк позива.

Пример вилице за Линук

Ево кода:

#инцлуде
#инцлуде
#инцлуде
#инцлуде
#инцлуде
инт маин ()
пид_т форкСтатус;
форкСтатус = форк ();
/ * Дете ... * /
иф (форкСтатус == 0)
принтф ("Дете је покренуто, обрађује се.\ н ");
спавање (5);
принтф ("Дете је готово, излази.\ н ");
/ * Родитељ… * /
елсе иф (форкСтатус != -1)
принтф ("Родитељ чека ... \ н");
сачекајте (НУЛЛ);
принтф ("Родитељ излази ... \ н");
остало
перрор ("Грешка при позивању функције виљушке");

ретурн 0;

Позивам вас да тестирате, компајлирате и извршите горе наведени код, али ако желите да видите како би излаз изгледао и превише сте лењи да бисте га компајлирали - уосталом, можда сте уморни програмер који је читав дан компајлирао програме Ц - излаз програма Ц можете наћи испод, заједно са наредбом коју сам користио за његово компајлирање:

$ гцц -стд = ц89 -Впедантиц -Зидна вилицаСпава.ц -о виљушкаСпава -О2
$ ./ форкСлееп
Родитељ чека ..
Дете трчи, обрађује.
Дете је готово, излази.
Родитељ излази ..

Молим вас, не бојте се ако излаз није 100% идентичан мојем горе наведеном. Имајте на уму да покретање ствари истовремено значи да задаци излазе из реда, нема унапред дефинисаног редоследа. У овом примеру ћете можда видети да дете трчи пре него што родитељ чека, и у томе нема ништа лоше. Генерално, редослед зависи од верзије језгра, броја ЦПУ језгара, програма који су тренутно покренути на вашем рачунару итд.

ОК, сада се вратите на код. Пре линије са форк (), овај Ц програм је сасвим нормалан: извршава се по 1 линија, постоји само један процес за овај програм (ако је дошло до малог кашњења пре форка, то можете потврдити у свом менаџеру задатака).

Након форк (), сада постоје 2 процеса која могу да се покрећу паралелно. Прво, постоји дечји процес. Овај поступак је створен на форк (). Овај подређени процес је посебан: није извршио ниједну линију кода изнад линије помоћу форк (). Уместо да тражи главну функцију, радије ће покренути линију форк ().

Шта је са променљивим декларисаним пре форк?

Па, Линук форк () је занимљив јер паметно одговара на ово питање. Променљиве и заправо сва меморија у Ц програмима се копирају у подређени процес.

Дозволите ми да дефинишем шта ради виљушка са неколико речи: ствара а клон процеса који га назива. 2 процеса су готово идентична: све променљиве ће садржавати исте вредности и оба процеса ће извршити линију одмах након форк (). Међутим, након процеса клонирања, одвојени су. Ако промените променљиву у једном процесу, другом процесу неће да се његова променљива ажурира. То је заиста клон, копија, процеси не деле готово ништа. Заиста је корисно: можете припремити пуно података, а затим форк () и користити те податке у свим клоновима.

Раздвајање почиње када форк () врати вредност. Оригинални поступак (зове се родитељски процес) добиће ИД процеса клонираног процеса. Са друге стране, клонирани процес (овај се зове процес детета) добиће број 0. Сада би требало да почнете да схватате зашто сам ставио иф / елсе иф изјаве иза линије форк (). Користећи повратну вредност, можете упутити дете да уради нешто другачије од онога што родитељ ради - и верујте ми, корисно је.

На једној страни, у примеру горњег примера, дете ради задатак који траје 5 секунди и штампа поруку. Да бих имитирао процес који траје дуго, користим функцију спавања. Тада дете успешно излази.

С друге стране, родитељ штампа поруку, сачека док дете не изађе и на крају одштампа другу поруку. Чињеница да родитељ чека своје дете је важна. Као пример, родитељ већину овог времена чека да сачека своје дете. Али, могао сам да наложим родитељу да ради било какве дуготрајне задатке пре него што му кажем да сачека. На овај начин би обављао корисне задатке, уместо да чека - уосталом, зато и користимо виљушка (), бр?

Међутим, као што сам горе рекао, то је заиста важно родитељ чека своје дете. И важно је због зомби процеси.

Колико је важно чекање

Родитељи углавном желе да знају да ли су деца завршила обраду. На пример, желите паралелно да покрећете задатке, али сигурно не желите родитељ да изађе пре него што деца заврше, јер ако се то догоди, љуска ће дати упит док дете још није завршило - што је чудно.

Функција чекања омогућава чекање док се један од подређених процеса не заврши. Ако родитељ позове 10 пута форк (), мораће и 10 пута да позове ваит (), једном за свако дете створена.

Али шта се дешава ако родитељ позива функцију чекања док то имају сва детета већ изашао? Ту су потребни зомби процеси.

Када дете изађе пре него што родитељски позиви ваит (), Линук кернел пусти дете да изађе али задржаће карту говорећи да је дете изашло. Затим, када родитељ позове ваит (), пронаћи ће карту, избрисати је и функција ваит () ће се вратити одмах јер зна да родитељ треба да зна када је дете завршило. Ова карта се зове а зомби процес.

Због тога је важно да позиви родитеља чекају (): ако то не учини, зомби процеси остају у меморији и Линук кернелу не могу задржати многе зомби процесе у меморији. Када се достигне ограничење, ваш рачунар иније у стању да креира било који нови процес и тако ћете бити у врло лош облик: Чак за убијање процеса, можда ћете морати да креирате нови процес за то. На пример, ако желите да отворите свој менаџер задатака да бисте убили процес, не можете, јер ће вашем менаџеру задатака требати нови процес. Чак и најгоре, не можеш убити зомби процес.

Због тога је позивање чека важно: омогућава језгро поспремити процес дете, уместо да се непрестано гомила списком окончаних процеса. А шта ако родитељ изађе а да никада није звао чекати()?

Срећом, пошто је родитељ прекинут, нико други не може да позове ваит () за ову децу, па постоји без разлога да задрже ове зомби процесе. Стога, када родитељ излази, сви преостали зомби процеси повезан са овим родитељем уклањају се. Зомби процеси су стварно корисно само да дозволи надређеним процесима да открију да се дете завршило пре него што се родитељ назвао ваит ().

Сада бисте можда више волели да знате неке мере безбедности како бисте без проблема омогућили најбоље коришћење виљушке.

Једноставна правила да виљушка ради како је предвиђено

Прво, ако знате мултитхреадинг, молимо вас да не форкирате програм који користи нити. У ствари, уопште избегавајте мешање више истовремених технологија. форк претпоставља да ради у нормалним Ц програмима, намерава да клонира само један паралелни задатак, не више.

Друго, избегавајте отварање или отварање датотека пре форк (). Датотеке су једина ствар поделио и не клониран између родитеља и детета. Ако прочитате 16 бајтова у родитељу, помериће курсор за читање унапред за 16 бајтова обоје у родитељу и у детету. Најгоре, ако дете и родитељ упишу бајтове у исти фајл у исто време, бајтови родитеља могу бити помешан са бајтовима детета!

Да будемо јасни, изван СТДИН, СТДОУТ и СТДЕРР, заиста не желите да делите ниједну отворену датотеку са клоновима.

Треће, будите опрезни око утичница. Утичнице јесу такође подељена између родитеља и детета. Корисно је да бисте преслушали порт, а затим омогућили да више деце-радника буде спремно за управљање новом клијентском везом. Међутим, ако га погрешно употребите, имаћете проблема.

Четврто, ако желите да позовете форк () унутар петље, учините ово са крајња брига. Узмимо овај код:

/ * НЕ САСТАВЉАЈТЕ ОВО * /
цонст инт таргетФорк = 4;
пид_т форкРесулт
 
за (инт и = 0; и < targetFork; i++)
форкРесулт = форк ();
/ *… * /
 

Ако прочитате код, могли бисте очекивати да створи 4 детета. Али пре ће створити 16 детета. То је зато што ће деца такође изврши петљу и тако ће деца, заузврат, позвати форк (). Када је петља бесконачна, назива се а виљушка бомба и један је од начина за успоравање Линук система толико да више не делује и биће потребно поновно покретање. Укратко, имајте на уму да Ратови клонова нису опасни само у Ратовима звезда!

Сад сте видели како једноставна петља може поћи по злу, како користити петље са форк ()? Ако вам треба петља, увек проверите повратну вредност форка:

цонст инт таргетФорк = 4;
пид_т форкРесулт;
инт и = 0;
урадите
форкРесулт = форк ();
/ *… * /
и ++;
вхиле ((форкРесулт != 0 && форкРесулт != -1) && (и < targetFork));

Закључак

Сада је време да сами направите експерименте са форк ()! Испробајте нове начине за оптимизацију времена обављањем задатака на више језгара ЦПУ-а или неку позадинску обраду док чекате читање датотеке!

Не оклевајте да прочитате странице приручника путем команде ман. Сазнаћете како тачно функционише форк (), које грешке можете добити итд. И уживајте у паралелности!

Најбоље игре командне линије за Линук
Командна линија није само ваш највећи савезник када користите Линук - она ​​такође може бити извор забаве јер је можете користити за играње многих заб...
Најбоље апликације за мапирање гамепада за Линук
Ако волите да играте игре на Линуку са гамепадом уместо са типичним системом за унос тастатуре и миша, за вас постоје неке корисне апликације. Многе и...
Корисни алати за Линук играче
Ако волите да играте игре на Линуку, велика је вероватноћа да сте можда користили апликације и услужне програме попут Вине, Лутрис и ОБС Студио за поб...