Chciałem znaleźć różnicę między tymi czterema w Google i spodziewałem się, że będzie wiele informacji na ten temat, ale tak naprawdę nie było żadnego solidnego porównania między czterema połączeniami.
Próbowałem skompilować rodzaj podstawowego spojrzenia na różnice między tymi wywołaniami systemowymi i oto, co otrzymałem. Czy wszystkie te informacje są prawidłowe / czy brakuje mi czegoś ważnego?
Fork
: Wywołanie rozwidlenia zasadniczo tworzy duplikat bieżącego procesu, identyczny pod 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 jest kod powrotu rozwidlenia - dziecko otrzymuje 0, rodzic otrzymuje PID dziecka. To wszystko oczywiście przy założeniu, że wywołanie fork działa - jeśli nie, nie zostanie utworzone żadne dziecko, a rodzic otrzyma kod błędu.
Vfork
: Podstawową różnicą między vfork a fork jest to, że gdy nowy proces jest tworzony za pomocą vfork (), proces nadrzędny jest tymczasowo zawieszony, a proces potomny może pożyczyć przestrzeń adresową rodzica. Ten dziwny stan rzeczy trwa do momentu, gdy proces potomny albo wyjdzie, albo wywoła execve (), w którym to momencie proces macierzysty jest kontynuowany.
Oznacza to, że proces potomny vfork () musi zachować ostrożność, aby uniknąć nieoczekiwanej modyfikacji zmiennych procesu macierzystego. W szczególności proces potomny nie może powracać z funkcji zawierającej wywołanie vfork () i nie może wywoływać exit () (jeśli musi wyjść, powinien użyć _exit (); w rzeczywistości dotyczy to również dziecka normalnego widelca ()).
Exec :
Wywołanie exec jest sposobem na zastąpienie całego bieżącego procesu nowym programem. Ładuje program do bieżącej przestrzeni procesu i uruchamia go od punktu wejścia. exec () zastępuje bieżący proces plikiem wykonywalnym wskazanym przez funkcję. Kontrola nigdy nie wraca do oryginalnego programu, chyba że wystąpi błąd exec ().
Clone :
Klon, jako widelec, tworzy nowy proces. W przeciwieństwie do fork, wywołania te pozwalają procesowi potomnemu na dzielenie się częściami jego kontekstu wykonawczego z procesem wywoływania, takim jak przestrzeń pamięci, tabela deskryptorów plików i tabela procedur obsługi sygnałów.
Gdy proces potomny jest tworzony za pomocą klonowania, wykonuje aplikację funkcji fn (arg). (Różni się to od fork, gdzie wykonywanie jest kontynuowane w potomku od momentu pierwotnego wywołania fork). Argument fn jest wskaźnikiem funkcji, która jest wywoływana przez proces potomny na początku jego wykonywania. Argument arg jest przekazywany do funkcji fn.
Gdy aplikacja funkcji fn (arg) powraca, proces potomny zostaje zakończony. Liczba całkowita zwracana przez fn jest kodem wyjścia dla procesu potomnego. Proces potomny może także zostać jawnie zakończony przez wywołanie exit (2) lub po odebraniu krytycznego sygnału.
Otrzymane informacje:
- Różnice między fork i exec
- http://www.allinterview.com/showanswers/59616.html
- http://www.unixguide.net/unix/programming/1.1.2.shtml
- http://linux.about.com/library/cmd/blcmdl2_clone.htm
Dziękuję za czas poświęcony na przeczytanie tego ! :)
fork()
w Linuksie i prawdopodobnie we wszystkich BSD) pożycza przestrzeń adresową swojego rodzica. Wszystko, co robi, oprócz dzwonieniaexecve()
lub_exit()
, ma ogromny potencjał, aby zepsuć rodzica. W szczególnościexit()
programyatexit()
obsługi połączeń i inne „finalizatory”, np .: opróżniają strumienie standardowego przekazu. Powrót odvfork()
dziecka może (podobnie jak poprzednio) zepsuć stos rodzica.fork
syscall?Odpowiedzi:
vfork()
to przestarzała optymalizacja. Przed dobrym zarządzaniem pamięcią wykonałemfork()
pełną kopię pamięci rodzica, więc była dość droga. ponieważ w wielu przypadkachfork()
następowało po niejexec()
, które odrzuca obecną mapę pamięci i tworzy nową, był to niepotrzebny wydatek. W dzisiejszych czasachfork()
nie kopiuje pamięci; jest po prostu ustawiony jako „kopiuj przy zapisie”, więcfork()
+exec()
jest tak samo wydajny jakvfork()
+exec()
.clone()
to wywołanie systemowe używane przezfork()
. z niektórymi parametrami tworzy nowy proces, z innymi tworzy wątek. różnica między nimi polega tylko na tym, które struktury danych (przestrzeń pamięci, stan procesora, stos, PID, otwarte pliki itp.) są wspólne, czy nie.źródło
vfork
unika się potrzeby tymczasowego angażowania znacznie większej ilości pamięci tylko po to, aby można było ją wykonaćexec
, i jest to nadal bardziej wydajne niżfork
, nawet jeśli nie prawie tak wysokie. W ten sposób można uniknąć nadpisywania pamięci, tak aby duży program mógł zrodzić proces potomny. Tak więc nie tylko zwiększenie wydajności, ale może w ogóle uczynić to wykonalnym.exec
.execve()
zastępuje bieżący obraz wykonywalny innym obrazem załadowanym z pliku wykonywalnego.fork()
tworzy proces potomny.vfork()
jest zoptymalizowaną historycznie wersjąfork()
, przeznaczoną do użycia, gdyexecve()
wywoływana jest bezpośrednio pofork()
. Okazało się, że działa dobrze w systemach innych niż MMU (gdziefork()
nie może działać wydajnie) i podczasfork()
przetwarzania procesów o dużej powierzchni pamięci w celu uruchomienia małego programu (pomyśl o JavieRuntime.exec()
). POSIX ustandaryzował,posix_spawn()
aby zastąpić te ostatnie dwa bardziej nowoczesne zastosowaniavfork()
.posix_spawn()
robi ekwiwalent afork()/execve()
, a także pozwala żonglerce fd pomiędzy. Ma to zastąpićfork()/execve()
, głównie na platformach innych niż MMU.pthread_create()
tworzy nowy wątek.clone()
to wywołanie specyficzne dla Linuksa, którego można użyć do zaimplementowania czegokolwiek odfork()
dopthread_create()
. Daje dużo kontroli. Inspirowanyrfork()
.rfork()
to połączenie specyficzne dla planu 9. To ma być ogólne wywołanie, umożliwiające kilka stopni udostępniania między pełnymi procesami i wątkami.źródło
fork()
- tworzy nowy proces potomny, który jest kompletną kopią procesu macierzystego. Procesy potomne i macierzyste używają różnych wirtualnych przestrzeni adresowych, które początkowo są zapełniane przez te same strony pamięci. Następnie, gdy oba procesy są wykonywane, wirtualne przestrzenie adresowe zaczynają się coraz bardziej różnić, ponieważ system operacyjny wykonuje leniwe kopiowanie stron pamięci zapisywanych przez jeden z tych dwóch procesów i przypisuje niezależne kopie zmodyfikowanych stron pamięć dla każdego procesu. Ta technika nazywa się Copy-On-Write (COW).vfork()
- tworzy nowy proces potomny, który jest „szybką” kopią procesu nadrzędnego. W przeciwieństwie do wywołania systemowegofork()
procesy potomne i macierzyste korzystają z tej samej wirtualnej przestrzeni adresowej. UWAGA! Korzystając z tej samej wirtualnej przestrzeni adresowej, zarówno rodzic, jak i dziecko używają tego samego stosu, wskaźnika stosu i wskaźnika instrukcji, jak w przypadku klasycznegofork()
! Aby zapobiec niepożądanej ingerencji między rodzicem a dzieckiem, które używają tego samego stosu, wykonywanie procesu nadrzędnego jest zawieszane, dopóki dziecko nie wywoła alboexec()
(utworzy nową wirtualną przestrzeń adresową i przejście do innego stosu), albo_exit()
(zakończenie wykonywania procesu ).vfork()
to optymalizacjafork()
modelu „fork-and-exec”. Można to wykonać 4-5 razy szybciej niżfork()
, ponieważ w przeciwieństwie dofork()
(nawet mając na uwadze COW), implementacjavfork()
wywołania systemowego nie obejmuje utworzenia nowej przestrzeni adresowej (przydzielanie i konfigurowanie nowych katalogów stron).clone()
- tworzy nowy proces potomny. Różne parametry tego wywołania systemowego określają, które części procesu nadrzędnego muszą zostać skopiowane do procesu podrzędnego i które części będą między nimi współużytkowane. W rezultacie tego wywołania systemowego można używać do tworzenia wszelkiego rodzaju jednostek wykonawczych, poczynając od wątków, a kończąc na całkowicie niezależnych procesach. W rzeczywistościclone()
wywołanie systemowe jest bazą używaną do realizacjipthread_create()
i całej rodzinyfork()
wywołań systemowych.exec()
- resetuje całą pamięć procesu, ładuje i analizuje określony plik binarny wykonywalny, konfiguruje nowy stos i przekazuje kontrolę do punktu wejścia załadowanego pliku wykonywalnego. To wywołanie systemowe nigdy nie zwraca kontroli dzwoniącemu i służy do ładowania nowego programu do już istniejącego procesu. To wywołaniefork()
systemowe wraz z wywołaniem systemowym tworzą razem klasyczny model zarządzania procesami UNIX o nazwie „fork-and-exec”.źródło
vfork
są tak słabe, że legalne byłoby utworzenievfork
synonimufork
(a POSIX.1-2008vfork
całkowicie usuwa ze specyfikacji). Jeśli zdarzy ci się przetestować kod w systemie, który je synonimizuje (np. Większość BSD po wersji 4.4 oprócz NetBSD, jądra Linuksa w wersjach wcześniejszych niż 2.2.0-pre6 itp.), Może działać nawet w przypadku naruszeniavfork
umowy, a następnie wybuchu jeśli uruchomisz go gdzie indziej. Niektóre z tych, które go symulująfork
(np. OpenBSD) nadal gwarantują, że rodzic nie wznowi działania, dopóki dzieckoexec
lub_exit
s. Jest absurdalnie nieprzenośny.Fork (), vfork () i clone () wywołują do_fork (), aby wykonać prawdziwą pracę, ale z różnymi parametrami.
W przypadku rozwidlenia dziecko i ojciec mają niezależną tabelę stron maszyny wirtualnej, ale ponieważ wydajność, rozwidlenie tak naprawdę nie kopiuje żadnych stron, po prostu ustawia wszystkie strony do zapisu tylko do odczytu dla procesu potomnego. Kiedy proces potomny chce napisać coś na tej stronie, zdarza się wyjątek strony i jądro przydzieli nową stronę sklonowaną ze starej strony z uprawnieniami do zapisu. To się nazywa „kopiuj przy zapisie”.
W przypadku vfork pamięć wirtualna jest dziełem dziecka i ojca - właśnie dlatego ojciec i dziecko nie mogą obudzić się jednocześnie, ponieważ będą na siebie wpływać. Tak więc ojciec będzie spał na końcu „do_fork ()” i obudzi się, gdy dziecko wywoła exit () lub execve () od tego czasu będzie właścicielem nowej tabeli stron. Oto kod (w do_fork ()), który śpi ojciec.
Oto kod (w mm_release () wywoływany przez exit () i execve ()), który wybudza ojca.
W przypadku sys_clone () jest bardziej elastyczny, ponieważ można do niego wprowadzać dowolne flagi clone_flags. Więc pthread_create () wywołuje to wywołanie systemowe z wieloma tagami clone_flag:
int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL | CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM);
Podsumowanie: fork (), vfork () i clone () utworzą procesy potomne z różnymi mocowaniami zasobów współużytkowania z procesem ojca. Możemy również powiedzieć, że vfork () i clone () mogą tworzyć wątki (w rzeczywistości są to procesy, ponieważ mają niezależne zadanie_strukt), ponieważ współużytkują tabelę stron maszyny wirtualnej z procesem ojca.
źródło
w fork () proces potomny lub nadrzędny zostanie wykonany na podstawie wyboru procesora. Ale w vfork () na pewno dziecko wykona się jako pierwsze. po zakończeniu potomka rodzic wykona polecenie.
źródło
vfork()
można po prostu zaimplementować jakofork()
.pid
) - system operacyjny może zaplanować równoległe uruchomienie nowego procesu, jeśli coś takiego ma sens (np. Wiele procesorów). Jeśli z jakiegoś powodu potrzebujesz, aby te procesy były wykonywane w określonej kolejności szeregowej, potrzebujesz dodatkowej synchronizacji, której nie zapewnia rozwidlenie; Szczerze mówiąc, prawdopodobnie nie chciałbyś nawet widelca.vfork()
, dziecko biegnie pierwsze. Jest na stronach podręcznika; egzekucja rodziców jest zawieszona do czasu śmierci dziecka lub śmierciexec
. I ninjalj sprawdza kod źródłowy jądra. Nie ma sposobu, aby wdrożyćvfork()
jakofork()
gdyż przechodzą one różne argumentydo_fork()
w jądrze. Możesz jednak zaimplementowaćvfork
za pomocąclone
syscall