目录
会话是什么
会话 是 Linux/Unix 系统中进程管理的核心概念之一。可以把会话理解为一个用户登录到退出的一次完整交互过程 ,它将一组相关的进程组织在一起,并关联到一个控制终端。
1. 会话的本质
会话(Session) 是一个进程集合,具有以下特征:
由一个会话首进程(通常是登录 Shell)创建。
所有属于该会话的进程共享一个控制终端(可选)。
会话中的进程分为前台进程组 和多个后台进程组。
同一会话的进程的**会话ID(sID)**相同
当控制终端关闭或会话首进程终止时,会话中的所有进程都会收到
SIGHUP信号。
每次你打开一个终端窗口,或者通过 SSH 登录系统,系统就会为你创建一个新的会话。这个会话就像一个"容器",把你接下来运行的所有进程都装在里面,直到你退出登录或关闭窗口。
2. 会话的层级结构
会话内部有清晰的层级关系:
会话 (Session)
├── 控制终端 (Controlling Terminal) ------ 通常是 /dev/tty* 或 /dev/pts/*
└── 进程组 (Process Groups)
├── 前台进程组 (Foreground Process Group)
│ └── 当前正在运行的进程(如 vim、top、正在执行命令的 Shell)
└── 后台进程组 (Background Process Groups)
├── 后台作业1(如 sleep 100 &)
├── 后台作业2(如 find / -name "*.txt" &)
└── ...
进程组 :由一个组长进程创建,一组相关进程的集合(如一个管道
cmd1 | cmd2 | cmd3中的所有进程属于同一进程组,cmd1 是组长)。单个启动的进程,如 ./test,自成一组。进程组与任务的关系:一个进程组被指派一个任务。会话:由一个或多个进程组组成。
控制终端:会话关联的终端设备,负责输入分发和信号传递。

| 字段 | 含义 |
|---|---|
| PPID | 父进程ID |
| PID | 进程ID |
| PGID | 进程组ID,一组相关进程的标识 |
| SID | 会话ID,一组进程组的标识 |
| TTY | 关联的终端(? 表示无终端,如守护进程) |
| TPGID | 终端前台进程组ID(控制终端当前前台进程组) |
| STAT | 进程状态 |
| UID | 启动该进程的用户ID |
| TIME | 进程累计占用CPU的时间 |
| COMMAND | 命令名称及参数 |
守护进程
我们平时创建的进程,会收到用户登陆和退出的影响,而守护进程自成一个会话,不会受到用户登陆和退出的影响。那如何让一个进程变成守护进程呢?
setsid - 创建新会话
cpp
#include <unistd.h>
pid_t setsid(void);
成功:返回新会话的 SID(等于调用进程的 PID)
失败:返回
-1,并设置errno
主要作用
创建新会话:调用进程成为新会话的唯一成员
创建新进程组:调用进程成为新进程组的组长
脱离终端:如果调用进程之前有控制终端,会失去该终端(成为无终端进程)
使用方法
调用进程不能是进程组组长(否则返回错误)
通常做法:1、忽略一些异常信号,比如 SIGCLD、SIGPIPE、SIGSTOP。2、先
fork(),父进程直接退出,子进程调用setsid()。3、更改调用进程的当前工作目录。4、由于子进程调用setsid()后标准输入/输出/错误仍指向已关闭的文件描述符,所以将标准输入/输出/错误重定向至 /dev/null(/dev/null:所有向 /dev/null 写入的字符串,会被自动丢弃,输出 /dev/null 的内容,结果为空白)。最好不要直接关闭0,1,2 文件描述符作为解决方案。守护进程的进程名通常以 d 结尾。
cpp#pragma once #include <iostream> #include <cstdlib> #include <unistd.h> #include <signal.h> #include <string> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> const std::string nullfile = "/dev/null"; void Daemon(const std::string &cwd = "") { // 1. 忽略其他异常信号 signal(SIGCLD, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGSTOP, SIG_IGN); // 2. 将自己变成独立的会话 if (fork() > 0) exit(0); setsid(); // 3. 更改当前调用进程的工作目录 if (!cwd.empty()) chdir(cwd.c_str()); // 4. 标准输入,标准输出,标准错误重定向至/dev/null int fd = open(nullfile.c_str(), O_RDWR); if(fd > 0) { dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); close(fd); } }
daemom 函数 - 简化创建过程
使用 daemom 函数可以方便的创建守护进程,该函数自动完成上述的 4 个过程
cpp
#include <unistd.h>
int daemon(int nochdir, int noclose);
nochdir:为 0 时,切换工作目录到/;非 0 时,保持当前工作目录
noclose:为 0 时,重定向 stdin/stdout/stderr 到/dev/null;非 0 时,保持原样成功:返回 0
失败:返回 -1,设置
errno
终端是什么
终端 是 Linux/Unix 系统中一个非常核心但又容易混淆的概念。简单来说,终端是用户与系统内核进行交互的入口。
可以从三个层面来理解它:物理层面、逻辑层面,以及你现在正在使用的"东西"。
1. 历史的起源:物理终端
在计算机早期,大型主机(Mainframe)昂贵且庞大,用户无法直接坐在主机前操作。
物理终端 :由键盘 (输入)和显示器(输出)组成的一套设备,通过串行线缆连接到主机。
每个坐在终端前的人,相当于获得了主机的一个会话。
虽然现在这种硬件设备很少见了,但它的概念被继承了下来。当你按下
Ctrl+Alt+F2切换到 Linux 的纯黑屏界面时,那个界面就叫 TTY(Teletypewriter,电传打字机),它模拟的就是这种物理终端。
2. 现在的形态:终端模拟器
在现代图形界面(如 Windows、macOS、Linux 桌面)下,我们通常打开的"黑框框"并不是真正的物理终端硬件,而是终端模拟器(Terminal Emulator)。
它是一个应用程序,用来模拟古老物理终端的行为。
常见的终端模拟器:GNOME Terminal(Linux 默认)、Konsole、iTerm2(macOS)、Windows Terminal、XShell、SecureCRT。
本质 :它捕获你的键盘输入,显示程序的输出,但它本身并不理解你输入的命令(如
ls、cd)。它只是一个"传话筒"。
3. 核心机制:终端 vs Shell
这是最容易被混淆的地方。你打开一个"黑框框",里面在运行程序,但终端 和Shell是两回事:
终端 :负责输入/输出(I/O)。
它提供一个窗口,接收键盘输入,显示文字输出。
它本身不解析命令。
Shell :负责解释和执行命令。
它是一个用户态程序(如
bash、zsh、sh)。它读取终端发来的输入,解析命令(如
ls),调用内核执行,然后将结果返回给终端显示。
类比:
如果系统是一个公司:
终端 (Terminal)是公司的前台/接待窗口(负责递纸条、展示结果)。
Shell 是坐在前台后面的执行人员(负责看懂纸条上的字,去干活,然后把结果拿回前台)。
验证方法 :
你可以打开终端,输入 echo $SHELL 查看当前 Shell 是什么。当你输入命令时,是 Shell 在工作;当你调整窗口大小、复制粘贴文字时,是终端模拟器在工作。
4. 常见的"终端"相关名词辨析
在日常工作中,你可能会听到以下说法,它们的含义各不相同:
| 名词 | 含义 |
|---|---|
| TTY | Teletypewriter 的缩写。在 Linux 中通常指物理终端或虚拟控制台(按 Ctrl+Alt+F1~F6 切换到的黑屏)。 |
| PTS | Pseudoterminal Slave(伪终端从设备)。当你在图形界面打开一个终端窗口,或者通过 SSH 远程连接时,分配的就是 PTS(如 /dev/pts/0)。 |
| Terminal | 泛指终端,可以是 TTY,也可以是 PTS。 |
| Shell | 命令行解释器,运行在终端之上。 |
| Console | 控制台。在 Linux 中,通常指物理连接的那一套键盘显示器(即 TTY1),权限级别通常高于普通终端。 |
总结 :你每次敲命令的那个"黑框框",在技术层面上是一个终端模拟器 ,里面运行着一个 Shell。终端负责让你"看见"和"输入",Shell 负责"干活"。
前台进程与后台进程
在 Linux 系统中,前台进程 和后台进程 是进程在终端会话中的两种运行模式,核心区别在于是否占用终端的输入输出控制权。
1. 前台进程
定义 :
前台进程是指当前与终端交互的进程。它会独占终端的标准输入 (键盘)、标准输出 (屏幕)和标准错误。在它运行期间,用户无法在该终端执行其他命令,除非它结束或被挂起。
特点:
占用终端 Shell,处于"正在运行"状态。
可以接收来自键盘的信号(如
Ctrl+C终止、Ctrl+Z挂起)。通常是用户直接执行的命令(如
vim、top、ping)。如果用 ctrl + z 暂停正在运行的前台进程,那么 bash 会被提到前台运行,接收用户输入的指令
示例:
bash
bash
# 直接运行一个命令,它默认在前台运行
ping baidu.com
此时终端会被 ping 的输出填满,无法输入其他命令。
2. 后台进程
定义 :
后台进程是指在后台运行的进程,不占用终端输入,但仍然可能向终端输出信息。用户可以在同一终端继续执行其他命令。
特点:
终端提示符立即返回,用户可以继续工作。
无法直接接收键盘信号(如
Ctrl+C无法直接终止后台进程)。如果尝试从终端读取输入(如
read命令),进程会被挂起(停止)。
如何启动后台进程 :
在命令末尾加上 & 符号。
bash
bash
# 在后台运行 ping
ping baidu.com > /dev/null &
此时终端会输出类似 [1] 12345 的信息,表示后台进程号和进程 PID。用户可以继续输入其他命令。
作业控制:
jobs:查看当前终端下的所有的后台进程。
bash[hxh@VM-16-12-centos 2026_3_24]$ jobs [1] Running ./test >> text1.txt & [2]- Running ./test >> text2.txt & [3]+ Running ./test >> text3.txt &
fg +后台进程号:将后台进程调回前台。
Ctrl+Z:挂起当前前台进程(使其停止,不运行,会显示给它分配的后台进程号)。
bg +后台进程号:将挂起的前台进程转为后台运行。
3. 关键区别总结
| 特性 | 前台进程 | 后台进程 |
|---|---|---|
| 终端占用 | 独占终端 | 不占用终端输入 |
| 用户交互 | 可以接收键盘输入 | 无法接收键盘输入 |
| 终端关闭影响 | 通常进程终止 | 可能被终止(取决于是否脱离终端) |
| 信号响应 | 可以响应 Ctrl+C、Ctrl+Z |
需要 kill 命令发送信号 |
| 启动方式 | 直接执行命令 | 命令后加 &,或使用 bg 转换 |
4. 后台进程与守护进程的区别
很多初学者会混淆后台进程 与守护进程:
后台进程 :仍然依附于某个终端(会话)。当终端关闭或用户退出登录时,后台进程通常会收到
SIGHUP信号而终止。守护进程(Daemon) :完全脱离终端,独立运行在后台,生命周期不受终端控制(如
sshd、nginx)。
如果需要让进程彻底脱离终端,可以使用:
nohup command &:忽略SIGHUP信号,并将输出重定向到nohup.out。或使用
disown命令将已有后台作业从当前 Shell 的作业表中移除。更规范的做法是使用 systemd 或编写守护进程脚本。
5. 实用场景
前台运行:交互式程序(编辑器、调试器、需要用户输入的程序)。
后台运行:长时间执行的任务(如压缩大文件、数据导入),避免阻塞终端。
混合使用 :用
Ctrl+Z挂起前台任务 →bg将其转入后台 →fg再调回。