Por que o Ctrl-C não funcionou?

9

Eu bati Ctrlcduas vezes no meu shell na tentativa de interromper um processo que está demorando muito tempo para terminar.

^C foi repetido duas vezes, mas o processo continuou.

Por que não Ctrlcencerrou o processo normalmente?

o espelho
fonte
4
Minha solução para programas irritantes que não querem morrer é geralmente suspendê-los com CTRL + Z e depois kill -9 %matá-lo. O sinal 9 não pode ser ignorado, nem o sinal de suspensão. A sequência do teclado CTRL + Z pode ser ignorada na teoria - mas não está na prática.
David Sainty 26/09
@DavidSainty O sinal de suspensão pode ser ignorado (ou, pelo menos, não parado). Exemplo: perl -E '$SIG{TSTP} = sub { say "ha ha" }; sleep 1 while 1'. Você provavelmente está pensando em SIGSTOP, que é um sinal diferente.
Derobert 27/09/13
Ah, certo, você está :) #
22713 David Sainty

Respostas:

13

Os processos podem optar por:

  • ignore o sinal SIGINT normalmente enviado ao pressionar Ctrl-C(como trap '' INTem um shell) ou tenha seu próprio manipulador para que decida não terminar (ou que não termine em tempo hábil).
  • diga ao dispositivo terminal que o caractere que faz com que um SIGINT seja enviado para o trabalho em primeiro plano é outra coisa (como stty int '^K'em um shell)
  • diga ao dispositivo do terminal para não enviar nenhum sinal (como stty -isigem uma concha).

Ou eles podem ser ininterruptos, como no meio de uma chamada do sistema que não pode ser interrompida.

No Linux (com um kernel relativamente recente), é possível saber se um processo está ignorando e / ou manipulando o SIGINT observando a saída de

$ kill -l INT
2
$ grep Sig "/proc/$pid/status"
SigQ:   0/63858
SigPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000000000002
SigCgt: 0000000000000000

SIGINT é 2. O segundo bit de SigIgn acima é 1, o que significa que SIGINT é ignorado.

Você pode automatizar isso com:

$ SIG=$(kill -l INT) perl -lane 'print $1 if $F[0] =~ /^Sig(...):/ && 
    $F[1] & (1<<($ENV{SIG}-1))' < "/proc/$pid/status"
Ign

Para verificar qual é o intrcaractere atual ou se isigestá ativado para um determinado terminal:

$ stty -a < /dev/pts/0
[...] intr = ^C [...] isig

(acima do intrcaractere é ^C(o caractere normalmente enviado pelo seu terminal (emulador)) ao pressionar CTRL-Ce os sinais de entrada não estão desativados.

$ stty -a < /dev/pts/1
[...] intr = ^K [...] -isig

(o intrcaractere está ^Ke isigestá desativado para /dev/pts/1).

Para completar, existem duas outras maneiras pelas quais um processo pode fazer algo para parar de receber SIGINTs, embora isso não seja algo que você normalmente veria.

Após Ctrl+C, o sinal SIGINT é enviado a todos os processos no grupo de processos em primeiro plano do terminal . Geralmente é o shell que coloca processos em grupos de processos (mapeados para trabalhos de shell ) e informa ao dispositivo terminal qual é o primeiro plano .

Agora, um processo poderia:

  • Deixe seu grupo de processos. Se ele for movido para outro grupo de processos (qualquer grupo de processos, exceto o primeiro plano ), ele não receberá mais o SIGINT Ctrl-C(nem os outros sinais relacionados ao teclado como SIGTSTP, SIGQUIT). No entanto, poderia ser suspenso se tentasse ler (possivelmente escrever também, dependendo das configurações do dispositivo terminal) do dispositivo terminal (como fazem os processos em segundo plano).

    Como um exemplo:

    perl -MPOSIX -e 'setpgid(0,getppid) or die "$!"; sleep 10'

    não pôde ser interrompido com Ctrl-C. Acima perl, tentará ingressar no grupo de processos cujo ID é igual ao seu processo pai. Em geral, não há garantia de que exista um grupo de processos com esse ID. Mas aqui, no caso desse perlcomando ser executado sozinho no prompt de um shell interativo, o ppid será o processo do shell e o shell normalmente será iniciado em seu próprio grupo de processos.

    Se o comando ainda não for um líder de grupo de processos (o líder desse grupo de processos em primeiro plano), iniciar um novo grupo de processos terá o mesmo efeito.

    Por exemplo, dependendo do shell,

    $ ps -j >&2 | perl -MPOSIX -e 'setpgid(0,0) or die "$!"; sleep 10'
      PID  PGID   SID TTY          TIME CMD
    21435 21435 21435 pts/12   00:00:00 zsh
    21441 21441 21435 pts/12   00:00:00 ps
    21442 21441 21435 pts/12   00:00:00 perl

    teria o mesmo efeito. pse perlsão iniciados no grupo de processos em primeiro plano, mas na maioria dos shells, psseria o líder desse grupo (como visto na pssaída acima, onde o pgid de ambos pse perlé o pid de ps), perlpode iniciar seu próprio grupo de processos.

  • Ou pode alterar o grupo de processos em primeiro plano. Diga basicamente ao dispositivo tty para enviar o SIGINT para algum outro grupo de processosCtrl+C

    perl -MPOSIX -e 'tcsetpgrp (0, getppid) ou morre $ !; dormir 5 '

    Lá, perlpermanece no mesmo grupo de processos, mas informa ao dispositivo de terminal que o grupo de processos em primeiro plano é aquele cujo ID é igual ao seu ID de processo pai (consulte a observação acima sobre isso).

Stéphane Chazelas
fonte
1
Um bom exemplo de uma chamada ininterrupta é acessar um dispositivo de hardware que não responde. Por exemplo, se você tentar usar hdparmou smartctlem um disco rígido com defeito que não responde, eles travarão para sempre e você não poderá matá-los com CTRL + C. Você pode saber se um processo está em suspensão ininterrupta olhando para a coluna stat ps auxou para a coluna S de top/ htop- Dsignifica suspensão ininterrupta. Porém, isso não é necessariamente uma coisa ruim, apenas pode significar que o processo está fazendo muito IO.
Martin von Wittich 26/09