J'ai essayé de comprendre comment le mécanisme fork-exec est utilisé dans Linux. Tout se passait conformément au plan jusqu'à ce que certaines pages Web commencent à m'embrouiller.
On dit qu'un processus enfant doit strictement utiliser _exit()
au lieu d'un simple exit()
ou d'un retour normal de main()
.
Comme je le sais, Linux Shell exécute chacune des commandes externes; En supposant que ce que j'ai dit ci-dessus soit vrai, la conclusion est qu'aucune de ces commandes externes ni aucune autre exécution se produisant dans le shell Linux ne peut effectuer un retour normal!
Wikipedia et certaines autres pages Web affirment que nous devons utiliser _exit()
uniquement pour empêcher un processus enfant de provoquer la suppression des fichiers temporaires du parent alors qu'un éventuel double vidage des tampons stdio peut survenir. Bien que je comprenne le premier, je ne vois pas en quoi un double vidage des mémoires tampons pourrait être dangereux pour un système Linux.
J'ai passé toute ma journée sur ce sujet .... Merci pour toute clarification.
Vous devez utiliser _exit
(ou son synonyme _Exit
) pour abandonner le programme enfant lorsque la variable exec
échoue, car dans cette situation, le processus enfant peut interférer avec les données externes (fichiers) du processus parent en appelant ses gestionnaires atexit
appeler ses gestionnaires de signaux et/ou vider les mémoires tampons.
Pour la même raison, vous devriez également utiliser _exit
dans tout processus enfant ne faisant pas de exec
, mais ceux-ci sont rares.
Dans tous les autres cas, utilisez simplement exit
. Comme vous l'avez vous-même noté partiellement, tous processus sous Unix/Linux (sauf un, init
) est l'enfant d'un autre processus. Utiliser _exit
dans chaque processus enfant signifierait que exit
est inutile en dehors de init
.
switch (fork()) {
case 0:
// we're the child
execlp("some", "program", NULL);
_exit(1); // <-- HERE
case -1:
// error, no fork done ...
default:
// we're the parent ...
}
exit()
vide les tampons io et effectue d'autres opérations, telles que les fonctions d'exécution enregistrées par atexit()
. exit()
appelle _end( )
_exit()
termine simplement le processus sans le faire. Vous appelez _exit()
à partir du processus parent lors de la création d'un démon, par exemple.
Avez-vous déjà remarqué que main()
est une fonction? Vous vous êtes déjà demandé comment cela s’appelait en premier lieu? Lorsque le programme courant exécute le shell dans lequel vous vous trouvez, il fournit le chemin de l’exécutable vers l’appel système 'exec' et le contrôle est transmis au noyau qui appelle à son tour la fonction de démarrage de chaque exécutable _start()
, appelle votre main()
, lorsque main()
le renvoie puis appelle _end()
Certaines implémentations de C utilisent des noms légèrement différents pour _end()
& _start()
...
exit()
et _exit()
invoke _end()
Normalement - pour chaque _main()
, il devrait y avoir un & un seul appel exit()
. (ou retour à la fin de main()
)
exit () est au sommet de _exit (), en utilisant la bibliothèque C conventionnelle.
Il y a les différences:
_exit () ne videra pas le tampon stdio tandis que exit () videra le tampon stdio avant de quitter.
_exit () ne peut pas exécuter de processus de nettoyage tandis que exit () peut être enregistré avec une fonction (par exemple, on_exit ou at_exit) pour effectuer un processus de nettoyage si quelque chose est requis avant d'exister.
exit (status) passe simplement le statut de sortie à _exit (status). Il est recommandé que chaque fois que vous exécutez fork (), l'un d'entre eux entre enfant et parent, l'un utilise _exit () et l'autre utilise exit ().
Dans la branche enfant d'une
fork()
, il est normalement incorrect d'utiliserexit()
, car cela peut entraîner le vidage des tampons stdio deux fois, et les fichiers temporaires étant supprimés de manière inattendue.
Extrait de: http://www.unixguide.net/unix/programming/1.1.3.shtml