为什么 Ctrl-C 的行为与kill -2 不同

为什么 Ctrl-C 的行为与kill -2 不同

我有一个程序应该处理 SIGINT 并正常关闭。当我从终端运行这个程序而不需要后台运行时,我可以使用 Ctrl-C 将其关闭。检查日志表明一切都按预期进行。

当我打开一个单独的终端并调用时kill -2 [pid]kill -s INT [pid]它什么也不做。我在日志中看不到任何内容,程序继续照常运行,直到我在启动它的终端中按下 Ctrl-C。

Ctrl-C 发送信号的方式与 Kill 发送信号的方式有什么区别吗?

额外细节:

该程序是一个由 bash shell 脚本启动的 Java 应用程序,该脚本设置一些环境变量(即CLASSPATH),然后调用java [main class].按 Ctrl-Z 然后运行ps结果如下:

$ ps -f
UID        PID  PPID  C STIME TTY          TIME CMD
mdeck    10251 10250  0 11:48 pts/2    00:00:00 -bash
mdeck    13405 10251  0 18:12 pts/2    00:00:00 /bin/bash /usr/local/bin/myapp.sh
mdeck    13509 13405 25 18:12 pts/2    00:00:03 java com.company.MyApp
mdeck    13526 10251  0 18:13 pts/2    00:00:00 ps -f

Gilles 要求的 stty 输出如下:

$ stty -a </dev/pts/2
speed 38400 baud; rows 40; columns 203; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>; eol2 = <undef>; swtch = <undef>; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke

答案1

一种可能性是程序捕获了Ctrl+C序列。检查stty -a;的输出该intr设置指示哪个组合键(如果有)发送 SIGINT 信号,并且 指示isig信号键是否启用(-isig表示它们被禁用)。

如果程序由多个进程组成,则按Ctrl+C向进程中的所有进程发送 SIGINT进程组。通过将信号发送到进程组而不是发送到其中一个进程,可以获得相同的效果。要向进程组发送信号,首先确定其领导者:这是启动所有其他进程的第一个进程;如果您在后台运行进程组,那就是 PID 显示的jobs -l。进程组领导者的PID是PGID(进程组id);将信号发送至其负极。例如,如果 PGID 是 1234,则运行kill -INT -1234.

如果程序由包装脚本和主应用程序组成,则需要考虑两种情况。如果不需要进行清理,以便包装器脚本在主应用程序终止后立即终止,请使包装器脚本调用exec

#!/bin/sh
export SOMEVAR=somevalue
exec /path/to/application "$@"

这样应用程序就可以替换脚本并继承其 PID。有些 shell 会优化以执行另一个程序结束的脚本,但不是全部。当脚本需要执行一些清理(例如删除临时文件)时,此方法不起作用。

考虑让脚本检测信号并将信号传输到应用程序。以下是整个流程的草图:

/path/to/application "$@" &
app_pid=$!
trap -INT 'kill -INT $app_pid'
wait $!
rm /temp/file

答案2

根据 @Gilles 在我原来问题的评论中提出的问题,我意识到kill -2我发送的命令是针对启动应用程序的包装器脚本的 pid,而不是处理信号本身的进程。

使用我的问题中的 pid 并kill -s 13405没有正确向应用程序发出信号,但调用kill -s 13509可以按预期工作。

现在我的问题是如何更新我的包装器脚本以将 SIGINT 传播到其子进程,但我将为此创建一个单独的问题。

相关内容