¿Cuál es la diferencia exacta entre un "subshell" y un "proceso hijo"?

16

Según esto y esto , se inicia una subshell utilizando paréntesis (…).

( echo "Hello" )

De acuerdo con esto , esto y esto , un proceso se bifurca cuando el comando se inicia con un&

echo "Hello" &

La especificación Posix usa la palabra subshellen esta página pero no la define y, también, en la misma página, no define "proceso hijo" .

Ambos están utilizando la fork()función del núcleo , ¿correcto?

¿Cuál es la diferencia exacta entonces llamar a algunos tenedores un "sub-shell" y otros tenedores un "proceso hijo"?

NotAnUnixNazi
fuente
No está claro por qué está vinculando POSIX Justificación: Definiciones básicas en lugar de las Definiciones básicas mismas: 3.93 Proceso secundario "Un proceso nuevo creado (por fork (), posix_spawn () o ...) por un proceso dado" ; 3.376 Subshell "Un entorno de ejecución de shell, distinguido del entorno de ejecución de shell principal o actual" . Entonces, no hay instancias del mismo tipo de cosas. ¿Es esta la distinción que estás buscando?
fra-san
@ fra-san A child processpodría tener un entorno distinto que main: Me gusta en ( LANG=C eval 'echo "$LANG"' ). ¿Es ese proceso hijo (dentro de paréntesis) también una subshell (entorno diferente)?
NotAnUnixNazi
La expresión en ( )es, por definición, una subshell con su propio entorno de ejecución. Mi punto es que no es necesario implementar una subshell como un proceso secundario (como Stéphane señala en su respuesta con el ejemplo ksh93). Parece que subnivel y proceso hijo no tienen que ser los dos resultados de una fork()llamada; Por lo tanto, buscar la diferencia entre dos tipos de tenedor no me parece el punto de vista correcto. Es por eso que estaba tratando de entender mejor tu pregunta.
fra-san
Ah, ahora veo que la página tldp a la que enlazaste realmente dice que una subshell es un proceso hijo. En mi opinión, esa definición es una simplificación posiblemente engañosa.
fra-san

Respuestas:

15

En la terminología POSIX, un entorno de subshell está vinculado a la noción de Entorno de ejecución de Shell .

Un entorno de subshell es un entorno de ejecución de shell separado creado como un duplicado del entorno principal. Ese entorno de ejecución incluye cosas como archivos abiertos, umask, directorio de trabajo, variables de shell / funciones / alias ...

Los cambios en ese entorno de subshell no afectan al entorno principal.

Tradicionalmente en el shell Bourne o ksh88 en el que se basa la especificación POSIX, eso se hizo bifurcando un proceso hijo.

Las áreas donde POSIX requiere o permite que el comando se ejecute en un entorno de subshell son aquellas donde tradicionalmente ksh88 bifurcó un proceso de shell hijo.

Sin embargo, no obliga a las implementaciones a usar un proceso hijo para eso.

Un shell puede elegir implementar ese entorno de ejecución separado de la forma que desee.

Por ejemplo, ksh93 lo hace guardando los atributos del entorno de ejecución principal y restaurándolos al terminar el entorno de subshell en contextos donde se puede evitar la bifurcación (ya que la optimización es bastante costosa en la mayoría de los sistemas).

Por ejemplo, en:

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

POSIX requiere cd /fooque se ejecute en un entorno separado y que genere algo como:

/foo
/bar
/foo

No requiere que se ejecute en un proceso separado. Por ejemplo, si stdout se convierte en una tubería rota, la pwdejecución en el entorno de subshell podría muy bien enviar el SIGPIPE al único proceso de shell.

La mayoría de los shells incluidos bashlo implementarán evaluando el código dentro (...)de un proceso secundario (mientras que el proceso principal espera su finalización), pero ksh93 lo hará al ejecutar el código dentro (...), todo en el mismo proceso:

  • recuerde que está en un entorno de subshell.
  • luego cd, guarde el directorio de trabajo anterior (generalmente en un descriptor de archivo abierto con O_CLOEXEC), guarde el valor de las variables OLDPWD, PWD y cualquier cosa que cdpueda modificar y luego realice elchdir("/bar")
  • Al regresar de la subshell, el directorio de trabajo actual se restaura (con un fchdir()fd guardado) y todo lo demás que la subshell puede haber modificado.

Hay contextos en los que no se puede evitar un proceso hijo. ksh93 no se bifurca:

  • var=$(subshell)
  • (subshell)

Pero lo hace en

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

Es decir, los casos en que las cosas tienen que ejecutarse en procesos separados para que puedan ejecutarse simultáneamente.

Las optimizaciones de ksh93 van más allá de eso. Por ejemplo, mientras que en

var=$(pwd)

la mayoría de los shells bifurcan un proceso, hacen que el niño ejecute el pwdcomando con su stdout redirigido a una tubería, pwdescribe el directorio de trabajo actual en esa tubería, y el proceso padre lee el resultado en el otro extremo de la tubería, ksh93virtualiza todo eso de ninguna manera requiriendo la horquilla ni la tubería. Una bifurcación y una tubería solo se usarían para comandos no integrados.

Tenga en cuenta que hay otros contextos que subcapas para los cuales las bifurcaciones bifurcan un proceso secundario. Por ejemplo, para ejecutar un comando que se almacena en un ejecutable separado (y que no es un script destinado al mismo intérprete de shell), un shell tendría que bifurcar un proceso para ejecutar ese comando en él, de lo contrario no sería capaz de ejecutar más comandos después de que ese comando regrese.

En:

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

Eso no es un subshell, el comando se evaluará en el entorno de ejecución de shell actual, la nvariable del entorno de ejecución de shell actual se incrementará, pero el shell bifurcará un proceso secundario para ejecutar ese /bin/echocomando en él con la expansión de $((n += 1))como argumento .

Muchos shells implementan una optimización en el sentido de que no bifurcan un proceso secundario para ejecutar ese comando externo si es el último comando de un script o un subshell (para aquellos subshell que se implementan como procesos secundarios). ( bashsin embargo, solo lo hace si ese comando es el único comando de la subshell).

Lo que eso significa es que, con esos shells, si el último comando en el subshell es un comando externo, el subshell no hace que se genere un proceso adicional. Si comparas:

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

con

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

habrá la misma cantidad de procesos creados, solo en el segundo caso, la segunda bifurcación se realiza antes para que a=2se ejecute en un entorno de subshell.

Stéphane Chazelas
fuente
1

Subshell

El shell secundario también se llama subshell. El subshell se puede crear desde el shell principal y desde otro shell. Subshell se puede crear usando:

1. Lista de procesos

Una lista de procesos es agrupación de comandos entre paréntesis. Ejemplo:

( pwd ; (echo $BASH_SUBSHELL)) 

Esto imprimirá el directorio de trabajo actual y el número de shell generado. NOTA Invocar subshell es costoso.

2. Coproceso

Genera una subshell en modo de fondo y ejecuta un comando dentro de esa subshell.

coproc sleep 10

Si escribe jobscomando

[1]+  Running                 coproc COPROC sleep 10 &

verá dormir como proceso en segundo plano ejecutándose en segundo plano.

Bifurcando un proceso hijo

Un proceso hijo en informática es un proceso creado por otro proceso. Cada vez que se ejecuta un comando externo, se crea un proceso secundario. Esta acción se denomina bifurcación.

$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

Al igual ps -fque un comando externo (es decir, un comando externo, a veces llamado comando del sistema de archivos, es un programa que existe fuera del bash shell). Esto creará un proceso secundario con la identificación principal del bash shell desde el cual se ejecuta.

Muhammad Umar Amanat
fuente
0

Ambos (subshell y shell hijo) son un proceso separado del shell padre (ambos son hijos del shell padre). Es decir, tienen diferentes PID. Y ambos comienzan con una bifurcación (copia) del shell principal.

Un subshell es una copia del shell principal en el que las variables, funciones, indicadores y todo está disponible como estaba en el shell principal. Las modificaciones de tales valores no afectan al padre.

Un shell hijo comienza como una bifurcación, pero se restablece a los valores predeterminados del shell dados por las configuraciones de inicio. Se convierte en un proceso utilizado para ejecutar algún código (ya sea un shell o un comando).

Una subshell podría acceder a valores variables:

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

Un shell hijo no pudo (variables no exportadas):

$ x=234; sh -c 'echo "x=$x"'
x=
NotAnUnixNazi
fuente