终端里到底有什么 书架

第 03 章 / 共 12 章

第三章 · 谁在画屏幕

上一章的结尾留下一个问题:程序只会往管子里塞字节,那屏幕上的字是谁画出来的?答案是:终端模拟器(terminal emulator)——一个和你电脑上的浏览器、微信完全同类的普通图形程序。iTerm2、macOS 自带的 Terminal.app、Linux 上的 xterm 和 GNOME Terminal、Windows 上的 Windows Terminal,全都是它。

名字里「模拟」两个字是字面意思:它在用软件模拟一台根本不存在的硬件——第一章里那台 1978 年的 VT100。你的电脑里没有串口线、没有荧光屏,但这个程序认认真真地扮演一台哑终端,认真到内核都分不出来。

模拟器的内心戏:一张字符表格

一台终端的「屏幕状态」其实出奇的简单,就是一个二维表格。比如 80 列 × 24 行,每个格子里记着三样东西:这里显示哪个字符、用什么颜色、要不要加粗。再加一个「光标现在在哪格」。这就是全部。

终端模拟器内部就维护着这样一张表。它从字节流里读字节,每读一个就更新表:

表更新完,模拟器把整张表重新画到窗口上:查字体文件找到每个字符的形状(这叫字形渲染),按格子记录的颜色涂上去。现代模拟器(iTerm2、Alacritty、Windows Terminal)会把这活交给显卡(GPU)干,因为「画一大堆小方块」正是显卡的老本行。

所以终端里其实没有「文字在屏幕上跑」。真相是:字节流进来 → 内存里的表格被改写 → 表格整体重画。你看到的滚动、闪烁、变色,全是这张表格的状态在变化。

滚回去看历史:那是表格的「仓库」

滚出屏幕顶部的行并没有消失。模拟器把它们存进一个叫回滚缓冲区(scrollback buffer)的仓库,所以你才能往上滚,看到十分钟前的输出。这也解释了一个常见误会:往上滚看到的内容,程序早就忘光了——那是模拟器自己的存货,跟正在跑的程序毫无关系。

字节到像素的完整流水线

把这一章和上一章合起来,一次输出的完整旅程是:

  1. 程序往 stdout 写字节;
  2. 内核把字节送到终端模拟器(中间经过第七章要讲的假终端);
  3. 模拟器的「状态机」按 VT100 的规矩解读字节,更新那张字符表格;
  4. 渲染器查字体、涂颜色,把表格画成窗口里的像素。

程序在整个过程中完全不知道像素的存在——这既是限制(终端里画不了真正的图表),也是自由(同一个程序在任何终端、任何系统上都能跑)。

字符也有「占地面积」:宽字符的坑

那张表格有个隐藏设定:每个格子宽度一样,这样光标位置才好算。但中文字符和 emoji 显然比一个字母宽——于是约定它们占两格(全角宽度)。问题来了:「这个字符占几格」是程序(算排版时)和模拟器(画格子时)各自猜的,两边用的宽字符表版本不同就会猜岔——这就是「中文对齐总是差一格」「某些 emoji 把表格顶歪」的根源。不是玄学,是两个程序对同一个字符的「占地面积」意见不合。

术语大扫除:terminal、console、shell

最后顺手清理三个老被混用的词:

一句话总结:终端模拟器 = 一张「哪个格子放什么字符」的表格 + 一台按 VT100 规矩改表格的状态机 + 一支把表格画成像素的画笔。iTerm2 们只是这套角色的不同演员。

不过,上面只讲了「普通字符怎么显示」。那串能改颜色、挪光标、清屏的「指令字节」——真正的魔法——值得单独一章。

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