进程、会话与终端——一次真实的 Linux Session 解剖

控制终端由进程向 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 脱离父会话,创建独立会话的过程。

flowchart TD KERNEL["⚙️ 内核 Kernel"] S1["PID=1 systemd<br/>系统全局会话"] S2["PID=4012 systemd --user<br/>用户管理会话"] G["PID=214886 gnome-terminal-server<br/>图形界面组"] TTY["🖥️ TTY = /dev/pts/X"] KERNEL --> S1 --> S2 --> G G -->|"fork + open 申请控制终端"| TTY subgraph SESS_A["🔵 会话 A · 有终端 (SID=225227)"] SA["PID=225227 bash<br/>Session Leader A<br/>STAT=Ss · TTY=pts/3"] NODE["PID=225241 node-MainThread<br/>STAT=Sl+"] OC["PID=225250 .opencode<br/>STAT=Sl+"] SA --> NODE --> OC end TTY --> SA subgraph SESS_B["🟢 会话 B · 无终端 (SID=258975)"] SB["PID=258975 bash<br/>Session Leader B<br/>STAT=Ss · TTY=?<br/>stdin=/dev/null"] end OC -.->|"⚡ fork + setsid()<br/>创立新会话"| SB style KERNEL fill:#37474f,color:#fff style S1 fill:#455a64,color:#fff style S2 fill:#455a64,color:#fff style G fill:#546e7a,color:#fff style TTY fill:#ff8f00,color:#000 style SA fill:#1565c0,color:#fff style NODE fill:#1976d2,color:#fff style OC fill:#1e88e5,color:#fff style SB fill:#2e7d32,color:#fff style SESS_A fill:#e3f2fd,stroke:#1565c0 style SESS_B fill:#e8f5e9,stroke:#2e7d32

关键数据对比

属性 会话 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() 工作原理

  1. .opencode (PID=225250) 调用 fork() 生出子进程 bash
  2. 子进程立刻调用 setsid() → 创立全新会话,自任 session leader
  3. 新会话不再绑定任何控制终端 → TTY 变为 ?,stdin 变为 /dev/null
  4. 结果:关闭 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 真实环境编写。

相关推荐
宋哥转AI6 分钟前
深入理解 AI Agent 03|RAG评估体系:量化检索增强效果,精准定位系统短板
人工智能·后端·agent
Leo2829 分钟前
数据库Executor-Selector排查实践
后端
Zane199411 分钟前
线程池入门:7 大核心参数与 4 种拒绝策略
java·后端
用户2986985301416 分钟前
无需安装 Excel,也能轻松将 CSV 转为 XLS 或 XLSX
后端·c#·excel
Gopher_HBo18 分钟前
Gin架构总览
后端
行者-全栈开发18 分钟前
【Linux内核】CVE-2026-46242:Bad Epoll Linux 内核 epoll UAF 漏洞修复指南(99%可靠root的云安全噩梦)
linux·运维·服务器·cve-2026-46242·linux内核漏洞·use-after-free·云多租户安全
BingoGo19 分钟前
PHP 8.6 新特性一览
后端·php
大陈AI21 分钟前
从“打开就卡“到“秒开“:一次 PHP GD 动态海报生成的性能优化实战
后端
他们叫我秃子23 分钟前
前端开发转 Go 全栈(三):从函数到 error,Go 连“失败”都要明确返回
前端·后端·go
我命由我1234525 分钟前
Kotlin 面向对象 - Kotlin 类变量与类方法
java·服务器·后端·java-ee·kotlin·android jetpack·android runtime