Jak radzisz sobie z czyszczeniem, gdy program otrzymuje sygnał zabicia?
Na przykład istnieje aplikacja, z którą się łączę, która chce, aby każda aplikacja innej firmy (moja aplikacja) wysyłała finish
polecenie podczas wylogowywania. Jak najlepiej wysłać to finish
polecenie, gdy moja aplikacja została zniszczona za pomocą kill -9
?
edycja 1: kill -9 nie może zostać przechwycone. Dziękuję za poprawienie mnie.
edycja 2: Myślę, że ten przypadek miałby miejsce, gdy wywołuje się po prostu kill, co jest tym samym, co ctrl-c
kill -9
znaczy dla mnie: „Precz, plugawy proces, precz z tobą!”, po czym proces ten ustanie. Natychmiast.kill
to nie to samo, co Ctrl-C, ponieważkill
bez określenia, który sygnał do wysłania wyśle SIGTERM, podczas gdy Ctrl-C wysyła SIGINT.Odpowiedzi:
To jest niemożliwe dla każdego programu, w dowolnym języku, aby obsłużyć SIGKILL. Dzięki temu zawsze można zakończyć działanie programu, nawet jeśli zawiera on błędy lub jest złośliwy. Ale SIGKILL to nie jedyny sposób na zakończenie programu. Drugim jest użycie SIGTERM. Programy mogą obsłużyć ten sygnał. Program powinien obsłużyć sygnał, wykonując kontrolowane, ale szybkie zamknięcie. Gdy komputer się wyłącza, ostatni etap procesu zamykania wysyła do każdego pozostałego procesu SIGTERM, daje tym procesom kilka sekund przerwy, a następnie wysyła im SIGKILL.
Sposób obsługi to do niczego innego niż
kill -9
byłoby zarejestrować zamykania haka. Jeśli możesz użyć ( SIGTERM ),kill -15
zaczep zamykający będzie działał. ( SIGINT )kill -2
POWODUJE, że program wdzięcznie kończy pracę i uruchamia przechwyty zamykające.Wypróbowałem następujący program testowy na OSX 10.6.3 i na
kill -9
nim NIE uruchomiłem haka zamykającego, zgodnie z oczekiwaniami. Na zasadziekill -15
, że NIE uruchomić hakiem wyłączanie za każdym razem.Nie ma sposobu, aby naprawdę wdzięcznie obsłużyć a
kill -9
w żadnym programie.Jedyną realną opcją do obsługi a
kill -9
jest posiadanie innego programu obserwującego, który obserwuje twój główny program, aby odszedł lub użyć skryptu opakowującego. Możesz to zrobić za pomocą skryptu powłoki, który odpytywałps
polecenie szukające twojego programu na liście i działał odpowiednio, gdy zniknął.źródło
Tam są sposoby obsługi własnych sygnałów w pewnych JVMs - patrz artykuł o HotSpot JVM na przykład.
Korzystając z wewnętrznego
sun.misc.Signal.handle(Signal, SignalHandler)
wywołania metody Sun, możesz również zarejestrować obsługę sygnału, ale prawdopodobnie nie dla sygnałów takich jakINT
lub w takiejTERM
postaci, w jakiej są one używane przez maszynę JVM.Aby móc obsłużyć dowolny sygnał, musiałbyś wyskoczyć z JVM na terytorium systemu operacyjnego.
To, co zwykle robię, aby (na przykład) wykryć nieprawidłowe zakończenie, to uruchomienie maszyny JVM w skrypcie Perla, ale skrypt czekał na maszynę JVM przy użyciu
waitpid
wywołania systemowego.Następnie jestem informowany o zamknięciu wirtualnej maszyny języka Java i o jego przyczynach oraz o możliwości podjęcia niezbędnych działań.
źródło
INT
i zaTERM
pomocąsun.misc.Signal
, ale nie możesz tego obsłużyć,QUIT
ponieważ JVM rezerwuje ją do debugowania lubKILL
ponieważ system operacyjny natychmiast zakończy JVM. Próba obsłużenia jednego z nich spowoduje podniesienieIllegalArgumentException
.Spodziewałbym się, że JVM z wdziękiem przerywa (
thread.interrupt()
) wszystkie uruchomione wątki utworzone przez aplikację, przynajmniej dla sygnałówSIGINT (kill -2)
iSIGTERM (kill -15)
.W ten sposób sygnał zostanie do nich przekazany, umożliwiając wdzięczne anulowanie wątku i finalizację zasobów w standardowy sposób .
Ale to nie jest tak (przynajmniej w moim implementacji 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)
.Jak komentowali inni użytkownicy, użycie haków zamykających wydaje się obowiązkowe.
Jak więc sobie z tym poradzę?
Po pierwsze, nie przejmuję się tym we wszystkich programach, tylko w tych, w których chcę śledzić anulowania użytkowników i nieoczekiwane zakończenia. Na przykład wyobraź sobie, że twój program java jest procesem zarządzanym przez inne. Możesz chcieć rozróżnić, czy zostało zakończone z wdziękiem (
SIGTERM
z procesu menedżera), czy też nastąpiło zamknięcie (w celu automatycznego ponownego uruchomienia zadania podczas uruchamiania).Jako podstawę, zawsze okresowo informuję moje długo działające wątki o stanie przerwania i wyrzucam,
InterruptedException
jeśli zostały przerwane. Umożliwia to finalizację wykonania w sposób kontrolowany przez dewelopera (również z wynikiem identycznym jak standardowe operacje blokujące). Następnie na najwyższym poziomie stosu niciInterruptedException
jest przechwytywany i wykonywane jest odpowiednie czyszczenie. Wątki te są zakodowane, aby wiedzieć, jak odpowiedzieć na żądanie przerwania. Konstrukcja o wysokiej spójności .Dlatego w takich przypadkach dodaję hak zamykający, który robi to, co myślę, że JVM powinna robić domyślnie: przerywa wszystkie wątki niebędące demonami utworzone przez moją aplikację, które nadal działają:
Kompletna aplikacja testowa na github: https://github.com/idelvall/kill-test
źródło
Możesz użyć
Runtime.getRuntime().addShutdownHook(...)
, ale nie możesz zagwarantować, że zostanie wywołany w każdym przypadku .źródło
Jest jeden sposób, aby zareagować na zabicie -9: to znaczy mieć oddzielny proces, który monitoruje zabijany proces i czyści po nim, jeśli to konieczne. Prawdopodobnie wymagałoby to IPC i wymagałoby sporo pracy, a nadal można to zastąpić, zabijając oba procesy w tym samym czasie. Zakładam, że w większości przypadków nie będzie to warte zachodu.
Ktokolwiek zabija proces z -9, powinien teoretycznie wiedzieć, co robi i że może to pozostawić rzeczy w stanie niespójności.
źródło