118

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

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

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

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

4

6 回答 6

152

任何语言的任何程序都不可能处理 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任何程序中的 a 。

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

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

#!/usr/bin/env bash

java TestShutdownHook
wait
# notify your other app that you quit
echo "TestShutdownHook quit"
于 2010-03-29T22:10:23.253 回答
14

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

于 2015-11-18T11:09:26.467 回答
12

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

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

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

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

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

于 2010-03-29T23:00:00.870 回答
7

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

于 2010-03-29T22:11:10.990 回答
2

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

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

于 2017-08-08T15:39:03.147 回答
2

参考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);
        }
    }
}
于 2021-10-12T01:46:14.483 回答