GNU/Linux >> Znalost Linux >  >> Linux

Proč je vfork() určeno k použití, když podřízený proces volá exec() nebo exit() ihned po vytvoření?

Když je podřízený proces vytvořen vfork() volání exec() , ne exec() upravit adresový prostor nadřazeného procesu načtením nového programu?

Ne, exec() poskytuje nový adresní prostor pro nový program; nemění nadřazený adresní prostor. Viz například diskuse o exec funkce v POSIX a Linux execve() manuálová stránka.

Když podřízený proces vytvořený pomocí vfork() zavolá exit(), nezmění exit() při ukončení podřízeného procesu adresní prostor nadřazeného procesu?

Obyčejný exit() možná – spouští výstupní háky nainstalované běžícím programem (včetně jeho knihoven). vfork() je více omezující; proto na Linuxu vyžaduje použití _exit() což není zavolejte čisticí funkce knihovny C.

vfork() ukázalo se, že je docela obtížné dostat se správně; byl odstraněn v aktuálních verzích standardu POSIX a posix_spawn() by měl být použit místo toho.

Pokud však skutečně vědět, co děláte, neměli byste použijte buď vfork() nebo posix_spawn(); držte se starého dobrého fork() a exec() .

Manuálová stránka Linuxu, na kterou odkazuje výše, poskytuje další kontext:

Nicméně, za starých časů fork(2) by vyžadovalo vytvoření úplné kopie datového prostoru volajícího, často zbytečně, protože obvykle bezprostředně poté exec(3) je hotovo. Pro větší efektivitu tedy BSD zavedlo vfork() systémové volání, které plně nezkopírovalo adresní prostor nadřazeného procesu, ale vypůjčilo si paměť rodiče a řídicí vlákno až do volání execve(2) nebo došlo k odchodu. Nadřazený proces byl pozastaven, zatímco dítě využívalo své prostředky. Použití vfork() byl ošemetný:například neupravování dat v rodičovském procesu záviselo na znalosti, které proměnné jsou uloženy v registru.


Když zavoláte vfork() , je vytvořen nový proces a tento nový proces si vypůjčí obraz procesu nadřazeného procesu s výjimkou zásobníku. Podřízenému procesu je přidělena vlastní nová hvězdička zásobníku, ale neumožňuje return z funkce, která volala vfork() .

Když je podřízený proces spuštěn, nadřazený proces je zablokován, protože si podřízený vypůjčil adresní prostor nadřízeného.

Bez ohledu na to, co děláte, vše, co právě přistupuje k zásobníku, upravuje pouze soukromý zásobník dítěte. Pokud však změníte globální data, změní se společná data a ovlivní to také nadřazená data.

Věci, které mění globální data, jsou např.:

  • volání malloc() nebo free()

  • pomocí stdio

  • úprava nastavení signálu

  • modifikace proměnných, které nejsou lokální pro funkci volající vfork() .

  • ...

Jakmile zavoláte _exit() (důležité, nikdy nevolejte exit() ), dítě je ukončeno a kontrola je předána zpět rodiči.

Pokud zavoláte jakoukoli funkci z exec*() rodina, je vytvořen nový adresní prostor s novým programovým kódem, novými daty a částí zásobníku z rodiče (viz níže). Jakmile je toto připraveno, dítě si již nevypůjčuje adresní prostor od dítěte, ale používá vlastní adresní prostor.

Ovládací prvek je vrácen nadřazenému prvku, protože jeho adresní prostor již nepoužívá jiný proces.

Důležité:V systému Linux neexistuje žádný skutečný vfork() implementace. Linux spíše implementuje vfork() na základě Copy on Write fork() koncept představený SunOS-4.0 v roce 1988. Aby uživatelé věřili, že používají vfork() , Linux pouze nastaví sdílená data a pozastaví rodiče, zatímco dítě nezavolá _exit() nebo jeden z exec*() funkce.

Linux proto nevyužívá faktu, že skutečný vfork() nepotřebuje nastavit popis adresního prostoru pro dítě v jádře. Výsledkem je vfork() která není rychlejší než fork() . Na systémech, které implementují skutečný vfork() , je obvykle 3x rychlejší než fork() a ovlivňuje výkon shellů, které používají vfork() - ksh93 , poslední Bourne Shell a csh .

Důvod, proč byste nikdy neměli volat exit() z vfork() ed child je to exit() vyprázdní stdio v případě, že existují nevyprázdněná data z doby před voláním vfork() . To by mohlo způsobit podivné výsledky.

BTW:posix_spawn() je implementován nad vfork() , tedy vfork() nebude odstraněn z operačního systému. Bylo zmíněno, že Linux nepoužívá vfork() pro posix_spawn() .

Pro zásobník existuje jen málo dokumentace, zde je to, co říká manuálová stránka 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. 

Implementace si tedy může dělat, co chce. Implementace Solaris používá sdílenou paměť pro zásobníkový rámec funkce volající vfork() . Žádná implementace neuděluje přístup ke starším částem zásobníku od rodiče.


Linux
  1. Výchozí kód ukončení při ukončení procesu?

  2. Proč není Pgid procesů dítěte PGID rodiče?

  3. Proč je výchozí vidlice mechanismu vytváření procesu?

  1. Proč Systemd zastavuje službu ihned po jejím spuštění?

  2. Jak nechat podřízený proces zemřít po odchodu rodiče?

  3. Rozdíl mezi fork(), vfork(), exec() a clone()

  1. Jaký je význam caddr_t a kdy se používá?

  2. Proč tento plášťový ropovod vystupuje?

  3. Bash:Proč se nadřazený skript neukončí na SIGINT, když podřízený skript zachytí SIGINT?