Onde está o fork () na bomba fork: () {: |: &};:?

24

Aviso: a execução desse comando na maioria dos shells resultará em um sistema corrompido que precisará de um desligamento forçado para corrigir

Eu entendo a função recursiva :(){ :|: & };: e o que ela faz. Mas eu não sei onde é a chamada do sistema fork. Não tenho certeza, mas suspeito no pipe | .

    
por mavillan 31.08.2013 / 17:58

2 respostas

29

Como resultado do pipe em x | y , uma subshell é criada para conter o pipeline como parte do grupo de processos em primeiro plano. Isso continua a criar sub-conjuntos (via fork() ) indefinidamente, criando assim uma bifurcação.

$ 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

O fork realmente não ocorre até que o código seja executado, no entanto, que é a invocação final de : em seu código.

Para desmontar o funcionamento da bomba do garfo:

  • :() - define uma nova função chamada :
  • { :|: & } - uma definição de função que canaliza recursivamente a função de chamada para outra instância da função de chamada em segundo plano
  • : - chame a função garfo bomba

Isto tende a não ser muito intensivo em memória, mas irá sugar PIDs e consumir ciclos de CPU.

    
por 31.08.2013 / 18:00
24

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:

  1. Determine o pseudo-terminal em que a garfo bomba será executada

    $ tty
    /dev/pts/4
    
  2. 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.

    
por 31.08.2013 / 18:04