SSH / Tmux / SCP 全链路详解(服务器内部视角)
一、SSH 登录:远程服务器内部发生了什么?
1️⃣ 常驻监听
远程服务器启动后,系统中常驻一个sshd 主进程:
socket(AF_INET, SOCK_STREAM, 0); // 创建 TCP 接口 bind(port=22); // 绑定 22 号端口 listen(); // 持续监听监听者:sshd(主)
权限:root
职责:只负责“接电话”
2️⃣ 你发起连接:fork 出子进程
当你在本地执行:
ssh user@server服务器内部:
TCP 三次握手完成
sshd(主)调用:
fork();生成一个sshd(子):
专门服务你这一条连接
主进程继续监听别人
3️⃣ sshd(子) 申请 PTY(伪终端)
sshd(子)向Linux 内核申请终端:
open("/dev/ptmx");内核创建PTY(一对):
名称 | 作用 |
|---|---|
PTY Master | 网络端(sshd 用它写/读) |
PTY Slave | 终端端(bash 看到的“假屏幕”) |
4️⃣ fork + exec 出 bash
sshd(子)再 fork 一次:
fork(); exec("/bin/bash");结果:
sshd(子) └── bash ├── stdin → PTY Slave ├── stdout → PTY Slave └── stderr → PTY Slave5️⃣ 你输入命令时发生了什么?
python train.py流程:
本地 SSH client → TCP → sshd(子)
sshd(子) 写入 PTY Master
内核 → PTY Slave
bash 读到命令
bash
fork()→exec("python")python:
使用CPU / 内存
通过CUDA 调用GPU
读写远程磁盘
输出原路返回:
python → PTY Slave → PTY Master → sshd(子) → TCP → 本地屏幕二、断网后:为什么普通 SSH 会死?
1️⃣ 断网瞬间
TCP 连接断开
sshd(子)发现网络 EOFsshd(子)退出PTY Master 被关闭
2️⃣ 内核发 SIGHUP
内核发现:
“PTY 的控制端(Master)没了”
于是:
向会话首进程(bash) 发送SIGHUP
bash 默认终止
bash 向前台进程组(python)转发 SIGHUP
python 退出
3️⃣ PTY 被回收
bash 关闭 PTY Slave
无人引用 PTY
内核回收 PTY
✅这就是“一断网,训练全没”的根本原因
三、Tmux:为什么断网后还能活着?
1️⃣ Tmux 做了什么?
你在 SSH 里输入:
tmux new -s train服务器内部:
bash fork → exec →tmux client
tmux client fork →tmux server
tmux server:
被
systemd/init收养(PPID=1)不依赖 sshd(子)
tmux server:
自己申请 PTY
fork → bash'(tmux 内 bash)
tmux client:
通过Unix Socket 连接 tmux server
充当 sshd(子) 与 tmux server 的桥梁
2️⃣ 断网时发生了什么?
组件 | 命运 |
|---|---|
sshd(子) | 退出 |
tmux client | 退出 |
tmux server | ✅ 活着 |
tmux PTY | ✅ 开着 |
bash' | ✅ 活着 |
python | ✅ 继续跑 |
✅因为 tmux server 的父不是 sshd,而是系统 init
四、SCP:为什么不能只用 SSH?
1️⃣ SSH 的局限
SSH 只能:
执行远程命令
传输终端 IO
❌不能把本地文件“变”到远程磁盘
2️⃣ SCP 的原理
SCP 本质是:
基于 SSH 加密通道的“文件复制工具”
执行:
scp train.py user@server:/home/user/内部流程:
本地 scp:
打开
train.py读文件内容
通过 SSH 通道发送
远程 sshd:
启动临时 scp 进程
把数据写成
/home/user/train.py
👉SCP 传的是“文件数据”,不是命令
3️⃣ SSH vs SCP 一句话区别
工具 | 传什么 | 用途 |
|---|---|---|
SSH | 命令字符 | 远程执行 |
SCP | 文件内容 | 远程存文件 |