Pourquoi Ctrl-C n'a-t-il pas fonctionné?

9

Je viens de frapper Ctrlcdeux fois sur ma coquille pour tenter d'arrêter un processus qui prend beaucoup de temps à terminer.

^C a été répété deux fois, mais le processus a continué.

Pourquoi n'a-t-il pas Ctrlcquitté le processus comme il le fait normalement?

le miroir
la source
4
Ma solution pour les programmes ennuyeux qui ne veulent pas mourir est généralement de les suspendre avec CTRL + Z puis kill -9 %de le tuer. Le signal 9 ne peut pas être ignoré, pas plus que le signal de suspension. La séquence de clavier CTRL + Z peut être ignorée en théorie - mais n'est pas en pratique.
David Sainty
@DavidSainty Le signal de suspension peut être ignoré (ou, du moins, pas arrêté). Exemple: perl -E '$SIG{TSTP} = sub { say "ha ha" }; sleep 1 while 1'. Vous pensez probablement au SIGSTOP, qui est un signal différent.
derobert
Ah, vous avez raison :)
David Sainty

Réponses:

13

Les processus peuvent choisir de:

  • ignorer le signal SIGINT habituellement envoyé en appuyant sur Ctrl-C(comme avec trap '' INTdans un shell) ou avoir son propre gestionnaire pour celui qui décide de ne pas se terminer (ou ne se termine pas en temps opportun).
  • dire au terminal que le caractère qui envoie un SIGINT au travail de premier plan est autre chose (comme avec stty int '^K'dans un shell)
  • dire au terminal de ne pas envoyer de signal (comme avec stty -isigdans un shell).

Ou, ils peuvent être ininterrompus, comme lorsqu'ils sont au milieu d'un appel système qui ne peut pas être interrompu.

Sous Linux (avec un noyau relativement récent), vous pouvez savoir si un processus ignore et / ou gère SIGINT en regardant la sortie de

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

SIGINT est 2. Le deuxième bit de SigIgn ci-dessus est 1, ce qui signifie que SIGINT est ignoré.

Vous pouvez automatiser cela avec:

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

Pour vérifier quel est le intrcaractère actuel ou s'il isigest activé pour un terminal donné:

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

(au-dessus du intrcaractère est ^C(le caractère généralement envoyé par votre terminal (émulateur) lorsque vous appuyez sur CTRL-Cet les signaux d'entrée ne sont pas désactivés.

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

(le intrcaractère est ^Ket isigest désactivé pour /dev/pts/1).

Pour être complet, il existe deux autres façons dont un processus peut faire quelque chose pour arrêter de recevoir des SIGINT bien que ce ne soit pas quelque chose que vous voyez généralement.

Dès lors Ctrl+C, le signal SIGINT est envoyé à tous les processus du groupe de processus de premier plan du terminal . C'est généralement le shell qui place les processus dans des groupes de processus (mappés sur des jobs shell ) et indique au périphérique terminal qui est le premier plan .

Maintenant, un processus pourrait:

  • Quittez son groupe de processus. S'il se déplace vers un autre groupe de processus (n'importe quel groupe de processus mais celui qui est au premier plan ), il ne recevra plus le SIGINT Ctrl-C(ni les autres signaux liés au clavier comme SIGTSTP, SIGQUIT). Il pourrait cependant être suspendu s'il tentait de lire (éventuellement aussi en fonction des paramètres du terminal) à partir du terminal (comme le font les processus d'arrière-plan).

    Par exemple:

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

    ne pouvait pas être interruptible avec Ctrl-C. Ci perl- dessus va essayer de rejoindre le groupe de processus dont l'ID est le même que son ID de processus parent. En général, il n'y a aucune garantie qu'il existe un tel groupe de processus avec cet identifiant. Mais ici, dans le cas de cette perlcommande exécutée seule à l'invite d'un shell interactif, le ppid sera le processus du shell et le shell aura généralement été démarré dans son propre groupe de processus.

    Si la commande n'est pas déjà un leader de groupe de processus (le leader de ce groupe de processus de premier plan), le démarrage d'un nouveau groupe de processus aurait le même effet.

    Par exemple, selon le 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
    

    aurait le même effet. pset perlsont démarrés dans le groupe de processus de premier plan, mais sur la plupart des shells, psserait le leader de ce groupe (comme indiqué dans la pssortie ci-dessus où le pgid des deux pset perlest le pid de ps), donc perlpeut démarrer son propre groupe de processus.

  • Ou cela pourrait changer le groupe de processus de premier plan. En gros, dites au périphérique tty d'envoyer le SIGINT à un autre groupe de processusCtrl+C

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

    Là, perlreste dans le même groupe de processus mais indique au terminal que le groupe de processus de premier plan est celui dont l'ID est le même que son ID de processus parent (voir la remarque ci-dessus à ce sujet).

Stéphane Chazelas
la source
1
Un bon exemple d'appel ininterrompu est l'accès à un périphérique matériel qui ne répond pas. Par exemple, si vous essayez d'utiliser hdparmou smartctlsur un disque dur défectueux qui ne répond pas, ils se bloqueront pour toujours et vous ne pourrez pas les tuer avec CTRL + C. Vous pouvez savoir si un processus est en sommeil sans interruption en regardant la colonne stat de ps auxou la colonne S de top/ htop- Dsignifie sommeil sans interruption. Ce n'est pas nécessairement une mauvaise chose, cela pourrait simplement signifier que le processus fait beaucoup d'E / S.
Martin von Wittich