Pourquoi un enfant d'un vfork ou d'un fork devrait-il appeler _exit () au lieu de exit ()?

12

À partir de la page de manuel de vfork():

vfork () diffère de fork () en ce que le parent est suspendu jusqu'à ce que l'enfant fasse un appel à execve (2) ou _exit (2). L'enfant partage toute la mémoire avec son parent, y compris la pile, jusqu'à ce que execve () soit émis par l'enfant. L'enfant ne doit pas revenir de la fonction actuelle ou appeler exit (), mais peut appeler _exit ().

Pourquoi l'enfant devrait-il utiliser _exit()plutôt qu'un simple appel exit()? J'espère que cela s'applique aux deux vfork()et fork().

Sen
la source
2
si vous êtes intéressé par une discussion
xenoterracide

Réponses:

11

Comme vu précédemment , vforkne permet pas au processus enfant d'accéder à la mémoire du parent. exitest une fonction de bibliothèque C (c'est pourquoi elle est souvent écrite comme exit(3)). Il effectue diverses tâches de nettoyage telles que le vidage et la fermeture des flux C (les fichiers s'ouvrent via les fonctions déclarées dans stdio.h) et l'exécution des fonctions spécifiées par l'utilisateur enregistrées avec atexit. Toutes ces tâches impliquent la lecture et l'écriture dans la mémoire de processus.

_exitsort sans nettoyage. Il s'agit directement d'un appel système (c'est pourquoi il est écrit sous la forme _exit(2)), généralement implémenté en plaçant le numéro d'appel système dans un registre de processeur et en exécutant une instruction de processeur particulière (branchement au gestionnaire d'appels système). Cela n'a pas besoin d'accéder à la mémoire du processus, il est donc sûr de le faire après vfork.

Après fork, il n'y a plus de restriction: les processus parent et enfant sont désormais complètement autonomes.

Gilles 'SO- arrête d'être méchant'
la source
vfork ne permet pas au processus enfant d'accéder à la mémoire du parent ? Mais je pensais qu'ils partagent le même espace d'adressage, afin que l'enfant puisse accéder à l'espace d'adressage des parents. Cette compréhension était-elle mauvaise?
Sen
Après fork, il n'y a pas une telle restriction: les processus parent et enfant sont maintenant complètement autonomes. Est-ce à dire que je peux appeler une sortie () d'un enfant d'une fourchette?
Sen
1
@Sen: L'enfant n'est pas autorisé à accéder à la mémoire des parents. Si vous l'essayez, cela pourrait fonctionner, car le noyau ne vous protégera pas. Mais l'effet pourrait ne pas être celui que vous vouliez; par exemple, si votre compilateur décide de laisser une valeur dans un registre, elle ne sera pas vue par le processus parent.
Gilles 'SO- arrête d'être méchant'
@Sen: Après un fork, vous pouvez appeler exit ou toute autre fonction. Chaque processus démarre après un fork (sous Linux, même le processus initial initest forké par le noyau).
Gilles 'SO- arrête d'être méchant'
3

exitfaire un nettoyage supplémentaire comme appeler des fonctions enregistrées par atexitdonc accéder aux données en dehors de la partie copiée. _exiteffectue syscall directement sans aucun nettoyage, sauf dans le noyau.

Maciej Piechotka
la source
... et il convient de noter que fork () copie tout, vous pouvez donc appeler exit (), et vous pouvez certainement revenir de la fonction actuelle.
derobert
3

Vous avez l'appel enfant _exit () pour éviter de vider les tampons stdio (ou autres) lorsque le processus enfant se termine. Puisque le processus enfant constitue une copie exacte du processus parent, le processus enfant a toujours tout ce que le parent avait dans "stdout" ou "stderr", les tampons de <stdio.h>. Vous pouvez (et obtiendrez, à des moments inopportuns) obtenir des sorties doubles en appelant exit (), une à partir des gestionnaires atexit du processus enfant et une à partir du parent, lorsque les tampons du processus parent sont saturés et vidés.

Je me rends compte que la réponse ci-dessus se concentre sur les spécificités de stdio.h, mais cette idée se transmet probablement à d'autres E / S tamponnées, comme l'indique l'une des réponses ci-dessus.

Bruce Ediger
la source
1

exit(): - effectue une tâche de nettoyage, comme la fermeture des flux d'E / S et de nombreux autres, puis revient au noyau. _exit(): - vient directement au noyau (n'effectuez aucune tâche de nettoyage).

fork() : le parent et l'enfant ont tous deux une table de fichiers différente, donc la modification effectuée par l'enfant n'affecte pas les paramètres d'environnement du parent, et vice versa.

vfork(): le parent et l'enfant utilisent la même table de fichiers, donc la modification effectuée par l'enfant affecte les paramètres d'environnement du parent. Par exemple, une variable var=10, maintenant exécutée var++par l'enfant puis exécutée par le parent, vous pouvez également voir l'effet de var++dans la sortie du parent.

Comme je l' ai dit , si vous utilisez exit()dans vfork()puis tous les i / o est déjà fermé. Ainsi, même si le parent s'exécute correctement, vous ne pouvez pas obtenir la sortie appropriée, car toutes les variables sont vidées et tous les flux sont fermés.

user2670535
la source