当程序收到终止信号时,如何处理清理?
例如,我连接到一个应用程序,该应用程序希望任何第三方应用程序(我的应用程序)finish
在注销时发送命令。finish
当我的应用被a破坏时,发送该命令的最好的说法是kill -9
什么?
编辑1:不能杀死-9。谢谢你们纠正我。
编辑2:我想这种情况将是当那只叫杀死与ctrl-c相同
kill
与Ctrl-C不同,因为kill
未指定发送哪个信号将发送SIGTERM,而Ctrl-C发送SIGINT。
当程序收到终止信号时,如何处理清理?
例如,我连接到一个应用程序,该应用程序希望任何第三方应用程序(我的应用程序)finish
在注销时发送命令。finish
当我的应用被a破坏时,发送该命令的最好的说法是kill -9
什么?
编辑1:不能杀死-9。谢谢你们纠正我。
编辑2:我想这种情况将是当那只叫杀死与ctrl-c相同
kill
与Ctrl-C不同,因为kill
未指定发送哪个信号将发送SIGTERM,而Ctrl-C发送SIGINT。
Answers:
用任何语言的任何程序都不可能处理SIGKILL。这样一来,即使程序有错误或恶意,也总是可以终止程序。但是SIGKILL并不是终止程序的唯一方法。另一种是使用SIGTERM。程序可以处理该信号。程序应通过受控但快速的关机来处理信号。当计算机关闭时,关闭过程的最后阶段将向其余所有进程发送SIGTERM,为这些进程提供几秒钟的宽限期,然后向其发送SIGKILL。
除了注册关闭钩子以外,处理此问题的其他方法。如果可以使用(SIGTERM),则关闭挂钩将起作用。(SIGINT)确实会使程序正常退出并运行关闭钩子。kill -9
kill -15
kill -2
注册一个新的虚拟机关闭挂钩。
Java虚拟机将响应以下两种事件而关闭:
- 当最后一个非守护线程退出时,或者调用exit(等效于System.exit)方法时,程序将正常退出,或者
- 响应于用户中断(例如键入^ C)或系统范围的事件(例如用户注销或系统关闭)来终止虚拟机。
我试图在OSX 10.6.3以下测试程序和kill -9
它没有不运行关闭挂钩,符合市场预期。在kill -15
它确实每次都运行关闭钩子。
public class TestShutdownHook
{
public static void main(String[] args) throws InterruptedException
{
Runtime.getRuntime().addShutdownHook(new Thread()
{
@Override
public void run()
{
System.out.println("Shutdown hook ran!");
}
});
while (true)
{
Thread.sleep(1000);
}
}
}
没有任何方法可以kill -9
在任何程序中真正正常地处理。
在极少数情况下,虚拟机可能会中止,即在不干净关闭的情况下停止运行。当虚拟机在外部终止时会发生这种情况,例如在Unix上使用SIGKILL信号或在Microsoft Windows上使用TerminateProcess调用。
处理a的唯一实际选择kill -9
是让另一个监视程序监视您的主程序消失或使用包装程序脚本。您可以使用外壳程序脚本执行此操作,该脚本会轮询ps
命令以在列表中查找您的程序,并在程序消失时采取相应措施。
#!/usr/bin/env bash
java TestShutdownHook
wait
# notify your other app that you quit
echo "TestShutdownHook quit"
在某些JVM中有一些处理您自己的信号的方法-例如,请参阅有关HotSpot JVM的本文。
通过使用Sun内部sun.misc.Signal.handle(Signal, SignalHandler)
方法调用,你也能注册一个信号处理程序,但可能不会像信号,INT
或TERM
因为它们是由JVM使用。
为了能够处理任何信号,您必须跳出JVM并进入操作系统领域。
我通常要做的(例如)检测异常终止是在Perl脚本中启动JVM,但是让脚本使用waitpid
系统调用等待JVM 。
然后,每当JVM退出时都会通知我,为什么退出,并可以采取必要的措施。
INT
并TERM
用sun.misc.Signal
,但你无法处理QUIT
,因为JVM储备成为调试,也不是KILL
因为操作系统将立即终止JVM。尝试处理其中任何一个都会引发错误IllegalArgumentException
。
我希望JVM能够优雅地中断(thread.interrupt()
)应用程序创建的所有正在运行的线程,至少对于信号SIGINT (kill -2)
和而言SIGTERM (kill -15)
。
这样,信号将被转发给他们,从而允许以标准方式优雅地取消线程和完成资源。
但是事实并非如此(至少在我的JVM实现中:)Java(TM) SE Runtime Environment (build 1.8.0_25-b17), Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
。
正如其他用户所评论的那样,使用关闭挂钩似乎是强制性的。
那么,我该如何处理呢?
首先,我并不在所有程序中都关心它,只是在那些我想跟踪用户取消和意外结束的程序中。例如,假设您的Java程序是由其他人管理的进程。您可能要区分是已正常终止(SIGTERM
从管理器进程中终止)还是已关闭(以便在启动时自动重新启动作业)。
作为基础,我总是让长时间运行的线程定期了解中断状态,InterruptedException
如果中断则抛出异常。这使得开发人员可以通过开发人员控制的方式完成执行(也可以产生与标准阻止操作相同的结果)。然后,在线程堆栈的顶层InterruptedException
捕获并执行适当的清理。这些线程被编码为已知的如何响应中断请求。高凝聚力设计。
因此,在这些情况下,我添加了一个关闭钩子,它执行了我认为JVM默认情况下应该执行的操作:中断应用程序创建的所有仍在运行的非守护程序线程:
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.out.println("Interrupting threads");
Set<Thread> runningThreads = Thread.getAllStackTraces().keySet();
for (Thread th : runningThreads) {
if (th != Thread.currentThread()
&& !th.isDaemon()
&& th.getClass().getName().startsWith("org.brutusin")) {
System.out.println("Interrupting '" + th.getClass() + "' termination");
th.interrupt();
}
}
for (Thread th : runningThreads) {
try {
if (th != Thread.currentThread()
&& !th.isDaemon()
&& th.isInterrupted()) {
System.out.println("Waiting '" + th.getName() + "' termination");
th.join();
}
} catch (InterruptedException ex) {
System.out.println("Shutdown interrupted");
}
}
System.out.println("Shutdown finished");
}
});
完整的测试应用程序位于github:https://github.com/idelvall/kill-test
您可以使用Runtime.getRuntime().addShutdownHook(...)
,但不能保证在任何情况下都会调用它。
有一种应对kill -9的方法:即拥有一个单独的进程来监视被杀死的进程,并在必要时对其进行清理。这可能涉及IPC,并且会花费很多工作,您仍然可以通过同时杀死两个进程来覆盖它。我认为在大多数情况下这样做是不值得的。
从理论上讲,杀死-9的任何人都应该知道他/她在做什么,并且可能会使事情处于不一致状态。
kill -9
对我来说意味:“骨干,肮脏的过程,与你同行!”,这个过程将不再存在。立即。