tmux 知识文档:从原理到实战
一份用于回顾与学习的 tmux 笔记,重点讲清「为什么需要 tmux」「它到底是什么」以及「怎么用」。
一、tmux 是什么?先理清它和 bash 的关系
很多人会把 tmux 和 bash 放在一起比较,但它们不是同一层级的东西:
- bash :是一个 shell ,负责解析并执行你敲的命令(
ls、cd、管道等)。 - tmux :是一个 终端复用器(terminal multiplexer) ,在你和 bash 之间加了一层,让 bash 会话能够 持久化、分屏、可重连。
它们的关系是「包含」而非「替代」:
你 → 终端窗口 → [ tmux ] → bash → 执行命令
↑
可选的中间层
tmux 里面跑的,通常还是 bash。所以严格说不是「tmux vs bash」,而是「直接用 bash vs 在 tmux 里用 bash」。
二、基础铺垫:终端窗口和 bash 的关系
在深入 tmux 之前,先搞清楚一个更基础的问题:终端窗口 和 bash 是什么关系?
一句话:终端窗口是「外壳 / 显示器」,bash 是「里面真正干活的大脑」。终端负责显示和收键盘,bash 负责理解和执行命令。
2.1 各自是什么
| 终端窗口(Terminal) | bash(Shell) | |
|---|---|---|
| 本质 | 一个图形程序(GUI 应用) | 一个命令解释器程序 |
| 职责 | 显示字符、接收键盘输入、处理字体/颜色/复制粘贴 | 解析你输入的命令、执行它、把结果交回去显示 |
| 例子 | macOS 的 Terminal.app、iTerm2、Windows Terminal、VSCode 内置终端 | bash、zsh、fish、sh |
| 类比 | 电视机(屏幕 + 遥控器) | 机顶盒 / 播放器(真正处理内容的) |
关键认知 :终端窗口本身不认识任何命令 。你打 ls,终端只是把这三个字符「显示出来、传给 bash」,真正执行 ls、列出文件的是 bash。
2.2 它们怎么连在一起------伪终端(pty)
终端窗口和 bash 是两个独立的进程 ,中间靠一条叫 伪终端(pseudo-terminal,pty) 的「管道」通信:
┌─────────────┐ 你的键盘输入 ┌──────────────┐
│ 终端窗口 │ ───────────────→ │ │
│ (Terminal) │ pty 通道 │ bash │
│ 负责显示 │ ←─────────────── │ 负责执行 │
└─────────────┘ 命令的输出结果 └──────────────┘
工作流程(你敲 ls 回车时):
- 你在终端窗口按键 → 终端把字符通过 pty 发给 bash;
- bash 收到
ls,解析它,执行(实际是 fork 出ls程序去列目录); ls的输出通过 pty 传回终端窗口;- 终端把这些字符画在屏幕上给你看。
终端管「输入输出的显示」,bash 管「命令的理解和执行」,pty 是它俩之间的传话筒。
2.3 进程关系:终端是「爹」,bash 是「孩子」
打开一个终端窗口时:
终端窗口程序 (如 iTerm2)
└─ bash ← 终端启动时,自动拉起一个 bash 作为子进程
└─ ls / python / vim ... ← 你跑的命令又是 bash 的子进程
- 打开终端 → 终端自动启动一个 bash 作为它的子进程;
- 你在里面跑的命令,都是 bash 的子进程;
- 关掉终端窗口 → 里面的 bash 被关 → bash 的子进程(你跑的任务)也跟着被杀。
这和后面要讲的「断 SSH 杀进程」是同一个道理:关终端 ≈ 父进程没了,子进程连坐。
2.4 为什么要分成两个东西?
把「显示」和「执行」解耦,带来很大灵活性:
-
同一个终端可以换不同 shell:iTerm2 里可跑 bash,也可跑 zsh、fish------终端不变,换里面的「大脑」即可。
-
同一个 shell 可以配不同终端:bash 既能在 Terminal.app 里跑,也能在 VSCode 内置终端、SSH 远程会话里跑。
-
远程场景天然适配 :SSH 时,终端在你本地 (显示),bash 在远程服务器(执行),中间靠网络 + pty 连接------这就是远程登录的本质。
本地终端窗口 ──网络(SSH)── 远程服务器的 bash
(在你电脑上显示) (在服务器上执行命令)
2.5 串起三个概念(终端 / bash / tmux)
终端窗口(显示外壳)
└─ [tmux](可选:会话管理层)
└─ bash(命令解释器)
└─ 你的命令 (ls / python ...)
- 终端窗口:最外层,负责画面和键盘;
- tmux(可选):中间层,托管多个 bash、让会话持久化;
- bash:真正执行命令的核心。
三、核心痛点:为什么断 SSH 会杀掉进程?
这是理解 tmux 价值的关键,要从「进程的父子关系」讲起。
3.1 这里的 SSH 是「从哪到哪」的连接?
是从你本地电脑到远程服务器的网络连接。
你的笔记本 远程服务器
┌─────────────┐ ┌──────────────────┐
│ 终端 (SSH │ ── SSH 网络连接 ──→ │ sshd 服务 │
│ 客户端) │ (走网络) │ └─ 你的 bash │
└─────────────┘ │ └─ python │
└──────────────────┘
- 你在本地终端敲
ssh server,本地的 SSH 客户端 通过网络连到远程服务器上的 sshd(SSH 服务端守护进程)。 - 连上后,sshd 在远程服务器上 给你启动一个 bash(登录会话 shell)。
- 你之后敲的命令(如
python train.py),都是这个远程 bash 的子进程。
所以「断 SSH」断的是本地 ↔ 远程的这条网络连接。
3.2 为什么断了连接,远程进程会被杀?------进程树与信号
进程有父子层级,断 SSH 时会发生「连坐」:
sshd
└─ bash(你的登录 shell) ← SSH 一断,这个 bash 收到挂断信号
└─ python train.py ← 跟着被波及
具体过程:
- SSH 连接断开 → 远程的伪终端(pty)被关闭;
- 系统向你那个登录 bash 发送
SIGHUP(hang up,挂断信号); - bash 收到 SIGHUP 后退出,并把 SIGHUP 传给它的子进程(你跑的任务);
- 子进程默认对 SIGHUP 的反应就是终止。
一句话:任务是「登录 bash 的孩子」,爹(bash)因为 SSH 断了被挂断信号杀死,孩子也跟着被杀。
nohup(no hang up)这个命令的名字就来源于此------它让进程忽略 SIGHUP,从而在断连后存活。
3.3 tmux 为什么能让任务不死?
因为 tmux 把任务挂到了另一棵不依赖 SSH 的进程树上:
sshd ── bash ── tmux attach(客户端,只是"接上去看")
⋮ (这条连接断了无所谓)
tmux 服务进程(后台 daemon,爹不是 sshd)
└─ bash
└─ python train.py ← 真正的任务挂在这里
- tmux 启动时会在服务器上拉起一个后台守护进程(tmux server),你的任务实际是这个 daemon 的子孙;
- 这个 daemon 的「爹」不是 sshd,所以 SSH 断开时,SIGHUP 杀不到它;
- 你的
tmux attach只是一个「观察窗口」,断开它(detach 或断网)不影响后台 daemon 里的任务。
tmux 持久化的本质:把进程从「SSH 会话进程树」里挪到「独立守护进程树」里。
四、tmux 的本质:它到底「管理」了什么?
常见说法是「tmux 管理多个 bash 窗口」,方向对,但更准确的是:
tmux 是一个独立的「会话/终端服务进程」,它在后台托管多个伪终端(pty),每个伪终端里通常跑一个 bash。
它的层级结构(从大到小):
| 层级 | 是什么 | 通俗理解 |
|---|---|---|
| Server(服务进程) | 后台 daemon,所有东西的根 | 真正「托管」一切、让任务不死的那个进程 |
| Session(会话) | 一套独立工作环境 | 一个项目 / 一个任务组 |
| Window(窗口) | 会话里的「标签页」 | 类似浏览器 tab |
| Pane(窗格) | 窗口里分屏的一块,每个 pane = 一个 pty + 一个 bash | 真正跑 bash 的地方 |
tmux「管理」的核心有两件事:
- 把这些 bash 托管在后台(不随 SSH 死);
- 把多个 bash 的画面在一个屏幕里组织起来(分屏、切换)。
一个好记的类比
- 普通 SSH:你和 bash 之间是「一根直连的电话线」,线断了 bash 就挂了。
- tmux :中间放了一台「总机 / 答录机」(tmux server),bash 接在总机上。你打电话进来(attach)能听,挂了电话(detach / 断网)总机和 bash 照常运转,下次再打进来接着听。
五、tmux vs 普通 bash 终端:能力对比
| 能力 | 普通 bash 终端 | tmux |
|---|---|---|
| 会话持久化 | 关窗口 / 断 SSH → 进程全被杀 | 会话在后台继续运行,可随时重连 |
| 断线重连 | 断网 = 任务中断 | tmux attach 接回原会话,任务没断过 |
| 分屏 / 多窗口 | 一个窗口一个 shell,要多开得开多个终端 | 一个窗口内横竖分屏、多窗口、多会话自由切换 |
| 后台挂任务 | 靠 nohup / & |
天然支持:丢进 tmux 里就行 |
| 多人协作 | 不行 | 多人 attach 同一会话,共享屏幕 |
| 学习成本 | 零 | 要记前缀键和快捷键 |
六、基本用法
6.1 会话管理(在普通 shell 里敲)
bash
tmux # 新建会话
tmux new -s work # 新建并命名为 work
tmux ls # 列出所有会话
tmux attach -t work # 接回 work 会话(可简写 tmux a -t work)
tmux kill-session -t work # 删除指定会话
tmux kill-server # 杀掉 tmux server(清空所有会话,慎用)
6.2 前缀键(Prefix)
tmux 的所有快捷键都要先按前缀键 ,默认是 Ctrl+b,松开后再按功能键。
下文用 C-b 表示 Ctrl+b。
6.3 会话内操作速查
# ------ 会话 ------
C-b d 分离会话(detach,回到普通终端,会话转后台)★最重要
C-b s 列出会话并切换
C-b $ 重命名当前会话
# ------ 窗口 Window(类似标签页)------
C-b c 新建窗口
C-b n / p 切到下一个 / 上一个窗口
C-b 数字 切到第 N 个窗口
C-b w 列出所有窗口供选择
C-b , 重命名当前窗口
C-b & 关闭当前窗口
# ------ 窗格 Pane(分屏)------
C-b % 左右分屏(垂直分割)
C-b " 上下分屏(水平分割)
C-b 方向键 在窗格间切换
C-b o 循环切换窗格
C-b z 当前窗格全屏 / 还原(zoom)★很实用
C-b x 关闭当前窗格
C-b 空格 切换窗格布局
C-b { / } 与上/下一个窗格交换位置
起步只需记住一个:
C-b然后d------ 分离会话,这是 tmux 持久化的关键动作。
6.4 复制模式(滚动查看历史输出)
默认情况下 tmux 里鼠标滚轮不能直接滚历史,需要进「复制模式」:
C-b [ 进入复制模式,之后可用方向键 / PageUp 滚动查看
q 退出复制模式
(开启鼠标支持后可直接滚轮滚动,见下方配置。)
七、典型使用场景
| 场景 | 是否该用 tmux |
|---|---|
| 本地敲几条命令、日常操作 | 不必,直接 bash 即可 |
| SSH 跑长任务(训练、编译、数据导入) | ✅ 强烈推荐,防断线丢任务 |
| 要同时盯日志 + 操作 + 编辑 | ✅ 用分屏 |
| 远程结对调试、共享屏幕 | ✅ 多人 attach 同一会话 |
| 想要「关了电脑任务还在」 | ✅(或 nohup / screen) |
实战示例:SSH 上跑长任务不怕断线
bash
ssh server
tmux new -s train # 建个叫 train 的会话
python train.py # 跑几小时的任务
# 直接关笔记本走人,断开 SSH ------ 任务在服务器上继续跑
# 回来后:
ssh server
tmux attach -t train # 接回去,任务还在跑,输出都在
八、常用配置(~/.tmux.conf)
tmux 的默认体验一般,下面是一些广受欢迎的优化,写进 ~/.tmux.conf 即可:
bash
# 开启鼠标支持(可点击切换窗格、滚轮滚动历史、拖拽调整分屏大小)
set -g mouse on
# 增大历史滚动缓冲行数(默认仅 2000 行)
set -g history-limit 50000
# 窗口/窗格编号从 1 开始(默认从 0,不顺手)
set -g base-index 1
setw -g pane-base-index 1
# 用更顺手的键分屏(| 竖分,- 横分)
bind | split-window -h
bind - split-window -v
# 减少 ESC 延迟(对 vim 用户很重要)
set -sg escape-time 10
# 支持 256 色
set -g default-terminal "screen-256color"
改完配置后,可在 tmux 内执行
C-b :然后输入source-file ~/.tmux.conf重新加载,或重启 tmux。
修改前缀键(可选)
很多人觉得 Ctrl+b 别扭,会改成 Ctrl+a(与 screen 一致):
bash
unbind C-b
set -g prefix C-a
bind C-a send-prefix
九、tmux vs screen vs nohup
| 工具 | 能力 | 局限 |
|---|---|---|
nohup cmd & |
让单个命令脱离终端后台跑 | 不能交互、不能重连看输出 |
| screen | tmux 的前辈,功能类似 | 分屏较弱、配置不如 tmux 灵活 |
| tmux | 持久化 + 分屏 + 多窗口 + 多人共享 | 需要学快捷键 |
现在多数人首选 tmux;只需「后台跑个命令」且不需要重连交互时,nohup 也够用。
十、一句话总结
- bash 是执行命令的 shell,tmux 是「会话管理器」,在 bash 外面包一层。
- 断 SSH 杀进程的根因:任务是登录 bash 的子进程,SSH 断 → bash 收到
SIGHUP被杀 → 子任务连坐。 - tmux 的本质 :一个后台常驻的服务进程,把每个 bash 放进自己托管的伪终端里,让它们不挂在 SSH 进程树上,从而断线不死、可重连,并提供分屏 / 多窗口 / 多会话的组织能力。
- 日常本地操作用 bash 足够;一旦 SSH 远程跑长任务或要分屏,就该用 tmux。