linux系列之:告訴他,他根本不懂kill

flydean發表於2021-09-15

簡介

和很多程式設計師打過交道,這些程式設計師可能熟知for遍歷的好幾種寫法,但是卻對寫出來的程式部署的環境一無所知。我敢打賭,在spring boot出現之後,已經很少有程式設計師知道tomcat到底是怎麼執行的了。對於他們來說,執行一個jar包就完事了。

工具的先進性確實帶給我們很多便利,也提升了程式設計師的開發效率,同時也降低了程式設計師的進入門檻。今天想和大家一起討論一下,linux中的kill命令到底是做什麼用的。

可能很很多小夥伴第一次接觸kill命令是同事告訴他,把程式kill掉。那麼kill真的是用來殺程式的嗎?

使用kill來殺死程式

先來看一個kill最基本,也是最常見的應用就是殺死程式。在殺死程式之前,我們需要找到這個程式ID。

一般情況下是使用ps命令找到這個程式ID。加入這個程式ID=54321。

那麼接下來就可以使用kill 54321來殺死這個程式了。

更資深一點的同學,可能還會使用kill -9 54321來強制殺死這個程式。

有沒有更深入的用法呢?有的,一起來看看。

kill的深入用法

先看一下kill的命令引數到底有那些:

kill 
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]

可以看到kill的引數是sig,也就是訊號。也就是說kill的本質是向程式傳遞訊號的。

如果使用 kill -l ,我們可以得到到底kill可以傳遞多少訊號:

kill -l 
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX

總共64個訊號,可能不同的kill版本,訊號有所不同,但是基本上都覆蓋了常用的訊號。

下面是一些常用訊號的含義:

HUP     1    終端斷線
INT       2    中斷(同 Ctrl + C)
QUIT    3    退出(同 Ctrl + \)
TERM    15    終止
KILL      9    強制終止
CONT   18    繼續(與STOP相反, fg/bg命令)
STOP    19    暫停(同 Ctrl + Z)

怎麼看kill的版本呢?

/bin/kill --version
kill from util-linux 2.23.2

如果kill不傳sig,那麼將會傳預設的sig=TERM,也就是15。所以上面的kill 54321和 kill -15 54321是等價的。

一般情況下,我們優先使用SIGTERM訊號。這是因為當程式收到了SIGTERM訊號之後,會做一些程式的清理操作,或者說是優雅的關閉。

如果傳入kill -9 也就是SIGKILL,那麼應用程式將無法捕捉這個訊號,從而導致程式強制被關閉,有可能會照成一些異常情況,比如資料還沒有儲存,資料傳輸還沒有結束等等。

sig還有一個特殊值叫做0,如果傳入0的話,那麼並不會傳送實際的訊號,這個只是做異常檢測用的。

pid就是process id,可以理解為是程式號。除了程式號之外,還可以傳入一些特殊值,比如:

  • 0 表示當前程式group的所有程式
  • -1 表示所有PID>1的程式

還有一個特殊的pid=1,這個pid表示的是初始程式init,這個程式是不可被殺死的。

除了PID之外,我們看到kill還可以接受jobspec。job id可以使用jobs命令來列出。

殭屍程式和kill

上面講到了pid=1的初始程式是不能被kill的。還有一種不能被kill的程式叫做殭屍程式。

殭屍程式是linux程式中一個非常獨特的狀態,它表示的是程式已經結束了,但是又還沒有完全死亡,就像殭屍一樣。

linux中的5大程式狀態分別是:RUNNING:正在執行或等待執行狀態,UNINTERRUPTABLE:不可中斷阻塞狀態,INTERRUPTABLE:可中斷阻塞狀態,STOPPED:掛起狀態和ZOMBIE:殭屍狀態。

那麼什麼是殭屍程式呢?

殭屍程式指的是程式在退出之後,該程式並不是馬上消失的,而是會保留一個被稱為殭屍的資料結構。這個資料結構很特殊,因為其沒有記憶體空間,沒有可執行的程式碼,當然也不可以被排程。它只是在程式列表中佔有一個位置,記錄了該程式退出時候的各種資訊。

殭屍程式主要是保留程式退出的現場,供父程式或者系統管理員進行分析使用的,所以殭屍程式是交由父程式來進行收集和釋放的。因為殭屍程式已經退出了,所以使用kill是沒有用的,只能等待其父程式退出,才能真正的退出。

怎麼檢視殭屍程式呢?最簡單的方法就是使用top命令:

top - 14:34:38 up 305 days,  4:23,  2 users,  load average: 0.20, 0.29, 0.47
Tasks:  93 total,   1 running,  92 sleeping,   0 stopped,   0 zombie
%Cpu(s):  2.0 us,  0.7 sy,  0.0 ni, 97.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  1882008 total,   525524 free,   311440 used,  1045044 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  1382560 avail Mem 

上面的輸出,我們可以看到裡面有0個zombie。

java thread dump

kill還有一個非常有用的地方就是生成java程式的thread dump,將當前java程式的執行緒資訊dump出來,可以進行一些有用的分析,比如死鎖分析等。

怎麼對java程式做thread dump呢?很簡單使用kill -3 命令即可:

kill -3 <pid>

從上面我們的介紹可以指定3代表的訊號是SIGQUIT。這說明JVM內建了這個訊號的捕捉,如果接收到了這個訊號,則會dump當前的執行緒資訊。

java thread dump在對java進行執行緒分析的時候非常有用。

總結

本文介紹了kill的深入用法和底層的工作原理,還介紹了kill的幾個應用,希望下次有人再問你kill到底是什麼的時候,大家都可以很自豪的告訴他!

本文已收錄於 http://www.flydean.com/01-that-is-kill/

最通俗的解讀,最深刻的乾貨,最簡潔的教程,眾多你不知道的小技巧等你來發現!

歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!

相關文章