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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2541597/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 09:04:49  来源:igfitidea点击:

How to gracefully handle the SIGKILL signal in Java

javasigkill

提问by Begui

How do you handle clean up when the program receives a kill signal?

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

For instance, there is an application I connect to that wants any third party app (my app) to send a finishcommand when logging out. What is the best say to send that finishcommand when my app has been destroyed with a kill -9?

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

edit 1: kill -9 cannot be captured. Thank you guys for correcting me.

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

edit 2: I guess this case would be when the one calls just kill which is the same as ctrl-c

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

采纳答案by Begui

It is impossiblefor any program, in any language, to handle a SIGKILL. This is so it is always possible to terminate a program, even if the program is buggy or malicious. But SIGKILL is not the only means for terminating a program. The other is to use a SIGTERM. Programs canhandle that signal. The program shouldhandle the signal by doing a controlled, but rapid, shutdown. When a computer shuts down, the final stage of the shutdown process sends every remaining process a SIGTERM, gives those processes a few seconds grace, then sends them a SIGKILL.

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

The way to handle this for anything otherthan kill -9would be to register a shutdownhook. If you can use (SIGTERM) kill -15the shutdown hook will work. (SIGINT) kill -2DOEScause the program to gracefully exit and run the shutdown hooks.

处理这个任何东西的方式不是kill -9会注册一个关闭挂钩。如果您可以使用(SIGTERM),kill -15则关闭挂钩将起作用。( SIGINT)kill -2确实会导致程序正常退出并运行关闭挂钩。

Registers a new virtual-machine shutdown hook.

The Java virtual machine shuts down in response to two kinds of events:

  • The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or
  • The virtual machine is terminated in response to a user interrupt, such as typing ^C, or a system-wide event, such as user logoff or system shutdown.

注册一个新的虚拟机关闭钩子。

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

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

I tried the following test program on OSX 10.6.3 and on kill -9it did NOTrun the shutdown hook, as expected. On a kill -15it DOESrun the shutdown hook every time.

我试图在OSX 10.6.3以下测试程序和kill -9它没有运行关闭挂钩,符合市场预期。在kill -15DOES运行关机钩每次。

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);
        }
    }
}

There isn't any way to really gracefully handle a kill -9in any program.

没有任何方法可以真正优雅地处理kill -9任何程序中的 a 。

In rare circumstances the virtual machine may abort, that is, stop running without shutting down cleanly. This occurs when the virtual machine is terminated externally, for example with the SIGKILL signal on Unix or the TerminateProcess call on Microsoft Windows.

在极少数情况下,虚拟机可能会中止,即在没有彻底关闭的情况下停止运行。当虚拟机从外部终止时会发生这种情况,例如在 Unix 上使用 SIGKILL 信号或在 Microsoft Windows 上使用 TerminateProcess 调用。

The only real option to handle a kill -9is to have another watcher program watch for your main program to go away or use a wrapper script. You could do with this with a shell script that polled the pscommand looking for your program in the list and act accordingly when it disappeared.

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

#!/usr/bin/env bash

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

回答by lexicore

You can use Runtime.getRuntime().addShutdownHook(...), but you cannot be guaranteed that it will be called in any case.

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

回答by Asgeir S. Nilsen

There areways to handle your own signals in certain JVMs -- see this article about the HotSpot JVMfor example.

一些方法可以在某些 JVM 中处理您自己的信号——例如,请参阅这篇关于 HotSpot JVM 的文章

By using the Sun internal sun.misc.Signal.handle(Signal, SignalHandler)method call you are also able to register a signal handler, but probably not for signals like INTor TERMas they are used by the JVM.

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

To be able to handle anysignal you would have to jump out of the JVM and into Operating System territory.

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

What I generally do to (for instance) detect abnormal termination is to launch my JVM inside a Perl script, but have the script wait for the JVM using the waitpidsystem call.

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

I am then informed whenever the JVM exits, and why it exited, and can take the necessary action.

然后我会在 JVM 退出时收到通知,以及退出的原因,并且可以采取必要的操作。

回答by idelvall

I would expect that the JVM gracefully interrupts(thread.interrupt()) all the running threads created by the application, at least for signals SIGINT (kill -2)and SIGTERM (kill -15).

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

This way, the signal will be forwarded to them, allowing a gracefully thread cancellation and resource finalization in the standard ways.

这样,信号将被转发给他们,允许以标准方式优雅地取消线程和资源终结。

But this is not the case(at least in my JVM implementation: Java(TM) SE Runtime Environment (build 1.8.0_25-b17), Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode).

但事实并非如此(至少在我的 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).

As other users commented, the usage of shutdown hooksseems mandatory.

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

So, how do I would handle it?

那么,我该如何处理呢?

Well first, I do not care about it in all programs, only in those where I want to keep track of user cancellations and unexpected ends. For example, imagine that your java program is a process managed by other. You may want to differentiate whether it has been terminated gracefully (SIGTERMfrom the manager process) or a shutdown has occurred (in order to relaunch automatically the job on startup).

首先,我不在所有程序中关心它,只关心那些我想跟踪用户取消和意外结束的程序。例如,假设您的 java 程序是一个由其他人管理的进程。您可能想要区分它是正常终止(SIGTERM来自管理器进程)还是发生了关闭(以便在启动时自动重新启动作业)。

As a basis, I always make my long-running threads periodically aware of interrupted status and throw an InterruptedExceptionif they interrupted. This enables execution finalization in way controlled by the developer (also producing the same outcome as standard blocking operations). Then, at the top level of the thread stack, InterruptedExceptionis captured and appropriate clean-up performed. These threads are coded to known how to respond to an interruption request. High cohesiondesign.

作为基础,我总是让我的长时间运行的线程定期了解中断状态,并InterruptedException在它们中断时抛出一个。这使得以开发人员控制的方式完成执行(也产生与标准阻塞操作相同的结果)。然后,在线程堆栈的顶层,InterruptedException捕获并执行适当的清理。这些线程被编码为知道如何响应中断请求。高内聚设计。

So, in these cases, I add a shutdown hook, that does what I think the JVM should do by default: interrupt all the non-daemon threads created by my application that are still running:

因此,在这些情况下,我添加了一个关闭钩子,它执行我认为 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");
    }
});

Complete test application at github: https://github.com/idelvall/kill-test

github上完整的测试应用:https: //github.com/idelvall/kill-test

回答by Arno Sch?fer

There is one way to react to a kill -9: that is to have a separate process that monitors the process being killed and cleans up after it if necessary. This would probably involve IPC and would be quite a bit of work, and you can still override it by killing both processes at the same time. I assume it will not be worth the trouble in most cases.

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

Whoever kills a process with -9 should theoretically know what he/she is doing and that it may leave things in an inconsistent state.

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