Qual è la differenza esatta tra una "subshell" e un "processo figlio"?


16

Secondo questo e questo , una subshell viene avviata usando la parentesi (…).

( echo "Hello" )

Secondo questo , questo e questo , un processo viene biforcato quando il comando viene avviato con a&

echo "Hello" &

La specifica Posix usa la parola subshellin questa pagina ma non la definisce e, inoltre, nella stessa pagina, non definisce "processo figlio" .

Entrambi stanno usando la fork()funzione kernel , giusto?

Qual è la differenza esatta quindi chiamare alcune forcelle una "sotto-shell" e alcune altre forcelle un "processo figlio".


Non è chiaro il motivo per cui si sta collegando POSIX Rationale: Definizioni di base anziché le stesse definizioni di base : 3.93 Processo figlio "Un nuovo processo creato (da fork (), posix_spawn (), o ...) da un determinato processo" ; 3.376 Sottostruttura "Un ambiente di esecuzione della shell, distinto dall'ambiente di esecuzione della shell principale o corrente" . Quindi, non casi dello stesso tipo di cose. È questa la distinzione che stai cercando?
fra-san,

@ fra-san A child processpotrebbe avere un ambiente distinto rispetto a main: Like in ( LANG=C eval 'echo "$LANG"' ). Quel processo figlio (tra parentesi) è anche una subshell (ambiente diverso)?
NotAnUnixNazi

L'espressione in ( )è per definizione una subshell con il suo ambiente di esecuzione. Il mio punto è che non è necessario implementare una subshell come processo figlio (come Stéphane sottolinea nella sua risposta con l'esempio ksh93). Sembra che subshell e il processo figlio non debbano essere entrambi i risultati di una fork()chiamata; quindi, cercare la differenza tra due tipi di forcella non mi sembra il giusto punto di vista. Ecco perché stavo cercando di capire meglio la tua domanda.
fra-san,

Ah, ora vedo che la pagina tldp a cui hai collegato in realtà dice che una subshell è un processo figlio. A mio avviso, tale definizione è una semplificazione forse fuorviante.
fra-san,

Risposte:


15

Nella terminologia POSIX, un ambiente subshell è collegato alla nozione di Shell Execution Environment .

Un ambiente subshell è un ambiente di esecuzione shell separato creato come duplicato dell'ambiente genitore. Quell'ambiente di esecuzione include cose come file aperti, umask, directory di lavoro, variabili / funzioni / alias della shell ...

Le modifiche all'ambiente subshell non influiscono sull'ambiente padre.

Tradizionalmente nella shell Bourne o ksh88 su cui si basa la specifica POSIX, ciò è stato fatto tramite il fork di un processo figlio.

Le aree in cui POSIX richiede o consente l'esecuzione di un comando in un ambiente subshell sono quelle in cui tradizionalmente ksh88 ha inoltrato un processo di shell figlio.

Non impone tuttavia implementazioni per utilizzare un processo figlio per quello.

Una shell può scegliere invece di implementare quell'ambiente di esecuzione separato nel modo che preferisce.

Ad esempio, ksh93 lo fa salvando gli attributi dell'ambiente di esecuzione principale e ripristinandoli al termine dell'ambiente subshell in contesti in cui è possibile evitare il fork (un'ottimizzazione in quanto il fork è piuttosto costoso sulla maggior parte dei sistemi).

Ad esempio, in:

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

POSIX richiede cd /foodi essere eseguito in un ambiente separato e che per produrre qualcosa di simile:

/foo
/bar
/foo

Non richiede che venga eseguito in un processo separato. Ad esempio, se stdout diventa una pipe interrotta, l' pwdesecuzione nell'ambiente subshell potrebbe far sì che SIGPIPE venga inviato all'unico e unico processo di shell.

La maggior parte delle shell comprese bashlo implementeranno valutando il codice all'interno (...)di un processo figlio (mentre il processo parent attende la sua conclusione), ma ksh93 eseguirà invece il codice all'interno (...), tutto nello stesso processo:

  • ricorda che è in un ambiente subshell.
  • su cd, salva la precedente directory di lavoro (in genere su un descrittore di file aperto con O_CLOEXEC), salva il valore delle variabili OLDPWD, PWD e tutto ciò che cdpotrebbe modificare e quindi fai ilchdir("/bar")
  • al ritorno dalla subshell, la directory di lavoro corrente viene ripristinata (con un fchdir()su quel fd salvato) e tutto il resto che la subshell potrebbe aver modificato.

Ci sono contesti in cui un processo figlio non può essere evitato. ksh93 non inserisce:

  • var=$(subshell)
  • (subshell)

Ma lo fa

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

Cioè, i casi in cui le cose devono funzionare in processi separati in modo che possano essere eseguite contemporaneamente.

Le ottimizzazioni di ksh93 vanno oltre. Ad esempio, mentre sei dentro

var=$(pwd)

la maggior parte delle shell esegue il fork di un processo, fa in modo che il figlio esegua il pwdcomando con lo stdout reindirizzato su una pipe, pwdscriva la directory di lavoro corrente su quella pipe e il processo genitore legge il risultato sull'altra estremità della pipe, ksh93virtualizza tutto ciò per nessuno dei due che richiede la forcella né il tubo. Una fork e una pipe verrebbero utilizzate solo per comandi non incorporati.

Si noti che esistono altri contesti che indicano quali shell eseguono il fork di un processo figlio. Ad esempio, per eseguire un comando memorizzato in un eseguibile separato (e che non è uno script destinato allo stesso interprete di shell), una shell dovrebbe eseguire il fork di un processo per eseguire quel comando in esso, altrimenti non lo sarebbe in grado di eseguire più comandi dopo il ritorno di quel comando.

In:

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

Non si tratta di una subshell, il comando verrà valutato nell'ambiente di esecuzione della shell corrente, la nvariabile dell'ambiente di esecuzione della shell corrente verrà incrementata, ma la shell eseguirà il fork di un processo figlio per eseguire quel /bin/echocomando al suo interno con l'espansione di $((n += 1))come argomento .

Molte shell implementano un'ottimizzazione in quanto non eseguono il fork di un processo figlio per eseguire quel comando esterno se è l'ultimo comando di uno script o di una shell secondaria (per quei subshell che sono implementati come processi figlio). ( bashtuttavia lo fa solo se quel comando è l'unico comando della subshell).

Ciò significa che, con quelle shell, se l'ultimo comando nella subshell è un comando esterno, la subshell non provoca la generazione di un processo aggiuntivo. Se si confronta:

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

con

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

ci sarà lo stesso numero di processi creati, solo nel secondo caso, il secondo fork viene eseguito in precedenza in modo che a=2venga eseguito in un ambiente subshell.


1

subshell

La shell figlio è anche chiamata subshell. Il subshell può essere creato dalla shell padre e da un'altra shell. Il subshell può essere creato usando:

1. Elenco dei processi

Un elenco di processi è il raggruppamento di comandi racchiuso tra parentesi. Esempio:

( pwd ; (echo $BASH_SUBSHELL)) 

Questo stamperà la directory di lavoro corrente e il numero di shell generate. NOTA Il richiamo della subshell è costoso.

2. Coprocesso

Genera una subshell in modalità background ed esegue un comando all'interno di quella subshell.

coproc sleep 10

Se si digita jobscomando

[1]+  Running                 coproc COPROC sleep 10 &

vedrai sleep come processo in background in esecuzione in background.

Forking a Child Process

Un processo figlio nell'informatica è un processo creato da un altro processo. Ogni volta che viene eseguito un comando esterno, viene creato un processo figlio. Questa azione è definita biforcazione.

$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

Come ps -fè il comando esterno (cioè un comando esterno, a volte chiamato un comando di filesystem, è un programma che esiste al di fuori della shell bash.) Questo creerà un processo figlio con ID genitore della shell bash da cui viene eseguito.


0

Entrambi (subshell e child shell) sono un processo separato rispetto alla shell parent (entrambi sono figli della shell parent). Cioè, hanno diversi PID. Ed entrambi iniziano con un fork (copia) della shell padre.

Una subshell è una copia della shell genitore in cui variabili, funzioni, flag e tutto è disponibile com'era nella shell genitore. Le modifiche di tali valori non influiscono sul genitore.

Una shell figlio inizia come fork ma viene reimpostata sui valori predefiniti della shell forniti dalle configurazioni di avvio. Diventa un processo utilizzato per eseguire del codice (una shell o un comando).

Una subshell potrebbe accedere ai valori delle variabili:

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

Una shell figlio non può (variabili non esportate):

$ x=234; sh -c 'echo "x=$x"'
x=
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.