当程序收到终止信号时,您如何处理清理工作?
例如,我连接的一个应用程序希望任何第三方应用程序(我的应用程序)finish
在注销时发送命令。finish
当我的应用程序被破坏时,发送该命令的最佳说法是kill -9
什么?
编辑 1:无法捕获 kill -9。谢谢你们纠正我。
编辑 2:我想这种情况是当一个调用只是 kill 这与 ctrl-c 相同
任何语言的任何程序都不可能处理 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
任何程序中的 a 。
在极少数情况下,虚拟机可能会中止,即在没有完全关闭的情况下停止运行。当虚拟机在外部终止时会发生这种情况,例如在 Unix 上使用 SIGKILL 信号或在 Microsoft Windows 上使用 TerminateProcess 调用。
处理 a 的唯一真正选择kill -9
是让另一个观察程序监视您的主程序离开或使用包装脚本。您可以使用 shell 脚本来执行此操作,该脚本会轮询ps
在列表中查找您的程序的命令,并在它消失时采取相应的行动。
#!/usr/bin/env bash
java TestShutdownHook
wait
# notify your other app that you quit
echo "TestShutdownHook quit"
我希望 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
有一些方法可以在某些 JVM 中处理您自己的信号——例如,请参阅这篇关于 HotSpot JVM的文章。
通过使用 Sun 内部sun.misc.Signal.handle(Signal, SignalHandler)
方法调用,您还可以注册一个信号处理程序,但可能不适用于 JVM 之类的信号INT
或TERM
它们所使用的信号。
为了能够处理任何信号,您必须跳出 JVM 并进入操作系统领域。
waitpid
我通常做的(例如)检测异常终止是在 Perl 脚本中启动我的 JVM,但让脚本使用系统调用等待 JVM 。
然后,每当 JVM 退出时,我都会收到通知,以及它退出的原因,并且可以采取必要的措施。
您可以使用Runtime.getRuntime().addShutdownHook(...)
,但不能保证在任何情况下都会调用它。
有一种方法可以对 kill -9 做出反应:即拥有一个单独的进程来监视被杀死的进程,并在必要时对其进行清理。这可能会涉及到 IPC,并且会做很多工作,您仍然可以通过同时杀死两个进程来覆盖它。我认为在大多数情况下不值得麻烦。
谁用 -9 终止进程,理论上应该知道他/她在做什么,并且可能会使事情处于不一致的状态。
参考https://aws.amazon.com/blogs/containers/graceful-shutdowns-with-ecs/
import sun.misc.Signal;
import sun.misc.SignalHandler;
public class ExampleSignalHandler {
public static void main(String... args) throws InterruptedException {
final long start = System.nanoTime();
Signal.handle(new Signal("TERM"), new SignalHandler() {
public void handle(Signal sig) {
System.out.format("\nProgram execution took %f seconds\n", (System.nanoTime() - start) / 1e9f);
System.exit(0);
}
});
int counter = 0;
while(true) {
System.out.println(counter++);
Thread.sleep(500);
}
}
}