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

发布于 2024-08-26 20:49:13 字数 239 浏览 11 评论 0原文

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

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

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

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

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 finish command when logging out. What is the best say to send that finish command when my app has been destroyed with a kill -9?

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

沙沙粒小 2024-09-02 20:49:13

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

对于除 kill -9 之外的任何其他处理此问题的方法是注册一个 关闭 钩子。如果您可以使用 (SIGTERM) kill -15 关闭挂钩将起作用。 (SIGINT) 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

在极少数情况下,虚拟
机器可能会中止,即停止
运行而不完全关闭。
当虚拟机
被外部终止,例如
与 Unix 上的 SIGKILL 信号或
对 Microsoft 的 TerminateProcess 调用
Windows。

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

#!/usr/bin/env bash

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

It is impossible for 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 can handle that signal. The program should handle 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.

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

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.

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

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 -9 in any program.

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.

The only real option to handle a kill -9 is 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 ps command looking for your program in the list and act accordingly when it disappeared.

#!/usr/bin/env bash

java TestShutdownHook
wait
# notify your other app that you quit
echo "TestShutdownHook quit"
挽袖吟 2024-09-02 20:49:13

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

这样,信号将被转发给它们,从而允许在 标准方式

但事实并非如此(至少在我的 JVM 实现中:Java(TM) SE 运行时环境(内部版本 1.8.0_25-b17)、Java HotSpot(TM) 64 位服务器 VM (版本 25.25-b02,混合模式)

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

要如何处理它?

那么,我首先 在所有程序中,仅在那些我想要跟踪用户取消和意外结束的程序中,例如,假设您的 java 程序是由其他程序管理的进程(< code>SIGTERM 来自管理器进程)或发生关闭(为了在启动时自动重新启动作业),

我总是让长时间运行的线程定期了解中断状态并抛出 < code>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

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).

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).

As other users commented, the usage of shutdown hooks seems 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 (SIGTERM from the manager process) or a shutdown has occurred (in order to relaunch automatically the job on startup).

As a basis, I always make my long-running threads periodically aware of interrupted status and throw an InterruptedException if 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, InterruptedException is captured and appropriate clean-up performed. These threads are coded to known how to respond to an interruption request. High cohesion design.

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:

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

一杯敬自由 2024-09-02 20:49:13

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

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

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

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

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

There are ways to handle your own signals in certain JVMs -- see this article about the HotSpot JVM for example.

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 INT or TERM as they are used by the JVM.

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

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 waitpid system call.

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

我喜欢麦丽素 2024-09-02 20:49:13

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

Reference 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);
        }
    }
}
素年丶 2024-09-02 20:49:13

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

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

笔落惊风雨 2024-09-02 20:49:13

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

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

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.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文