Quelle est la différence exacte entre un «sous-shell» et un «processus enfant»?

16

Selon ceci et cela , un sous-shell est démarré en utilisant des parenthèses (…).

( echo "Hello" )

Selon ceci , ceci et cela , un processus est bifurqué lorsque la commande est lancée avec un&

echo "Hello" &

La spécification Posix utilise le mot subshelldans cette page mais ne le définit pas et, également, sur la même page, ne définit pas le "processus enfant" .

Les deux utilisent la fork()fonction noyau , n'est-ce pas?

Quelle est alors la différence exacte d'appeler certaines fourches un "sous-shell" et d'autres fourches un "processus enfant".

Isaac
la source
Vous ne savez pas pourquoi vous liez POSIX Justification: définitions de base au lieu des définitions de base elles-mêmes: 3.93 Processus enfant "Un nouveau processus créé (par fork (), posix_spawn () ou ...) par un processus donné" ; 3.376 Sous-shell "Un environnement d'exécution shell, distinct de l'environnement d'exécution shell principal ou actuel" . Donc, pas des exemples du même genre de chose. Est-ce la distinction que vous recherchez?
fra-san
@ fra-san A child processpourrait avoir un environnement distinct que main: comme dans ( LANG=C eval 'echo "$LANG"' ). Ce processus enfant (entre parenthèses) est-il également un sous-shell (environnement différent)?
Isaac
L'expression in ( )est par définition un sous-shell avec son propre environnement d'exécution. Mon point est qu'il n'est pas nécessaire d'implémenter un sous-shell en tant que processus enfant (comme le souligne Stéphane dans sa réponse avec l'exemple ksh93). Il semble que le sous - shell et le processus enfant ne doivent pas être les deux résultats d'un fork()appel; ainsi, chercher la différence entre deux types de fourches ne me semble pas le bon point de vue. C'est pourquoi j'essayais de mieux comprendre votre question.
fra-san
Ah, maintenant je vois que la page tldp à laquelle vous avez lié indique en fait qu'un sous - shell est un processus enfant. À mon avis, cette définition est une simplification peut-être trompeuse.
fra-san

Réponses:

15

Dans la terminologie POSIX, un environnement de sous-shell est lié à la notion d' environnement d'exécution Shell .

Un environnement de sous-shell est un environnement d'exécution de shell distinct créé en tant que doublon de l'environnement parent. Cet environnement d'exécution comprend des choses comme les fichiers ouverts, umask, le répertoire de travail, les variables / fonctions / alias du shell ...

Les modifications apportées à cet environnement de sous-shell n'affectent pas l'environnement parent.

Traditionnellement dans le shell Bourne ou ksh88 sur lequel la spécification POSIX est basée, cela se faisait en forçant un processus enfant.

Les domaines dans lesquels POSIX requiert ou autorise l'exécution de la commande dans un environnement de sous-shell sont ceux où ksh88 a traditionnellement bifurqué un processus shell enfant.

Cela ne force cependant pas les implémentations à utiliser un processus enfant pour cela.

Un shell peut choisir d'implémenter cet environnement d'exécution séparé comme bon lui semble.

Par exemple, ksh93 le fait en enregistrant les attributs de l'environnement d'exécution parent et en les restaurant à la fin de l'environnement de sous-shell dans des contextes où le forking peut être évité (comme une optimisation car le forking est assez cher sur la plupart des systèmes).

Par exemple, dans:

cd /foo; pwd
(cd /bar; pwd)
pwd

POSIX a besoin cd /foode s'exécuter dans un environnement séparé et de produire quelque chose comme:

/foo
/bar
/foo

Il ne nécessite pas qu'il s'exécute dans un processus distinct. Par exemple, si stdout devient un tube cassé, pwdexécuté dans l'environnement de sous-shell pourrait très bien envoyer le SIGPIPE au seul et unique processus shell.

La plupart des shells, y compris bash, l'implémenteront en évaluant le code à l'intérieur (...)d'un processus enfant (pendant que le processus parent attend sa fin), mais ksh93 le fera à la place lors de l'exécution du code à l'intérieur (...), le tout dans le même processus:

  • rappelez-vous que c'est dans un environnement de sous-shell.
  • sur cd, enregistrez le répertoire de travail précédent (généralement sur un descripteur de fichier ouvert avec O_CLOEXEC), enregistrez la valeur des variables OLDPWD, PWD et tout ce qui cdpourrait modifier, puis effectuez lachdir("/bar")
  • au retour du sous-shell, le répertoire de travail actuel est restauré (avec un fchdir()sur ce fd enregistré), et tout ce que le sous-shell peut avoir modifié.

Il existe des contextes où un processus enfant ne peut pas être évité. ksh93 ne débouche pas:

  • var=$(subshell)
  • (subshell)

Mais en

  • { subshell; } &
  • { subshell; } | other command

C'est-à-dire les cas où les choses doivent s'exécuter dans des processus séparés pour pouvoir s'exécuter simultanément.

Les optimisations de ksh93 vont plus loin. Par exemple, en

var=$(pwd)

la plupart des shells bifurquent un processus, demandent à l'enfant d'exécuter la pwdcommande avec sa sortie standard redirigée vers un canal, pwdécrivent le répertoire de travail actuel dans ce canal et le processus parent lit le résultat à l'autre extrémité du canal, ksh93virtualise tout cela par aucun des deux nécessitant la fourche ni le tuyau. Une fourche et un tuyau ne seront utilisés que pour les commandes non intégrées.

Notez qu'il existe des contextes autres que des sous-shell pour lesquels les shells forkent un processus enfant. Par exemple, pour exécuter une commande qui est stockée dans un exécutable séparé (et qui n'est pas un script destiné au même interpréteur de shell), un shell devrait bifurquer un processus pour exécuter cette commande, sinon il ne le serait pas capable d'exécuter plus de commandes après le retour de cette commande.

Dans:

/bin/echo "$((n += 1))"

Ce n'est pas un sous-shell, la commande sera évaluée dans l'environnement d'exécution du shell actuel, la nvariable de l'environnement d'exécution du shell actuel sera incrémentée, mais le shell forkera un processus enfant pour exécuter cette /bin/echocommande avec l'expansion de l' $((n += 1))argument as .

De nombreux shells implémentent une optimisation en ce sens qu'ils ne forcent pas un processus enfant pour exécuter cette commande externe s'il s'agit de la dernière commande d'un script ou d'un sous-shell (pour les sous-shells qui sont implémentés en tant que processus enfants). ( bashmais seulement si cette commande est la seule commande du sous-shell).

Cela signifie que, avec ces shells, si la dernière commande dans le sous-shell est une commande externe, le sous-shell ne provoque pas la génération d'un processus supplémentaire. Si vous comparez:

a=1; /bin/echo "$a"; a=2; /bin/echo "$a"

avec

a=1; /bin/echo "$a"; (a=2; /bin/echo "$a")

il y aura le même nombre de processus créés, seulement dans le deuxième cas, le deuxième fork est fait plus tôt pour que le a=2soit exécuté dans un environnement de sous-shell.

Stéphane Chazelas
la source
1

Sous-coque

L'enveloppe enfant est également appelée sous-coque. La sous-coque peut être créée à partir du shell parent et d'un autre shell. La sous-coque peut être créée en utilisant:

1. Liste des processus

Une liste de processus est un regroupement de commandes entre parenthèses. Exemple:

( pwd ; (echo $BASH_SUBSHELL)) 

Cela affichera le répertoire de travail actuel et le nombre de shell générés. REMARQUE Invoquer un sous-shell coûte cher.

2. Coprocessus

Il génère un sous-shell en mode arrière-plan et exécute une commande dans ce sous-shell.

coproc sleep 10

Si vous tapez la jobscommande

[1]+  Running                 coproc COPROC sleep 10 &

vous verrez le sommeil comme processus d'arrière-plan exécuté en arrière-plan.

Forker un processus enfant

Un processus enfant en informatique est un processus créé par un autre processus. Chaque fois qu'une commande externe est exécutée, un processus enfant est créé. Cette action est appelée fourche.

$ps -f
UID        PID  PPID  C STIME TTY          TIME CMD  
umcr7     3647  3638  0 13:54 pts/0    00:00:00 bash
umcr7     3749  3647  0 13:59 pts/0    00:00:00 ps -f

Tout ps -fcomme la commande externe (c'est-à-dire qu'une commande externe, parfois appelée commande de système de fichiers, est un programme qui existe en dehors du shell bash.), Cela créera un processus enfant avec l'ID parent du shell bash à partir duquel il est exécuté.

Muhammad Umar Amanat
la source
0

Les deux (sous-shell et shell enfant) sont un processus distinct du shell parent (les deux sont des enfants du shell parent). Autrement dit, ils ont différents PID. Et les deux commencent par un fork (copie) du shell parent.

Un sous-shell est une copie du shell parent dans lequel les variables, fonctions, drapeaux et tout est disponible tel qu'il était dans le shell parent. Les modifications de ces valeurs n'affectent pas le parent.

Un shell enfant démarre en tant que fork mais il est réinitialisé aux valeurs par défaut du shell données par les configurations de démarrage. Il devient un processus utilisé pour exécuter du code (soit un shell, soit une commande).

Un sous-shell peut accéder aux valeurs variables:

$ x=123; ( echo "$x")
123

Un shell enfant n'a pas pu (variables non exportées):

$ x=234; sh -c 'echo "x=$x"'
x=
Isaac
la source