Dlaczego dziecko z vfork lub fork powinno wywoływać _exit () zamiast exit ()?

12

Ze strony podręcznika użytkownika vfork():

vfork () różni się od fork () tym, że rodzic jest zawieszony, dopóki dziecko nie wykona wywołania execve (2) lub _exit (2). Dziecko dzieli całą pamięć ze swoim rodzicem, w tym stos, dopóki funkcja execve () nie zostanie wydana przez dziecko. Dziecko nie może powrócić z bieżącej funkcji lub wywołać exit (), ale może wywołać _exit ().

Dlaczego dziecko powinno _exit()zamiast dzwonić exit()? Mam nadzieję, że dotyczy to zarówno vfork()i fork().

Sen
źródło
2
jeśli jesteś zainteresowany wcześniejszą dyskusją na temat ontopicowości wywołań API UNIX C
xenoterracide

Odpowiedzi:

11

Jak widać wcześniej , vforkproces potomny nie pozwala na dostęp do pamięci rodzica. exitjest funkcją biblioteki C (dlatego często jest zapisywana jako exit(3)). Wykonuje różne zadania czyszczenia, takie jak opróżnianie i zamykanie strumieni C (pliki otwierane za pomocą funkcji zadeklarowanych w stdio.h) oraz wykonywanie funkcji określonych przez użytkownika zarejestrowanych w atexit. Wszystkie te zadania obejmują czytanie i zapisywanie w pamięci procesu.

_exitwychodzi bez czyszczenia. Jest to bezpośrednio wywołanie systemowe (dlatego jest napisane jako _exit(2)), zwykle realizowane przez umieszczenie numeru wywołania systemowego w rejestrze procesora i wykonanie określonej instrukcji procesora (rozgałęzienie do obsługi wywołania systemowego). To nie musi mieć dostępu do pamięci procesu, więc można to bezpiecznie zrobić po vfork.

Po forktym nie ma takich ograniczeń: proces nadrzędny i podrzędny są teraz całkowicie autonomiczne.

Gilles „SO- przestań być zły”
źródło
vfork nie pozwala procesowi potomnemu na dostęp do pamięci rodzica ? Ale myślałem, że dzielą tę samą przestrzeń adresową, aby dziecko mogło uzyskać dostęp do przestrzeni adresowej rodziców. Czy to zrozumienie było złe?
Sen
Po rozwidleniu nie ma takich ograniczeń: proces nadrzędny i podrzędny są teraz całkowicie autonomiczne. Czy to oznacza, że ​​mogę wywołać exit () z podrzędnego elementu rozwidlenia?
Sen
1
@Sen: Dziecko nie ma dostępu do pamięci rodzica. Jeśli spróbujesz, może działać, ponieważ jądro cię nie ochroni. Ale efekt może nie być taki, jak zamierzałeś; na przykład, jeśli twój kompilator zdecyduje się pozostawić jakąś wartość w rejestrze, proces macierzysty nie zobaczy tego.
Gilles „SO- przestań być zły”
@Sen: Po rozwidleniu możesz wykonać wywołanie exit lub dowolną inną funkcję. Każdy proces rozpoczyna się po rozwidleniu (w Linuksie nawet proces początkowy initjest rozwidlany przez jądro).
Gilles „SO- przestań być zły”
3

exitwykonaj dodatkowe czyszczenie, takie jak wywoływanie funkcji zarejestrowanych przez atexitto, aby uzyskać dostęp do danych poza skopiowaną częścią. _exitwykonuje syscall bezpośrednio bez żadnego czyszczenia poza jądrem.

Maciej Piechotka
źródło
... i należy zauważyć, że fork () kopiuje wszystko, więc możesz być w stanie wywołać exit () i na pewno możesz wrócić z bieżącej funkcji.
derobert
3

Masz wywołanie potomne _exit (), aby uniknąć opróżniania buforów stdio (lub innych) po zakończeniu procesu potomnego. Ponieważ proces potomny stanowi dokładną kopię procesu macierzystego, proces potomny nadal ma wszystko, co rodzic miał w „stdout” lub „stderr”, bufory z <stdio.h>. Możesz (i będzie, w nieodpowiednich momentach) uzyskać podwójne dane wyjściowe, wywołując exit (), jedno z procedur obsługi atexit procesu potomnego, a drugie od rodzica, gdy bufory w procesie nadrzędnym zapełniają się i są opróżniane.

Zdaję sobie sprawę, że powyższa odpowiedź koncentruje się na szczegółach stdio.h, ale ta idea prawdopodobnie przenosi się na inne buforowane operacje we / wy, tak jak wskazuje jedna z powyższych odpowiedzi.

Bruce Ediger
źródło
1

exit(): - wykonuje pewne zadania czyszczenia, takie jak zamykanie strumieni we / wy i wielu, a następnie wraca do jądra. _exit(): - bezpośrednio przychodzi do jądra (nie wykonuj żadnych zadań czyszczenia).

fork() : zarówno rodzic, jak i dziecko mają inną tabelę plików, więc zmiana dokonana przez dziecko nie wpływa na parametry środowiska rodzica i odwrotnie.

vfork(): zarówno rodzic, jak i dziecko używają tej samej tabeli plików, więc zmiana dokonana przez dziecko wpływa na parametry środowiska rodzica. np. jakaś zmienna var=10, teraz uruchamiana var++przez dziecko, a następnie uruchamiana nadrzędna, można zobaczyć efekt również var++w danych wyjściowych nadrzędnych.

Jak powiedziałem, jeśli używasz exit()w vfork()wówczas wszystkie I / O jest już zamknięta. Tak więc, nawet jeśli rodzic działa poprawnie, nie można uzyskać prawidłowego wyniku, ponieważ wszystkie zmienne są opróżniane, a wszystkie strumienie zamykane.

użytkownik2670535
źródło