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

时间:2010-03-29 22:06:23

标签: java sigkill

当程序收到终止信号时,如何处理清理?

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

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

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

5 个答案:

答案 0 :(得分:117)

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

处理其他而不是kill -9的任何方法的方法是注册shutdown挂钩。如果您可以使用( SIGTERM kill -15,则关闭钩子将起作用。 ( SIGINT kill -2 DOES 导致程序正常退出并运行关闭挂钩。

  

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

     

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

     
      
  • 当最后一个非守护程序线程退出或调用退出(等效,System.exit)方法时,程序正常退出,或者
  •   
  • 虚拟机将终止以响应用户中断,例如键入^ C或系统范围的事件,例如用户注销或系统关闭。
  •   

我在OSX 10.6.3和kill -9上尝试了以下测试程序,正如预期的那样 NOT 运行关闭挂钩。在kill -15 DOES 每次都会运行关闭挂钩。

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的TerminateProcess调用   视窗。

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

#!/usr/bin/env bash

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

答案 1 :(得分:13)

方式在某些JVM中处理自己的信号 - 例如,请参阅this article about the HotSpot JVM

通过使用Sun内部sun.misc.Signal.handle(Signal, SignalHandler)方法调用,您还可以注册信号处理程序,但可能不适用于JVM使用的INTTERM等信号。

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

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

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

答案 2 :(得分:8)

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

这样,信号将被转发给他们,允许standard ways中正常的线程取消和资源确定。

但事实并非如此(至少在我的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并执行适当的清理。这些线程被编码为已知如何响应中断请求。高cohesion设计。

所以,在这些情况下,我添加了一个关闭钩子,这就是我认为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

答案 3 :(得分:6)

您可以使用Runtime.getRuntime().addShutdownHook(...),但不能保证在任何情况下都会将称为

答案 4 :(得分:0)

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

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