为什么Ctrl-C不起作用?


9

我只是打了Ctrlc两次壳,以试图终止需要很长时间才能完成的过程。

^C 被重复两次,但过程一直在进行。

为什么Ctrlc不像往常一样退出流程?


4
对于那些不想死的烦人程序,我的解决方案通常是使用CTRL + Z挂起它们然后kill -9 %将其杀死。信号9不能忽略,暂停信号也不能忽略。CTRL + Z键盘顺序在理论上可以被忽略-但实际上却不是。
David Sainty 2013年

@DavidSainty挂起信号可以忽略(或者至少不停止)。范例:perl -E '$SIG{TSTP} = sub { say "ha ha" }; sleep 1 while 1'。您可能正在考虑SIGSTOP,这是一个不同的信号。
derobert 2013年

啊,你是对的:)
David Sainty

Answers:


13

流程可以选择:

  • 忽略通常在按下时发送的SIGINT信号Ctrl-C(例如trap '' INT在shell中使用),或者有自己的处理程序来决定不终止(或无法及时终止)。
  • 告诉终端设备导致SIGINT发送到前台作业的字符是其他字符(例如stty int '^K'在shell中)
  • 告诉终端设备不要发送任何信号(例如stty -isig在外壳中)。

或者,它们可以是不间断的,例如在无法中断的系统调用中间。

在Linux(具有相对较新的内核)上,可以通过查看以下命令的输出来判断进程是否正在忽略和/或处理 SIGINT。

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

SIGINT为2。上面SigIgn的第二位为1,这意味着SIGINT被忽略。

您可以使用以下方法实现自动化:

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

要检查当前intr字符是什么或是否isig为给定的终端启用了该字符:

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

intr字符上方是^C(当不禁用按CTRL-C和输入信号时,通常由终端(仿真器)发送的字符。

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

intr字符是^Kisig用于被禁用/dev/pts/1)。

为了完整起见,进程通常可以通过其他两种方式停止接收SIGINT,尽管您通常不会看到这种方式。

此后Ctrl+C,将SIGINT信号发送到终端前台进程组中的所有进程。通常是将进程放入进程组(映射到shell 作业)并告诉终端设备哪个是前台的外壳。

现在,一个过程可以:

  • 离开其流程组。如果它移动到另一处理组(任何处理组,但一个是前景的一个),则其将不再接收在所述SIGINT Ctrl-C(还是其他键盘相关的信号如SIGTSTP,SIGQUIT)。但是,如果尝试从终端设备读取(也可能根据终端设备的设置进行写入),则它可能会被挂起(就像后台进程一样)。

    举个例子:

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

    不能用中断Ctrl-C。上面perl将尝试加入ID与父进程ID相同的进程组。通常,不能保证具有该ID的进程组。但是在这里,如果该perl命令在交互式shell的提示下单独运行,则ppid将是该shell的进程,并且该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
    

    将具有相同的效果。ps并且perl在前台进程组中启动,但是在大多数shell上,ps它将是该组的领导者(如ps上面的输出所示,两者的pgid psperl是的pid ps),因此perl可以启动自己的进程组。

  • 或者它可以更改前台进程组。基本上告诉tty设备将SIGINT发送给其他进程组Ctrl+C

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

    在那里,perl保留在同一个进程组中,但是却告诉终端设备,前台进程组是其ID与其父进程ID相同的组(请参阅上面的说明)。


1
不间断呼叫的一个很好的例子是访问没有响应的硬件设备。例如,如果您尝试使用hdparmsmartctl在没有响应的有缺陷的硬盘上将它们永久挂起,则无法使用CTRL + C杀死它们。您可以通过查看/的stat列ps auxtop/ 的S列来判断进程是否处于不间​​断睡眠状态htop- D表示不间断睡眠。不过,这不一定是一件坏事,这可能仅意味着该进程正在执行大量IO。
Martin von Wittich
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.