终端里到底有什么 书架

第 02 章 / 共 12 章

第二章 · 一切皆是字节流

上一章说,终端的祖传规矩是「键盘出字节、屏幕收字节、中间一根线」。这一章把这根线彻底看清楚——因为理解终端的一切迷惑行为,最后都会回到这一句:终端里没有图形、没有界面、没有「字」,只有字节在流。

做一个三十秒的实验

打开终端,敲 cat,回车。然后随便打几个字,比如 hello,再回车。你会看到 hello 出现了两次。

这两次是谁干的?这是理解终端的关键一幕:

所以一行字显示两次,是两个人各打了一遍:内核的 tty 层负责「让你看见自己在敲什么」,程序负责「处理完之后输出」。你可以用 stty -echo 把回显关掉再试——敲什么都看不见了,但 cat 照样工作(记得 stty echo 改回来)。顺便说,输入密码时屏幕一片空白,就是程序临时把回显关了。

字节就是数字,字符只是约定

线上跑的字节,每个就是一个 0 到 255 的数字。「字符」是对数字的一套约定:ASCII 码表说 104 是 h、105 是 i。想亲眼看看?试试 echo hi | od -c,你会看到 h i \n——三个字节,最后那个 \n 就是你敲的回车。

中文稍微绕一点:UTF-8 编码里一个汉字通常占三个字节。你可以用 echo 你 | od -x 看到十六进制的 e4 bd a0。这解释了终端里一个经典怪事:为什么删一个汉字有时要按两三下退格,或者删掉半个字留下乱码?因为终端按字节处理,一个汉字是三个字节,粗暴地按字节删就会把一个汉字撕碎。

stdin 和 stdout:程序嘴边的两根管子

每个程序一出生,操作系统就在它嘴边接好两根管子:标准输入(stdin)和标准输出(stdout)。在终端里启动程序时,这两根管子的另一头就是终端:stdin 连着「你敲键盘产生的那条字节流」,stdout 连着「内核送去显示的那条字节流」。还有第三根管子 stderr(标准错误),专走报错——第六章讲管道时它很重要。

要点是:程序根本不知道屏幕的存在。cat 不认识光标、不认识颜色、不知道窗口多大,它只会往 stdout 这根管子里塞字节。把字节变成屏幕上发光的字符,是管子另一头的事。这个「程序只吐字节」的设计是 Unix 最深远的决定之一,后面几章——管道、重定向、SSH、tmux——全部建立在这两个字节流的朴素之上。

行缓冲:为什么回车之前程序什么都不知道

刚才实验里还有一个细节值得停下来:你敲 h、e、l、l、o 的时候,cat 完全没有收到任何东西——内核把字节攒着,直到你按回车才整行交出。这叫规范模式(canonical mode),又叫「行模式」:内核替你管一行的编辑,所以你可以用退格键改掉打错的字,程序收到的永远是干净的成品。

但 vim、less、以及一切要「按一个键立刻反应」的程序显然不能等回车。它们启动时会通知内核切换成原始模式(raw mode):别攒行、别回显、来一个字节立刻给我。这就是为什么 vim 里按 j 光标立刻动,而你退出 vim 的瞬间世界又恢复正常——vim 把模式切回去了。当程序异常退出忘了切回来,你的终端就会「发疯」:敲字不回显、回车不换行。老手都知道此时盲敲 reset 回车就能救回来——它干的活就是把 tty 恢复出厂设置。

这一章记住三件事:线上只有字节;你看到的回显是内核打的,程序输出是程序打的,两拨人;程序默认按行拿输入,vim 这类程序会把终端切成「来一个字节给一个」的原始模式。

字节流到程序这一头讲完了。另一头——字节流到了屏幕那边,是谁把它变成发光的字的?那些彩色的字、满屏的进度条又是谁画的?下一章讲终端模拟器。

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