El último bit del código, ;:
está ejecutando la función :(){ ... }
. Aquí es donde está ocurriendo la bifurcación.
El punto y coma termina el primer comando, y estamos comenzando otro, es decir, invocando la función :
. La definición de esta función incluye una llamada a sí mismo ( :
) y la salida de esta llamada se canaliza a una versión en segundo plano :
. Esto apuntala el proceso indefinidamente.
Cada vez que usted está llamando a la función :()
que está llamando la función C fork()
. Finalmente, esto agotará todas las ID de proceso (PID) en el sistema.
Ejemplo
Puede intercambiarlo |:&
con otra cosa para tener una idea de lo que está sucediendo.
Configurar un observador
En una ventana de terminal, haga esto:
$ watch "ps -eaf|grep \"[s]leep 61\""
Configurar la bomba de horquilla "fusible retrasado"
En otra ventana, ejecutaremos una versión ligeramente modificada de la bomba tenedor. Esta versión intentará estrangularse para que podamos estudiar lo que está haciendo. Nuestra versión dormirá durante 61 segundos antes de llamar a la función :()
.
También haremos un fondo de la llamada inicial, después de que se invoque. Ctrl+ z, luego escriba 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 : | :
Registrarse con el observador
Mientras tanto, en la otra ventana donde estamos corriendo 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 a 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
A 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 y gentil que no desgarrará cada bash
caparazón sería hacer lo siguiente:
Determine en qué pseudo terminal se ejecutará la bomba tenedor
$ tty
/dev/pts/4
Mata el pseudo terminal
$ pkill -t pts/4
Entonces, ¿qué está pasando?
Bueno, cada invocación de bash
y sleep
es una llamada a la función C fork()
desde el bash
shell desde donde se ejecutó el comando.