Scroll to navigation

vfork(2) System Calls Manual vfork(2)

NUME

vfork - creează un proces copil și blochează părintele

BIBLIOTECA

Biblioteca C standard (libc, -lc)

SINOPSIS

#include <unistd.h>
pid_t vfork(void);

Cerințe pentru macrocomenzi de testare a caracteristicilor pentru glibc (consultați feature_test_macros(7)):

vfork():


Începând cu glibc 2.12:
(_XOPEN_SOURCE >= 500) && ! (_POSIX_C_SOURCE >= 200809L)
|| /* Începând cu glibc 2.19: */ _DEFAULT_SOURCE
|| /* glibc <= 2.19: */ _BSD_SOURCE
Înainte de glibc 2.12:
_BSD_SOURCE || _XOPEN_SOURCE >= 500

DESCRIERE

Descrierea standard

(Din POSIX.1 ) Funcția vfork() are același efect ca și fork(2), cu excepția faptului că comportamentul este nedefinit dacă procesul creat de vfork() fie modifică alte date decât o variabilă de tip pid_t utilizată pentru a stoca valoarea de returnare de la vfork(), fie returnează din funcția în care a fost apelată vfork(), fie apelează orice altă funcție înainte de a apela cu succes _exit(2) sau una dintre funcțiile din familia exec(3).

Descrierea Linux

vfork(), la fel ca fork(2), creează un proces-copil al procesului apelant. Pentru detalii și valoarea de returnare și erori, consultați fork(2).

vfork() este un caz special de clone(2). Acesta este utilizat pentru a crea procese noi fără a copia tabelele de pagini ale procesului părinte. Aceasta poate fi utilă în aplicațiile sensibile la performanță în care se creează un proces-copil care apoi emite imediat un execve(2).

vfork() diferă de fork(2) prin faptul că firul apelant este suspendat până când firul copil termină (fie în mod normal, prin apelarea _exit(2), fie în mod anormal, după transmiterea unui semnal fatal), sau efectuează un apel la execve(2). Până în acel moment, copilul împarte toată memoria cu părintele său, inclusiv stiva. Copilul nu trebuie să se întoarcă din funcția curentă sau să apeleze exit(3) (care ar avea ca efect apelarea gestionarilor de ieșire stabiliți de procesul părinte și golirea tampoanelor stdio(3) ale părintelui), dar poate apela _exit(2).

Ca și în cazul fork(2), procesul-copil creat de vfork() moștenește copii ale diferitelor atribute ale procesului apelantului (de exemplu, descriptori de fișiere, dispoziții de semnal și directorul curent de lucru); apelul vfork() diferă doar în ceea ce privește tratarea spațiului de adrese virtuale, așa cum s-a descris mai sus.

Semnalele trimise părintelui sosesc după ce copilul eliberează memoria părintelui (de exemplu, după ce copilul termină sau apelează execve(2)).

Descrierea istorică (veche)

În Linux, fork(2) este implementat folosind pagini copy-on-write, astfel încât singura penalizare suferită de fork(2) este timpul și memoria necesare pentru a duplica tabelele de pagini ale părintelui și pentru a crea o structură de sarcini unică pentru copil. Cu toate acestea, în vremurile de demult, un fork(2) ar fi necesitat realizarea unei copii complete a spațiului de date al apelantului, adesea inutil, deoarece, de obicei, imediat după aceea se realizează un exec(3). Astfel, pentru o mai mare eficiență, BSD a introdus apelul de sistem vfork(), care nu copia complet spațiul de adrese al procesului părinte, ci împrumuta memoria și firul de control al părintelui până la un apel la execve(2) sau până la o ieșire. Procesul părinte a fost suspendat în timp ce copilul folosea resursele sale. Utilizarea vfork() era complicată: de exemplu, pentru a nu modifica datele din procesul părinte, era necesar să se cunoască variabilele care se aflau într-un registru.

STANDARDE

4.3BSD; POSIX.1-2001 (dar marcat ca OBSOLET). POSIX.1-2008 elimină specificația vfork().

Cerințele impuse lui vfork() de către standarde sunt mai slabe decât cele impuse lui fork(2), astfel încât o implementare în care cele două sunt sinonime este conformă. În special, programatorul nu se poate baza pe faptul că părintele rămâne blocat până când copilul fie se termină, fie apelează execve(2) și nu se poate baza pe niciun comportament specific cu privire la memoria partajată.

NOTE

Unii consideră că semantica lui vfork() este o imperfecțiune arhitecturală, iar pagina de manual 4.2BSD a declarat: „Acest apel de sistem va fi eliminat atunci când sunt implementate mecanisme adecvate de partajare a sistemului. Utilizatorii nu ar trebui să depindă de semantica de partajare a memoriei a vfork() deoarece, în acest caz, va fi sinonim cu fork(2).” Totuși, chiar dacă hardware-ul modern de gestionare a memoriei a redus diferența de performanță dintre fork(2) și vfork(), există diverse motive pentru care Linux și alte sisteme au păstrat vfork():

Unele aplicații cu performanțe critice necesită micul avantaj de performanță conferit de vfork().
Funcția vfork() poate fi implementată pe sisteme care nu dispun de o unitate de gestionare a memoriei (MMU), dar funcția fork(2) nu poate fi implementată pe astfel de sisteme. (POSIX.1-2008 a eliminat vfork() din standard; justificarea POSIX pentru funcția posix_spawn(3) menționează că această funcție, care oferă o funcționalitate echivalentă cu fork(2)+ exec(3), este concepută pentru a putea fi implementată pe sisteme care nu dispun de o MMU).
Pe sistemele în care memoria este limitată, vfork() evită necesitatea de a angaja temporar memoria (a se vedea descrierea /proc/sys/vm/overcommit_memory în proc(5)) pentru a executa un program nou. (Acest lucru poate fi deosebit de benefic în cazul în care un proces părinte mare dorește să execute un mic program ajutător într-un proces copil). În schimb, utilizarea fork(2) în acest scenariu necesită fie angajarea unei cantități de memorie egală cu dimensiunea procesului părinte (în cazul în care este în vigoare supraangajarea strictă), fie supraangajarea memoriei cu riscul ca un proces să fie întrerupt de oom-killer.

Avertismente

Procesul-copil trebuie să aibă grijă să nu modifice memoria în moduri neintenționate, deoarece astfel de modificări vor fi văzute de procesul părinte odată ce copilul termină sau execută un alt program. În această privință, gestionarii de semnal pot fi deosebit de problematici: dacă un gestionar de semnal care este invocat în copilul lui vfork() modifică memoria, aceste modificări pot duce la o stare inconsecventă a procesului din perspectiva procesului părinte (de exemplu, modificările de memorie ar fi vizibile în procesul părinte, dar modificările stării descriptorilor de fișiere deschise nu ar fi vizibile).

Atunci când vfork() este apelat într-un proces cu mai multe fire de execuție, numai firul de execuție apelant este suspendat până când copilul termină sau execută un nou program. Aceasta înseamnă că copilul împarte un spațiu de adrese cu alt cod care rulează. Acest lucru poate fi periculos dacă un alt fir de execuție din procesul părinte își schimbă acreditările (utilizând setuid(2) sau similar), deoarece există acum două procese cu niveluri de privilegii diferite care rulează în același spațiu de adrese. Ca exemplu al pericolelor, să presupunem că un program cu mai multe fire de execuție care rulează ca root creează un copil folosind vfork(). După vfork(), un fir de execuție din procesul părinte scade procesul la un utilizator fără privilegii pentru a rula un cod de neîncredere (de exemplu, poate prin intermediul unui modul deschis cu dlopen(3)). În acest caz, sunt posibile atacuri în care procesul părinte utilizează mmap(2) pentru a pune în memorie codul care va fi executat de procesul copil privilegiat.

Note Linux

Gestionarii de bifurcare stabiliți utilizând pthread_atfork(3) nu sunt apelați atunci când un program multi-fir care utilizează biblioteca multi-fire NPTL apelează vfork(). În acest caz, gestionarii de bifurcări sunt apelați într-un program care utilizează biblioteca multi-fire LinuxThreads. Consultați pthreads(7) pentru o descriere a bibliotecilor de multi-fire Linux.

Un apel la vfork() este echivalent cu un apel la clone(2) cu flags specificat ca:



CLONE_VM | CLONE_VFORK | SIGCHLD

Istoric

Apelul de sistem vfork() a apărut în 3.0BSD. În 4.4BSD a devenit sinonim cu fork(2), dar NetBSD l-a introdus din nou; a se vedea http://www.netbsd.org/Documentation/kernel/vfork.html Linux, a fost echivalent cu fork(2) până la Linux 2.2.0-pre6 aproximativ. De la Linux 2.2.0-pre9 (pe i386, ceva mai târziu pe alte arhitecturi) este un apel de sistem independent. Suportul pentru acesta a fost adăugat în glibc 2.0.112.

ERORI

Detaliile de gestionare a semnalelor sunt obscure și diferă de la un sistem la altul. Pagina de manual BSD afirmă: „Pentru a evita o posibilă situație de blocaj, procesele care sunt copii în mijlocul unui vfork() nu primesc niciodată semnale SIGTTOU sau SIGTTIN; mai degrabă, sunt permise ieșirile sau ioctls, iar încercările de intrare au ca rezultat o indicație de sfârșit de fișier.”

CONSULTAȚI ȘI

clone(2), execve(2), _exit(2), fork(2), unshare(2), wait(2)

TRADUCERE

Traducerea în limba română a acestui manual a fost făcută de Remus-Gabriel Chelu <remusgabriel.chelu@disroot.org>

Această traducere este documentație gratuită; citiți Licența publică generală GNU Versiunea 3 sau o versiune ulterioară cu privire la condiții privind drepturile de autor. NU se asumă NICIO RESPONSABILITATE.

Dacă găsiți erori în traducerea acestui manual, vă rugăm să trimiteți un e-mail la translation-team-ro@lists.sourceforge.net.

5 februarie 2023 Pagini de manual de Linux 6.03