如何在Java中正常处理SIGKILL信号


113

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

例如,我连接到一个应用程序,该应用程序希望任何第三方应用程序(我的应用程序)finish在注销时发送命令。finish当我的应用被a破坏时,发送该命令的最好的说法是kill -9什么?

编辑1:不能杀死-9。谢谢你们纠正我。

编辑2:我想这种情况将是当那只叫杀死与ctrl-c相同


44
kill -9对我来说意味:“骨干,肮脏的过程,与你同行!”,这个过程将不再存在。立即。
ZoogieZork 2010年

11
在大多数* nixes据我所知,杀死-9无法拦截和优雅的,不管用什么语言它是写在任何程序进行处理。
总统波尔克

2
@Begui:除了其他人评论和回答的内容外,如果您的Un x操作系统没有立即终止*并保留终止-9'ed的程序使用的所有资源,那么……该操作系统已损坏。
语法T3rr0r,2010年

1
关于kill -9命令,联机帮助页更准确地说:“ 9 KILL(不可捕获,不可忽略的kill)”。SIGKILL由操作系统而不是应用程序处理的信号。
user1338062 2012年

4
Just kill与Ctrl-C不同,因为kill未指定发送哪个信号将发送SIGTERM,而Ctrl-C发送SIGINT。
alesguzik

Answers:


136

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

除了注册关闭钩子以外,处理此问题的其他方法。如果可以使用(SIGTERM),则关闭挂钩将起作用。(SIGINT确实会使程序正常退出并运行关闭钩子。kill -9kill -15kill -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"

12

在某些JVM中有一些处理您自己的信号的方法-例如,请参阅有关HotSpot JVM的本文

通过使用Sun内部sun.misc.Signal.handle(Signal, SignalHandler)方法调用,你也能注册一个信号处理程序,但可能不会像信号,INTTERM因为它们是由JVM使用。

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

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

然后,每当JVM退出时都会通知我,为什么退出,并可以采取必要的措施。


3
请注意,您可以捕捉INTTERMsun.misc.Signal,但你无法处理QUIT,因为JVM储备成为调试,也不是KILL因为操作系统将立即终止JVM。尝试处理其中任何一个都会引发错误IllegalArgumentException
dimo414

12

我希望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



1

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.