Por que um loop while para após ser suspenso?

10

Por que, usando o bash e suspendendo um loop while, o loop para após ser retomado? Pequeno exemplo abaixo.

$ while true; do echo .; sleep 1; done
.
.
^Z
[1]+  Stopped                 sleep 1
$ fg
sleep 1
$

Estou familiarizado com os sinais e acho que esse pode ser o comportamento natural do bash, mas gostaria de entender melhor por que ele se comporta dessa maneira específica.

bkzland
fonte
porque ele tem que lidar com uma interrupção e deve refletir com precisão isso no $?retorno, e assim truenão é true. provavelmente. eu acho que.
mikeserv
1
Espero que esse comentário não seja sinalizado, mas responderei sua pergunta com outra pergunta, no estilo koan do Unix: "Por que um estudante para de brigar no playground depois de ser suspenso?" A resposta é, porque ele não está mais no parquinho, onde tem a capacidade de começar brigas. Assim, o comportamento em questão foi simplesmente interrompido.
rubynorails
Você para o comando, o loop está quebrado. Em seguida, você retoma o comando de suspensão única 1, não o loop.
123
1
Relevante
123

Respostas:

10

Parece um bug em vários shells, funciona como esperado com o ksh93 e o zsh .

Fundo:

A maioria das conchas parece executar o loop while dentro do shell principal e

O Bourne Shell suspende o shell inteiro se você digitar ^ Z com um shell sem login

o bash suspende apenas o sleepe depois deixa o loop while em favor da impressão de um novo prompt de shell

o traço torna este comando não suspenso

Com o ksh93 , as coisas funcionam de maneira muito diferente:

O ksh93 faz o mesmo, enquanto o comando é iniciado pela primeira vez, mas como sleepocorre no ksh93, o ksh93 possui um manipulador que faz com que o loop while bifurque o shell principal e suspenda no momento em que você digita ^ Z.

Se você digitar mais tarde o ksh93fg , o filho bifurcado que ainda executa o loop será continuado.

Você vê a principal diferença ao comparar as mensagens de controle de tarefas do bash e do ksh93:

relatórios do bash :

[1]+ Stopped sleep 1

mas o ksh93 informa:

^Z[1] + Stopped while true; do echo .; sleep 1; done

O zsh se comporta de maneira semelhante ao ksh93

Nos dois shells, você tem um único processo (o shell principal), desde que não digite ^ Z, e dois processos de shell após digitar ^ Z.

esperto
fonte
dashrealmente não processa o sinal quando o loop termina? no [d]?ashcódigo fonte, existem todas essas macros para INTON e INTOFF dispersas por todo o corpo , e normalmente os sinais recebidos enquanto estão no estado INTOFF são realmente manipulados (ou ao redor) por INTON . de qualquer forma, estou curioso porque acho que você sabe melhor - é uma ótima resposta. obrigado.
mikeserv
Eu raramente uso o dash e o busquei e compilei recentemente para comparações de desempenho com o bash, ksh93 e meu Bourne Shell. Ao fazer esses testes, descobri que o traço parece ser mais rápido porque não inclui suporte para caracteres de vários bytes. Um único sleep 100pode ser suspenso e retomado dash, portanto, parece que ele dashconhece os problemas desse comando e desativa seletivamente o controle do trabalho.
schily
então, em seus testes, você conseguiu igualar dasho desempenho de outros shells ao eliminar o processamento multibyte? e sim, dashsuporta o controle do trabalho, mas o padrão diz que um shell interativo deve ignorar o TSTP, e executar um loop while no shell atual em um terminal interativo não é menos um shell interativo do que qualquer outro.
mikeserv
Eu não testei exatamente isso, mas pude ver que, embora o dash consuma mais tempo de CPU do sistema que ksh93 ou o Bourne Shell (principalmente porque emite mais chamadas fork ()), ele usa menos tempo de CPU do usuário e isso resulta em um total semelhante Tempo de CPU comparado à minha versão do Bourne Shell. Ao tentar reduzir o tempo de CPU do usuário no Bourne Shell, sei que a maior parte desse tempo é gasta em conversões de vários bytes.
schily
4

Eu escrevi um dos co-autores de Bash sobre o assunto, e aqui está sua resposta:

Não é realmente um bug, mas certamente é uma ressalva.

A idéia aqui é que você suspenda os processos, que são uma unidade de granularidade diferente dos comandos do shell. Quando um processo é suspenso, ele retorna ao shell (com um status diferente de zero, que tem consequências quando, por exemplo, você interrompe um processo que é o teste de loop), que tem uma opção: ele pode interromper ou continuar o loop , deixando o processo parado para trás. Bash escolhe - e sempre escolheu - interromper os loops quando um trabalho é interrompido. Continuar o ciclo raramente é o que você deseja.

Alguns outros shells fazem coisas como forçar uma cópia do shell quando um processo é suspenso devido ao SIGTSTP e interromper esse processo. Bash nunca fez isso - parece mais complicado do que o benefício justifica - mas se alguém quiser enviar esse código como um patch, eu daria uma olhada na incorporação das alterações.

Portanto, se alguém quiser enviar um patch, use os endereços de email encontrados nas páginas de manual.

fortrina
fonte