Lorsqu'un processus est démarré à partir d'un shell, pourquoi ce dernier se lance-t-il avant d'exécuter le processus?
Par exemple, lorsque l'utilisateur entre grep blabla foo
, pourquoi le shell ne peut-il pas simplement appeler exec()
grep sans un shell enfant?
En outre, lorsqu'un shell se place dans un émulateur de terminal à interface graphique, démarre-t-il un autre émulateur de terminal? (comme le pts/13
démarrage pts/14
)
la source
exec grep blabla foo
. Bien sûr, dans ce cas particulier, cela ne sera pas très utile (puisque la fenêtre de votre terminal se fermera dès que le grep sera terminé), mais cela peut être utile à l’occasion (par exemple, si vous démarrez un autre shell, peut-être via ssh / sudo / screen, et n’avez pas l’intention de revenir à l’original, ou si le processus de shell sur lequel vous l’utilisez est un sous-shell qui n’a jamais été conçu pour exécuter plus d’une commande).bash -c 'grep foo bar'
et que vous appelez exec il ya une forme d’optimisation automatiquement pour vousSelon le
pts
, vérifiez vous-même: dans un shell, exécutezpour connaître votre identifiant de processus (PID), j'ai par exemple
Puis lancez par exemple
sleep 60
puis, dans un autre terminalDonc non, en général, vous avez le même terminal associé au processus. (Notez que ceci est votre
sleep
parce qu'il a votre shell comme parent).la source
TL; DR : Parce que c'est la méthode optimale pour créer de nouveaux processus et garder le contrôle dans un shell interactif.
fork () est nécessaire pour les processus et les tubes
Pour répondre à la partie spécifique de cette question, si elle
grep blabla foo
devait être appeléeexec()
directement via parent, le parent saisirait pour exister et son PID avec toutes les ressources serait repris pargrep blabla foo
.Cependant, parlons en général de
exec()
etfork()
. La raison principale de ce comportement est qu’ilfork()/exec()
s’agit de la méthode standard pour créer un nouveau processus sous Unix / Linux, et que ceci n’est pas spécifique à Bash; cette méthode est en place depuis le début et influencée par cette même méthode à partir des systèmes d’exploitation déjà existants. Pour paraphraser quelque peu la réponse de goldilocks sur une question connexe, ilfork()
est plus facile de créer un nouveau processus car le noyau a moins de travail à faire en ce qui concerne l'allocation des ressources et beaucoup de propriétés (telles que les descripteurs de fichier, l'environnement, etc.) - tout peut être hérité du processus parent (dans ce cas debash
).Deuxièmement, en ce qui concerne les shells interactifs, vous ne pouvez pas exécuter une commande externe sans forking. Pour lancer un exécutable qui vit sur le disque (par exemple,
/bin/df -h
), vous devez appeler l’une desexec()
fonctions de la famille, telle queexecve()
remplacer le parent par le nouveau processus, reprendre son PID et ses descripteurs de fichier existants, etc. Pour le shell interactif, vous souhaitez que le contrôle revienne à l'utilisateur et laisse le shell interactif parent continuer. Ainsi, le meilleur moyen est de créer un sous-processus viafork()
et de le laisser prendre en charge viaexecve()
. Ainsi, le shell interactif PID 1156 engendrerait un enfant viafork()
PID 1157, puis appeleraexecve("/bin/df",["df","-h"],&environment)
, ce qui l’/bin/df -h
exécutera avec le PID 1157. Il ne reste plus qu’à attendre que le processus se termine et que le contrôle lui soit renvoyé.Dans le cas où vous devez créer un canal entre deux commandes ou plus, par exemple
df | grep
, vous avez besoin d'un moyen de créer deux descripteurs de fichier (lecture et écriture en fin de canal qui proviennent depipe()
syscall), puis laissez en quelque sorte deux nouveaux processus en hériter. Cela consiste à créer un nouveau processus, puis à copier l'extrémité d'écriture du canal viadup2()
call sur sonstdout
alias fd 1 (ainsi, si write end est à fd 4, nous le faisonsdup2(4,1)
). Lorsqu'ilexec()
sedf
produit, le processus enfant ne pense plus à rienstdout
et écrit dessus sans se rendre compte (à moins qu'il ne vérifie activement) que sa sortie est réellement dirigée. Le même processus se produitgrep
, sauf que nousfork()
prenons la fin du tuyau avec fd 3 etdup(3,0)
avant le fraigrep
avecexec()
. Pendant tout ce temps, le processus parent est toujours là, attendant de reprendre le contrôle une fois le pipeline terminé.Dans le cas de commandes intégrées, généralement pas le shell
fork()
, à l'exception de lasource
commande. Sous-coquilles exigentfork()
.En bref, il s’agit d’un mécanisme nécessaire et utile.
Inconvénients de forking et optimisations
Maintenant, ceci est différent pour les shells non interactifs , tels que
bash -c '<simple command>'
. Bien qu’ilfork()/exec()
s’agisse d’une méthode optimale dans laquelle vous devez traiter de nombreuses commandes, c’est un gaspillage de ressources lorsque vous n’avez qu’une seule commande. Pour citer Stéphane Chazelas de ce post :Par conséquent, de nombreux shells (pas seulement
bash
)exec()
permettent de permettre à cette commandebash -c ''
d'être prise en charge par cette unique commande simple. Et précisément pour les raisons indiquées ci-dessus, il est préférable de minimiser les pipelines dans les scripts shell. Souvent, vous pouvez voir les débutants faire quelque chose comme ceci:Bien sûr, cela aura
fork()
3 processus. Ceci est un exemple simple, mais considérons un fichier volumineux, dans la plage de gigaoctets. Ce serait beaucoup plus efficace avec un processus:Le gaspillage de ressources peut en réalité être une forme d’attaque par déni de service, et en particulier des bombes fourchettes sont créées via des fonctions de shell qui s’appellent elles-mêmes dans le pipeline, ce qui leur donne plusieurs copies. De nos jours, cela est atténué par la limitation du nombre maximal de processus dans les groupes de contrôle sur systemd , qu'Ubuntu utilise également depuis la version 15.04.
Bien sûr, cela ne signifie pas que bricoler est simplement mauvais. C’est toujours un mécanisme utile, comme indiqué précédemment, mais si vous pouvez vous en tirer avec moins de processus, consécutivement moins de ressources et donc de meilleures performances, évitez-les
fork()
si possible.Voir également
la source
Pour chaque commande (exemple: grep) que vous exécutez à l'invite bash, vous avez réellement l'intention de démarrer un nouveau processus, puis de revenir à l'invite bash après son exécution.
Si le processus shell (bash) appelle exec () pour exécuter grep, le processus shell sera remplacé par grep. Grep fonctionnera correctement, mais après exécution, le contrôle ne pourra plus retourner dans le shell car le processus bash est déjà remplacé.
Pour cette raison, bash appelle fork (), ce qui ne remplace pas le processus actuel.
la source