終端、會話、進程組、前台/後台作業、孤兒進程組——這組概念構成了 Linux 作業控制的核心骨架。本文從 POSIX 定義、內核實現、Shell 行為、守護進程雙重 Fork 技巧、SIGHUP/SIGTTOU/SIGTTIN 信號語意五個維度,系統梳理「誰控制終端、誰能讀寫終端、斷開連接時發生什麼」。
| 概念 | 定義 | 關鍵屬性 |
|---|---|---|
| 會話 | 一組進程組的集合,由會話首進程(Session Leader)創建 | setsid() 創建,擁有控制終端權 |
| 進程組 | 一組相關進程集合,同一會話內 | PGID = 組長 PID,setpgid() 設置 |
| 前台進程組 | 當前佔用控制終端的進程組 | 唯一,可讀寫終端,接收終端信號 |
| 後台進程組 | 非前台進程組 | 讀寫終端將收到 SIGTTIN/SIGTTOU |
| 控制終端 | 會話綁定的終端設備 | /dev/tty,open() 時 O_NOCTTY 可拒絕 |
| 孤兒進程組 | 組內所有進程的父進程「不在同一會話的其他組」 | 會話首進程退出後,其子進程組變孤兒 |
當進程組成為孤兒,且組內有進程處於停止狀態(SIGSTOP/SIGTSTP),內核必須向該組發送 SIGHUP + SIGCONT。
目的:防止停止態進程永久凍結,同時通知它們「控制終端已失去」,讓其選擇退出或忽略信號繼續運行(但無法再訪問終端)。
getty → login → shell,Shell 成為會話首進程,調用 setsid()、打開控制終端(/dev/tty)。fork() 子進程 → setpgid() 設新組 → tcsetpgrp() 將該組設為前台 → exec() 運行程序。Shell 等待(wait())。& → Shell 不 tcsetpgrp(),子進程組留在後台。Shell 提示 [1] 1234(作業號 + PID)。fg %1 將作業 1 設前台(tcsetpgrp);Ctrl+Z 發 SIGTSTP 停止前台組 → Shell 重獲前台。SIGHUP。SIGHUP+SIGCONT。void daemonize() {
// 1. 第一次 fork:父進程退出,子進程成為背景進程(非組長)
if (fork() != 0) _exit(0);
// 2. setsid:成為新會話首進程、新進程組組長、無控制終端
if (setsid() < 0) _exit(1);
// 3. 第二次 fork:防止會話首進程重新獲得控制終端
if (fork() != 0) _exit(0);
// 4. 切換工作目錄、重設 umask、關閉所有 fd
chdir("/");
umask(0);
for (int i = 0; i < maxfd; ++i) close(i);
open("/dev/null", O_RDWR); // fd 0
dup(0); // fd 1
dup(0); // fd 2
}
setsid() 前提。setsid() 後進程成為會話首領,**理論上可重新打開控制終端**(若打開終端設備且無 O_NOCTTY)。| 信號 | 觸發場景 | 默認動作 | 典型處理 |
|---|---|---|---|
| SIGHUP | 控制終端掛斷、會話首進程退出、孤兒組停止態喚醒 | 終止 | 守護進程忽略或重讀配置 |
| SIGINT | Ctrl+C | 終止 | 前台程序清理退出 |
| SIGQUIT | Ctrl+\ | 終止+核心轉儲 | 調試用 |
| SIGTSTP | Ctrl+Z | 停止 | Shell 作業控制 |
| SIGTTIN | 後台進程嘗試讀終端 | 停止 | fg 切前台或重定向輸入 |
| SIGTTOU | 後台進程嘗試寫終端(若 TOSTOP 模式) | 停止 | fg 切前台或重定向輸出 |
| SIGCONT | 喚醒停止態進程 | 繼續 | fg/bg、孤兒組喚醒 |
if (!noctty && current->signal->leader && !current->signal->tty && tty->session == 0) {
task_lock(current);
current->signal->tty = tty; // 綁定控制終端
task_unlock(current);
current->signal->tty_old_pgrp = 0;
tty->session = current->signal->session;
tty->pgrp = process_group(current); // 設前台進程組
}
只有會話首領、無控制終端、終端無會話三條件滿足時,才能成為控制終端擁有者。
Linux 作業控制以會話-進程組-前台/後台三層模型,配合控制終端唯一歸屬、孤兒進程組 SIGHUP/SIGCONT 機制、後台訪問終端停止信號,構建出完整的終端多路複用與斷開保護體系。
守護進程雙重 Fork + setsid() + 關閉 fd 是脫離終端的標準範式;理解孤兒進程組規則,能預測 SSH 斷開、screen/tmux 保活、systemd 管理服務等場景的進程生死走向。