GNU/Linux >> Tutoriales Linux >  >> Linux

¿Dónde está el tenedor() en la bomba tenedor :(){ :|:&};:?

Como resultado de la canalización en x | y , se crea una subcapa para contener la canalización como parte del grupo de procesos en primer plano. Esto continúa creando subcapas (a través de fork() ) indefinidamente, creando así una bomba de horquilla.

$ for (( i=0; i<3; i++ )); do
>     echo "$BASHPID"
> done
16907
16907
16907
$ for (( i=0; i<3; i++ )); do
>     echo "$BASHPID" | cat
> done
17195
17197
17199

Sin embargo, la bifurcación en realidad no ocurre hasta que se ejecuta el código, que es la invocación final de : en tu código.

Para desmontar cómo funciona la bomba de horquilla:

  • :() - definir una nueva función llamada :
  • { :|: & } - una definición de función que canaliza recursivamente la función de llamada a otra instancia de la función de llamada en segundo plano
  • : - llamar a la función de bomba de horquilla

Esto tiende a no hacer un uso intensivo de la memoria, pero absorberá los PID y consumirá ciclos de CPU.


El último bit del código, ;: está ejecutando la función :(){ ... } . Aquí es donde se produce la bifurcación.

El punto y coma termina el primer comando y estamos iniciando otro, es decir, invocando la función : . La definición de esta función incluye una llamada a sí misma (: ) y la salida de esta llamada se canaliza a una versión en segundo plano : . Esto apuntala el proceso indefinidamente.

Cada vez que llamas a la función :() estás llamando a la función C fork() . Eventualmente, esto agotará todos los ID de proceso (PID) en el sistema.

Ejemplo

Puedes cambiar el |:& con algo más para que puedas tener una idea de lo que está pasando.

Configurar un observador

En una ventana de terminal, haga esto:

$ watch "ps -eaf|grep \"[s]leep 61\""

Configurar la bomba de horquilla "fusible retardado"

En otra ventana ejecutaremos una versión ligeramente modificada de la bomba de horquilla. Esta versión intentará acelerarse a sí misma para que podamos estudiar lo que está haciendo. Nuestra versión dormirá durante 61 segundos antes de llamar a la función :() .

También pondremos en segundo plano la llamada inicial, después de que se invoque. Ctrl + z , luego escribe bg .

$ :(){ sleep 61; : | : & };:

# control + z
[1]+  Stopped                 sleep 61
[2] 5845
$ bg
[1]+ sleep 61 &

Ahora si ejecutamos el jobs comando en la ventana inicial veremos esto:

$ jobs
[1]-  Running                 sleep 61 &
[2]+  Running                 : | : &

Después de un par de minutos:

$ jobs
[1]-  Done                    sleep 61
[2]+  Done                    : | :

Comuníquese con el observador

Mientras tanto, en la otra ventana donde estamos ejecutando watch :

Every 2.0s: ps -eaf|grep "[s]leep 61"                                                                                                                                             Sat Aug 31 12:48:14 2013

saml      6112  6108  0 12:47 pts/2    00:00:00 sleep 61
saml      6115  6110  0 12:47 pts/2    00:00:00 sleep 61
saml      6116  6111  0 12:47 pts/2    00:00:00 sleep 61
saml      6117  6109  0 12:47 pts/2    00:00:00 sleep 61
saml      6119  6114  0 12:47 pts/2    00:00:00 sleep 61
saml      6120  6113  0 12:47 pts/2    00:00:00 sleep 61
saml      6122  6118  0 12:47 pts/2    00:00:00 sleep 61
saml      6123  6121  0 12:47 pts/2    00:00:00 sleep 61

Jerarquía de procesos

Y un ps -auxf muestra esta jerarquía de procesos:

$ ps -auxf
saml      6245  0.0  0.0 115184  5316 pts/2    S    12:48   0:00 bash
saml      6247  0.0  0.0 100988   468 pts/2    S    12:48   0:00  \_ sleep 61
....
....
saml      6250  0.0  0.0 115184  5328 pts/2    S    12:48   0:00 bash
saml      6268  0.0  0.0 100988   468 pts/2    S    12:48   0:00  \_ sleep 61
saml      6251  0.0  0.0 115184  5320 pts/2    S    12:48   0:00 bash
saml      6272  0.0  0.0 100988   468 pts/2    S    12:48   0:00  \_ sleep 61
saml      6252  0.0  0.0 115184  5324 pts/2    S    12:48   0:00 bash
saml      6269  0.0  0.0 100988   464 pts/2    S    12:48   0:00  \_ sleep 61
...
...

Tiempo de limpieza

Un killall bash detendrá las cosas antes de que se salgan de control. Hacer la limpieza de esta manera puede ser un poco pesado, una forma más amable que no romperá potencialmente cada bash shell down, sería hacer lo siguiente:

  1. Determine en qué pseudo terminal se ejecutará la bomba de horquilla

    $ tty
    /dev/pts/4
    
  2. Mata al pseudo terminal

    $ pkill -t pts/4
    

Entonces, ¿qué está pasando?

Pues cada invocación de bash y sleep es una llamada a la función C fork() del bash shell desde donde se ejecutó el comando.


Linux
  1. ¿Por qué se bifurca el mecanismo de creación de procesos predeterminado?

  2. ¿Dónde está el ejecutable de PHP en Ubuntu?

  3. ¿Dónde está el comando de gemas en RHEL6?

  4. ¿Dónde está la vista de terminal de tty7?

  5. ¿Dónde se encuentra la base de datos updatedb?

¿Dónde está el archivo Inittab?

¿Dónde está el archivo php.ini en una PC con Linux/CentOS?

¿Dónde está documentada la ABI x86-64 System V?

¿Dónde se encuentra el sistema de archivos del subsistema Linux en Windows 10?

¿Adónde va la memoria restante de vm.overcommit_ratio?

Linux:dónde colocar el archivo de intercambio