Como exatamente a típica “bomba de forquilha” se chama duas vezes?

15

Depois de passar pelas famosas perguntas de Fork Bomb no Askubuntu e em muitos outros sites do Stack Exchange, não entendo bem o que todo mundo está dizendo como se fosse óbvio.

Muitas respostas ( melhor exemplo ) dizem o seguinte:

" {:|: &}significa executar a função :e enviar sua saída para a :função novamente"

Bem, qual é exatamente a saída :? O que está sendo passado para o outro :?

E também:

Essencialmente, você está criando uma função que se chama duas vezes a cada chamada e não tem como se finalizar.

Como exatamente isso é executado duas vezes ? Na minha opinião, nada é passado para o segundo :até que o primeiro :termine sua execução, o que realmente nunca terminará.

Por Cexemplo,

foo()
{
    foo();
    foo(); // never executed 
}

o segundo foo()não é executado, apenas porque o primeiro foo()nunca termina.

Eu estou pensando que a mesma lógica se aplica a :(){ :|: & };:e

:(){ : & };:

faz o mesmo trabalho que

:(){ :|: & };:

Por favor me ajude a entender a lógica.

Severus Tux
fonte
9
O comando nos pipelines é executado em paralelo; no :|:segundo, :não é necessário esperar o primeiro ser concluído.
cuonglm

Respostas:

26

A tubulação não exige que a primeira instância termine antes da outra iniciar. Na verdade, tudo o que está realmente fazendo é redirecionar o stdout da primeira instância para o stdin da segunda, para que eles possam estar executando simultaneamente (como é necessário para o fork bomb funcionar).

Bem, qual é exatamente a saída :? o que está sendo passado para o outro :?

':' não está escrevendo nada para a outra instância ':', apenas redirecionando o stdout para o stdin da segunda instância. Se ele escreve alguma coisa durante a sua execução (que nunca, uma vez que não faz nada, mas se bifurcar em si) que iria para o stdin da outra instância.

Ajuda a imaginar stdin e stdout como uma pilha:

Tudo o que está escrito no stdin será empilhado pronto para quando o programa decidir ler a partir dele, enquanto o stdout funciona da mesma maneira: uma pilha na qual você pode escrever, para que outros programas possam ler quando quiserem.

Dessa forma, é fácil imaginar situações como um canal sem comunicação (duas pilhas vazias) ou gravações e leituras não sincronizadas.

Como exatamente isso é executado duas vezes? Na minha opinião, nada é passado para o segundo :até que o primeiro :termine sua execução, o que realmente nunca terminará.

Como estamos apenas redirecionando a entrada e a saída das instâncias, não é necessário que a primeira instância termine antes do início da segunda. Na verdade, geralmente é desejável que ambos sejam executados simultaneamente para que o segundo possa trabalhar com os dados analisados ​​pelo primeiro em tempo real. É o que acontece aqui, ambos serão chamados sem precisar esperar o primeiro final. Isso se aplica a todas as linhas de comandos de cadeias de tubulação .

Eu estou pensando que a mesma lógica se aplica a: () {: |: &} ;: e

:(){ : & };:

Faz o mesmo trabalho que

:(){ :|: & };:

O primeiro não funcionaria, porque, embora esteja executando recursivamente, a função está sendo chamada em segundo plano ( : &). O primeiro :não espera até que o "filho" :retorne antes de terminar, então no final você provavelmente terá apenas uma instância de :execução. Se você tivesse :(){ : };:, funcionaria, pois o primeiro :aguardaria o :retorno do "filho" , o que esperaria o retorno do seu "filho" :, e assim por diante.

Veja como seriam os comandos diferentes em termos de quantas instâncias seriam executadas:

:(){ : & };:

1 instância (chamadas :e saídas) -> 1 instância (chamadas :e saídas) -> 1 instância (chamadas :e saídas) -> 1 instância -> ...

:(){ :|: &};:

1 instância (chama 2 :'s e encerra) -> 2 instâncias (cada uma chama 2 :' e encerra) -> 4 instâncias (cada uma chama 2 :'e encerra) -> 8 instâncias -> ...

:(){ : };:

1 instância (chama :e aguarda o retorno) -> 2 instâncias (o filho chama outro :e espera o retorno) -> 3 instâncias (o filho chama outro :e espera o retorno) -> 4 instâncias -> ...

:(){ :|: };:

1 instância (chama 2 :e espera que eles retornem) -> 3 instâncias (filhos chama 2 :e cada um e espera que eles retornem) -> 7 instâncias (filhos chama 2 :e cada e espera que eles retornem) -> 15 instâncias -> ...

Como você pode ver, chamar a função em segundo plano (usando &) na verdade reduz a velocidade da bifurcação, porque o chamado termina antes que as funções chamadas retornem.

IanC
fonte
Questão. Será que :(){ : & && : &}; :também funcionam como uma bomba garfo? Você também aumentaria exponencialmente e, de fato, poderia colocar múltiplos : &para aumentar ainda mais rapidamente.
JFA
@JFA `─> $: () {: & &&: &}; : `fornece erro de sintaxe bash: syntax error near unexpected token &&' . Você pode fazer o seguinte:, :(){ $(: &) && $(: &)}; :Mas, ao contrário do pipeline, isso não será executado em paralelo. O que seria equivalente a :(){: & };:. Gostaria de verificar? tente isso time $( $(sleep 1 & ) && $(sleep 1 &) )e isso #time $(sleep 1 | sleep 1)
Severus Tux
Exatamente, :(){ $(: &) && $(: &)};é uma função que está emitindo uma operação AND lógica nos valores de retorno da primeira e da segunda instância. O problema é que, uma vez que um AND lógico é verdadeiro apenas se ambos os valores forem verdadeiros, por eficiência, apenas a primeira instância será executada. Se seu valor de retorno for 1, a segunda instância será executada. Se você quiser fazer a bomba fork ainda mais rápido eu acho que você poderia apenas tubos mais instâncias na cadeia, como:(){ :|:|: &}; :
CNI
Apenas como uma observação lateral, muitos scripts usam esse comportamento de AND na seguinte situação: Digamos que você queira executar prog2 se prog1 retornar true (que no bash é 0). Em vez de fazer uma declaração if ( if [ prog1 ]; then; prog2; fi), você poderia apenas escrever ( prog1 && prog2), e prog2 seria executado apenas se o valor de retorno de prog1 fosse verdadeiro.
21816 IanC
Ok, esses são todos grandes pontos. Eu costumo &&ligar apt-get update && apt-get upgradee, &no final da linha, executar em segundo plano, mas esse é um ótimo ponto para eles não funcionarem juntos. Um ponto-e-vírgula também não funciona com o e comercial.
JFA 17/10