Mengapa Ctrl-C tidak berfungsi?

9

Saya hanya menekan Ctrlcdua kali pada shell saya dalam upaya untuk menghentikan proses yang membutuhkan waktu lama untuk menyelesaikannya.

^C digemakan dua kali, tetapi prosesnya terus berjalan.

Mengapa tidak Ctrlcberhenti dari proses seperti biasanya?

kaca
sumber
4
Solusi saya untuk program menjengkelkan yang tidak ingin mati biasanya untuk menunda mereka dengan CTRL + Z kemudian kill -9 %membunuhnya. Sinyal 9 tidak dapat diabaikan, juga tidak dapat menangguhkan sinyal. Urutan keyboard CTRL + Z dapat diabaikan dalam teori - tetapi tidak dalam praktiknya.
David Sainty
@ DavidSainty Sinyal penangguhan dapat diabaikan (atau, setidaknya tidak berhenti). Contoh: perl -E '$SIG{TSTP} = sub { say "ha ha" }; sleep 1 while 1'. Anda mungkin berpikir tentang SIGSTOP, yang merupakan sinyal berbeda.
derobert
Ah, benar kamu :)
David Sainty

Jawaban:

13

Proses dapat memilih untuk:

  • abaikan sinyal SIGINT yang biasanya dikirim setelah ditekan Ctrl-C(seperti dengan trap '' INTdi shell) atau memiliki handler sendiri untuk itu yang memutuskan untuk tidak mengakhiri (atau gagal untuk mengakhiri tepat waktu).
  • beri tahu perangkat terminal bahwa karakter yang menyebabkan SIGINT dikirim ke pekerjaan latar depan adalah sesuatu yang lain (seperti dengan stty int '^K'di shell)
  • beri tahu perangkat terminal untuk tidak mengirim sinyal apa pun (seperti dengan stty -isigdalam shell).

Atau, mereka bisa tidak terputus, seperti ketika di tengah panggilan sistem yang tidak dapat diganggu.

Di Linux (dengan kernel yang relatif baru), Anda dapat mengetahui apakah suatu proses mengabaikan dan / atau menangani SIGINT dengan melihat output dari

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

SIGINT adalah 2. Bit kedua SigIgn di atas adalah 1, yang berarti SIGINT diabaikan.

Anda dapat mengotomatiskannya dengan:

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

Untuk memeriksa apa intrkarakter saat ini atau jika isigdiaktifkan untuk terminal yang diberikan:

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

(di atas intrkarakter adalah ^C(karakter yang biasanya dikirim oleh terminal Anda (emulator) ketika menekan CTRL-Cdan sinyal input tidak dinonaktifkan.

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

( intrkarakter adalah ^Kdan isigdinonaktifkan untuk /dev/pts/1).

Untuk kelengkapan, ada dua cara lain proses dapat melakukan sesuatu untuk berhenti menerima SIGINT meskipun itu bukan sesuatu yang biasanya Anda lihat.

Setelah itu Ctrl+C, sinyal SIGINT dikirim ke semua proses dalam kelompok proses latar depan terminal . Biasanya shell yang menempatkan proses dalam kelompok proses (dipetakan ke pekerjaan shell ) dan memberi tahu perangkat terminal yang merupakan yang paling depan .

Sekarang sebuah proses bisa:

  • Tinggalkan grup prosesnya. Jika pindah ke grup proses lain (grup proses mana pun kecuali yang merupakan latar depan ), maka ia tidak akan lagi menerima SIGINT pada Ctrl-C(atau sinyal terkait keyboard lainnya seperti SIGTSTP, SIGQUIT). Namun bisa ditangguhkan jika mencoba membaca (mungkin menulis juga tergantung pada pengaturan perangkat terminal) dari perangkat terminal (seperti proses latar belakang lakukan).

    Sebagai contoh:

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

    tidak dapat diganggu dengan Ctrl-C. Di atas perlakan mencoba untuk bergabung dengan grup proses yang ID-nya sama dengan ID proses induknya. Secara umum, tidak ada jaminan bahwa ada grup proses dengan id itu. Tetapi di sini, dalam kasus perlperintah dijalankan sendiri pada prompt shell interaktif, ppid akan menjadi proses shell dan shell biasanya sudah dimulai dalam grup prosesnya sendiri.

    Jika perintah belum menjadi pemimpin grup proses (pemimpin grup proses latar depan), maka memulai grup proses baru akan memiliki efek yang sama.

    Misalnya, tergantung pada 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

    akan memiliki efek yang sama. psdan perldimulai pada grup proses latar depan, tetapi pada kebanyakan shell, psakan menjadi pemimpin grup tersebut (seperti yang terlihat pada psoutput di atas di mana pgid keduanya psdan perlmerupakan pid ps), sehingga perldapat memulai grup prosesnya sendiri.

  • Atau bisa mengubah grup proses latar depan. Pada dasarnya beritahu perangkat tty untuk mengirim SIGINT ke beberapa grup proses lainnyaCtrl+C

    perl -MPOSIX -e 'tcsetpgrp (0, getppid) atau mati $!; tidur 5 '

    Di sana, perltetap berada dalam grup proses yang sama tetapi sebaliknya memberi tahu perangkat terminal bahwa grup proses latar depan adalah ID yang sama dengan ID proses induknya (lihat catatan di atas tentang itu).

Stéphane Chazelas
sumber
1
Contoh bagus dari panggilan tanpa gangguan adalah mengakses perangkat perangkat keras yang tidak merespons. Misalnya, jika Anda mencoba menggunakan hdparmatau smartctlpada hard disk yang rusak yang tidak merespons, mereka akan menggantung selamanya, dan Anda tidak dapat membunuhnya dengan CTRL + C. Anda dapat mengetahui apakah suatu proses berada dalam tidur yang tidak pernah terputus dengan melihat kolom stat ps auxatau di kolom S dari top/ htop- Dcara yang tidak pernah terputus tidur. Ini tidak selalu merupakan hal yang buruk, itu bisa berarti bahwa prosesnya melakukan banyak IO.
Martin von Wittich