TTY 和 X窗口系统:在Unix上的进化史

在NetBSD中,wscons控制台驱动程序既可以模拟一个VT100终端,也模拟一个Sun终端。就在最近,有一个新手问我为什么不能模拟VT220,以及wacons所模拟的终端类型和在/etc/tty文件中设置的终端类型有什么差别。这让我意识到谈谈终端模拟的历史对BSD开发人员很可能有所帮助。

首先,我会讲讲在当初设计Unix操作系统时的硬件概况。然后讲讲这些硬件对与其它硬件通讯的应用程序的支持,以及信息是如何生成的。接下来,我们回到当前(2001),比较一下X窗口系统和传统的基于终端的Unix系统。最后,对今天的模拟终端程序进行探索,目的是展示那些古老的Unix终端依旧有用。

本文中的例子均取自NetBSD,但对于所有Unix系统来说,概念是一致的 — 无论是上了年纪的PDS/Cadmus,还是年轻的BSD
Solaris或Linux。

古老的Unix系统

让我们回到Unix的石器时代,那时的计算机系统通常是一个灯光闪烁的大盒子,装有内存、存储设备,和用于运行由操作人员启动的进程的处理单元。由于硬件价格昂贵,那时的系统都是多用户系统。和现在的Unix工作站相比,那时候的机器没有显示器和键盘,数据的输入输出是经过串口线的,最开始用电传打字机(teletypewriter),稍后则是CRT终端。

电传打字机,Unix世界里称其为’tty’,把用户按键经由串口线发送到主机;主机的响应同样经由串口线一个字母一个字母地传回点传打字机,由电传打字机内置的打印机把信息打印到纸上。

对输入设备的早期支持

有些电传打字机只支持大写字母,另一些则同时支持输入并打印大小写字母。告知系统仅支持大写,还是仅支持小写,或是同时支持的方法在现在的Unix系统上已近绝迹:如果登陆账号都用小写字母,系统会假设你的终端同时支持大小写字母。但是如果你都用大写字母(或是你的电传打字机把你的输入都转为大写),系统则认为终端只支持大写字母,并在响应信息中使用大写,并加上前置的反斜杠’\’:

AIX Version 4
(C) Copyrights by IMB and by others 1982, 1996.
login: ESP
3004-033 YOU LOGGED IN USING ALL UPPERCASE CHARACTERS. IF YOUR WORKSTATION ALSO SUPPORTS LOWERCASE CHARACTERS, LOG OFF, THEN LOG IN AGAIN USING LOWERCASE CHARACTERS.
\E\S\P'S \PASSWORD:

终端是如何工作的

更先进的电传打字机提供change from black to red ribbon的功能,通过再次输入的方式实现粗体字也随处可见 — 打印一个字母,把打印机头回退(通过输入backspace),再打一次相同的字母。没过多久,电传打字机上的打印机部分就被CRT取代了。CRT通过将电子束打到涂磷的玻璃表面上来显示字母(绿色)。到了这个时候,终端的定义就变成了键盘和CRT的组合。

这个终端和电传打字机的功能是一样的 — 击键通过串行线送到主机,主机的响应逐个字母的送回来,并由终端显示出来。终端自己没有任何处理能力,因此常被成为’哑’终端。如果主机发送了一个f和两个o,屏幕上就会显示’foo’,并在后面显示一个闪烁的光标(cursor)。字符集是7位的ASCII字符集,至今仍在Unix世界里广泛使用,但也逐步被另一些竞争标准,如ISO8859-1或unicode,慢慢超越。

命令集

在送出’foo’之后,主机还要发出一个字符命令,指示’移动到下一行’,根据终端设置的不同,有时还要再发一个字符命令,要求’移到该行的开始处’。这些控制字符通常被称为CR (carriage return)和LF (line feed)。命令字符最初被编码为ASCII字符集的前32个。

由于需要使用更多的命令,32个字符很快就不够用了。人们被迫采用多字符命令的方案。这种方式往往需要一个控制序列引导(Control Sequence Introducer, CSI),后面跟一个或多个命令字节(例如:“move cursor”)和数据字节(如行/列信息)。与此同时,终端制造商开始制定针对自己产品的命令集,这让事情变得更糟,因为命令集互不兼容。具有代表性的命令集是DEC(Compaq)VT100/VT220 escape sequences(如此命名是因为所有命令都前置一个’escape’字符,ASCII 27,作为命令的第一个字符), ANSI codes。

应用程序如何与不同的终端通讯

Unix系统是真正的多用户系统,不同的终端可以通过标准串行协议连接到主机上。然而,哪些想要使用特殊终端的特殊功能的应用程序则必须设法应对终端的差异。在应用程序内部包含对特定终端的支持根本不是一个选项,因为这意味着使用该终端的所有应用都要实现类似的逻辑,没有可维护性。

另一个方法是将一个命令映射表放入主机的操作系统,该映射表把操作映射为特殊终端的特定命令。这样当应用程序想要清空整个终端屏幕,或移动光标时,它会通过该表查找到该终端的相应命令,并将其发往终端执行。如此一来,应用程序就不必记住特定终端的命令,而只要记住通用的命令。当支持一种新的终端时,只要更新映射表就好。这是一个巨大的进步!

如今,有两个库负责操作映射表。System V和linux使用terminfo库。
基于BSD的termcap库将所有终端信息放在一个文件当中,通常是/usr/share/misc/termcap. 请一定去看看!

$TERM和ttys

还有一个问题没有解决: 一个用户通过终端登录并启动一个应用,系统如何知道要模拟何种终端并使用何种命令?答案就是每个终端链接到系统的特定串口(dev/ttySomething),并且文件/etc/ttys记录了终端的类型。(请留意:这里是以BSD为例。System V使用文件/etc/inittab)

文件中记录的终端类型信息经过登陆系统传递到用户的登陆shell,而用户的所有应用都是从该shell启动的。应用程序进而可以使用termcap查询命令。

终端类型信息被存储到环境变量TERM中,如有必要,该变量还可以被设置为其它值。另外,当前使用的终端线路可以用tty命令查询。

回到当前(2001)

现在的工作站不再配置终端(硬件),相反,CRT通常直接连到主机的图形系统,输入设备则是键盘和鼠标,避免了使用慢速的串行线路。

控制台驱动和虚拟控制台

上面介绍的模型,终端能处理一种特定的命令集,仍然适用于今天的控制台驱动。控制台驱动是内核的一部分,处理来自键盘、鼠标的输入和对显示设备的输出。它通过一个软件终端设备传递数据,该设备使用和串行设备一样的接口,因此也要在/dev/ttys中配置。和之前的终端设备一样,送到控制台的信息也需要被解析。但控制台支持更多的字符集,包括各种VT100/VT220的变体,ANSI及更多。

大多数PC上的操作系统提供另一种机制,称为虚拟控制台。该机制让用户坐在一套终端(display/keyboard/mouse)面前,但可以看到多个’虚拟的’控制台,通常通过CTRL+Alt+Fn切换。系统把每一个虚拟控制台看作一个单独的线路,并且需要在/etc/ttys中对每一个都进行配置。

图形

我们已经讨论过字符数据在主机和终端之间传递,并显示在控制台上。然而,图形究竟是如何工作的还没有答案。

让我们稍微回退一段时间。终端设备制造商不断地在他们的产品上添加新的功能。其中之一就是不止显示80x24的文本,还能显示位图。理所当然支持这一功能的命令是非标准的。当时不存在类似terminfo/termcap这样应用级的标准供应用程序使用以使用设备无关的命令去操作不同的图形终端。

渐渐地,计算机被设计为具有一个主运算单元(CPU)和一个图形处理单元(GPU),两者通过一个比串行线路快得多的线路连接。于此同时,计算机网络也变得流行。

MIT的一组研究人员设计出了一个基于模块化组件的图形用户界面(GUI),组件间的关系就像经典的主机/终端。它包含一个发出命令(比如”清空屏幕”或”打开窗口”)的应用进程。命令被发往表现服务器(presentation server),由其对命令解码,并采取任何必要的操作以把屏幕涂黑或画一个带边框的矩形。命令可以来自本地或远程计算机。表现服务器知道如何与显卡通讯以生成输出;也知道如何与鼠标/键盘通讯以获取输入。输入被服务器包装为”事件”(Event)的形式并发送给应用程序。

命名

上一节的内容你觉得熟悉吗?
不?或许在我介绍上述概念的名字后你会舒服很多。

前面说的窗口系统就是X窗口系统(X Window System,以下简称X)。
应用进程被称为 X client。
负责硬件访问的表现服务器就是X server。
X server和X client之间的通讯使用X协议,基于TCP/IP协议栈。

Unix主机可以运行多个用户进程,一个运行多个X clients的主机可以向多个X server发出绘图指令。X server可以运行在本机,使用本机的显卡/鼠标/键盘,也可以运行在远程计算机上。

就在制造商们大张旗鼓地销售和VT220兼容的字符终端的同时,一种称为”X终端”的新面孔也悄悄出现了。X终端基本就是只能处理输入输出,不具备任何其它处理能力的传统字符终端的翻版, 除了在X终端上运行着X server!一般来说,X终端由一个显示器、键盘、鼠标还有一个小盒子组成。这个小盒子只负责运行X server。X终端上运行的这个X server知道如何与显卡/键盘/鼠标打交道,并把输入输入传递给X clients。

一个X终端的变种就是今天的Unix工作站,把CPU,显卡,IO设备都装进一个PC当中。这个工作站运行和硬件通讯的X server,并于若干X clients协同工作。

X终端模拟程序 — xterm

截止目前,我们谈到了Unix世界里经典的终端的概念,谈到了和主机-终端架构类似的X窗口系统。现在,我们有了X,任何人都能把PC变成一个工作站,大可以忘掉那些来回传递字符、escape sequence的晦涩难懂的终端的概念了。

但是,我们真得可以吗?让我们来看看Unix系统是如何通讯的。

今天,大多数和操作系统的互动仍旧发生在命令行界面,比如列出一个目录的内容,启动一个进程,观测进程的输出。以上既可以通过X也可以通过命令行环境。这里所说的命令行环境通常就是运行在系统控制台,或某个虚拟控制台,或X桌面中的终端窗口中的shell。

如果使用系统控制台或虚拟控制台,很明显传统的终端处理方式仍然在用,因为它是基于命令的输入输出的基础。这对基于行的程序如ls或cat,以及基于屏幕的程序如vi和top都同样适用,它们都使用terminfo或termcap库以查询控制台的功能。

但我们刚刚达成一致,控制台不再有用,因为可以利用X访问系统。可以用xterm或konsole(KDE),或gnome-terminal(Gnomes)打开一个终端窗口。在下面的例子中,我们使用xterm,但其它两个选择实际上也差不多。当启动X并运行xterm,会打开一个矩形的窗口,里面有一个正在运行的shell。你可以输入命令,该命令的输出就显示在xterm窗口中。这个时候,是X server捕获键盘输入并将其送到xterm进程。

xterm进程连接到shell进程,键盘输入因此由xterm送往shell — 就像虚拟控制台和远古的哑终端一样。shell进程从标准输入读取输入,处理命令,输出则经标准输出送回给xterm进程。xterm进而通过X协议指示X server重绘xterm窗口。

如果应用程序要清空屏幕或将光标移往指定位置又会发生些什么?就像以前一样,应用知道它所连接的终端类型,因此使用terminfo/termcap查询终端属性,并把相应的escape sequences发给xterm。xterm进程识别并解析命令,但并不把这些基于字符的命令直接发给X server,而是把该命令转为X server可识别的语言(X协议)。

有两个细节值得关注。

首先,xterm和shell的通讯是通过Unix正常的终端IO例程进行的 — 对X server和运行在命令行环境的应用之间通讯而言,X没有’发明’任何特殊的机制,而只是由xterm在两种环境之间充当’翻译’。这意味着Unix的终端处理机制在30年后的环境中依然有用。

其次,是xterm和X server之间的通讯。 和其它任何图形应用一样,xterm进程发送绘图请求给X server — 一个大的白色的矩形,一个小的黑色的矩形,移动屏幕上的一块区域到另一个位置,当然还有使用X的字库机制以使用特定的字型并将字符显示到表现服务器上。尽管自身不提供特殊的命令,xterm允许你更改X server使用的字库(字型)和其它一些配置。同时按下CTRL键和鼠标左键,你能看到调整字体的弹出窗口 — 这正是xterm向X server发出的请求。

传承

如你所见,GUI并未取代Unix系统中的传统概念,而是与其协同工作,这样一来,既有的应用无需移植就可以运行在窗口环境中;终端最早代表一种硬件,现在则更多是软件的概念。通过这样的传承,’老的’和’新的’技术具有同样的生存权。它们并非彼此竞争,而是协同工作。

Unix的简单好用让它在这么多年以后仍然生机勃勃。

结束本文之前,需要澄清一下我故意遗漏了一些内容 — 用于xterm和shell之间通讯的(伪)终端同样应用于rsh和ssh,也应用于将在不同主机上传递数据的多个进程伪装成就像在同一个主机上一样。同样,有关X系统也有很多概念没有提到,我马上能想到的有窗口管理器(window manager)和会话管理(session management)。这些都有待以后完善。

我希望你在读这篇文章时得到了乐趣和我写它时一样多。

你可能感兴趣的:(GUI)