Skip to content

tmux 会话管理

在远程服务器上执行长时间任务时,SSH 连接随时可能意外断开——网络波动、笔记本合盖休眠、WiFi 切换、VPN 超时。普通 shell 里的前台进程在 SSH 断开后会收到 SIGHUP 信号,往往直接被终止。tmux 把会话留在服务器上,SSH 断了、任务还在跑,重连后可以恢复现场。

一、tmux 的位置在哪

SSH 是客户端到服务器的一条加密通道。这条通道断了,里面的 shell 也就没了。tmux 在服务器上启动一个独立的会话进程,SSH 只是"借来看一眼"——断开不伤害 tmux,tmux 里的程序照跑。

text
本地终端 -> SSH -> 服务器 -> tmux session -> shell / 命令 / 程序

也就是说,tmux 跑在 SSH 连接之外。ssh 只是远程操控 tmux 的工具,连接断了,tmux 还在。

tmux 里几个基础概念:

概念是什么可以类比
session一个独立的 tmux 工作区一个项目或一次变更操作
windowsession 里的标签页浏览器的一个标签页,每个里面跑着不同的终端
panewindow 里的分屏一个窗口里切成上下或左右两块,可以同时看两样东西
prefixtmux 的快捷键前缀默认是 Ctrl+b,告诉 tmux "下一个按键是给我的指令"

tmux 对运维人员来说,最核心的功能不是分屏——是会话持久化。生产机器上跑升级、迁移、批量脚本之类的事情,在 tmux 里面执行,至少不用怕断网。

二、安装和启动

bash
# RHEL / CentOS / Rocky
yum install tmux -y

# Debian / Ubuntu
apt install tmux -y

查看版本确认装上了:

bash
tmux -V

创建一个命名会话:

bash
tmux new -s ops

-s 给会话起个名字。tmux 默认会分配数字编号(0、1、2),多个会话同时存在的时候,数字根本判断不了哪个是生产变更、哪个是临时排查。起名字是个很小的习惯,但出了事要恢复会话时,"prod-change"比"2"好用得多。

列出所有会话:

bash
tmux ls

连接到已有会话:

bash
tmux attach -t ops

从会话里暂时退出(detach),回普通 shell:

text
Ctrl+b  再按 d

detach 之后 tmux 里面的程序继续跑,不会被关掉。这和按 Ctrl+d 或输入 exit 不一样——那些是真退出 shell,tmux 窗口会被关闭。

三、session 管理

session 是工作区。开始一项变更时新建一个 session,做完后关掉。

操作命令
新建会话tmux new -s ops
查看所有会话tmux ls
进入已有会话tmux attach -t ops
重命名会话tmux rename-session -t ops deploy
删除会话tmux kill-session -t deploy

在 tmux 里用快捷键操作 session:

快捷键作用
Ctrl+b ddetach——暂时离开,保留会话里的一切
Ctrl+b $重命名当前 session
Ctrl+b s列出所有 session,用方向键选一个切换进去

一段典型的用法:

bash
tmux new -s prod-change

# 执行变更命令
./deploy.sh

# 下班了,关笔记本回家
# 先 detach:Ctrl+b 再按 d

家里 ssh 回服务器:

bash
tmux attach -t prod-change
# 看到的是 deploy.sh 的完整输出,包括你离开后跑出来的部分

如果 tmux attach 时报这个:

text
sessions should be nested with care, unset $TMUX to force

意思是当前 shell 已经在另一个 tmux 会话里了。tmux 套 tmux 可以跑,但是快捷键会绕(外层的 Ctrl+b 被外层 tmux 吃掉,里层接收不到)。检查下当前是不是在 tmux 里:

bash
echo "$TMUX"  # 有值说明当前已经在 tmux 里

确认是不是已经在 tmux 里很重要,否则会误敲很多命令到错的位置。

四、window 管理

window 是 session 里面的标签页。一个 session 可以放多个 window,每个做不同的事:

window用途
0: shell执行命令
1: log看日志
2: db数据库连接
3: deploy部署脚本

快捷键:

快捷键作用
Ctrl+b c新建一个 window
Ctrl+b ,重命名当前 window
Ctrl+b n切到下一个 window
Ctrl+b p切到上一个 window
Ctrl+b 09直接跳到指定编号的 window
Ctrl+b &关闭当前 window(会确认一次)

命令行创建 window 也可以:

bash
tmux new-window -n log
tmux new-window -n deploy

-n 指定窗口名字。排查问题时把窗口名改成 logtcpdumpapp-status 这种短名,比默认的 bash 直观很多。

五、pane 分屏

pane 是在同一个 window 里把屏幕切成几块。同时看日志和执行命令的时候很好用。

快捷键作用
Ctrl+b %左右分屏(竖线切)
Ctrl+b "上下分屏(横线切)
Ctrl+b 方向键在分屏之间移动光标
Ctrl+b x关闭当前所在的 pane
Ctrl+b z当前 pane 最大化/还原
Ctrl+b 空格循环切换预设布局(水平、垂直、平铺)

命令行分屏:

bash
tmux split-window -h   # 左右分屏
tmux split-window -v   # 上下分屏

一个常见的排查场景,上面看日志、下面敲请求:

bash
# 上面一个 pane:持续看日志
journalctl -u nginx -f

# 下面一个 pane:反复请求接口
watch -n 2 'curl -fsS http://127.0.0.1/healthz || echo failed'

watch -n 2 每隔 2 秒循环执行一次命令。curl -fsS 是做健康检查常用的参数:-f 让 HTTP 错误(4xx/5xx)返回非 0 退出码;-s 静默不输出进度条;-S 搭配 -s 时让错误信息仍然显示。

分屏超过 4 个之后可读性明显下降——字太小、线太多、找焦点也慢。这时候多开个 window 比继续切分更合理。

六、复制模式和滚动

tmux 里面鼠标滚轮不一定会滚终端的输出历史。翻看之前的输出用"复制模式"。

进入复制模式:

text
Ctrl+b  [

进去后可以用键盘上下翻、PageUp/PageDown 翻页、/ 搜索。看完按 q 退出。

开启鼠标支持后,鼠标滚轮可以直接翻历史,拖拽可以选 pane 边界:

bash
tmux set -g mouse on

这句话只对当前 tmux server 生效。想每次打开 tmux 都自动开启,写到 ~/.tmux.conf

tmux
set -g mouse on
set -g history-limit 50000

history-limit 是每个 pane 能保存的历史行数,默认是 2000 行。看大量日志的时候 2000 行根本不够,可以适当调大。受限于 tmux server 的内存,几十万行还 ok,但每个 pane 都极度调大时要看在内存上的开销。

配置写完后加载:

bash
tmux source-file ~/.tmux.conf

七、长任务放在 tmux 里

适合放进 tmux 的任务:

场景原因
数据迁移时间长,不能因为 SSH 断开就退出
应用发布需要边执行边看日志、边调整
批量巡检多轮命令,结果保留在 pane 里回头翻
抓包tcpdump 可能跑很久,中途断开不能丢
数据库备份任务长,失败后要看现场输出定位原因

放到 tmux 里执行的备份例子:

bash
tmux new -s backup

mkdir -p /backup/mysql

mysqldump \
    --single-transaction \
    --routines \
    --triggers \
    app_db \
    > /backup/mysql/app_db-$(date +%F).sql

--single-transaction 在整个备份过程中保持同一个事务视图,适合 InnoDB,能减少锁表影响。--routines--triggers 带上存储过程和触发器,不然恢复时这些对象就没了。

长任务的输出保留很重要。只靠 tmux 滚动缓冲区不够稳——缓冲区被新输出冲掉也正常,而且你连回来之前跑了什么也看不到。用 tee 同时显示和存档:

bash
./deploy.sh 2>&1 | tee deploy-$(date +%F-%H%M%S).log

2>&1 把 stderr 合并到 stdout 后才进管道,tee 把流分成两份——一份继续打到终端,一份写入文件。这样即使 tmux 的滚动历史已经覆盖了,日志文件里还能找到完整输出。

八、断线恢复

SSH 意外断开,重新登录服务器后:

bash
tmux ls                           # 看有哪些会话
tmux attach -t ops                # 恢复

如果 attach 时报错说会话已被另一个客户端占用——可能是之前的连接还没被超时踢掉:

bash
tmux attach -d -t ops

-d 把那个客户端 detach 掉,然后自己接管。通常是自己断线的残留连接,但多人共用同一个系统用户(比如都登 root)时,-d 会把别人正在操作的界面清掉。这个场景下用 tmux list-clients -t ops 先看一眼有谁连着。

会话名忘了用 tmux ls 列出所有的:

text
ops: 3 windows (created Thu May 21 10:00:00 2026) [120x35]

输出里 ops 是会话名,3 windows 表示这 session 里有 3 个窗口,120x35 是创建时的终端尺寸。

如果 tmux ls 返回这个:

text
no server running on /tmp/tmux-1000/default

意思是当前用户名下没有任何活着的 tmux 会话。需要留意操作者的身份:root 创建的 tmux,普通用户是看不到的;普通用户创建的 tmux,切到 root 后也访问不到,因为 tmux server 的 socket 文件位于各用户的临时目录下。

九、screen:老环境里的备选

有些老系统上没装 tmux,但预装了 screen。screen 也能实现会话持久化。

bash
# 安装
yum install screen -y
apt install screen -y

# 创建会话
screen -S ops

# detach(离开但保留)
Ctrl+a  再按 d

# 查看会话
screen -ls

# 恢复会话
screen -r ops

screen 的 prefix 是 Ctrl+a 而不是 Ctrl+b,其他概念差不多。screen 能解决问题,但 window/pane 管理、配置体验、社区生态这几个方面,tmux 都更完善。新环境能用 tmux 就直接用 tmux。

十、一份简单的 ~/.tmux.conf

服务器上的 tmux 配置越精简越好——它是救场工具,不是日常开发环境:

tmux
# 开启鼠标支持:滚轮翻历史、点击切 pane
set -g mouse on

# 滚动缓冲区行数,排查日志时够翻一阵
set -g history-limit 50000

# window 编号从 1 开始
set -g base-index 1
setw -g pane-base-index 1

# 状态栏刷新间隔
set -g status-interval 5

base-index 1 让窗口从 1 开始编号,和键盘数字行对齐。pane-base-index 1 同理,pane 编号也从 1 开始。

服务器上 tmux 不适合堆插件和复杂配置。插件依赖 tmux 版本、依赖网络安装、依赖本地文件系统——在陌生机器和应急场景下这些不确定因素都可能失效。朴素配置在任何一台服务器上都能直接工作。

十一、速查

操作命令或快捷键
新建会话tmux new -s ops
查看会话tmux ls
恢复会话tmux attach -t ops
强制接管tmux attach -d -t ops
离开但保留Ctrl+b d
新建 windowCtrl+b c
重命名 windowCtrl+b ,
左右分屏Ctrl+b %
上下分屏Ctrl+b "
关闭 paneCtrl+b x
最大化/还原 paneCtrl+b z
进复制模式Ctrl+b [
关闭会话tmux kill-session -t ops