Po wywołaniu vfork()
tworzony jest nowy proces, który pożycza obraz procesu nadrzędnego, z wyjątkiem stosu. Proces potomny otrzymuje własną nową gwiazdkę stosu, jednak nie pozwala na return
to funkcja, która go wywołała vfork()
.
Podczas gdy dziecko jest uruchomione, proces nadrzędny jest blokowany, ponieważ dziecko pożyczyło przestrzeń adresową rodzica.
Niezależnie od tego, co robisz, wszystko, co tylko uzyskuje dostęp do stosu, modyfikuje tylko prywatny stos dziecka. Jeśli jednak zmodyfikujesz dane globalne, zmodyfikuje to wspólne dane, a zatem wpłynie również na element nadrzędny.
Czynnikami modyfikującymi dane globalne są np .:
wywoływanie malloc () lub free ()
za pomocą stdio
modyfikowanie ustawień sygnału
modyfikowanie zmiennych, które nie są lokalne dla wywoływanej funkcji vfork()
.
...
Gdy zadzwonisz _exit()
(ważne, nigdy nie dzwonisz exit()
), dziecko zostaje zakończone, a kontrola zostaje zwrócona rodzicowi.
Jeśli wywołasz dowolną funkcję z exec*()
rodziny, tworzona jest nowa przestrzeń adresowa z nowym kodem programu, nowymi danymi i częścią stosu nadrzędnego (patrz poniżej). Gdy to jest gotowe, dziecko nie pożycza już przestrzeni adresowej od dziecka, ale używa własnej przestrzeni adresowej.
Kontrola jest zwracana rodzicowi, ponieważ jej przestrzeń adresowa nie jest już używana przez inny proces.
Ważne: w systemie Linux nie ma rzeczywistej vfork()
implementacji. Linux raczej implementuje się w vfork()
oparciu o fork()
koncepcję Copy on Write wprowadzoną przez SunOS-4.0 w 1988 roku. Aby użytkownicy uwierzyli, że ich używają vfork()
, Linux po prostu konfiguruje wspólne dane i zawiesza rodzica, podczas gdy dziecko nie dzwoniło ani żadnej _exit()
z exec*()
funkcji.
Linux nie korzysta zatem z faktu, że rzeczywisty vfork()
nie musi konfigurować opisu przestrzeni adresowej dla dziecka w jądrze. Powoduje to, vfork()
że nie jest szybszy niż fork()
. W systemach, które implementują wartość rzeczywistą vfork()
, jest ona zazwyczaj 3 razy szybsza fork()
i wpływa na wydajność używanych powłok vfork()
- ksh93
, najnowszej Bourne Shell
i csh
.
Powodem, dla którego nigdy nie powinieneś dzwonić exit()
od vfork()
dziecka ed, jest to, że exit()
opróżnia stdio w przypadku, gdy dane nie zostały odłączone od czasu przed dzwonieniem vfork()
. Może to powodować dziwne wyniki.
BTW: posix_spawn()
jest zaimplementowany vfork()
, więc vfork()
nie będzie usuwany z systemu operacyjnego. Wspomniano już, że Linux nie korzysta vfork()
za posix_spawn()
.
Dla stosu jest niewiele dokumentacji, oto, co mówi strona podręcznika Solaris:
The vfork() and vforkx() functions can normally be used the
same way as fork() and forkx(), respectively. The calling
procedure, however, should not return while running in the
child's context, since the eventual return from vfork() or
vforkx() in the parent would be to a stack frame that no
longer exists.
Implementacja może więc robić, co chce. Implementacja Solaris korzysta z pamięci współużytkowanej dla ramki stosu wywołania funkcji vfork()
. Żadna implementacja nie zapewnia dostępu do starszych części stosu od rodzica.