如何在java中优雅地处理sigkill信号

tuwxkamq  于 2021-06-07  发布在  Kafka
关注(0)|答案(5)|浏览(481)

当程序收到终止信号时,如何处理清除?
例如,我连接的一个应用程序希望任何第三方应用程序(我的应用程序)发送 finish 注销时的命令。寄那封信最好说什么 finish 当我的应用程序被 kill -9 ?
编辑1:无法捕获kill-9。谢谢你们纠正我。
编辑2:我想这种情况应该是当一个调用just kill时,这与ctrl-c是一样的

wbgh16ku

wbgh16ku1#

有一种方法可以对kill-9做出React:那就是有一个单独的进程来监视被kill的进程,并在必要时进行清理。这可能会涉及ipc,并且需要大量的工作,您仍然可以通过同时终止两个进程来覆盖它。我想在大多数情况下不值得麻烦。
无论是谁用-9杀死一个进程,理论上都应该知道他/她在做什么,这可能会使事情处于不一致的状态。

xqk2d5yq

xqk2d5yq2#

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

sd2nnvve

sd2nnvve3#

在某些jvm中,有一些方法可以处理您自己的信号——例如,请参阅这篇关于热点jvm的文章。
利用太阳内部 sun.misc.Signal.handle(Signal, SignalHandler) 方法调用您还可以注册一个信号处理程序,但可能不适用于 INT 或者 TERM 因为它们被jvm使用。
为了能够处理任何信号,您必须跳出jvm进入操作系统领域。
我通常(例如)检测异常终止的方法是在perl脚本中启动jvm,但是让脚本使用 waitpid 系统调用。
然后,每当jvm退出时,就会通知我它退出的原因,并可以采取必要的操作。

rpppsulh

rpppsulh4#

我希望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) .
正如其他用户所评论的,shutdown钩子的使用似乎是强制性的。
那么,我该怎么处理呢?
首先,我并不关心所有的程序,只关心那些我想跟踪用户取消和意外结束的程序。例如,假设您的java程序是一个由其他人管理的进程。您可能需要区分它是否被优雅地终止( SIGTERM 或者发生了关闭(以便在启动时自动重新启动作业)。
作为基础,我总是让我的长时间运行的线程周期性地意识到中断状态,并抛出一个 InterruptedException 如果他们打断了。这使得执行最终化以开发人员控制的方式进行(也产生与标准阻塞操作相同的结果)。然后,在线程堆栈的顶层, InterruptedException 捕获并执行适当的清理。这些线程被编码为知道如何响应中断请求。高内聚性设计。
因此,在这些情况下,我添加了一个shutdown hook,它完成了我认为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

kpbpu008

kpbpu0085#

任何语言的程序都不可能处理sigkill。因此,即使程序有缺陷或恶意,也始终可以终止程序。但是sigkill并不是终止程序的唯一方法。另一种是使用sigterm。程序可以处理这个信号。程序应该通过控制但快速的关机来处理信号。当计算机关闭时,关闭进程的最后一个阶段会向每个剩余进程发送一个sigterm,给这些进程几秒钟的宽限期,然后向它们发送一个sigkill。
处理这件事的方法除了 kill -9 就是注册一个关机钩子。如果您可以使用(sigterm) kill -15 关闭挂钩可以工作(信号) kill -2 不会导致程序正常退出并运行关闭挂钩。
注册新的虚拟机关闭挂钩。
java虚拟机将关闭,以响应两种事件:
当最后一个非守护进程线程退出或调用exit(相当于system.exit)方法时,程序正常退出,或者
响应用户中断(如键入^c)或系统范围的事件(如用户注销或系统关闭),虚拟机被终止。
我在OSX10.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 是让另一个监视程序监视您的主程序离开或使用 Package 器脚本。您可以使用轮询 ps 命令在列表中查找您的程序,并在它消失时采取相应的行动。


# !/usr/bin/env bash

java TestShutdownHook
wait

# notify your other app that you quit

echo "TestShutdownHook quit"

相关问题