Linux 系統中有一個 job control 的概念,本文簡單介紹什么是 job,以及常見的 job control 命令。本文中演示部分使用的環境為 ubuntu 18.04。
進程組(job)
執行一個命令會創建一個或多個進程,這些進程被稱為一個進程組(process group)。進程組中包含一個或多個進程,每個進程都會屬於一個進程組,進程組也叫 job。
每個進程組都有一個領頭進程(process group leader),領頭進程的 PID 就是進程組的 ID(process group ID,PGID),我們可以通過 ps 命令查看進程的 PGID:
$ ps -o pid,ppid,pgid,comm | cat

紅框中的兩個進程屬於同一進程組(通過管道符連接的進程屬於相同的進程組)。這個進程組中的領頭進程為 16823,因此它的 PID 成了進程組的 PGID。我們可以通過下圖來理解這幾個進程之間的關系:

領頭進程可以先退出,這時進程組依然存在並且 PGID 也不會發生變化。在進程組中的所有進程都退出后,進程組的證明周期結束。
將進程划分到進程組中的主要原因是可以對它們進行統一的管理,說白了就是同時發信號給組內的所有進程,這就是我們接下來要介紹的 job 管理。
Job 管理
有了前面 job 的概念(進程組),接下來我們介紹如何管理 job。
jobs 命令
使用 vim 打開文件 test.txt,然后按下 ctrl + z,此時 vim 進入了后台:

輸出的第一列方括號中的數字表示 jobID,第二列 Stopped 表示 job 當前的狀態,第三列則表示該 job 執行的命令。
使用 jobs 命令可以查看當前會話中的的所有 jobs,此時執行 jobs 命令,輸出的結果和上面一樣:

& 符
在命令的后面加上 & 符號,可以直接讓 job 運行在后台:
$ sleep 1000 &

sleep 命令的 jobID 為 2,狀態為 Running。
fg 命令
fg 命令是 foreground 的縮寫。命令格式為 fg %n,它把當前或指定 ID 的 job 放到前台。下面我們操作一次 job 2:
$ fg %2

此時 sleep 命令運行在前台,通過 ctrl + z 我們可以再次把它送回后台:

請注意此時 sleep 命令的狀態已經變成了 Stopped。
ctrl + z
嚴格來說 ctrl + z 並不是一個 job 管理命令,它只是向當前進程發送一個 SIGSTOP 信號,該信號使進程進入暫停(stopped)狀態,也就是掛起進程,此狀態下,進程狀態會被系統保存,此進程會被放置到作業隊列中去,從而讓出終端。使用 ctrl + z 我們可以暫停正在占用終端的進程而不結束它,然后我們可以使用終端命令來操作此進程。
bg 命令
bg 命令是 background 的縮寫,命令格式為 bg %n,bg 命令和 ctrl + z 配合可以把前台命令切換到后台去執行。比如剛才我們通過 ctrl + z 把 sleep 命令切到了后台,但變成了 Stopped 狀態,此時執行 bg %2 命令可以讓 sleep 命令繼續在后台執行:

kill 命令
kill 命令負責向進程發送信號,當然它也可以向 job 發送信號,在 jobID 前面添加 % 就可以了。比如 SIGCONT 是喚醒一個掛起的進程,所以我們也可以使用下面的命令把處於 Stooped 狀態的 sleep 命令喚醒:

殺死進程
有時候使用 ctrl + c 無法殺死一個正在運行的前台進程,這是因為 ctrl + c 的本質是向進程發送 SIGINT 信號。SIGINT 是用來終止進程的,但是這是一個可以被忽略的信號,如果程序忽略了它,我們就無法通過 ctrl + c 來終止該進程。
此時我們可以先使用 ctrl + z 把進程切換到后台,然后使用 kill %n(n 為進程的 jobID)來終止進程。kill 命令默認向進程發送 SIGTERM 信號,程序一般會在 SIGTERM 信號的處理函數中正常地終止程序並執行資源清理工作。既然 SIGTERM 信號能夠被程序處理,那么它也能夠被忽略,所以也無法通過這種方式結束那些頑固的進程。
殺死進程的終極手段是 kill -SIGKILL PID(kill -9 PID)。SIGKILL 信號是不能被忽略的,所以這一招肯定管用。但是由於它過於強硬,使用這種方式殺死進程后往往會有后遺症,比如進程使用的資源沒有在退出前清理干凈,常見的例子是用這種方法殺死 vim 進程后會遺留下 .swp 文件。
暫停 tail 命令的輸出
我們一般會使用 tail -f 命令查看實時的日志,但很多程序產生日志的速度非常快以至於我們跟不上節奏。此時使用
ctrl + s 命令可以暫停日志輸出到終端,這樣我們就可以仔細的分析當前終端中顯示的日志。如果要接着輸出日志,可以使用 ctrl + q 命令恢復日志的輸出。
這兩個命令的原理是:
ctrl + s 會告訴終端暫停,阻塞所有讀寫操作,即不轉發任何數據,只有按了 ctrl + q 后,才會繼續。這個功能應該是歷史遺留的產物,以前終端和服務器之間沒有流量控制功能,所以有可能服務器發送數據過快,導致終端處理不過來,於是需要這樣一個命令告訴服務器不要再發了,等終端處理完了后再通知服務器繼續。
參考:
Job Control
