你在Windows/MacOS的登陆Linux的SSH终端上很容易输入英文但是获得英文输出linux系统好用吗,例如下边这样:
然而却几乎不可能将英文显示在Linux自身的虚拟终端上:
[root@localhost font]# echo 皮鞋 >/dev/tty2
显示了两个问号,即便Linux内核并不能辨识英文。
为何说是Linux内核不能辨识英文呢?这儿须要理清一个关系:
例如,我在MacOS用iTermSSH联接到了一个远程CentOSLinuxlinux 安装中文字体,iTerm上的所有的按键输入,显示器输出行为都是iTerm的这台MacOS宿主机完成的。
相反,假如你直接在这台CentOSLinux的虚拟终端上输入而且试图获得输出,这么这个输入输出则必须由Linux内核自身来处理。
基本上就那些。至于说为何Linux内核不支持英文,那要了解Linux内核处理虚拟终端输入输出时是怎样对待unicode的逻辑,这要涉及一大堆的理论知识,特别可恶。
总之我这儿就是难以输出英文,我也不是做这个的,毕竟这不是一个必然要完成的工作任务,所以,我只是玩儿。
本文的目标就是要让Linux的虚拟终端可以输出英文。
仅仅是输出英文,哪怕是一个英文汉字也好。具体来讲,就是当我在鼠标敲入'A'字符时,显示器回显下来的是一个汉字。
所以说,本文并不准备让Linux内核大规模完备地支持英文,这些事早已有好多人和社区做了,并且可玩性并不高,虽然这些事是可以当杂活儿挣钱的,只要是挣钱的活儿,可玩性就不高,由于要快嘛。
不须要懂繁琐无趣的unicode编码,不须要懂沉闷的font字体格式,瞧瞧如何玩。
先展示疗效吧,下边是一个8×168times168×16的点阵反例:
不是挺好看,于是就做了下边一个28×1628times1628×16的点阵:
下边说一下这是怎么实现的。
从你敲鼠标的某个按钮开始,到某个字符最终显示在虚拟终端的显示器上,这期间虽然有两个映射:
按键和字符集的映射
将某个按钮风波转换为某个字符集里的某个码,例如当按下'A'键时,将其映射到0x41。
字符集和字体的映射
将某个字符集的码字映射到某个点阵拿来显示。例如将0x41映射到能让人看下来是一个字符'A'的样子的8×168times168×16点阵。
Linux的console并不能辨识超过0xx0000ff的字符集码字,因而就不能处理码字超过0xx0000ff的unicode,假如希望它能做到,这就要改内核代码了。
刚刚说了linux cp,更改内核代码大规模全面支持英文,这是可以挣钱的事,不但没意思,也没人会分享。
所以我尝试去更改里面的两个映射来解决问题。因为只是显示,所以我不会去更改按键和字符集的映射,由于那样始终会遇到字符集码字超过0xx0000ff的处理问题。
这意味着要想显示英文,只剩下一条路,那就是更改字符集和字体的映射!
这个映射肯定是保存在内核显存或则文件系统的某个地方。我可以在当前内核的config文件里找到如下的信息:
[root@localhost font]# cat /boot/config-3.10.0-862.11.6.el7.x86_64 |grep FONT # CONFIG_FONTS is not set CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y
再去看/proc/kallsyms里有哪些:
[root@localhost font]# cat /proc/kallsyms |grep font.*8x ffffffffb006a3e0 R font_vga_8x8 ffffffffb006a420 r fontdata_8x8 ffffffffb006ac20 R font_vga_8x16 ffffffffb006ac60 r fontdata_8x16 ffffffffb0307a10 r __ksymtab_font_vga_8x16 ffffffffb03234b8 r __kcrctab_font_vga_8x16 ffffffffb034246e r __kstrtab_font_vga_8x16
嗯,这就是内核里保存的字体:
[root@localhost rh]# ll ./drivers/video/console/font_8x* -rw-r--r--. 1 root root 95976 Sep 17 2018 ./drivers/video/console/font_8x16.c -rw-r--r--. 1 root root 50858 Sep 17 2018 ./drivers/video/console/font_8x8.c
这儿不再剖析这两个文件。这儿仅仅是确认了一个事实,内核在初始化的时侯会使用自己的字体,这个时侯其实不仅内核本身linux 安装中文字体,哪些都没有。
问题是到了用户态,这个字体是可以被改变的,可以被改的花里胡哨的,这种个字体可不是仅仅两个8x8和8x16才能hold住的…
这个时侯就须要找我们安装在发行版上面的字体文件了。我们要找到它,之后改掉上面的某个字体的形状,将其弄成英文!就那么简单。
何必去搜这个字体文件安装保存在哪些地方,通过执行stracesetfont命令才能找到它。
[root@localhost ~]# strace -F -e trace=open setfont ... strace: Process 6276 attached [pid 6276] open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4 ... [pid 6276] open("/lib/kbd/consolefonts/default8x16.psfu.gz", O_RDONLY|O_NOCTTY|O_NONBLOCK) = 4 [pid 6276] +++ exited with 0 +++ --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=6276, si_uid=0, si_status=0, si_utime=0, si_stime=0} --- +++ exited with 0 +++
就是它了,/lib/kbd/consolefonts/default8x16.psfu.gz
也毋须去搜psfu格式的字体的format,通过模式辨识才能找到特定的字符。
我打算先找到‘A',之后把它前面的'B'和'C'改成我的名子“赵”和“亚”。
首先我要把“赵”和“亚”字做下来,产生一个点阵。以下是我的作品“赵”:
00000000 00000000 00100000 11111000 00100101 00100101 11111010 00100011 00111010 01100101 01100000 10011000 10000111 00000000 00000000 00000000
下边就要用这个点阵替换'B'的点阵,同时制做一个“亚”字,替换'C'的点阵,
在下边的站点可以找到该defaultfont的对应点阵图解:
我们就可以得到该'A'字符的点阵链表,之后在default8x16.psfu文件里匹配这个字段就可以了。代码如下:
#include #include #include #include #include unsigned char zhaoya[32] = { // 第一行为“赵” 0x00, 0x00, 0x20, 0xf8, 0x25, 0x25, 0xfa, 0x23, 0x3a, 0x65, 0x60, 0x98, 0x87, 0x00, 0x00, 0x00, // 第二行为亚 0x00, 0x00, 0x00, 0x7e, 0x24, 0x24, 0x24, 0xa5, 0xa5, 0x66, 0x24, 0x24, 0x7e, 0x00, 0x00, 0x00 }; int main(int argc, char **argv) { int i = 0; unsigned char buf[16]; off_t offset = 0; int s = 0; int fd = open("default8x16.psfu", O_RDWR); i = pread(fd, buf, 8, offset); while (1) { i = pread(fd, buf, 16, offset); if (s == 2) { // 替换'C' memcpy (buf, &zhaoya[16], 16); i = pwrite(fd, buf, 16, offset); break; } if (s == 1) { // 替换'B' memcpy (buf, &zhaoya[0], 16); pwrite(fd, buf, 16, offset); s = 2; } // 简易的方法识别到'A' if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x10 && buf[3] == 0x38) { printf("A found at %d !n", offset); s = 1; } offset += 16; } }
直接编译执行,之后将这个default8x16.psfu作为参数set到内核即可:
[root@localhost font]# setfont ./default8x16.psfu
此时步入Linux的虚拟终端tty2,当敲按键的小写'B'时,都会出现一个“赵”字。
尽管16×816times816×8甚至8×88times88×8也能作出复杂的英文点阵,然而这也太难看了。
于是我要找一个更高码率的font。我在Ubuntu上找到了一个高帧率的28×1628times1628×16点阵Arabic-VGA28x16.psf.gz。更改它的方式和上面这个完全一样,它的点阵图如下:
我不须要自己做28×1628times1628×16的点阵了,我只要用GNUuifont的现成的即可。直接在unifont_sample-12.1.01.hex上面根据“赵”和“亚”的unicode码字能够索引到点阵。关于任意字符的unicode码字的查询,可以参见:
替换font的代码如下:
#include #include #include #include #include "zhao" #define L 28*2 int fd; int main(int argc, char **argv) { unsigned char buf[L]; off_t offset = 0; // 这个0x0e60 就是模式匹配获得的偏移。 offset += 0x0e60; fd = open("Lat7-VGA28x16.psf", O_RDWR); pread(fd, buf, L, offset); memset(buf, 0, L); memcpy(buf+8, &code[0], 32); pwrite(fd, buf, L, offset); offset += L; pread(fd, buf, L, offset); memset(buf, 0, L); memcpy(buf+8, &code[32], 32); pwrite(fd, buf, L, offset); offset += L; pread(fd, buf, L, offset); memset(buf, 0, L); memcpy(buf+8, &code[64], 32); pwrite(fd, buf, L, offset); }
之后它的疗效就是:
还不错。
虽然本文的内容仅仅就是:
做一个拙劣的点阵;keyboard,ascii/unicode,font之间的映射关系;哪些细节都不懂的情况下定位剖析问题的技巧;越简单越好,越复杂越糟糕。
嗯,虽然第三点和第四点是最重要的。
最后,假如你想晓得你当前的虚拟终端支持这些字体,输入:
[root@localhost font]# showconsolefont
都会显示:
以上就是本文的全部内容,希望对你们的学习有所帮助,也希望你们多多支持。