@Justin, ponieważ chcemy, aby SO stało się miejscem, w którym można znaleźć pytania dotyczące programowania.
paxdiablo
4
@ Polaris878: och, teraz! : D
Janusz Lenar
podobnie forkjest w zasadzie klonowanie: O
Sebastian Hojas
Odpowiedzi:
364
Wykorzystanie forki execprzykład ducha systemu UNIX polega na tym, że zapewnia on bardzo prosty sposób na rozpoczęcie nowych procesów.
forkWezwanie zasadzie sprawia duplikat bieżącego procesu, identyczny w prawie każdym względem. Nie wszystko jest kopiowane (na przykład limity zasobów w niektórych implementacjach), ale pomysł polega na stworzeniu jak najbliższej kopii.
Nowy proces (podrzędny) otrzymuje inny identyfikator procesu (PID) i ma PID starego procesu (nadrzędnego) jako nadrzędny PID (PPID). Ponieważ oba procesy działają teraz dokładnie w tym samym kodzie, mogą stwierdzić, który kod powrotu fork- dziecko otrzymuje 0, rodzic otrzymuje PID dziecka. To wszystko oczywiście przy założeniu, że forkpołączenie działa - jeśli nie, nie zostanie utworzone żadne dziecko, a rodzic otrzyma kod błędu.
execRozmowa jest sposobem na zasadzie wymienić cały proces bieżący z nowym programem. Ładuje program do bieżącej przestrzeni procesu i uruchamia go od punktu wejścia.
Tak, forki execsą często stosowane w kolejności, aby otrzymać nowy program działający jako dziecko aktualnego procesu. Powłoki zwykle robią to za każdym razem, gdy próbujesz uruchomić program taki jak find- powłoka się rozwidla, a następnie dziecko ładuje findprogram do pamięci, ustawiając wszystkie argumenty wiersza poleceń, standardowe operacje we / wy i tak dalej.
Ale nie muszą być używane razem. Jest całkowicie akceptowalny dla forksamego programu bez execing, jeśli na przykład program zawiera zarówno kod nadrzędny, jak i podrzędny (musisz uważać, co robisz, każda implementacja może mieć ograniczenia). Było to dość często używane (i nadal jest) dla demonów, które po prostu nasłuchują na porcie TCP i forkna swoich kopiach, aby przetworzyć określone żądanie, podczas gdy rodzic wraca do nasłuchiwania.
Podobnie programy, które wiedzą, że są gotowe i po prostu chcą uruchomić inny program, nie muszą tego robić fork, execa potem waitdla dziecka. Mogą po prostu załadować dziecko bezpośrednio do swojej przestrzeni procesowej.
Niektóre implementacje UNIX mają zoptymalizowane, forkktóre wykorzystują to, co nazywają kopiowaniem przy zapisie. Jest to sztuczka polegająca na opóźnieniu kopiowania przestrzeni procesu, forkdopóki program nie spróbuje czegoś zmienić w tej przestrzeni. Jest to przydatne dla tych programów, które używają tylko, forka nie execdlatego, że nie muszą kopiować całej przestrzeni procesu.
Jeśli wywoływany execjest następujący fork(i tak się najczęściej dzieje), powoduje to zapis w przestrzeni procesu, a następnie jest kopiowany do procesu potomnego.
Należy pamiętać, że istnieje cała rodzina execpołączeń ( execl, execle, execvei tak dalej), ale execw kontekście oznacza tutaj żadnej z nich.
Poniższy diagram ilustruje typową fork/execoperację, w której bashpowłoka służy do wyświetlenia katalogu z lspoleceniem:
+--------+| pid=7|| ppid=4|| bash |+--------+|| calls fork
V
+--------++--------+| pid=7| forks | pid=22|| ppid=4|---------->| ppid=7|| bash || bash |+--------++--------+||| waits for pid 22| calls exec to run ls
| V
|+--------+|| pid=22||| ppid=7||| ls |
V +--------++--------+|| pid=7|| exits
| ppid=4|<---------------+| bash |+--------+|| continues
V
fork()dzieli bieżący proces na dwa procesy. Innymi słowy, twój przyjemny liniowy, łatwy do pomyślenia program nagle staje się dwoma oddzielnymi programami uruchamiającymi jeden fragment kodu:
int pid = fork();if(pid ==0){
printf("I'm the child");}else{
printf("I'm the parent, my child is %i", pid);// here we can kill the child, but that's not very parently of us}
To może trochę zaskoczyć twój umysł. Teraz masz jeden fragment kodu o prawie identycznym stanie wykonywanym przez dwa procesy. Proces potomny dziedziczy cały kod i pamięć procesu, który go właśnie utworzył, w tym począwszy od miejsca, w którym fork()wywołanie zostało przerwane. Jedyną różnicą jest fork()kod powrotu informujący, czy jesteś rodzicem czy dzieckiem. Jeśli jesteś rodzicem, zwracaną wartością jest identyfikator dziecka.
execjest nieco łatwiejszy do uchwycenia, po prostu każesz execwykonać proces przy użyciu docelowego pliku wykonywalnego i nie masz dwóch procesów uruchamiających ten sam kod lub dziedziczących ten sam stan. Jak mówi @Steve Hawkins, execmożna go użyć po forkwykonaniu w bieżącym procesie docelowego pliku wykonywalnego.
istnieje również stan, kiedy pid < 0i fork()połączenia nie powiodła się
Jonathan Fingland
3
To wcale mnie nie denerwuje :-) Jeden kawałek kodu wykonywany przez dwa procesy dzieje się za każdym razem, gdy używana jest biblioteka współdzielona lub DLL.
Program jest zbiorem instrukcji i danych, które są trzymane w zwykłym pliku na dysku. (od 1.1.2 Programy, procesy i wątki)
.
Aby uruchomić program, jądro jest najpierw proszone o utworzenie nowego procesu , który jest środowiskiem, w którym program się wykonuje. (także z 1.1.2 Programy, procesy i wątki)
.
Nie można zrozumieć wywołań systemowych exec lub fork bez pełnego zrozumienia różnicy między procesem a programem. Jeśli te warunki są dla Ciebie nowe, możesz wrócić i przejrzeć sekcję 1.1.2. Jeśli jesteś gotowy, aby kontynuować, podsumujemy rozróżnienie w jednym zdaniu: Proces to środowisko wykonawcze, które składa się z instrukcji, danych użytkownika i segmentów danych systemowych, a także wielu innych zasobów pozyskanych w czasie wykonywania , podczas gdy program to plik zawierający instrukcje i dane, które są używane do inicjalizacji segmentu instrukcji i danych użytkownika. (z 5.3 execwywołań systemowych)
Po zrozumieniu różnicy między programem a procesem zachowanie fork()i exec()działanie można podsumować jako:
fork() tworzy duplikat bieżącego procesu
exec() zastępuje program w bieżącym procesie innym programem
(jest to w zasadzie uproszczona wersja paxdiablo „dla manekinów” )
Fork tworzy kopię procesu wywoływania. ogólnie podąża za strukturą
int cpid = fork();if(cpid ==0){//child code
exit(0);}//parent code
wait(cpid);// end
(dla tekstu procesu potomnego (kodu), danych, stos jest taki sam jak proces wywołujący) proces potomny wykonuje kod w bloku if.
EXEC zastępuje bieżący proces kodem, danymi i stosem nowego procesu. ogólnie podąża za strukturą
int cpid = fork();if(cpid ==0){//child code
exec(foo);
exit(0);}//parent code
wait(cpid);// end
(po wykonaniu przez jądro unix wywołania kasuje tekst procesu potomnego, dane, stos i wypełnia tekstem / danymi związanymi z procesem foo), dlatego proces potomny ma inny kod (kod foo {inny niż nadrzędny})
Jest to trochę niezwiązane z pytaniem, ale czy powyższy kod nie powoduje warunków wyścigu, jeśli proces potomny zakończy swój kod jako pierwszy? W takim przypadku proces nadrzędny zostałby zawieszony na zawsze i czekałby, aż dziecko się zakończy, prawda?
stdout
7
Są one używane razem, aby utworzyć nowy proces potomny. Po pierwsze, wywołanie forktworzy kopię bieżącego procesu (proces potomny). Następnie execjest wywoływany z poziomu procesu potomnego w celu „zastąpienia” kopii procesu nadrzędnego nowym procesem.
Proces przebiega mniej więcej tak:
child = fork();//Fork returns a PID for the parent process, or 0 for the child, or -1 for Failif(child <0){
std::cout <<"Failed to fork GUI process...Exiting"<< std::endl;
exit (-1);}elseif(child ==0){// This is the Child Process// Call one of the "exec" functions to create the child process
execvp (argv[0],const_cast<char**>(argv));}else{// This is the Parent Process//Continue executing parent process}
W siódmym wierszu jest wspomniane, że funkcja exec () tworzy proces potomny. Czy to prawda, że fork () już utworzył proces potomny, a wywołanie exec () po prostu zastępuje program właśnie utworzonego nowego procesu
cbinder
4
fork () tworzy kopię bieżącego procesu, a wykonywanie w nowym potomku rozpoczyna się zaraz po wywołaniu fork (). Po fork () są identyczne, z wyjątkiem wartości zwracanej przez funkcję fork (). (RTFM, aby uzyskać więcej informacji.) Dwa procesy mogą się dalej rozchodzić, przy czym jeden nie jest w stanie zakłócać drugiego, chyba że za pośrednictwem współdzielonych uchwytów plików.
exec () zastępuje bieżący proces nowym. Nie ma to nic wspólnego z fork (), z wyjątkiem tego, że exec () często podąża za fork (), gdy potrzebne jest uruchomienie innego procesu potomnego, zamiast zastąpienia bieżącego.
Główna różnica między fork()i exec()polega na tym, że
fork()Wezwanie system tworzy klona program uruchomiony. Oryginalny program kontynuuje wykonywanie od następnego wiersza kodu po wywołaniu funkcji fork (). Klon rozpoczyna także wykonywanie od następnego wiersza kodu. Spójrz na następujący kod, który otrzymałem z http://timmurphy.org/2014/04/26/using-fork-in-cc-a-minimum-working-example/
#include<stdio.h>#include<unistd.h>int main(int argc,char**argv){
printf("--beginning of program\n");int counter =0;pid_t pid = fork();if(pid ==0){// child processint i =0;for(; i <5;++i){
printf("child process: counter=%d\n",++counter);}}elseif(pid >0){// parent processint j =0;for(; j <5;++j){
printf("parent process: counter=%d\n",++counter);}}else{// fork failed
printf("fork() failed!\n");return1;}
printf("--end of program--\n");return0;}
Ten program deklaruje zmienną licznika, ustawioną na zero, przed fork()ing. Po wywołaniu rozwidlenia mamy równolegle dwa procesy, oba zwiększające własną wersję licznika. Każdy proces zostanie uruchomiony do końca i zakończony. Ponieważ procesy przebiegają równolegle, nie mamy możliwości dowiedzieć się, który zakończy się jako pierwszy. Uruchomienie tego programu spowoduje wydrukowanie czegoś podobnego do pokazanego poniżej, chociaż wyniki mogą się różnić w zależności od serii.
--beginning of program
parent process: counter=1
parent process: counter=2
parent process: counter=3
child process: counter=1
parent process: counter=4
child process: counter=2
parent process: counter=5
child process: counter=3--end of program--
child process: counter=4
child process: counter=5--end of program--
exec()Rodzina wywołań systemowych zastąpi aktualnie wykonywanie kodu procesu z innego kawałka kodu. Proces zachowuje swój PID, ale staje się nowym programem. Weźmy na przykład następujący kod:
#include<stdio.h>#include<unistd.h>
main(){char program[80],*args[3];int i;
printf("Ready to exec()...\n");
strcpy(program,"date");
args[0]="date";
args[1]="-u";
args[2]=NULL;
i=execvp(program,args);
printf("i=%d ... did it work?\n",i);}
Ten program wywołuje execvp()funkcję zastępowania kodu programem daty. Jeśli kod jest zapisany w pliku o nazwie exec1.c, wówczas jego wykonanie daje następujące dane wyjściowe:
Ready to exec()...TueJul1520:17:53 UTC 2008
Program wypisuje wiersz eReady to exec (). . . ‖ I po wywołaniu funkcji execvp (), zastępuje swój kod programem daty. Zauważ, że linia -. . . to zadziałało‖ nie wyświetla się, ponieważ w tym momencie kod został zastąpiony. Zamiast tego widzimy wynik działania funkcji -data -u.‖
Tworzy kopię uruchomionego procesu. Działający proces nazywa się procesem nadrzędnym, a nowo utworzony proces nazywa się procesem potomnym . Sposobem na rozróżnienie tych dwóch jest spojrzenie na zwróconą wartość:
fork() zwraca identyfikator procesu (pid) procesu potomnego w rodzicu
fork() zwraca 0 w potomku.
exec():
Inicjuje nowy proces w ramach procesu. Ładuje nowy program do bieżącego procesu, zastępując istniejący.
fork()+ exec():
Podczas uruchamiania nowego programu należy najpierw fork()utworzyć nowy proces, a następnie exec()(tj. Załadować do pamięci i wykonać) program binarny, który ma uruchomić.
int main(void){int pid = fork();if( pid ==0){
execvp("find", argv );}//Put the parent to sleep for 2 sec,let the child finished executing
wait(2);return0;}
Doskonałym przykładem do zrozumienia pojęcia fork()i exec()jest powłoka , program interpretujący polecenia, który użytkownicy zwykle wykonują po zalogowaniu się do systemu. Powłoka interpretuje pierwsze słowo wiersza poleceń jako nazwę polecenia
W przypadku wielu poleceń powłoka rozwidla się, a proces potomny wykonuje polecenie powiązane z nazwą, traktując pozostałe słowa w wierszu poleceń jako parametry polecenia.
Powłoki pozwala trzech rodzajów poleceń. Po pierwsze, polecenie może być
plikiem wykonywalnym, który zawiera kod obiektowy wytworzony przez kompilację kodu źródłowego (na przykład program w języku C). Po drugie, polecenie może być plikiem wykonywalnym, który zawiera sekwencję wierszy poleceń powłoki. Wreszcie, polecenie może być poleceniem powłoki wewnętrznej (zamiast pliku wykonywalnego ex-> cd , ls itp.)
fork
jest w zasadzie klonowanie: OOdpowiedzi:
Wykorzystanie
fork
iexec
przykład ducha systemu UNIX polega na tym, że zapewnia on bardzo prosty sposób na rozpoczęcie nowych procesów.fork
Wezwanie zasadzie sprawia duplikat bieżącego procesu, identyczny w prawie każdym względem. Nie wszystko jest kopiowane (na przykład limity zasobów w niektórych implementacjach), ale pomysł polega na stworzeniu jak najbliższej kopii.Nowy proces (podrzędny) otrzymuje inny identyfikator procesu (PID) i ma PID starego procesu (nadrzędnego) jako nadrzędny PID (PPID). Ponieważ oba procesy działają teraz dokładnie w tym samym kodzie, mogą stwierdzić, który kod powrotu
fork
- dziecko otrzymuje 0, rodzic otrzymuje PID dziecka. To wszystko oczywiście przy założeniu, żefork
połączenie działa - jeśli nie, nie zostanie utworzone żadne dziecko, a rodzic otrzyma kod błędu.exec
Rozmowa jest sposobem na zasadzie wymienić cały proces bieżący z nowym programem. Ładuje program do bieżącej przestrzeni procesu i uruchamia go od punktu wejścia.Tak,
fork
iexec
są często stosowane w kolejności, aby otrzymać nowy program działający jako dziecko aktualnego procesu. Powłoki zwykle robią to za każdym razem, gdy próbujesz uruchomić program taki jakfind
- powłoka się rozwidla, a następnie dziecko ładujefind
program do pamięci, ustawiając wszystkie argumenty wiersza poleceń, standardowe operacje we / wy i tak dalej.Ale nie muszą być używane razem. Jest całkowicie akceptowalny dla
fork
samego programu bezexec
ing, jeśli na przykład program zawiera zarówno kod nadrzędny, jak i podrzędny (musisz uważać, co robisz, każda implementacja może mieć ograniczenia). Było to dość często używane (i nadal jest) dla demonów, które po prostu nasłuchują na porcie TCP ifork
na swoich kopiach, aby przetworzyć określone żądanie, podczas gdy rodzic wraca do nasłuchiwania.Podobnie programy, które wiedzą, że są gotowe i po prostu chcą uruchomić inny program, nie muszą tego robić
fork
,exec
a potemwait
dla dziecka. Mogą po prostu załadować dziecko bezpośrednio do swojej przestrzeni procesowej.Niektóre implementacje UNIX mają zoptymalizowane,
fork
które wykorzystują to, co nazywają kopiowaniem przy zapisie. Jest to sztuczka polegająca na opóźnieniu kopiowania przestrzeni procesu,fork
dopóki program nie spróbuje czegoś zmienić w tej przestrzeni. Jest to przydatne dla tych programów, które używają tylko,fork
a nieexec
dlatego, że nie muszą kopiować całej przestrzeni procesu.Jeśli wywoływany
exec
jest następującyfork
(i tak się najczęściej dzieje), powoduje to zapis w przestrzeni procesu, a następnie jest kopiowany do procesu potomnego.Należy pamiętać, że istnieje cała rodzina
exec
połączeń (execl
,execle
,execve
i tak dalej), aleexec
w kontekście oznacza tutaj żadnej z nich.Poniższy diagram ilustruje typową
fork/exec
operację, w którejbash
powłoka służy do wyświetlenia katalogu zls
poleceniem:źródło
fork()
dzieli bieżący proces na dwa procesy. Innymi słowy, twój przyjemny liniowy, łatwy do pomyślenia program nagle staje się dwoma oddzielnymi programami uruchamiającymi jeden fragment kodu:To może trochę zaskoczyć twój umysł. Teraz masz jeden fragment kodu o prawie identycznym stanie wykonywanym przez dwa procesy. Proces potomny dziedziczy cały kod i pamięć procesu, który go właśnie utworzył, w tym począwszy od miejsca, w którym
fork()
wywołanie zostało przerwane. Jedyną różnicą jestfork()
kod powrotu informujący, czy jesteś rodzicem czy dzieckiem. Jeśli jesteś rodzicem, zwracaną wartością jest identyfikator dziecka.exec
jest nieco łatwiejszy do uchwycenia, po prostu każeszexec
wykonać proces przy użyciu docelowego pliku wykonywalnego i nie masz dwóch procesów uruchamiających ten sam kod lub dziedziczących ten sam stan. Jak mówi @Steve Hawkins,exec
można go użyć pofork
wykonaniu w bieżącym procesie docelowego pliku wykonywalnego.źródło
pid < 0
ifork()
połączenia nie powiodła sięMyślę, że niektóre koncepcje z „Zaawansowanego programowania unixowego” Marc'a Rochkinda były pomocne w zrozumieniu różnych ról
fork()
/exec()
, szczególnie dla kogoś przyzwyczajonego doCreateProcess()
modelu Windows :.
.
Po zrozumieniu różnicy między programem a procesem zachowanie
fork()
iexec()
działanie można podsumować jako:fork()
tworzy duplikat bieżącego procesuexec()
zastępuje program w bieżącym procesie innym programem(jest to w zasadzie uproszczona wersja paxdiablo „dla manekinów” )
źródło
Fork tworzy kopię procesu wywoływania. ogólnie podąża za strukturą
(dla tekstu procesu potomnego (kodu), danych, stos jest taki sam jak proces wywołujący) proces potomny wykonuje kod w bloku if.
EXEC zastępuje bieżący proces kodem, danymi i stosem nowego procesu. ogólnie podąża za strukturą
(po wykonaniu przez jądro unix wywołania kasuje tekst procesu potomnego, dane, stos i wypełnia tekstem / danymi związanymi z procesem foo), dlatego proces potomny ma inny kod (kod foo {inny niż nadrzędny})
źródło
Są one używane razem, aby utworzyć nowy proces potomny. Po pierwsze, wywołanie
fork
tworzy kopię bieżącego procesu (proces potomny). Następnieexec
jest wywoływany z poziomu procesu potomnego w celu „zastąpienia” kopii procesu nadrzędnego nowym procesem.Proces przebiega mniej więcej tak:
źródło
fork () tworzy kopię bieżącego procesu, a wykonywanie w nowym potomku rozpoczyna się zaraz po wywołaniu fork (). Po fork () są identyczne, z wyjątkiem wartości zwracanej przez funkcję fork (). (RTFM, aby uzyskać więcej informacji.) Dwa procesy mogą się dalej rozchodzić, przy czym jeden nie jest w stanie zakłócać drugiego, chyba że za pośrednictwem współdzielonych uchwytów plików.
exec () zastępuje bieżący proces nowym. Nie ma to nic wspólnego z fork (), z wyjątkiem tego, że exec () często podąża za fork (), gdy potrzebne jest uruchomienie innego procesu potomnego, zamiast zastąpienia bieżącego.
źródło
Główna różnica między
fork()
iexec()
polega na tym, żefork()
Wezwanie system tworzy klona program uruchomiony. Oryginalny program kontynuuje wykonywanie od następnego wiersza kodu po wywołaniu funkcji fork (). Klon rozpoczyna także wykonywanie od następnego wiersza kodu. Spójrz na następujący kod, który otrzymałem z http://timmurphy.org/2014/04/26/using-fork-in-cc-a-minimum-working-example/Ten program deklaruje zmienną licznika, ustawioną na zero, przed
fork()
ing. Po wywołaniu rozwidlenia mamy równolegle dwa procesy, oba zwiększające własną wersję licznika. Każdy proces zostanie uruchomiony do końca i zakończony. Ponieważ procesy przebiegają równolegle, nie mamy możliwości dowiedzieć się, który zakończy się jako pierwszy. Uruchomienie tego programu spowoduje wydrukowanie czegoś podobnego do pokazanego poniżej, chociaż wyniki mogą się różnić w zależności od serii.exec()
Rodzina wywołań systemowych zastąpi aktualnie wykonywanie kodu procesu z innego kawałka kodu. Proces zachowuje swój PID, ale staje się nowym programem. Weźmy na przykład następujący kod:Ten program wywołuje
execvp()
funkcję zastępowania kodu programem daty. Jeśli kod jest zapisany w pliku o nazwie exec1.c, wówczas jego wykonanie daje następujące dane wyjściowe:Program wypisuje wiersz eReady to exec (). . . ‖ I po wywołaniu funkcji execvp (), zastępuje swój kod programem daty. Zauważ, że linia -. . . to zadziałało‖ nie wyświetla się, ponieważ w tym momencie kod został zastąpiony. Zamiast tego widzimy wynik działania funkcji -data -u.‖
źródło
fork()
:Tworzy kopię uruchomionego procesu. Działający proces nazywa się procesem nadrzędnym, a nowo utworzony proces nazywa się procesem potomnym . Sposobem na rozróżnienie tych dwóch jest spojrzenie na zwróconą wartość:
fork()
zwraca identyfikator procesu (pid) procesu potomnego w rodzicufork()
zwraca 0 w potomku.exec()
:Inicjuje nowy proces w ramach procesu. Ładuje nowy program do bieżącego procesu, zastępując istniejący.
fork()
+exec()
:Podczas uruchamiania nowego programu należy najpierw
fork()
utworzyć nowy proces, a następnieexec()
(tj. Załadować do pamięci i wykonać) program binarny, który ma uruchomić.źródło
Doskonałym przykładem do zrozumienia pojęcia
fork()
iexec()
jest powłoka , program interpretujący polecenia, który użytkownicy zwykle wykonują po zalogowaniu się do systemu. Powłoka interpretuje pierwsze słowo wiersza poleceń jako nazwę poleceniaW przypadku wielu poleceń powłoka rozwidla się, a proces potomny wykonuje polecenie powiązane z nazwą, traktując pozostałe słowa w wierszu poleceń jako parametry polecenia.
Powłoki pozwala trzech rodzajów poleceń. Po pierwsze, polecenie może być plikiem wykonywalnym, który zawiera kod obiektowy wytworzony przez kompilację kodu źródłowego (na przykład program w języku C). Po drugie, polecenie może być plikiem wykonywalnym, który zawiera sekwencję wierszy poleceń powłoki. Wreszcie, polecenie może być poleceniem powłoki wewnętrznej (zamiast pliku wykonywalnego ex-> cd , ls itp.)
źródło