控制終端機與行程群組/工作階段(process group / session)
當你打一條 shell 管線像 cat file | grep error | wc -l,你一次啟動了三個行程——但你把它們想成同一個工作。按 Ctrl-C,三個一起停;這不是巧合。Unix 把相關的行程分組,好讓信號與終端機控制能一次套用到整批上。兩層分組讓這件事成立:行程群組與工作階段,兩者都繫於一個控制終端機。
順著層級往上走。行程群組是一小組相關的行程(通常是一條 shell 管線裡的那些行程),共用一個行程群組 ID。你可以一次對整個群組發信號——這正是 Ctrl-C 做的事:它對前景群組裡的每個行程送出一個中斷信號,這就是為什麼管線裡三個指令一起死。工作階段是更大的集合——通常是從一次登入或一個終端機視窗啟動的所有行程群組——由一個工作階段 ID 標識,並由一個工作階段領導者(通常是 shell)領頭。一個工作階段可以有一個控制終端機:把按鍵與信號送給它的鍵盤與螢幕裝置。在一個工作階段內,恰好有一個行程群組是前景群組(它收你的按鍵與終端機產生的信號);其餘的在背景執行。shell 的工作控制——打 fg、bg、Ctrl-Z——正是 shell 在重新安排哪個行程群組在前景,並向背景群組送出停止/繼續信號。一個行程可以用 setsid() 完全脫離它的工作階段,成為一個沒有控制終端機的新工作階段領導者——這正是常駐程式為了與任何終端機脫離所做的事。
為何重要:這種分組是工作控制得以運作的原因、是關掉終端機能殺掉你在裡面啟動的一切的原因(一個掛斷信號送給整個工作階段)、也是一個長時間執行的伺服器為了在你登出後存活、必須逃離它的工作階段的原因。最有用的一個重點:Ctrl-C 不是對準單一程式的魔法——它是發給整個前景行程群組的信號,這就是為什麼它能清掉一整條管線、為什麼背景工作會忽略它。而脫離控制終端機(透過 setsid)是常駐程式做的第一件事,好讓它不再受啟動它的那個終端機擺布。
在 shell 裡執行 sleep 100 | cat &,再執行 jobs -l 與 ps -o pid,pgid,sid,comm。兩個被管線串起的指令共用一個 PGID(這個工作);兩者也都與 shell 共用工作階段的 SID。對前景管線按 Ctrl-C,會把 SIGINT 送給整個前景 PGID,一次停下它們全部。
一條管線共用一個行程群組 ID;Ctrl-C 對整個前景群組發信號,而非單一行程。
Ctrl-C 不是針對單一程式——它對整個前景行程群組發信號,這就是為什麼一整條管線一起死、而背景工作不受影響。信號作為一種機制的深入內容,在欄位 o 介紹。