W Uniksie, ilekroć chcemy utworzyć nowy proces, rozwidlamy bieżący proces, tworząc nowy proces potomny, który jest dokładnie taki sam jak proces macierzysty; następnie wykonujemy wywołanie systemowe exec, aby zastąpić wszystkie dane z procesu nadrzędnego danymi z nowego procesu.
Dlaczego w pierwszej kolejności tworzymy kopię procesu nadrzędnego, a nie bezpośrednio nowego?
process
architecture
fork
Sarthak
źródło
źródło
Odpowiedzi:
Krótka odpowiedź brzmi:
fork
jest w Uniksie, ponieważ w tamtym czasie łatwo było go dopasować do istniejącego systemu, a ponieważ poprzedni system w Berkeley zastosował koncepcję wideł.From the Evolution of the Unix Time-Sharing System ( zaznaczono odpowiedni tekst ):
Od tego czasu Unix ewoluował.
fork
następnieexec
nie jest już jedynym sposobem na uruchomienie programu.vfork został stworzony, aby być bardziej wydajnym rozwidleniem w przypadku, gdy nowy proces zamierza wykonać exec zaraz po rozwidleniu. Po wykonaniu vfork procesy nadrzędne i podrzędne współużytkują tę samą przestrzeń danych, a proces nadrzędny jest zawieszany do czasu, aż proces podrzędny wykona program lub zakończy działanie.
posix_spawn tworzy nowy proces i wykonuje plik w jednym wywołaniu systemowym. Wymaga zestawu parametrów, które umożliwiają selektywne udostępnianie otwartych plików dzwoniącego i kopiowanie jego dyspozycji sygnału i innych atrybutów do nowego procesu.
źródło
posix_spawn()
wykonania tych samych zadań związanych z ponownym numerowaniem po rozwidleniu, które można łatwo wykonać za pomocąfork()
wbudowanego kodu, stanowią przekonujący argument za tym,fork()
że jest o wiele prostszy w użyciu.[Będę powtarzać część mojej odpowiedzi od tutaj .]
Dlaczego po prostu nie ma polecenia, które tworzy nowy proces od zera? Czy nie jest absurdalne i nieefektywne kopiowanie takiego, który zostanie zastąpiony od razu?
W rzeczywistości prawdopodobnie nie byłoby to tak skuteczne z kilku powodów:
„Kopiuj”, wyprodukowany przez
fork()
to trochę abstrakcji, ponieważ jądro wykorzystuje kopiowanie przy zapisie systemu ; wszystko, co naprawdę trzeba stworzyć, to wirtualna mapa pamięci. Jeśli kopia następnie natychmiast wywołujeexec()
, większość danych, które zostałyby skopiowane, gdyby zostały zmodyfikowane przez aktywność procesu, nigdy tak naprawdę nie musi być kopiowana / tworzona, ponieważ proces nie robi nic wymagającego jego użycia.Różne znaczące aspekty procesu potomnego (np. Jego środowisko) nie muszą być indywidualnie duplikowane ani ustawiane w oparciu o złożoną analizę kontekstu itp. Po prostu zakłada się, że są takie same jak proces wywołujący, i jest to dość intuicyjny system, który znamy.
Aby wyjaśnić # 1 nieco dalej, pamięć, która jest „kopiowana”, ale nigdy później dostępna, nigdy nie jest tak naprawdę kopiowana, przynajmniej w większości przypadków. Wyjątkiem w tym kontekście może być rozwidlenie procesu, a następnie zakończenie procesu nadrzędnego przed zastąpieniem go przez dziecko
exec()
. Mówię, że może, ponieważ duża część rodzica mogłaby być buforowana, jeśli jest wystarczająca ilość wolnej pamięci, i nie jestem pewien, w jakim stopniu zostanie to wykorzystane (co zależy od implementacji systemu operacyjnego).Oczywiście, to nie sprawia, że używanie kopii jest bardziej wydajne niż używanie pustej tabliczki - z wyjątkiem tego, że „pusta tablica” nie jest dosłownie niczym i musi obejmować przydział. System może mieć ogólny pusty / nowy szablon procesu, który kopiuje w ten sam sposób 1, ale tak naprawdę nie zachowałby niczego w porównaniu z widelcem kopiowania przy zapisie. Więc # 1 pokazuje tylko, że użycie „nowego” pustego procesu nie byłoby bardziej wydajne.
Punkt 2 wyjaśnia, dlaczego korzystanie z widelca jest prawdopodobnie bardziej wydajne. Środowisko dziecka jest dziedziczone od jego rodzica, nawet jeśli jest to zupełnie inny plik wykonywalny. Na przykład, jeśli proces nadrzędny jest powłoką, a podrzędna przeglądarka internetowa,
$HOME
jest nadal taka sama dla obu z nich, ale ponieważ oba mogą później to zmienić, muszą to być dwie osobne kopie. Ten w dziecku jest produkowany przez oryginałfork()
.1. Strategia, która może nie ma większego sensu, ale chodzi mi o to, że tworzenie procesu wymaga więcej niż kopiowania jego obrazu do pamięci z dysku.
źródło
fork()
może to bardzo szybko (jak wspomniano GL, rzędu 27 linii montażowych). Patrząc w drugą stronę, jeśli chcesz „utworzyć proces od zera”,fork()
kosztuje tylko odrobinę więcej niż rozpoczęcie od pustego procesu (27 linii montażu + koszt zamykania uchwytów plików). Więc dobrzefork
radzi sobie zarówno z widelcem, jak i tworzeniem, acreate
tylko z tworzeniem dobrze.fork
kopiował całą pamięć procesu i była bardzo droga.Myślę, że powodem, dla którego Unix miał tylko
fork
funkcję tworzenia nowych procesów, jest wynik filozofii UniksaBudują jedną funkcję, która dobrze robi jedną rzecz. Tworzy proces potomny.
To, co zrobisz z nowym procesem, zależy od programisty. Może użyć jednej z
exec*
funkcji i uruchomić inny program, lub nie może użyć exec i użyć dwóch instancji tego samego programu, co może być przydatne.Dzięki temu zyskujesz większą swobodę, ponieważ możesz z niego korzystać
a ponadto trzeba tylko zapamiętać
fork
, aexec*
wywołania funkcji, które w 1970 roku trzeba było zrobić.źródło
Istnieją dwie filozofie tworzenia procesów: rozwidlaj z dziedziczeniem i twórz z argumentami. Oczywiście Unix używa widelca. (Na przykład OSE i VMS używają metody create.) Unix ma WIELE odziedziczonych cech, a kolejne są dodawane okresowo. Poprzez dziedziczenie te nowe cechy można dodawać BEZ ZMIANY ISTNIEJĄCYCH PROGRAMÓW! Używając modelu tworzenia z argumentami, dodanie nowych cech oznaczałoby dodanie nowych argumentów do wywołania create. Model uniksowy jest prostszy.
Daje to również bardzo użyteczny model typu fork-without-exec, w którym proces można podzielić na wiele części. Było to niezbędne, gdy nie było żadnej formy asynchronicznych operacji we / wy, i jest przydatne, gdy wykorzystuje się wiele procesorów w systemie. (Wątki wstępne). Robiłem to wiele lat, nawet ostatnio. Zasadniczo pozwala on na umieszczenie w kontenerach wielu „programów” w jeden program, więc absolutnie nie ma miejsca na uszkodzenia lub niedopasowania wersji itp.
Model rozwidlenia / egzekucji umożliwia także dziecku odziedziczenie radykalnie dziwnego środowiska, ustawionego między rozwidleniem a exec. Szczególnie takie jak odziedziczone deskryptory plików. (Rozszerzenie stdio fd.) Model tworzenia nie oferuje możliwości dziedziczenia niczego, co nie zostało przewidziane przez twórców wywołania create.
Niektóre systemy mogą również obsługiwać dynamiczną kompilację kodu natywnego, w którym proces faktycznie pisze własny program natywny. Innymi słowy, chce nowego programu, który sam pisze w locie, BEZ konieczności przechodzenia przez cykl kodu źródłowego / kompilatora / linkera i zajmowania miejsca na dysku. (Wierzę, że istnieje system językowy Verilog, który to robi.) Model rozwidlenia to obsługuje, model tworzenia zwykle by tego nie robił.
źródło
Funkcja fork () nie służy tylko do kopiowania procesu ojca, zwraca wartość wskazującą, że proces jest procesem ojca lub syna, poniższy rysunek wyjaśnia, w jaki sposób można korzystać z fork () jako ojca i syn:
jak pokazano, gdy proces jest ojcem fork () zwraca identyfikator procesu son
PID
, w przeciwnym razie zwraca0
na przykład możesz z niego skorzystać, jeśli masz proces (serwer WWW), który odbiera żądania, i na każde żądanie tworzy a,
son process
aby przetworzyć to żądanie, tutaj ojciec i jego synowie mają różne zadania.SO, brak uruchamiania kopii procesu nie jest dokładnie taki, jak fork ().
źródło
fork
jeśli zakładać, że chceszfork
w pierwszej kolejnościPrzekierowanie we / wy najłatwiej jest zaimplementować po rozwidleniu i przed wykonaniem. Dziecko, wiedząc, że jest dzieckiem, może zamykać deskryptory plików, otwierać nowe, dup () lub dup2 (), aby uzyskać właściwy numer fd itp., Wszystko bez wpływu na rodzica. Po wykonaniu tej czynności i być może dowolnych zmianach zmiennych środowiskowych (również nie wpływających na element nadrzędny), może uruchomić nowy program w dostosowanym środowisku.
źródło
Myślę, że wszyscy tutaj wiedzą, jak działa rozwidlenie, ale pytanie brzmi: dlaczego musimy stworzyć dokładną kopię rodzica za pomocą rozwidlenia? Odpowiedź ==> Weź przykład serwera (bez rozwidlenia), gdy klient-1 uzyskuje dostęp do serwera, jeśli w tym samym czasie przybywa drugi klient-2 i chce uzyskać dostęp do serwera, ale serwer nie daje pozwolenia nowo przybyłemu klient-2, ponieważ serwer jest zajęty obsługą klienta-1, więc klient-2 musi czekać. po zakończeniu wszystkich usług dla klienta-1 klient-2 może teraz uzyskać dostęp do serwera. Teraz zastanów się, czy w tym samym czasie przybywa klient-3, więc klient-3 musi poczekać, aż wszystkie usługi dla klienta-2 zostaną zakończone. Weźmy scenariusz, w którym tysiące klientów muszą uzyskać dostęp do serwera w tym samym czasie ... wtedy wszyscy klienci muszą czekaj (serwer jest zajęty !!).
Można tego uniknąć, tworząc (za pomocą rozwidlenia) dokładną duplikat serwera (tj. Dziecko) serwera, przy czym każde dziecko (które jest dokładnie duplikatem kopii jego rodzica, czyli serwera) jest dedykowane nowo przybyłemu klientowi, a tym samym wszyscy klienci uzyskują dostęp do tego samego serwer.
źródło
fork
wywołania, które kopiuje proces nadrzędny, jest to, że nie trzeba mieć dwóch osobnych programów - ale posiadanie osobnych programów (np.inetd
) Może uczynić system bardziej modułowym.