老闆:kill -9 的原理都不知道就敢去線上執行?明天不用來了!

GitHub 14.5k Star 的Java工程師成神之路,開放閱讀了!git

相信不少程序員對於Linux系統都不陌生,即便本身的平常開發機器不是Linux,那麼線上服務器也大部分都是的,因此,掌握經常使用的Linux命令也是程序員必備的技能。程序員

可是,怕就怕不少人對於部分命令只是只知其一;不知其二,使用不當就能致使線上故障。github

前段時間,咱們的線上應用報警,頻繁FGC,須要緊急處理問題,因而有同事去線上重啓機器(正常程序應該是先採集堆dump,而後再重啓,方便排查是否存在內存泄露等問題)。web

可是在重啓過程當中,同事發現正常的重啓命令應用無反應,而後嘗試使用kill命令"殺"掉Java進程,可是仍然無效。因而他私自決定使用 "kill -9"結束了進程的生命。spring

雖然應用進程被幹掉了,可是隨之而來帶來了不少問題,首先是上游系統忽然發生大量報警,對應開發找過來講調用咱們的RPC服務無響應,頻繁超時。tomcat

後來,咱們又發現系統中存在部分髒數據,有些在同一個事務中須要完整更新的數據,只跟新了一半...安全

爲何正常的kill沒法"殺掉"進程,而kill -9就能夠?爲何kill -9會引起這一連串連鎖反應?正常的kill執行時,JVM會如何處理的呢?服務器

要搞清楚這些問題,咱們要先從kill命令提及。app

kill 命令

咱們都知道,想要在Linux中終止一個進程有兩種方式,若是是前臺進程可使用Ctrl+C鍵進行終止;若是是後臺進程,那麼須要使用kill命令來終止。(其實Ctrl+C也是kill命令)工具

kill命令的格式是:

kill[參數][進程號]

如:
kill 21121
kill -9 21121
複製代碼

其中[參數]是可選的,進程號能夠經過jps/ps/pidof/pstree/top等工具獲取。

kill的命令參數有如下幾種:

-l 信號,若果不加信號的編號參數,則使用「-l」參數會列出所有的信號名稱

-a 當處理當前進程時,不限制命令名和進程號的對應關係

-p 指定kill 命令只打印相關進程的進程號,而不發送任何信號

-s 指定發送信號

-u 指定用戶

一般狀況下,咱們使用的-l(信號)的時候比較多,如咱們前文提到的kill -9中的9就是信號。

信號若是沒有指定的話,默認會發出終止信號(15)。經常使用的信號以下:

HUP 1 終端斷線

INT 2 中斷(同 Ctrl + C)

QUIT 3 退出(同 Ctrl + \)

TERM 15 終止

KILL 9 強制終止

CONT 18 繼續(與STOP相反, fg/bg命令)

STOP 19 暫停(同 Ctrl + Z)

比較經常使用的就是強制終止信號:9終止信號:15,另外,中斷信號:2其實就是咱們前文提到的Ctrl + C結束前臺進程。

那麼,kill -9kill -15到底有什麼區別呢?該如何選擇呢?

kill -9 和 kill -15的區別

kill命令默認的信號就是15,首先來講一下這個默認的kill -15信號。

當使用kill -15時,系統會發送一個SIGTERM的信號給對應的程序。當程序接收到該信號後,具體要如何處理是本身能夠決定的。

這時候,應用程序能夠選擇:

  • 一、當即中止程序

  • 二、釋放響應資源後中止程序

  • 三、忽略該信號,繼續執行程序

由於kill -15信號只是通知對應的進程要進行"安全、乾淨的退出",程序接到信號以後,退出前通常會進行一些"準備工做",如資源釋放、臨時文件清理等等,若是準備工做作完了,再進行程序的終止。

可是,若是在"準備工做"進行過程當中,遇到阻塞或者其餘問題致使沒法成功,那麼應用程序能夠選擇忽略該終止信號。

這也就是爲何咱們有的時候使用kill命令是沒辦法"殺死"應用的緣由,由於默認的kill信號是SIGTERM(15),而SIGTERM(15)的信號是能夠被阻塞和忽略的。

kill -15相比,kill -9就相對強硬一點,系統會發出SIGKILL信號,他要求接收到該信號的程序應該當即結束運行,不能被阻塞或者忽略。

因此,相比於kill -15命令,kill -9在執行時,應用程序是沒有時間進行"準備工做"的,因此這一般會帶來一些反作用,數據丟失或者終端沒法恢復到正常狀態等。

Java是如何處理SIGTERM(15)的

咱們都知道,在Linux中,Java應用是做爲一個獨立進程運行的,Java程序的終止運行是基於JVM的關閉實現的,JVM關閉方式分爲3種:

正常關閉:當最後一個非守護線程結束或者調用了System.exit或者經過其餘特定平臺的方法關閉(接收到SIGINT(2)、SIGTERM(15)信號等)

強制關閉:經過調用Runtime.halt方法或者是在操做系統中強制kill(接收到SIGKILL(9)信號)

異常關閉:運行中遇到RuntimeException異常等。

JVM進程在接收到kill -15信號通知的時候,是能夠作一些清理動做的,好比刪除臨時文件等。

固然,開發者也是能夠自定義作一些額外的事情的,好比讓tomcat容器中止,讓dubbo服務下線等。

而這種自定義JVM清理動做的方式,是經過JDK中提供的shutdown hook實現的。JDK提供了Java.Runtime.addShutdownHook(Thread hook)方法,能夠註冊一個JVM關閉的鉤子。

例子以下:

package com.hollis;

public class ShutdownHookTest {

    public static void main(String[] args) {
        boolean flag = true;
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("hook execute...");
        }));

        while (flag) {
            // app is runing
        }

        System.out.println("main thread execute end...");
    }
}
複製代碼

執行命令:

➜ jps
6520 ShutdownHookTest
6521 Jps
➜ kill 6520
複製代碼

控制檯輸出內容:

hook execute...
Process finished with exit code 143 (interrupted by signal 15: SIGTERM)
複製代碼

能夠看到,當咱們使用kill(默認kill -15)關閉進程的時候,程序會先執行我註冊的shutdownHook,而後再退出,而且會給出一個提示:interrupted by signal 15: SIGTERM

若是咱們執行命令kill -9

➜ kill -9 6520
複製代碼

控制檯輸出內容:

Process finished with exit code 137 (interrupted by signal 9: SIGKILL)
複製代碼

能夠看到,當咱們使用kill -9 強制關閉進程的時候,程序並無執行shutdownHook,而是直接退出了,而且會給出一個提示:interrupted by signal 9: SIGKILL

總結

kill命令用於終止Linux進程,默認狀況下,若是不指定信號,kill 等價於kill -15

kill -15執行時,系統向對應的程序發送SIGTERM(15)信號,該信號是能夠被執行、阻塞和忽略的,因此應用程序接收到信號後,能夠作一些準備工做,再進行程序終止。

有的時候,kill -15沒法終止程序,由於他可能被忽略,這時候可使用kill -9,系統會發出SIGKILL(9)信號,該信號不容許忽略和阻塞,因此應用程序會當即終止。

這也會帶來不少反作用,如數據丟失等,因此,在非必要時,不要使用kill -9命令,尤爲是那些web應用、提供RPC服務、執行定時任務、包含長事務等應用中,由於kill -9 沒給spring容器、tomcat服務器、dubbo服務、流程引擎、狀態機等足夠的時間進行收尾。

最後,不少人會說,說了這麼多,不是還得用 kill -9 嗎?

其實,本文的目的不是不讓你們用,那就是因噎廢食了。本文是但願你們能夠了解其背後的原理,知道他可能帶來的反作用。在選擇要不要執行的時候,能夠考慮到這些因素,若是可以針對可能發生的反作用,提早作好預案和心理準備,而後再執行,那就很完美了。

在執行以後,發生了非預期的問題時,你們能夠想到有可能和kill -9有關,那本文的目的也算達到了。

歡迎關注個人公衆號,帶給你更多避坑指南:

相關文章
相關標籤/搜索