Как грациозно обрабатывать сигнал SIGKILL в Java

Как вы обрабатываете очистку, когда программа получает сигнал kill?

Например, приложение, к которому я подключаюсь, хочет, чтобы какое-либо стороннее приложение (мое приложение) отправляло команду finish при выходе из системы. Что лучше сказать отправить эту команду finish, когда мое приложение было уничтожено с помощью kill -9?

edit 1: kill -9 не может быть захвачен. Спасибо, ребята, за то, что вы меня исправили.

edit 2: Я предполагаю, что этот случай будет, когда один вызывает просто kill, который является таким же, как ctrl-c

Ответ 1

Способ справиться с этим для чего-либо другого, чем kill -9, состоял бы в регистрации shutdown hook, Если вы можете использовать (SIGTERM) kill -15, то он будет работать. ( SIGINT) kill -2 ЛИБО заставляют программу изящно выйти и запустить крючки отключения.

Регистрирует новую виртуальную машину выключение крюка.

Виртуальная машина Java отключается ответ на два вида событий:

* The program exits normally, when the last non-daemon thread exits or

когда выход (эквивалентно, System.exit), или

* The virtual machine is terminated in response to a user

например, набрав ^ C или общесистемное событие, такое как выключение пользователя или отключение системы.

Я попробовал следующую тестовую программу в OSX 10.6.3, а на kill -9 он сделал НЕ запустил hookdown, не думал, что это будет. На kill -15 он DOES запускает крюк остановки каждый раз.

public class TestShutdownHook
{
    public static void main(final 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 в любой программе.

В редких случаях виртуальный машина может прервать, то есть остановиться бег без выключения чисто. Это происходит, когда виртуальная машина нарушается, например, с сигналом SIGKILL на Unix или Вызов TerminateProcess в Microsoft Windows.

Единственная реальная опция для обработки kill -9 заключается в том, чтобы посмотреть другую программу наблюдателя, чтобы ваша основная программа ушла или использовала обертку script. Вы можете сделать это с помощью оболочки script, которая опросила команду ps, которая ищет вашу программу в списке и действует соответственно, когда она исчезла.

#!/bin/bash

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

Ответ 2

Существуют способы обработки собственных сигналов в определенных JVM - см. эту статью о JVM HotSpot.

Используя внутренний вызов метода sun.misc.Signal.handle(Signal, SignalHandler) Sun, вы также можете зарегистрировать обработчик сигнала, но, вероятно, не для сигналов типа INT или TERM, поскольку они используются JVM.

Чтобы иметь возможность обрабатывать любой сигнал, вам придется выпрыгнуть из JVM и на территорию операционной системы.

То, что я обычно делаю (например), обнаруживает аномальное завершение, заключается в том, чтобы запустить мою JVM внутри Perl script, но ждать script для JVM с помощью системного вызова waitpid.

Затем мне сообщают, когда JVM выходит и почему она вышла, и может предпринять необходимые действия.

Ответ 3

Вы можете использовать Runtime.getRuntime().addShutdownHook(...), но вы не можете гарантировать, что он будет вызываться в любом случае.

Ответ 4

Я ожидал бы, что 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).

Как прокомментировали другие пользователи, использование hookdown кажется обязательным.

Итак, как мне с этим справиться?

Ну, во-первых, я не забочусь об этом во всех программах, только в тех, где я хочу отслеживать отмену пользователей и неожиданные концы. Например, представьте, что ваша Java-программа - это процесс, управляемый другим. Возможно, вам захочется отличить, было ли это изящно прекращено (SIGTERM из процесса менеджера) или произошло выключение (чтобы автоматически возобновить работу при запуске).

В качестве основы я всегда делаю свои длительные потоки периодически осведомленными о прерванном статусе и бросаю InterruptedException, если они прерываются. Это позволяет завершить выполнение, контролируемое разработчиком (также создавая тот же результат, что и стандартные операции блокировки). Затем на верхнем уровне в стеке потоков InterruptedException фиксируется и выполняется соответствующая очистка. Эти потоки кодируются, чтобы знать, как реагировать на запрос прерывания. Высокий cohesion дизайн.

Итак, в этом случае я добавляю крюк отключения, который делает то, что, по моему мнению, должен делать JVM по умолчанию: прерывайте все потоки не-daemon, созданные моим приложением, которые все еще запущены:

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

Ответ 5

Существует один способ реагировать на kill -9: это отдельный процесс, который контролирует процесс, который будет убит, и после этого очистит его, если это необходимо. Это, вероятно, будет включать IPC и будет довольно немного работать, и вы все равно можете отменить его, убив оба процесса одновременно. Я предполагаю, что в большинстве случаев это не будет проблемой.

Тот, кто убивает процесс с -9, теоретически должен знать, что он делает, и что он может оставить вещи в противоречивом состоянии.