如何改造 Linux 虚拟终端显示文字


简介: 本文就 CJKTTY 补丁如何让 linux 虚拟终端显示汉字的原理进行了讨论,为此介绍了 Linux 虚拟终端和其依赖的硬件的工作原理。过程中我们分析了 Linux 字符终端的不足之处,并向读者介绍前沿的 Wayland system compositor 是什么以及为什么 需要它。

CJKTTY 补丁是什么,为什么我写了它

当你不使用 X 的时候,打开电脑,你就在使用虚拟终端。这么多年来它工作的很好,直到它来到了中国。包含中文字符的文件名无法正确显示,中文文档无法阅读。当然可以使用 X , 但是我为什么不能让终端也能显示汉字呢?如果在 X 下我能让屏幕显示汉字,终端下一定也能。为此我开始了 internet 上的搜寻。我找到了 fbterm,这是个可以利用 /dev/fb0 实现的终端模拟器,和 XTERM 一样,只不过 XTERM 利用的 X 绘制文字,而 fbterm 直接写入 /dev/fb0。

/dev/fb0 是什么?

帧缓冲区设备。帧缓冲区是一块存储区域(内存或者显存或者其他的输出设备的存储空间),内核将其抽象为一个设备。通过访问该设备就能访问帧缓冲区。帧缓冲区的内容既是屏幕映像。由输出设备不停的扫描帧缓冲区生成显示设备的控制信号。

然而我似乎总是忘记登录后开启 fbterm,对我来说,等看到乱码的时候突然想起没有开启 fbterm 并不是那么愉快的经历。这只是其中一个缺点。fbterm 占用了帧缓冲区设备,导致 w3m 这类使用帧缓存绘制图像的终端 www 浏览器不能正常工作。许多依赖帧缓冲区设备的终端程序都不能被 fbterm 良好的兼容。于是我继续寻找,找到了 youbest 写的中文补丁。我有个喜欢使用最新内核的习惯,当内核升级导致 youbest 的补丁再也不能使用的时候,我开始到 youbest 发布补丁的页面留言,希望 youbest 百忙中能修改一下他的补丁。

Youbest 似乎很忙,在新内核的诱惑下,我决定自己在修改。那个时候我才真正的开始看内核的代码。终于了解了虚拟终端的工作原理后,我开始修改内核。由于内核内部结构变动导致 youbest 的补丁无法应用,我几乎是从头开始了开发而不是简单的将无法应用的部分进行修整。唯一得到保留的就是 youbest 补丁中的点阵字库。我将其命名为 CJKTTY , 取能显示 CJK 的 TTY 之意。

从那里获得中文显示补丁呢?

我将补丁托管在了 http://repo.or.cz/w/linux-2.6/cjktty.git,依据你使用的内核版本,签出内核版本 -utf8 分支就可以。

控制台是如何显示文字呢?

那么,为了能在控制台下显示出汉字到底需要做什么样的修改么?在开始前,我想先介绍一些名字,并介绍一些控制台在硬件上是如何进行文字显示的。首先我解释一下几个名词,知道的人可以到这里开始阅读。

UNICODE

为每一个字符分配全球唯一的一个数字,但是并没有规定这个数字的表示方法。数字的表示方法由 UTF 规范规定。UTF-16 使用 2 个字节表示一个 UNICODE 数字,但是对于 >=216的数字使用 4 字节来表达。UTF-8 则对于 <127 的数字采取单字节表示,大于 127 的数字要根据其大小选择 2~6 个字节进行表示。UNICODE 在程序内部则简单的使用 unsigned long 即可表示一个字符。

GLYPH

GLYPH 指的是字体里的字形。字符总是要在特定的字体下表示的,该表示就是字形。比如一个只包含 26 个大小写字母的字体,只包含了 52 个字形,如果该字体是先大写后小写排列的,那么数字 0 就表示字形 'A' , 数字 1 就表示字形 'B'。UNICODE 或者 ASCII 到 GLYPH 的映射是由一个称作 CMAP 的映射表做的。如果字体里字符就是按照 UNICODE 排列的,则其 CMAP 就是 UNICODE CMAP。同理也有 ASCII CMAP。 VGA 自带字体没有提供 CMAP,操作系统假定它的 CMAP 是 ASCII CMAP。事实上也是如此。

TTY

内核为终端提供的接口,对应用程序而言就是 TTY 设备。通常是使用 stdin stdout 来访问。TTY 提供各种 IOCTL 用来设置终端的模式。TTY 也提供了用户控制程序的方法,比如 Ctrl-C 终止当前程序。 TTY 可以是显示器 + 键盘构成的控制台,也可以是串口(可以通过猫链接到电话线上),可以通过 pts 模拟。XTERM 即利用 pts 为里面运行的程序提供的模拟的终端 , 对应的设备文件 /dev/pts/* 由模拟终端程序动态创建。

控制台 (CONSOLE)

控制台特指由显示器 + 键盘构成的终端。其中显示器由显卡控制,而且当前 VGA 兼容显卡有两种模式,文字模式和图形模式。Linux 即可以使用文字模式也可以使用图形模式。

控制台对于程序是无法访问的,程序只能通过虚拟终端使用控制台

虚拟终端 (VT)

如果你的电脑只有一个终端,那将是多么乏味。一个需要长时间执行的任务就能导致你什么也做不了,Linux 的多任务机制的好处荡然无存。所以,你需要更多的终端。Linux 内核使用复用机制,将一个控制台复用为多个终端 (63 个,/dev/tty1 到 dev/tty63)。按键 Alt+F1-F12 ( 如果当前在 X 中,需要再按下 Ctrl 键 ) 能在 12 个终端中进行切换。事实上你拥有 63 个终端,键盘只能切换其中的 12 个,其他的终端你可以通过 chvt 命令进行切换。

当前拥有显示器和键盘的虚拟终端被称为活动终端或者当前终端。

TTY、控制台和虚拟终端有啥区别和联系?

当你按下 Ctrl-C 的时候,当前执行的程序会被终止。因为 Linux 发送了 SIGTERM 信号给此终端的前台程序。该信号并不是由 Shell 产生,而是内核。不论是在虚拟终端下,还是在 X 里的终端模拟器里,这个功能都是一样的。终端的一大功能就是进行任务控制,另一个功能是输入输出。输入输出模式下,还可以选择行编辑模式,回显模式,设置终端速率等等。不管你使用的是何种终端,这些功能都是存在的,因为他们都是一个类型的设备。内核将他们抽象为 TTY 设备。也就是说,应用程序都是在和 TTY 这个抽象层打交道,而不是和具体的设备打交道。能作为 TTY 的设备除了控制台外,还有串口。将两台电脑的串口连接起来,其中一台电脑为串口打开登录程序(执行 /sbin/agetty ttyS0 38400),另一台就能通过可以进行串口通信的程序 ( 比如 putty、minicom) 登录对方。控制台可以作为 TTY 设备,但是一台电脑一般只有一个屏幕,也就使用一个控制台,所以 Linux 在控制台和 TTY 之间加了一层虚拟终端。由虚拟终端将控制台复用,这样就可以使用多个终端而不是只有一个了。多个虚拟终端设备合作使用一个控制台。除了串口和虚拟终端,这些都是在内核实现的 TTY 设备,内核还提供了一个叫 PTY 的为终端设备,XTERM 之类的程序利用 PTY 提供的功能可以在程序里实现 TTY 的功能。那么,虚拟终端就是利用控制台复用出了多个 TTY 。TTY 逻辑由 TTY 子系统完成,复用逻辑由虚拟终端实现,而具体的显示则交给控制台完成。如果说这是一个观察者模型的话,控制台就是观察者,它将虚拟终端的内容呈现到屏幕上。 在 Linux 下,控制台分文字模式控制台(vgacon)和图形模式控制台 (fbcon)。

文字模式控制台 (VGA 文字模式 )

 

文字模式控制台使用 VGA 兼容显卡的文字模式实现 VGA 兼容显卡初始化时默认就处于文字模式,能显示 80x25 个字符。

 

在文字模式下,显卡虽然输出给显示器的是图像,但是显卡提供给内核的却只能显示文字功能。要显示一个字符,内核将要显示的字符的代码和属性写入字符缓冲区相应地址即可。缓冲区如下面所示:


图 1. 缓冲区
图 1. 缓冲区

表 1. 每个字符的格式

属性字节字符字节
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
闪烁 背景色 前景色 GLYPH

 

VGA 显卡处于文字模式时,物理地址 0xB8000 即文字缓冲区起始地址,大小由其文字模式决定,最大 32KB 。VGA 显卡内建字符发生器,不断的扫描字符缓冲区并将文字转换为图形驱动显示器显示。其 GLYPH 即为字符的 ASCII 码。

字符发生器和字体

字符发生器内建一个或者多个位图字体。使用何种字体取决于设置的文字模式代码。Linux 默认使用 80x25 字符 16 色模式 , 每字符 8x16 ��阵。字符发生器的字体包含 256 个字形。字符发生器里的文字和字符的对应关系在 DOS 系统下被称为代码页。显卡自带的被称为 OEM 代码页。

字符发生器的字体可以修改。DOS 下通过修改代码页实现。Linux 下可以使用 setfont。

因为这一历史原因,VGA 的文字模式最多同屏出现 256 种不同的字符。对于只有 26 个字母的拉丁文字绰绰有余,但是却无法满足拥有上万字符的中文。文字模式控制台由 vgacon (drivers/video/console/vgacon.c)实现。 VGA 还有不常使用的单色文字模式,起始地址为 0xB0000 。并且字符格式也有所不同,这里不再介绍。

图形模式控制台

图形模式控制台直接操作帧缓冲区显示文字。帧缓冲区是一块存储区域(通常是在显存中),其内容就是显示在屏幕上的图像。显卡直接将其内容转化为显示器控制信号。对于程序而言,帧缓冲区就是屏幕。

图像模式控制台使用帧缓冲区作为屏幕输出,要想使用图像模式控制台,必须加载帧缓冲区驱动并有相应的硬件。

不同的显示设备通常使用不同的帧缓冲区驱动,对于 VGA 兼容显卡,除了能使用针对显卡写的帧缓冲区驱动 ( 如果有的话 ),还可以使用通用的 VESA 驱动。 VESA 驱动利用 VGA 兼容的显卡的 BIOS 将显卡转入 VGA 图形模式并设置期望的缓冲区模式,由显示模式内核参数 vga= 给出。 VGA 图形模式下,帧缓冲区的起始地址为 0xA0000 ,最大 64KB 大小。像素格式看显示色深。典型的 256 色模式下每像素一个字节。如果 BIOS 支持 VBE 扩展,能设置更大的分辨率和色彩深度,帧缓冲区大小也会超过 64KB,因而起始地址也不再是 0xA0000,具体细节请参考 维基百科. 帧缓存模式下,Linux 内核将需要显示的字符首先转换为位图,然后将位图字体写入帧缓存即可变呈现于屏幕。也就是说,图形模式下,由内核来承担字符发生器的工作。理论上来说,这个内核自带的字符发生器将能支持显示多于 255 种字符。但是我将在后面告诉读者,因为历史原因,内核实现的字符发生器和 VGA 的字符发生器有着一样的缺点。图形控制台由 fbcon(drivers/video/console/fbcon.c)实现。图形控制台由于需要生成位图字形,需要带上位图字体,编译内核的时候至少需要选择一个字体。位图字体在内核是一个大数组,不同的字体 ( drivers/video/console/font_* ) 存储在各自的数组中。

  • 1
  • 2
  • 3
  • 下一页

相关内容