Por que 'kill -s INT zsh PID' está se comportando de maneira diferente de 'Ctrl-C'?

4

Começando com:

% donothing () { echo $$; sleep 1000000 }
% donothing
47139

Se neste momento eu atingi Ctrl-C no mesmo terminal que está controlando o shell, então a função donothing de fato termina e recebo o prompt de comando de volta.

Mas se, em vez disso, a partir de uma sessão de shell diferente, eu corro

% kill -s INT 47139

... a função donothing não não é terminada. (Idem se eu passar -47139 como o último argumento para kill .)

Preciso usar um PID diferente daquele que estou usando para obter com kill -s INT o mesmo efeito que posso alcançar interativamente com Ctrl-C no terminal de controle?

(Estou interessado principalmente na resposta para zsh .)

EDIT: em resposta ao comentário de Gilles: não, eu não defini nenhuma armadilha (embora eu estaria interessado em aprender como confirmar que não há armadilhas: existe uma maneira de listar todas as armadilhas ativas?); e sim, posso reproduzir esse comportamento com zsh -f . Na verdade, posso reproduzi-lo da maneira mais radical que conheço para obter um "co_de%" absolutamente "nu" (por favor, deixe-me saber se existe uma maneira melhor):

% /usr/bin/env -i /usr/bin/zsh -f
% donothing () { echo $$; sleep 1000000 }
12345

... etc.

Desde o meu post original, eu repliquei o comportamento descrito acima em Darwin e no NetBSD, mas não no Linux (por exemplo, no Linux zsh mata a função kill -s INT <zsh PID> definida acima, sem matar o shell pai no processo).

Então talvez isso seja uma coisa BSD-ish. Mais especificamente, em todos os Unix com que tentei isso, o processo donothing é um filho do processo sleep , mas apenas no Linux é o sinal zsh que foi enviado para o processo SIGINT propagado para o zsh process.

Portanto, para poder eliminar a função sleep não interativamente sob Darwin e NetBSD, preciso de uma maneira de encontrar o PID do filho donothing -ing e enviar o sleep para ele ( que, reconhecidamente, soa bastante sem coração). Mais geralmente, para matar uma função de shell de forma não interativa sob Darwin e NetBSD, preciso recursivamente / em profundidade primeiro encontrar os descendentes do processo SIGINT e enviar-lhes o zsh para eles. ..

    
por kjo 04.04.2013 / 02:11

2 respostas

4

Control-c do terminal envia um sinal de interrupção para o grupo de processos associado ao terminal, que inclui o processo de suspensão. O envio de SIGINT via kill atinge apenas um processo e, nesse caso, acontece que o processo está errado porque $$ retorna o ID do processo do shell, não o processo de suspensão. Shells normalmente ignoram SIGINT.

    
por 04.04.2013 / 04:35
1

Basicamente, você pode não enviar sinais para uma função de shell específica. Funções são uma ferramenta de conveniência para compor blocos de lógica processual. Isso é semelhante, mas não é equivalente a scripts de shell.

Ao executar um script de shell, o interpretador de linha de comando / shell atualmente em execução, faz com que o interpretador / shell filho execute o script. Bifurcação implica a criação de um novo processo. Esse processo pode ser enviado sinais arbitrários.

Ao executar uma função de shell, não haverá fork (implícito), portanto, nenhum processo dedicado, portanto, nenhum destino para o qual um sinal poderia ser enviado.

Quando você atinge Ctrl-C no terminal, e o processo ocupado é o próprio shell, ele simplesmente pára o que está fazendo atualmente. No seu exemplo, nem é fácil saber se o sleep recebe o sinal (para que a função saia porque chega ao fim) ou se a função parou de ser executada.

$ domuch () { for (( i=0; i<1000000; i++)) {} }

também será interrompido por Ctrl-C , e definitivamente não há nada para enviar um sinal.

Se você quiser enviar um sinal, você deve envolver um sub-shell, colocando suas linhas em um script, ou usando backticks (não tem idéia de como mostrá-las aqui: - /) ou a alternativa $() notação:

$ doless () { $(sleep 1000) }

Você pode usar o parâmetro $! special que informa o PID do processo de segundo plano iniciado mais recentemente, mas somente no mesmo shell, então você pode ir:

$ doless &
$ kill -int $!
[1] 30769
[1]  + interrupt  dosome

Ainda não é a função que recebe o sinal, mas sleep faz.

Curiosamente, se você enviar o sono para o segundo plano de dentro da função, como em

dotoomuch()  () { $(sleep 1000) & }

você não poderá interrompê-lo por meio se Ctrl-C , embora esteja bloqueando o terminal. Não faço ideia se isso é um comportamento desejado (zsh 4.3.10).

    
por 16.07.2013 / 12:37