Perché Ctrl-C non ha funzionato?


9

Ho appena colpito Ctrlcdue volte sulla mia shell nel tentativo di interrompere un processo che sta impiegando molto tempo per terminare.

^C è stato ripetuto due volte, ma il processo è continuato.

Perché non ha Ctrlcabbandonato il processo come fa normalmente?


4
La mia soluzione per programmi fastidiosi che non vogliono morire è di solito sospenderli con CTRL + Z e poi kill -9 %ucciderli. Il segnale 9 non può essere ignorato, né può essere sospeso il segnale. La sequenza di tasti CTRL + Z può essere ignorata in teoria, ma non è in pratica.
David Sainty,

@DavidSainty Il segnale di sospensione può essere ignorato (o, almeno, non arrestato). Esempio: perl -E '$SIG{TSTP} = sub { say "ha ha" }; sleep 1 while 1'. Probabilmente stai pensando a SIGSTOP, che è un segnale diverso.
derobert,

Ah, hai ragione :)
David Sainty,

Risposte:


13

I processi possono scegliere di:

  • ignora il segnale SIGINT di solito inviato premendo Ctrl-C(come con trap '' INTin una shell) o ha il proprio gestore per esso che decide di non terminare (o non riesce a terminare in modo tempestivo).
  • dire al terminale che il carattere che causa l'invio di un SIGINT al lavoro in primo piano è qualcos'altro (come con stty int '^K'in una shell)
  • dire al dispositivo terminale di non inviare alcun segnale (come con stty -isigin una shell).

Oppure, possono essere ininterrotti, come quando si è nel mezzo di una chiamata di sistema che non può essere interrotta.

Su Linux (con un kernel relativamente recente), puoi capire se un processo sta ignorando e / o gestendo SIGINT guardando l'output di

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

SIGINT è 2. Il secondo bit di SigIgn sopra è 1, il che significa che SIGINT viene ignorato.

Puoi automatizzarlo con:

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

Per verificare quale sia il intrcarattere corrente o se isigè abilitato per un determinato terminale:

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

(sopra il intrcarattere è ^C(il carattere solitamente inviato dal terminale (emulatore) quando i CTRL-Csegnali di pressione e di input non sono disabilitati.

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

(il intrpersonaggio è ^Ked isigè disabilitato per /dev/pts/1).

Per completezza, ci sono altri due modi in cui un processo può fare qualcosa per interrompere la ricezione di SIGINT sebbene non sia qualcosa che normalmente si vedrebbe.

In seguito Ctrl+C, il segnale SIGINT viene inviato a tutti i processi nel gruppo di processi in primo piano del terminale . Di solito è la shell che colloca i processi in gruppi di processi (mappati su processi shell ) e indica al dispositivo terminale quale è il primo piano .

Ora un processo potrebbe:

  • Lascia il suo gruppo di processi. Se si sposta in un altro gruppo di processi (qualsiasi gruppo di processi tranne quello in primo piano ), non riceverà più SIGINT su Ctrl-C(né gli altri segnali relativi alla tastiera come SIGTSTP, SIGQUIT). Potrebbe tuttavia essere sospeso se provasse a leggere (possibilmente anche in base alle impostazioni del dispositivo terminale) dal dispositivo terminale (come fanno i processi in background).

    Come esempio:

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

    non può essere interrompibile con Ctrl-C. Sopra perltenterà di unirsi al gruppo di processi il cui ID è uguale al suo ID di processo principale. In generale, non esiste alcuna garanzia che esista un tale gruppo di processi con quell'ID. Ma qui, nel caso di quel perlcomando eseguito da solo al prompt di una shell interattiva, il ppid sarà il processo della shell e la shell sarà in genere avviata nel suo gruppo di processi.

    Se il comando non è già un leader del gruppo di processi (il leader di quel gruppo di processi in primo piano), l'avvio di un nuovo gruppo di processi avrebbe lo stesso effetto.

    Ad esempio, a seconda della 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

    avrebbe lo stesso effetto. pse perlvengono avviati nel gruppo di processi in primo piano, ma sulla maggior parte delle shell, pssarebbe il leader di quel gruppo (come si vede psnell'output sopra dove pgid di entrambi psed perlè il pid di ps), quindi perlpuò iniziare il proprio gruppo di processi.

  • Oppure potrebbe cambiare il gruppo di processi in primo piano. Fondamentalmente dire al dispositivo tty di inviare SIGINT ad un altro gruppo di processiCtrl+C

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

    Lì, perlrimane nello stesso gruppo di processo ma invece dice al dispositivo terminale che il gruppo di processo in primo piano è quello il cui ID è uguale al suo ID di processo principale (vedere la nota sopra a riguardo).


1
Un buon esempio di chiamata ininterrotta è l'accesso a un dispositivo hardware che non risponde. Ad esempio, se si tenta di utilizzare hdparmo smartctlsu un disco rigido difettoso che non risponde, si bloccheranno per sempre e non è possibile ucciderli con CTRL + C. Puoi sapere se un processo è in modalità di sospensione ininterrotta osservando la colonna stat di ps auxo la colonna S di top/ htop- Dsignifica sospensione ininterrotta. Questa non è necessariamente una cosa negativa, potrebbe solo significare che il processo sta facendo molto IO.
Martin von Wittich,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.