Linux 如何“殺死”一個程序?
經常讓我感到困惑的是,儘管我從事電腦專業工作已有幾十年,從事 Linux 工作已有十年,但實際上我將作業系統的大部分功能視為一個黑匣子,與魔法沒什麼兩樣。
今天我想到了這個
kill
命令,雖然我每天多次使用它(無論是在它的“正常”和-9
風味中),但我必須承認我完全不知道它在幕後是如何工作的。從我的角度來看,如果一個正在執行的程序被“掛起”,我呼叫
kill
它的 PID,然後它突然不再執行了。魔法!那裡真的發生了什麼?手冊頁談論“信號”,但肯定這只是一個抽象。發送
kill -9
到程序不需要程序的合作(如處理信號),它只是將其殺死。
- Linux如何阻止程序繼續佔用CPU時間?
- 它是否從計劃中刪除?
- 它是否斷開程序與其打開的文件句柄的連接?
- 程序的虛擬記憶體是如何釋放的?
- 記憶體中是否有類似全域表的東西,Linux 保留對程序佔用的所有資源的引用,當我“殺死”一個程序時,Linux 只是簡單地遍歷該表並一一釋放資源?
我真的很想知道這一切!
向程序發送 kill -9 不需要程序的合作(如處理信號),它只是將其殺死。
你假設因為一些信號可以被捕捉和忽略,它們都涉及合作。但根據
man 2 signal
,“信號SIGKILL 和 SIGSTOP 不能被擷取或忽略”。SIGTERM 可以被擷取,這就是為什麼 plainkill
並不總是有效的——通常這意味著程序的處理程序中的某些東西出錯了。1如果程序沒有(或不能)為給定信號定義處理程序,核心將執行預設操作。 在 SIGTERM 和 SIGKILL 的情況下,這是終止程序(除非它的 PID 為 1;核心不會終止
init
)2 意味著它的文件句柄被關閉,它的記憶體返回到系統池,它的父程序收到 SIGCHILD,它的孤兒子代由 init 等繼承,就好像它呼叫過一樣exit
(參見 參考資料man 2 exit
)。該程序不再存在——除非它最終成為殭屍,在這種情況下,它仍然在核心的程序表中列出一些資訊;當其父母不這樣做時會發生這種情況wait
並妥善處理這些資訊。但是,殭屍程序不再分配任何記憶體,因此無法繼續執行。記憶體中是否有類似全域表的東西,Linux 保留對程序佔用的所有資源的引用,當我“殺死”一個程序時,Linux 只是簡單地遍歷該表並一一釋放資源?
我認為這已經足夠準確了。物理記憶體按頁跟踪(一頁通常等於 4 KB 塊),這些頁從全域池中取出並返回到全域池。稍微複雜一點的是,一些已釋放的頁面會被記憶體,以防再次需要它們包含的數據(即從仍然存在的文件中讀取的數據)。
手冊頁談論“信號”,但肯定這只是一個抽象。
當然,所有信號都是抽象的。它們是概念性的,就像“過程”一樣。我在玩語義,但是如果您的意思是 SIGKILL 在質量上與 SIGTERM 不同,那麼是和否。是的,因為它無法被擷取,但不是,因為它們都是信號。以此類推,蘋果不是橙子,但根據先入為主的定義,蘋果和橙子都是水果。SIGKILL 似乎更抽象,因為你無法捕捉到它,但它仍然是一個信號。這是一個 SIGTERM 處理的例子,我相信你以前見過這些:
#include <stdio.h> #include <signal.h> #include <unistd.h> #include <string.h> void sighandler (int signum, siginfo_t *info, void *context) { fprintf ( stderr, "Received %d from pid %u, uid %u.\n", info->si_signo, info->si_pid, info->si_uid ); } int main (void) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_sigaction = sighandler; sa.sa_flags = SA_SIGINFO; sigaction(SIGTERM, &sa, NULL); while (1) sleep(10); return 0; }
這個過程將永遠休眠。您可以在終端中執行它並使用 SIGTERM 發送它
kill
。它吐出類似的東西:Received 15 from pid 25331, uid 1066.
1066 是我的 UID。PID 將
kill
是執行的 shell 的 PID,或者如果你 fork 它,則為 kill 的 PID (kill 25309 & echo $?
)。同樣,為 SIGKILL 設置處理程序沒有意義,因為它不起作用。3如果我
kill -9 25309
,該過程將終止。但這仍然是一個信號;核心有關於誰發送了信號,它是什麼類型的信號等資訊。
如果您還沒有查看可能的信號列表,請參閱
kill -l
。另一個例外,正如 Tim Post 下面提到的,適用於不間斷睡眠中的程序。在解決根本問題之前無法喚醒這些信號,因此所有信號(包括 SIGKILL)都會延遲一段時間。然而,一個過程不能故意創造這種情況。
這並不意味著
kill -9
在實踐中使用是更好的事情。我的範例處理程序是一個糟糕的處理程序,因為它不會導致exit()
. SIGTERM 處理程序的真正目的是讓程序有機會做一些事情,比如清理臨時文件,然後自願退出。如果你使用kill -9
,它就沒有這個機會,所以只有在“自願退出”部分似乎失敗時才這樣做。