如何在Java中优雅地处理SIGKILL信号

问题描述 投票:101回答:5

当程序收到kill信号时,你如何处理清理?

例如,我连接的应用程序需要任何第三方应用程序(我的应用程序)在注销时发送finish命令。当我的应用程序被finish销毁时,发送kill -9命令的最佳发音是什么?

编辑1:无法捕获kill -9。谢谢你们纠正我。

编辑2:我想这种情况是当一个人调用kill时与ctrl-c相同

java sigkill
5个回答
119
投票

任何语言的任何程序都不可能处理SIGKILL。这样就可以终止程序,即使程序有错误或恶意。但SIGKILL并不是终止程序的唯一方法。另一种是使用SIGTERM。程序可以处理该信号。程序应该通过控制但快速的关闭来处理信号。当计算机关闭时,关闭过程的最后阶段会向每个剩余进程发送一个SIGTERM,为这些进程提供几秒宽限,然后向它们发送一个SIGKILL。

处理除kill -9以外的任何东西的方法是注册一个shutdown钩子。如果你可以使用(SIGTERM)kill -15,那么关闭钩子就可以了。 (SIGINT)kill -2导致程序正常退出并运行关闭挂钩。

注册新的虚拟机关闭挂钩。

Java虚拟机关闭以响应两种事件:

  • 当最后一个非守护程序线程退出或调用退出(等效,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调用。

处理kill -9的唯一真正选择是让另一个观察程序监视您的主程序消失或使用包装脚本。您可以使用shell脚本来执行此操作,该脚本轮询ps命令以在列表中查找您的程序,并在消失时相应地执行操作。

#!/usr/bin/env bash

java TestShutdownHook
wait
# notify your other app that you quit
echo "TestShutdownHook quit"

13
投票

有一些方法可以在某些JVM中处理您自己的信号 - 例如,请参阅this article about the HotSpot JVM

通过使用Sun内部sun.misc.Signal.handle(Signal, SignalHandler)方法调用,您还可以注册一个信号处理程序,但可能不会像Qazxswpoi或INT这样的信号,因为它们被JVM使用。

为了能够处理任何信号,您必须跳出JVM并进入操作系统区域。

我通常做的(例如)检测异常终止是在Perl脚本中启动我的JVM,但让脚本使用TERM系统调用等待JVM。

每当JVM退出时,我都会收到通知,以及退出的原因,并且可以采取必要的措施。


8
投票

我希望JVM优雅地中断(waitpid)应用程序创建的所有正在运行的线程,至少对于信号thread.interrupt()SIGINT (kill -2)

这样,信号将被转发给它们,允许在SIGTERM (kill -15)中正常地取消线程和资源完成。

但事实并非如此(至少在我的JVM实现中:standard ways

正如其他用户评论的那样,关闭钩子的使用似乎是强制性的。

那么,我该如何处理呢?

首先,我不关心它在所有程序中,只在那些我想跟踪用户取消和意外结束的程序中。例如,假设您的java程序是由其他人管理的进程。您可能希望区分它是否已正常终止(来自管理器进程的Java(TM) SE Runtime Environment (build 1.8.0_25-b17), Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode))或已发生关闭(以便在启动时自动重新启动作业)。

作为一个基础,我总是让我长时间运行的线程周期性地意识到中断的状态,如果他们中断就抛出一个SIGTERM。这使得能够以开发人员控制的方式执行最终确定(也产生与标准阻塞操作相同的结果)。然后,在线程堆栈的顶层,捕获InterruptedException并执行适当的清理。这些线程被编码为已知如何响应中断请求。高InterruptedException设计。

所以,在这些情况下,我添加了一个关闭钩子,这就是我认为JVM默认应该做的事情:中断我的应用程序仍在运行的所有非守护程序线程:

cohesion

在github完成测试应用程序: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"); } });


6
投票

您可以使用https://github.com/idelvall/kill-test,但不能保证在任何情况下都会调用它。


0
投票

有一种方法可以对kill -9作出反应:那就是有一个单独的进程监视被杀死的进程,并在必要时进行清理。这可能涉及IPC并且会有相当多的工作,你仍然可以通过同时杀死两个进程来覆盖它。我认为在大多数情况下这不值得。

用-9杀死进程的人理论上应该知道他/她在做什么,并且可能会使事情处于不一致状态。

© www.soinside.com 2019 - 2024. All rights reserved.