Por que (…) não gera um novo processo filho quando executado em segundo plano?

4

Depois de executar o comando

{ sleep 5; } &  

saída de ps é (saída 1)

 PID TTY           TIME CMD
  972 ttys000    0:00.27 -bash
 2556 ttys000    0:00.00 -bash
 2557 ttys000    0:00.00 sleep 5   

enquanto para

( sleep 5 ) &    

de ps é (saída 2)

PID TTY           TIME CMD
 972 ttys000    0:00.28 -bash
2566 ttys000    0:00.00 sleep 5  

() causa um ambiente de subshell e eu espero "output 1" para este caso, pois isso resulta em um processo filho de bifurcação enquanto espero "output 2" para { sleep 5; } & quando ele é executado no shell atual. Isso pode parecer uma pergunta boba, mas eu realmente não entendo esse comportamento.
O que eu sinto falta aqui?

    
por haccks 13.03.2018 / 21:21

1 resposta

10

O Bash executará o último ou único comando de um subshell com exec se pode fazê-lo com segurança, como uma otimização. Você pode validar isso claramente com pstree :

$ pstree $$
bash---pstree
$ ( pstree $$ )
bash---pstree
$ ( pstree $$ ; echo)
bash---bash---pstree

$ 

É por isso que o ( sleep 5 ) é exibido apenas como o comando sleep e nenhum shell intermediário. No último exemplo acima, o echo força o shell a fazer algo depois que pstree for concluído, portanto, há um shell real por perto para usar; no caso do meio, o shell gerado apenas imediatamente exec s pstree , por isso parece o mesmo que o primeiro caso (que é padrão fork-exec ). Muitas conchas fazem isso.

Por outro lado, qualquer coisa executado em segundo plano requer a geração de um novo processo : fundamentalmente, é isso que "background" é. Comandos em chaves do executam ordinariamente no shell atual, mas isso não pode acontecer se eles forem executados em segundo plano. Para que o shell pai possa continuar fazendo o que está fazendo enquanto os comandos background são executados, um novo shell deve ser criado para executar todo o { ... } in. Assim

$ { pstree $$ ; }
bash---pstree
$ { pstree $$ ; } &
bash---bash---pstree

Neste caso, não há diferença se há outro comando à direita ou não. Bash documenta esse comportamento para & :

If a command is terminated by the control operator ‘&’, the shell executes the command asynchronously in a subshell. This is known as executing the command in the background. The shell does not wait for the command to finish, and the return status is 0 (true).

Você também pode ver isso acontecer colocando em segundo plano um comando interno, como read :

$ read &
$ pstree $$
[1] 32394
[1]+  Stopped                 read
$ pstree $$
bash-+-bash
     '-pstree

Meu shell agora tem dois filhos: um bash executando read e o comando pstree imprimindo essa saída.

É verdade que o shell poderia fazer uma otimização adicional e aplicá-lo ao background { ... } como às subshells com parênteses, mas isso não acontece. É possível que algumas outras shells façam, mas eu não encontrei nenhuma em testes rápidos. É um caso muito raro e formalmente diferente, e há alguns casos esquisitos.

    
por 13.03.2018 / 21:49