控制终端由进程向 system 申请。一个 TTY 唯一对应一个 Session,一个 Session 可以创建多个 Process Group。进程组通常由时间上产生的第一个进程 的 PID 作为 PGID。一个不是进程组组长的进程可以通过 fork + setsid 脱离父进程,创立独立会话或成为守护进程。

scss
会话 (Session) → 进程组 (Process Group) → 进程 (Process) → 线程 (Thread)
打个形象的比方:会话是一栋大楼,进程组是办公室组,进程是独立办公室(有自己的地址空间、文件描述符、堆空间),线程是办公室里干活的工人。
一、进程概念
| 概念词 | 释义 | 注释 | |
|---|---|---|---|
| TTY | 控制终端设备(? 表示无终端) | Ctrl+Alt+T 打开的 terminal 就是一个终端,每个 session 可以绑定一个 TTY | |
| PR | priority | PR = 20 + NI(内核调度优先级) | |
| NI | nice | 用户层可调节(-20 ~ 19) | |
| PID | 进程ID | --- | |
| PGID | 进程组ID | `cat file | grep test` 中两个进程属于同一进程组 |
| SID | 会话ID | 打开一个终端窗口就创建了一个会话,Shell 是 Session Leader | |
| PPID | 父进程ID | --- |
二、进程状态
| 状态 | 含义 |
|---|---|
| R | RUNNING/RUNNABLE --- 正在运行或等待调度 |
| S | sleep --- 可中断睡眠 |
| D | disk sleep --- 不可中断睡眠(等 I/O) |
| Z | Zombie --- 子进程已死,父进程尚未回收 |
| T | stop --- 被 SIGSTOP 暂停 |
| I | IDLE --- 空闲内核线程 |
状态附加符号:
| 符号 | 含义 |
|---|---|
| < | 高优先级 |
| N | 低优先级 |
| L | 有内存页被锁,不能 swap |
| s | 会话首进程(Session Leader) |
| l | 多线程进程 |
| + | 位于前台进程组 |
tpgid :内核在每个 TTY 结构体里记录了一个
tpgid(Terminal Process Group ID),STAT 里的+号就是内核通过比对进程组 ID 与 tpgid 是否一致来打上的标签。
三、会话分裂实战
以真实系统快照为例。下图展示了一次进程的"惊天大逃亡"------opencode agent 通过 fork+setsid 脱离父会话,创建独立会话的过程。
关键数据对比
| 属性 | 会话 A (终端 bash) | 会话 B (opencode bash) |
|---|---|---|
| PID / SID | 225227 / 225227 | 258975 / 258975 |
| Session Leader | ✅ | ✅ |
| TTY | pts/3 | ? (无) |
| stdin | /dev/pts/3 | /dev/null |
| stdout | socket | socket |
| STAT | Ss | Ss |
| 创立方式 | gnome-terminal fork | fork + setsid() 脱离旧会话 |
fork + setsid() 工作原理
.opencode(PID=225250) 调用fork()生出子进程 bash- 子进程立刻调用
setsid()→ 创立全新会话,自任 session leader - 新会话不再绑定任何控制终端 → TTY 变为
?,stdin 变为/dev/null - 结果:关闭 gnome-terminal 不会波及 opencode 的 shell
💡 这就是守护进程 (daemon) 的标准脱壳流程:fork → setsid → 脱离终端 → 永生。
四、Electron 应用中的 fork + setsid
当你在 Electron 应用里启动后台服务时,底层发生了什么?
第一步:fork
主进程调用 fork(),在内核里复制出一个一模一样的子进程。子进程刚出生时继承了父亲的全部特征------同一个进程组、同一个会话、同一个终端。
第二步:setsid
子进程调用 setsid() 系统调用:
- 断开父子关系:脱离父进程的进程组和会话
- 成为新会话首领:创建全新会话,自任 Session Leader(SID = 自己的 PID)
- 分离 TTY :主动丢弃从父进程继承的控制终端(TTY →
?)
第三步:重定向与守护
把 stdin、stdout 全部重定向到 /dev/null。自此,它变成了一个隐形在系统后台的独立守护进程(Daemon)。
五、实用指令速查




ps aux vs ps -ef
ps -ef:遵循 System V (SysV) 标准,注重进程的层级关系(父子进程)ps aux:遵循 BSD 标准,注重进程对系统资源(CPU、内存)的占用
关键指标解读
PPID(父进程 ID) :排查恶意进程时,通过 ps -ef 找到 PPID 就能溯源到谁启动了它。
VSZ(虚拟内存大小) :进程总共申请的虚拟内存,包含未实际使用的、共享库等。即使机器只有 8G 物理内存,VSZ 也可以是 10G。
RSS(常驻内存大小) :进程当前实际占用的物理内存,不含 swap。含共享库的完整大小,多进程共享时会被重复计算。
通过 STAT 判断前台/后台
ps aux 的 TTY 列:
pts/0,pts/1→ SSH 或终端模拟器打开的虚拟终端tty1~tty6→ 服务器物理/虚拟控制台终端?→ 已脱离终端(系统服务),永远不会有+状态
STAT 中的 + 是内核通过比对"进程组 ID"与"终端前台组 ID"是否一致来打上的标签。
本文基于 Ubuntu 24.04 + i7-13650HX + RTX 4060 真实环境编写。