问题描述
如何以比 killall
更温和的方式结束所有同名进程?我不想中断进程,但要给它们时间正确退出。
也可以看看:
How do I kill processes in Ubuntu?
In System monitor, what is the difference between Kill Process and End Process?
Why does killall (sometimes?) needs to be applied twice?
最佳答案
1. `killall` 已经很好了(SIGTERM)
killall
默认发送 SIGTERM
。这已经是一个很好的方法,让应用程序有机会自行清理。 “去死吧,现在!”方法是发送 SIGKILL
信号,这需要将其指定为 killall
的选项。来自 The GNU C Library: Termination Signals :
\\n
Macro: int SIGTERM
\\n
[…] It is the normal way to politely ask a program to terminate.
\\n
2. 系统监视器的 “End process” 同样好(SIGTERM 也是如此)
您链接到有关 GNOME 系统监视器的问题。这也确实使用 SIGTERM
来执行 “End process” 操作(我意识到我与该问题的答案相矛盾)。你可以在源码中找到自己验证一下:
<item>
<attribute name="label" translatable="yes">_End</attribute>
<attribute name="action">win.send-signal-end</attribute>
<attribute name="accel"><Primary>e</attribute>
<attribute name="target" type="i">15</attribute>
</item>
这里的15是信号编号。信号 15 是 SIGTERM
。在提出和回答其他问题之前,系统监视器就已经使用了 SIGTERM
。
技术附录(针对评论)
查看 git blame
的 Github 表示形式,以下是 GNOME 系统监视器源代码中信号拼写方式的更改:
383007f2 24 Jul 2013 用 GAction 参数替换了用于发送信号的重复代码\n 0e766b2d 18 Jul 2013 将进程弹出菜单移植到 GAction\n 97674c79 3 Oct 2012 摆脱 ProcData 结构\n 38c5296c 3 Jul 2011 使源文件之间的缩进一致。\ n
这些都没有从 SIGQUIT
更改为 SIGTERM
,最后一个是在提出链接问题之前。
次佳答案
@hvd 的回答基本上是正确的。为了进一步支持这一点,当您关闭计算机时,init
进程将首先向进程发送 SIGTERM
,然后在延迟后发送 SIGKILL
(如果它们尚未退出)。进程无法处理/忽略 SIGKILL
。
不过,更详细地说,真正的答案是您无法确定程序是否会处理它。 SIGTERM
是最常用的信号,用于礼貌地要求程序退出,但所有信号处理都取决于程序对信号执行某些操作。
换句话说,根据其他答案,如果您有一个由 @Jos 或 @AlexGreg 编写的程序,那么他们可能会处理 SIGQUIT
但可能不会处理 SIGTERM
,因此发送 SIGTERM
的 “soft” 会比 SIGQUIT
少。
我已经编写了一些代码,因此您可以自己尝试一下。将以下内容保存为 signal-test.c
,然后使用
gcc -o signal-test signal-test.c
然后您可以运行它 ./signal-test
,并查看当您使用 killall -s <signal>
发送不同信号时会发生什么。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
int flag = 0;
void handle_signal(int s)
{
flag = s;
}
int main(int argc, char *argv[])
{
signal(SIGTERM, handle_signal);
signal(SIGQUIT, handle_signal);
while(flag == 0){
sleep(1);
}
printf("flag is %d\n", flag);
return flag;
}
就目前而言,代码可以优雅地处理 SIGTERM 和 SIGQUIT。您可以尝试注释掉 signal(SIG...
行(在行开头使用 //
)以删除信号处理程序,然后再次运行并发送信号。您应该能够看到这些不同的输出:
$ ./signal-test
Terminated
$ ./signal-test
Quit (core dumped)
$ ./signal-test
flag is 15
$ ./signal-test
flag is 3
取决于您是否处理信号。
您也可以尝试忽略这些信号:
signal(SIGTERM, SIG_IGN);
如果您这样做,那么发送 SIGTERM
将不会执行任何操作,您必须使用 SIGKILL
来结束该进程。
更多详细信息请参见 man 7 signal
。请注意,以这种方式使用 signal()
被认为是不可移植的 – 但它比其他方法要容易得多!
另一个小脚注 – 在 Solaris 上 killall
尝试终止所有进程。他们全部。如果您以 root 身份运行它,那么您可能会感到惊讶:)