O último bit do código, ;:
está executando a função :(){ ... }
. É aqui que o garfo está ocorrendo.
O ponto-e-vírgula encerra o primeiro comando e estamos começando outro, ou seja, invocando a função :
. A definição dessa função inclui uma chamada para si mesmo ( :
) e a saída dessa chamada é canalizada para uma versão em background :
. Isso apóia o processo indefinidamente.
Toda vez que você chama a função :()
, está chamando a função C fork()
. Eventualmente, isso esgotará todos os IDs de processo (PIDs) no sistema.
Exemplo
Você pode trocar o |:&
por outra coisa para ter uma ideia do que está acontecendo.
Configurar um observador
Em uma janela de terminal, faça isso:
$ watch "ps -eaf|grep \"[s]leep 61\""
Configure a bomba do garfo "fusível atrasada"
Em outra janela, executaremos uma versão ligeiramente modificada da bomba bifurcada. Esta versão tentará se estrangular para que possamos estudar o que está fazendo. Nossa versão durará 61 segundos antes de chamar a função :()
.
Também abordaremos a chamada inicial também após a chamada. Ctrl + z , depois digite bg
.
$ :(){ sleep 61; : | : & };:
# control + z
[1]+ Stopped sleep 61
[2] 5845
$ bg
[1]+ sleep 61 &
Agora, se executarmos o comando jobs
na janela inicial, veremos isto:
$ jobs
[1]- Running sleep 61 &
[2]+ Running : | : &
Após alguns minutos:
$ jobs
[1]- Done sleep 61
[2]+ Done : | :
Verifique com o observador
Enquanto isso, na outra janela em que estamos executando 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
Hierarquia de processo
E um ps -auxf
mostra essa hierarquia de processos:
$ 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
...
...
Tempo de limpeza
Um killall bash
parará as coisas antes que elas saiam do controle. Fazendo sua limpeza desta forma pode ser um pouco pesado, uma maneira gentil mais gentil que não pode potencialmente rasgar todos os bash
shell para baixo, seria fazer o seguinte:
-
Determine o pseudo-terminal em que a garfo bomba será executada
$ tty
/dev/pts/4
-
Mate o pseudo-terminal
$ pkill -t pts/4
Então, o que está acontecendo?
Bem, cada chamada de bash
e sleep
é uma chamada para a função C fork()
do shell bash
de onde o comando foi executado.