Tmux知识文档

tmux 知识文档:从原理到实战

一份用于回顾与学习的 tmux 笔记,重点讲清「为什么需要 tmux」「它到底是什么」以及「怎么用」。


一、tmux 是什么?先理清它和 bash 的关系

很多人会把 tmuxbash 放在一起比较,但它们不是同一层级的东西:

  • bash :是一个 shell ,负责解析并执行你敲的命令(lscd、管道等)。
  • 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 回车时):

  1. 你在终端窗口按键 → 终端把字符通过 pty 发给 bash;
  2. bash 收到 ls,解析它,执行(实际是 fork 出 ls 程序去列目录);
  3. ls 的输出通过 pty 传回终端窗口
  4. 终端把这些字符画在屏幕上给你看。

终端管「输入输出的显示」,bash 管「命令的理解和执行」,pty 是它俩之间的传话筒。

2.3 进程关系:终端是「爹」,bash 是「孩子」

打开一个终端窗口时:

复制代码
终端窗口程序 (如 iTerm2)
   └─ bash          ← 终端启动时,自动拉起一个 bash 作为子进程
        └─ ls / python / vim ...   ← 你跑的命令又是 bash 的子进程
  • 打开终端 → 终端自动启动一个 bash 作为它的子进程;
  • 你在里面跑的命令,都是 bash 的子进程;
  • 关掉终端窗口 → 里面的 bash 被关 → bash 的子进程(你跑的任务)也跟着被杀。

这和后面要讲的「断 SSH 杀进程」是同一个道理:关终端 ≈ 父进程没了,子进程连坐。

2.4 为什么要分成两个东西?

把「显示」和「执行」解耦,带来很大灵活性:

  1. 同一个终端可以换不同 shell:iTerm2 里可跑 bash,也可跑 zsh、fish------终端不变,换里面的「大脑」即可。

  2. 同一个 shell 可以配不同终端:bash 既能在 Terminal.app 里跑,也能在 VSCode 内置终端、SSH 远程会话里跑。

  3. 远程场景天然适配 :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          ← 跟着被波及

具体过程:

  1. SSH 连接断开 → 远程的伪终端(pty)被关闭;
  2. 系统向你那个登录 bash 发送 SIGHUP(hang up,挂断信号)
  3. bash 收到 SIGHUP 后退出,并把 SIGHUP 传给它的子进程(你跑的任务);
  4. 子进程默认对 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「管理」的核心有两件事:

  1. 把这些 bash 托管在后台(不随 SSH 死);
  2. 把多个 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