终端里到底有什么 书架

第 08 章 / 共 12 章

第八章 · 千里之外

第七章结尾说:把 pty 的主面从「本地窗口」换成「一根网线」,你就得到了远程终端。干这件事的程序就是 ssh。这一章拆开 ssh user@server 这一行字背后的全过程。

第一步:先建立一条谁也无法偷听的通道

你敲下回车,本地的 ssh 客户端先和服务器 22 端口上的 sshd(ssh 守护进程)建立 TCP 连接。然后双方干一件数学上很优雅的事:在不安全的网络上,当着所有窃听者的面,协商出一个只有他们俩知道的密钥(这叫密钥交换,直觉类似:两人各自调一份秘密颜料,公开交换半成品,最后调出同一个颜色,旁观者却配不出来)。此后所有流量都用这个密钥加密——包括你的密码。

但还有个漏洞:万一你连的根本是台假冒的服务器呢?所以第一次连接某台服务器时,ssh 会出示服务器的「公钥指纹」并问你确认不信任(那段著名的 Are you sure you want to continue connecting?)。你答 yes,指纹就存进 ~/.ssh/known_hosts,以后每次连接自动核对——指纹变了会大声报警,因为那意味着「服务器换人了」或者「有人在中间冒充」。这套「第一次信任、之后核对」的策略行话叫 TOFU。

第二步:证明「你是你」

密码认证好理解:密码通过加密通道发过去,服务器核对。但更好的方式你肯定见过配置——公钥认证:

这就是为什么公钥认证比密码安全得多:密码是「把秘密交给对方核对」,公钥是「当场表演一个只有拿钥匙的人才做得出的动作」。服务器被攻破,黑客偷到的只是一堆公钥——没用。

第三步:服务器给你发一台「假终端」

认证通过,关键动作来了,全是第七章的积木:服务器上的 sshd 申请一对 pty,把从面交给一个新启动的 shell(作为它的 stdin/stdout/stderr),自己拿着主面。然后你的每个按键开始这样的旅程:

  1. iTerm2 收到按键,发给本地 ssh 客户端(注意:本地 ssh 已把本地终端切成原始模式——行编辑不归本地管了);
  2. ssh 把这个字节加密,发上网线;
  3. 服务器 sshd 解密,写进 pty 主面;
  4. 远端的 tty 层处理(回显、行缓冲、Ctrl-C 翻译成信号……),字节到达远端 shell;
  5. 输出原路返回:远端程序写 pty 从面 → sshd 从主面读到 → 加密 → 网络 → 本地 ssh 解密 → iTerm2 画出来。

几个推论立刻显而易见:远程的卡顿为什么那么明显——你敲的每个键都要飞越半个地球,由远端的 shell 回显回来你才看得见;为什么在 ssh 里按 Ctrl-C 杀的是远程程序——那个字节飞到服务器,被服务器的 tty 层翻译成了信号(第十章细讲);为什么远端程序的 isatty 回答「是」——它对着的是服务器上的 pty 从面,网线它根本看不见。

同一根通道还能干别的

加密通道一旦建好,就不止能传「终端字节」这一样东西:scp/rsync/sftp 传文件,走的是同一套加密和认证;端口转发(ssh -L)能把「访问本机某端口」变成「访问服务器那头的某服务」,相当于借道。理解成一条多用途的加密隧道就行,终端会话只是它最常见的一种货。

阿喀琉斯之踵:网线一断,满盘皆输

但这套结构有个致命弱点,每个用 ssh 跑过夜长任务的人都体会过:笔记本合盖 / Wi-Fi 抖动 / 网络切换,回来一看连接断了,服务器上跑了一夜的程序也没了。

用本章的模型能精确说出死因:TCP 连接断开 → sshd 进程退出 → 它手里的 pty 主面被关闭 → 内核发现「终端没了」,给从面那边的 shell 发一个 SIGHUP 信号(「挂电话」,hangup,字面意思是电传时代「挂掉听筒」)→ shell 和它带着的所有程序连环阵亡。你的程序不是被网络杀死的,是被「终端消失」连坐处死的。

一句话总结:SSH = 先建加密隧道(密钥交换 + 主机指纹核对 + 公钥挑战认证),然后服务器申请一对 pty,把「终端主面」架到网线上。你远程的一切终端体验都是本地那一套的原样搬运——包括「终端没了大家一起死」这个祖传缺陷。

那怎么才能让程序不陪葬?思路很直接:在服务器上,给程序配一台永远不会挂断的假终端。这就是 tmux,下一章。

终端里到底有什么
费曼式写法 · 由多个 AI 代理撰写与互相审校