Как грациозно обрабатывать сигнал 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, теоретически должен знать, что он делает, и что он может оставить вещи в противоречивом состоянии.