O último pedaço do código, ;:
está a executar a função :(){ ... }
. É aqui que o garfo está ocorrendo.
O ponto-e-vírgula finaliza o primeiro comando e estamos iniciando outro, ou seja, invocando a função :
. A definição dessa função inclui uma chamada para si mesma ( :
) e a saída dessa chamada é canalizada para uma versão em segundo plano :
. Isso sustenta o processo indefinidamente.
Toda vez que você está chamando a função :()
que você está chamando a função C fork()
. Eventualmente, isso esgotará todos os IDs de processo (PIDs) no sistema.
Exemplo
Você pode trocar por |:&
outra coisa para ter uma idéia do que está acontecendo.
Configurar um inspetor
Em uma janela do terminal, faça o seguinte:
$ watch "ps -eaf|grep \"[s]leep 61\""
Configure a bomba do garfo com atraso de fusível
Em outra janela, rodaremos uma versão ligeiramente modificada da bomba de forquilha. Esta versão tentará se controlar para que possamos estudar o que está fazendo. Nossa versão irá dormir por 61 segundos antes de chamar a função :()
.
Também chamaremos a chamada inicial em segundo plano, depois que ela for chamada. Ctrl+ ze digite bg
.
$ :(){ sleep 61; : | : & };:
# control + z
[1]+ Stopped sleep 61
[2] 5845
$ bg
[1]+ sleep 61 &
Agora, se executarmos o jobs
comando na janela inicial, veremos o seguinte:
$ 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 do processo
E a 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
A killall bash
irá parar as coisas antes que elas fiquem fora de controle. Fazer a limpeza dessa maneira pode ser um pouco pesado, uma maneira mais gentil e gentil, que não bash
destrói todos os cascos, seria o seguinte:
Determine em que pseudo terminal a bomba dos garfos será executada
$ tty
/dev/pts/4
Mate o pseudo terminal
$ pkill -t pts/4
Então o que está acontecendo?
Bem, cada invocação de bash
e sleep
é uma chamada para a função C fork()
do bash
shell de onde o comando foi executado.