GNU/Linux >> Tutoriels Linux >  >> Linux

Pourquoi vfork() est-il destiné à être utilisé lorsque le processus enfant appelle exec() ou exit() immédiatement après la création ?

Lorsqu'un processus enfant créé par vfork() appelle exec() , ne exec() modifier l'espace d'adressage du processus parent, en chargeant le nouveau programme ?

Non, exec() fournit un nouvel espace d'adressage pour le nouveau programme ; il ne modifie pas l'espace d'adressage parent. Voir par exemple la discussion du exec fonctions dans POSIX et Linux execve() page de manuel.

Lorsqu'un processus enfant créé par vfork() appelle exit(), exit() ne modifie-t-il pas l'espace d'adressage du processus parent lors de la terminaison de l'enfant ?

exit() ordinaire pourrait - il exécute les crochets de sortie installés par le programme en cours d'exécution (y compris ses bibliothèques). vfork() est plus restrictif ; ainsi, sous Linux, il impose l'utilisation de _exit() ce qui ne le fait pas appeler les fonctions de nettoyage de la bibliothèque C.

vfork() s'est avéré assez difficile à obtenir correctement; il a été supprimé dans les versions actuelles de la norme POSIX, et posix_spawn() devrait être utilisé à la place.

Cependant, à moins que vous vraiment savoir ce que vous faites, vous ne devriez pas utilisez soit vfork() ou posix_spawn(); s'en tenir au bon vieux fork() et exec() .

La page de manuel Linux liée ci-dessus fournit plus de contexte :

Cependant, au mauvais vieux temps, un fork(2) nécessiterait de faire une copie complète de l'espace de données de l'appelant, souvent inutilement, car généralement immédiatement après un exec(3) est fait. Ainsi, pour plus d'efficacité, BSD a introduit le vfork() appel système, qui n'a pas entièrement copié l'espace d'adressage du processus parent, mais a emprunté la mémoire et le fil de contrôle du parent jusqu'à un appel à execve(2) ou une sortie s'est produite. Le processus parent a été suspendu pendant que l'enfant utilisait ses ressources. L'utilisation de vfork() était délicat :par exemple, ne pas modifier les données dans le processus parent dépendait de la connaissance des variables contenues dans un registre.


Lorsque vous appelez le vfork() , un nouveau processus est créé et ce nouveau processus emprunte l'image de processus du processus parent à l'exception de la pile. Le processus enfant reçoit sa propre nouvelle étoile de pile mais ne permet pas de return de la fonction qui a appelé vfork() .

Pendant que l'enfant est en cours d'exécution, le processus parent est bloqué, car l'enfant a emprunté l'espace d'adressage du parent.

Quoi que vous fassiez, tout ce qui accède à la pile ne modifie que la pile privée de l'enfant. Si vous modifiez par contre les données globales, cela modifie les données communes et affecte donc également le parent.

Les éléments qui modifient les données globales sont par exemple :

  • appeler malloc() ou free()

  • en utilisant stdio

  • modifier les paramètres du signal

  • modifier les variables qui ne sont pas locales à la fonction qui a appelé vfork() .

  • ...

Une fois que vous avez appelé le _exit() (important, ne jamais appeler le exit() ), l'enfant est supprimé et le contrôle est rendu au parent.

Si vous appelez une fonction depuis le exec*() famille, un nouvel espace d'adressage est créé avec un nouveau code de programme, de nouvelles données et une partie de la pile du parent (voir ci-dessous). Une fois que c'est prêt, l'enfant n'emprunte plus l'espace d'adressage de l'enfant, mais utilise son propre espace d'adressage.

Le contrôle est rendu au parent, car son espace d'adressage n'est plus utilisé par un autre processus.

Important :Sous Linux, il n'y a pas de véritable vfork() la mise en oeuvre. Linux implémente plutôt vfork() basé sur la copie sur écriture fork() concept introduit par SunOS-4.0 en 1988. Afin de faire croire aux utilisateurs qu'ils utilisent vfork() , Linux configure simplement les données partagées et suspend le parent alors que l'enfant n'a pas appelé _exit() ou l'un des exec*() fonctions.

Linux ne profite donc pas du fait qu'un vrai vfork() n'a pas besoin de configurer une description d'espace d'adressage pour l'enfant dans le noyau. Cela se traduit par un vfork() qui n'est pas plus rapide que fork() . Sur les systèmes qui implémentent un vrai vfork() , il est généralement 3 fois plus rapide que fork() et affecte les performances des shells qui utilisent vfork() - ksh93 , le récent Bourne Shell et csh .

La raison pour laquelle vous ne devriez jamais appeler le exit() du vfork() ed enfant est que exit() vide stdio au cas où il y aurait des données non vidées à partir du moment avant d'appeler vfork() . Cela pourrait entraîner des résultats étranges.

Au fait :posix_spawn() est implémenté au-dessus de vfork() , donc vfork() ne va pas être supprimé du système d'exploitation. Il a été mentionné que Linux n'utilise pas vfork() pour posix_spawn() .

Pour la pile, il y a peu de documentation, voici ce que dit la page de manuel de 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. 

Ainsi, l'implémentation peut faire ce qu'elle veut. L'implémentation Solaris utilise la mémoire partagée pour le cadre de pile de la fonction appelant vfork() . Aucune implémentation n'accorde l'accès aux anciennes parties de la pile depuis le parent.


Linux
  1. Code de sortie par défaut lorsque le processus est terminé ?

  2. Pourquoi le Pgid des processus enfants n'est-il pas le PID du parent ?

  3. La différence entre fork(), vfork(), exec() et clone()

  4. Pourquoi le processus enfant est-il toujours en vie après la suppression du processus parent sous Linux?

  5. Pourquoi ce pipeline shell sort-il ?

Comment tuer un processus enfant après un délai d'attente donné dans Bash?

Quelle est la signification de caddr_t et quand est-il utilisé ?

Comment puis-je supprimer un utilisateur sous Linux lorsque le système indique qu'il est actuellement utilisé dans un processus

pourquoi la liste des frères et sœurs est utilisée pour obtenir le task_struct lors de la récupération des enfants d'un processus

pourquoi une boucle bash while ne se termine-t-elle pas lorsqu'elle est dirigée vers une sous-commande terminée?

Existe-t-il une distribution Linux ou un correctif du noyau qui efface l'espace mémoire d'un processus après la fin du processus ?