¿Por qué no funcionó Ctrl-C?

9

Solo golpeé Ctrlcdos veces mi caparazón en un intento de detener un proceso que está tardando mucho en terminar.

^C se repitió dos veces, pero el proceso simplemente continuó.

¿Por qué no Ctrlcabandonó el proceso como lo hace normalmente?

el espejo
fuente
44
Mi solución para programas molestos que no quieren morir es suspenderlos con CTRL + Z y luego kill -9 %matarlos. La señal 9 no se puede ignorar, ni la señal de suspensión. La secuencia de teclado CTRL + Z puede ignorarse en teoría, pero no en la práctica.
David Sainty
@DavidSainty La señal de suspensión puede ignorarse (o, al menos, no detenerse). Ejemplo: perl -E '$SIG{TSTP} = sub { say "ha ha" }; sleep 1 while 1'. Probablemente estés pensando en SIGSTOP, que es una señal diferente.
derobert
Ah, tienes razón :)
David Sainty

Respuestas:

13

Los procesos pueden elegir:

  • ignore la señal SIGINT que generalmente se envía al presionar Ctrl-C(como con trap '' INTun intérprete de comandos) o tiene su propio controlador que decida no terminar (o no puede terminar de manera oportuna).
  • dígale al dispositivo terminal que el carácter que hace que se envíe un SIGINT al trabajo en primer plano es otra cosa (como stty int '^K'en un shell)
  • dígale al dispositivo terminal que no envíe ninguna señal (como con stty -isigun shell).

O bien, pueden ser ininterrumpidos, como cuando se encuentra en medio de una llamada del sistema que no se puede interrumpir.

En Linux (con un kernel relativamente reciente), puede saber si un proceso está ignorando y / o manejando SIGINT mirando la salida de

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

SIGINT es 2. El segundo bit de SigIgn anterior es 1, lo que significa que SIGINT se ignora.

Puede automatizar eso con:

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

Para verificar cuál es el intrcarácter actual o si isigestá habilitado para un terminal dado:

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

(arriba del intrcarácter está ^C(el carácter generalmente enviado por su terminal (emulador) al presionar CTRL-Cy las señales de entrada no están desactivadas.

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

(el intrcarácter está ^Ky isigestá deshabilitado para /dev/pts/1).

Para completar, hay otras dos formas en que un proceso puede hacer algo para dejar de recibir SIGINT, aunque eso no es algo que normalmente vería.

Luego Ctrl+C, la señal SIGINT se envía a todos los procesos en el grupo de procesos en primer plano del terminal . Por lo general, es el shell el que coloca los procesos en grupos de procesos (asignados a trabajos de shell ) y le dice al dispositivo terminal cuál es el primer plano .

Ahora un proceso podría:

  • Deje su grupo de procesos. Si se mueve a otro grupo de procesos (cualquier grupo de procesos excepto el que está en primer plano ), ya no recibirá el SIGINT Ctrl-C(ni las otras señales relacionadas con el teclado como SIGTSTP, SIGQUIT). Sin embargo, podría suspenderse si intentara leer (posiblemente escribir también dependiendo de la configuración del dispositivo terminal) desde el dispositivo terminal (como lo hacen los procesos en segundo plano).

    Como ejemplo:

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

    no podría ser interrumpible con Ctrl-C. Lo anterior perlintentará unirse al grupo de procesos cuya ID es la misma que su ID de proceso principal. En general, no hay garantía de que haya un grupo de procesos con esa identificación. Pero aquí, en el caso de que ese perlcomando se ejecute solo en el indicador de un shell interactivo, el ppid será el proceso del shell y el shell normalmente se habrá iniciado en su propio grupo de procesos.

    Si el comando aún no es un líder de grupo de proceso (el líder de ese grupo de proceso en primer plano), entonces comenzar un nuevo grupo de proceso tendría el mismo efecto.

    Por ejemplo, dependiendo del 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
    

    tendría el mismo efecto. psy perlse inician en el grupo de procesos en primer plano, pero en la mayoría de los shells, pssería el líder de ese grupo (como se ve en el psresultado anterior donde el pgid de ambos psy perlel pid de ps), por lo que perlpuede comenzar su propio grupo de procesos.

  • O podría cambiar el grupo de procesos en primer plano. Básicamente diga al dispositivo tty que envíe la SIGINT a algún otro grupo de procesosCtrl+C

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

    Allí, perlpermanece en el mismo grupo de procesos, pero en cambio le dice al dispositivo terminal que el grupo de procesos en primer plano es aquel cuya ID es la misma que su ID de proceso principal (consulte la nota anterior sobre eso).

Stéphane Chazelas
fuente
1
Un buen ejemplo de una llamada ininterrumpida es acceder a un dispositivo de hardware que no responde. Por ejemplo, si intenta usar hdparmo smartctlen un disco duro defectuoso que no responde, se bloqueará para siempre, y no puede matarlos con CTRL + C. Puede saber si un proceso está en suspensión ininterrumpida mirando la columna estadística ps auxo en la columna S de top/ htop- Dsignifica suspensión ininterrumpida. Sin embargo, esto no es necesariamente algo malo, solo podría significar que el proceso está haciendo mucho IO.
Martin von Wittich