为什么 Ctrl-C 不起作用?

为什么 Ctrl-C 不起作用?

我只是敲了两下Ctrlc我的外壳,试图停止一个需要很长时间才能完成的进程。

^C被重复了两次,但这个过程一直在继续。

为什么Ctrlc不像平常那​​样退出进程?

答案1

进程可以选择:

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

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

在 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 将进程放置在进程组中(映射到 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
    

    会有同样的效果。psperl在前台进程组中启动,但在大多数 shell 上,ps将是该组的领导者(如上面的输出所示,其中和ps的 pgid是 的 pid ),因此可以启动自己的进程组。psperlpsperl

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

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

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

相关内容