Les parenthèses démarrent toujours un sous-shell. Ce qui se passe, c'est que bash détecte que sleep 5
c'est la dernière commande exécutée par ce sous-shell, donc il appelle à la exec
place de fork
+ exec
. La sleep
commande remplace le sous-shell dans le même processus.
En d'autres termes, le scénario de base est:
( … )
créer un sous-shell. Le processus d'origine appelle fork
et wait
. Dans le sous-processus, qui est un sous-shell:
sleep
est une commande externe qui nécessite un sous-processus du sous-processus. Le sous-shell appelle fork
et wait
. Dans le sous-processus:
- Le sous-processus exécute la commande externe →
exec
.
- Finalement, la commande se termine →
exit
.
wait
se termine dans le sous-shell.
wait
se termine dans le processus d'origine.
L'optimisation est:
( … )
créer un sous-shell. Le processus d'origine appelle fork
et wait
. Dans le sous-processus, qui est un sous-shell jusqu'à ce qu'il appelle exec
:
sleep
est une commande externe, et c'est la dernière chose que ce processus doit faire.
- Le sous-processus exécute la commande externe →
exec
.
- Finalement, la commande se termine →
exit
.
wait
se termine dans le processus d'origine.
Lorsque vous ajoutez quelque chose d'autre après l'appel sleep
, le sous-shell doit être conservé, donc cette optimisation ne peut pas se produire.
Lorsque vous ajoutez quelque chose d'autre avant l'appel à sleep
, l'optimisation peut être effectuée (et ksh le fait), mais bash ne le fait pas (c'est très conservateur avec cette optimisation).
Gilles 'SO- arrête d'être méchant'
la source
fork
et le processus enfant est créé (pour exécuter des commandes externes) en appelantfork + exec
. Mais votre premier paragraphe suggère que celafork + exec
est également nécessaire pour le sous-shell. Qu'est-ce que je me trompe ici?fork
+exec
n'est pas appelé pour le sous-shell, il est appelé pour la commande externe. Sans aucune optimisation, il y a unfork
appel pour le sous-shell et un autre pour la commande externe. J'ai ajouté une description détaillée du flux à ma réponse.(...)
(dans le cas de base), il peut y avoir ou non un appel àexec
dépend si le sous-shell a une commande externe à exécuter, alors qu'en cas d'exécution d'une commande externe, il doit y en avoirfork + exec
.date
dans un shell?strace -f -e clone,execve,write bash -c 'date'
etstrace -f -e clone,execve,write bash -c 'date; true'
Dans le guide de programmation avancé de Bash :
"En général, une commande externe dans un script se dérobe à un sous-processus, contrairement à une commande intégrée Bash. Pour cette raison, les commandes intégrées s'exécutent plus rapidement et utilisent moins de ressources système que leurs équivalents de commandes externes."
Et un peu plus loin:
"Une liste de commandes intégrée entre parenthèses s'exécute en tant que sous-shell."
Exemples:
Exemple d'utilisation du code OP (avec des périodes de sommeil plus courtes car je suis impatient):
Le résultat:
la source
sleep
s'exécute dans un sous-shell (peut-être sur le processus du sous-shell car il est intégré, plutôt qu'un sous-processus du sous-shell). Cependant, dans tous les cas, je m'attendais à ce qu'un sous-shell existe, c'est-à-dire un sous-processus Bash sous le processus Bash parent. Pour l'extrait B ci-dessus, cela ne semble pas être le cas.sleep
qu'il ne semble pas être intégré, je m'attendrais à ce que le deuxièmesleep
appel dans les deux extraits s'exécute dans un sous-processus du processus de sous-shell.$BASHPID
variable. Malheureusement, la façon dont vous le faisiez ne vous donnait pas toute l'histoire, je crois. Voir ma sortie ajoutée dans la réponse.Une note supplémentaire à la réponse @Gilles.
Comme l'a dit Gilles:
The parentheses always start a subshell.
Cependant, les chiffres que possède ce sous-shell peuvent se répéter:
Comme vous pouvez le voir, le $$ continue de se répéter, et c'est comme prévu, car (exécutez cette commande pour trouver la bonne
man bash
ligne):C'est-à-dire: si le shell n'est pas réinitialisé, le $$ est le même.
Ou avec ça:
Le
$$
est l'ID de l'enveloppe actuelle ( et non le sous - shell).la source