Як витончено поводитися з сигналом SIGKILL на Java


113

Як ви справляєтеся з очищенням, коли програма отримує сигнал вбивства?

Наприклад, є додаток, до якого я підключаюсь, який хоче, щоб будь-який третій додаток (мій додаток) надсилав finishкоманду під час виходу з системи. Що найкраще сказати, щоб надіслати цю finishкоманду, коли мій додаток було знищено kill -9?

редагувати 1: kill -9 не може бути захоплено. Дякую, хлопці, що виправили мене.

редагувати 2: Я думаю, що цей випадок був би тоді, коли той, хто дзвонить, просто вбиває, те саме, що і ctrl-c


44
kill -9означає для мене: "Почався, недобрий процес, геть з тобою!", після якого процес перестане бути. Відразу.
ZoogieZork

11
У більшості * nixx, про які я знаю, вбивство -9 не може бути перехоплено і витончено обробляється жодною програмою, незалежно від того, на якій мові вона написана.
Президент Джеймс К. Полк

2
@Begui: окрім того, що прокоментували та відповіли інші, ЯКЩО ваша Un x OS не вмить вмикає * І РЕГІОНУЙТЕ ВСІ РЕСУРСИ, використовувані програмою для вбивства -9'ed , ну ... ОС зламана.
SyntaxT3rr0r

1
Про команду kill -9 в manpage написано точніше: "9 KILL (невловимий, неігнорований вбивство)". SIGKILL сигнал, який обробляє ОС, а не програма.
user1338062

4
Просто killце не те саме, що Ctrl-C, так як killбез вказівки, який сигнал для відправки посилатиме SIGTERM, тоді як Ctrl-C посилає SIGINT.
alesguzik

Відповіді:


136

Це неможливо для будь-якої програми, на будь-якій мові, обробляти SIGKILL. Це так, що завжди можна припинити програму, навіть якщо програма є помилковою чи шкідливою. Але SIGKILL - не єдиний засіб для припинення програми. Інше - використовувати SIGTERM. Програми можуть обробляти цей сигнал. Програма повинна обробляти сигнал, роблячи контрольоване, але швидке відключення. Коли комп'ютер вимикається, завершальний етап процесу відключення надсилає кожному процесу, що залишився, SIGTERM, надає цим процесам кілька секунд грації, а потім надсилає їм SIGKILL.

Спосіб впоратися з цим що - небудь інше , ніж kill -9було б зареєструвати вимикання гачок. Якщо ви можете використовувати ( SIGTERM ), kill -15гак відключення спрацює. ( SIGINT ) kill -2 НЕ викликає програму витончено закрити та запустити гачки відключення.

Реєструє новий гак відключення віртуальної машини.

Віртуальна машина Java вимикається у відповідь на два види подій:

  • Програма завершується нормально, коли завершується останній недемонний потік або коли викликається метод виходу (еквівалентно 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будь-якою програмою.

У рідкісних випадках віртуальна машина може перерватись, тобто перестати працювати без чистого вимикання. Це відбувається, коли віртуальна машина припиняється зовні, наприклад, сигналом SIGKILL на Unix або викликом TerminateProcess в Microsoft Windows.

Єдиний реальний варіант впоратися з цим kill -9- це ще один годинник програми для перегляду вашої основної програми, щоб відійти або використовувати сценарій обгортки. Ви можете зробити це за допомогою скрипта оболонки, який опитував psкоманду, яка шукає вашу програму в списку, і діяти відповідно, коли вона зникла.

#!/usr/bin/env bash

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

12

Там є способи обробки власних сигналів в певних віртуальних машинах - см цій статті про HotSpot JVM , наприклад.

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

Щоб мати змогу обробляти будь-який сигнал, вам доведеться вистрибнути з JVM та на територію операційної системи.

Що я зазвичай роблю (наприклад) для виявлення аномального припинення, це запустити свій JVM всередині сценарію Perl, але змусити сценарій чекати JVM за допомогою waitpidсистемного виклику.

Потім мене інформують про те, коли СП виходить, і чому він вийшов, і можу вжити необхідних заходів.


3
Зауважте, ви можете захоплювати INTі TERMз sun.misc.Signal, але ви не можете впоратися, QUITоскільки JVM залишає його для налагодження, а також KILLтому, що ОС негайно припинить JVM. Спроба впоратися з будь-якою мірою підвищить IllegalArgumentException.
dimo414

12

Я б очікував, що 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


6

Ви можете використовувати Runtime.getRuntime().addShutdownHook(...), але вам не можна гарантувати, що він буде викликаний у будь-якому випадку .


12
Але у випадку вбивства -9 він майже точно не працюватиме.
Президент Джеймс К. Полк

1

Є один із способів реагування на вбивство -9: це мати окремий процес, який відстежує процес вбиття та очищує його після необхідності. Це, ймовірно, пов’язане з IPC, і це було б дуже багато роботи, і ви все одно можете перекрити це, вбиваючи обидва процеси одночасно. Я припускаю, що це не буде вартих клопотів у більшості випадків.

Хто вбиває процес із -9, теоретично повинен знати, що він робить, і що він може залишити речі в непослідовному стані.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.