Linux内核如何输出中文字符的方法示例
你在Windows/MacOS的登录Linux的SSH终端上很容易输入中文并且获得中文输出,比如下面这样:
但是却几乎不可能将中文显示在Linux自身的 虚拟终端 上:
[root@localhost font]# echo 皮鞋 /dev/tty2
显示了两个问号,显然Linux内核并不能识别中文。
为什么说是Linux内核不能识别中文呢?这里需要理清一个关系:
你在远程SSH终端上的输入和显示输出的行为,都是SSH终端的宿主机完成的,比如Windows,MacOS,和Linux无关。 你在Linux本地虚拟终端,比如/dev/tty1上的输入和显示输出行为,则是由Linux内核自己处理的。比如,我在MacOS用iTerm SSH连接到了一个远程CentOS Linux,iTerm上的所有的键盘输入,显示器输出行为都是iTerm的这台MacOS宿主机完成的。
相反,如果你直接在这台CentOS Linux的虚拟终端上输入并且企图获得输出,那么这个输入输出则必须由Linux内核自身来处理。
基本上就这些。至于说为什么Linux内核不支持中文,那要了解Linux内核处理虚拟终端输入输出时是如何对待unicode的逻辑,这要涉及一大堆的理论知识,非常烦人。
反正我这里就是无法输出中文,我也不是做这个的,显然这不是一个必然要完成的工作任务,所以,我只是玩玩。
本文的目标就是要让Linux的虚拟终端可以输出中文。
仅仅是输出中文,哪怕是一个中文汉字也好。具体来讲,就是 当我在键盘敲入 A 字符时,显示器回显出来的是一个汉字。
所以说,本文并不打算 让Linux内核大规模完备地支持中文 ,这种事已经有很多人和社区做了,但是可玩性并不高,毕竟这种事是可以当私活儿赚钱的,只要是赚钱的活儿,可玩性就不高,因为要快嘛。
不需要懂冗长枯燥的unicode编码,不需要懂枯燥的font字体格式,看看怎么玩。
先展示效果吧,下面是一个8×168\times 168×16的点阵例子:
不是很好看,于是就做了下面一个28×1628\times 1628×16的点阵:
下面说一下这是如何实现的。
从你敲键盘的某个按键开始,到某个字符最终显示在虚拟终端的显示器上,这期间其实有两个映射:
键盘和字符集的映射
将某个按键事件转换为某个字符集里的某个码,比如当按下 A 键时,将其映射到0x41。
字符集和字体的映射
将某个字符集的码字映射到某个点阵用来显示。比如将0x41映射到能让人看出来是一个字符 A 的样子的8×168\times 168×16点阵。
Linux的console并不能识别超过0x00ff的字符集码字,因此就不能处理码字超过0x00ff的unicode,如果希望它能做到,这就要改内核代码了。
刚才说了,修改内核代码大规模全面支持中文,这是可以赚钱的事,不但没意思,也没人会分享。
所以我尝试去修改上面的两个映射来解决问题。由于只是显示,所以我不会去修改 键盘和字符集的映射 ,因为那样仍然会碰到字符集码字超过0x00ff的处理问题。
这意味着要想显示中文,只剩下一条路,那就是修改 字符集和字体的映射 !
这个映射肯定是保存在内核内存或者文件系统的某个地方。我可以在当前内核的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
这里不再分析这两个文件。这里仅仅是确认了一个事实, 内核在初始化的时候会使用自己的字体 ,这个时候毕竟除了内核本身,什么都没有。
问题是到了用户态,这个字体是可以被改变的,可以被改的花里胡哨的,这些个字体可不是仅仅两个8 8和8 16就能hold住的…
这个时候就需要找我们安装在发行版里面的字体文件了。我们要找到它,然后改掉里面的某个字体的形状,将其变成中文!就这么简单。
不必去搜这个字体文件安装保存在什么地方,通过执行strace setfont命令就能找到它。
[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 的点阵,
在下面的站点可以找到该default font的对应点阵图解:
https://www.zap.org.au/software/fonts/console-fonts-distributed/psftx-centos-7.5/default8x16.psfu.large.pdf
我们就可以得到该 A 字符的点阵数组,然后在default8x16.psfu文件里匹配这个数组就可以了。代码如下:
#include stdio.h #include stdlib.h #include fcntl.h #include linux/fb.h #include string.h 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此时进入Linux的虚拟终端tty2,当敲键盘的大写 B 时,就会出现一个“赵”字。
虽然16×816\times 816×8甚至8×88\times 88×8也能做出复杂的中文点阵,但是这也太难看了。
于是我要找一个更高分辨率的font。我在Ubuntu上找到了一个高分辨率的28×1628\times 1628×16点阵 Arabic-VGA28x16.psf.gz 。修改它的方法和前面这个完全一样,它的点阵图如下:
https://www.zap.org.au/software/fonts/console-fonts-distributed/psftx-debian-9.4/Lat7-VGA28x16.psf.pdf我不需要自己做28×1628\times 1628×16的点阵了,我只要用GNU uifont的现成的即可。直接在 unifont_sample-12.1.01.hex 里面按照“赵”和“亚”的unicode码字就能索引到点阵。关于任意字符的unicode码字的查询,可以参见:
https://graphemica.com/替换font的代码如下:
#include stdio.h #include stdlib.h #include fcntl.h #include string.h #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就会显示:
本篇文章到此结束,如果您有相关技术方面疑问可以联系我们技术人员远程解决,感谢大家支持本站!
我想要获取技术服务或软件
服务范围:MySQL、ORACLE、SQLSERVER、MongoDB、PostgreSQL 、程序问题
服务方式:远程服务、电话支持、现场服务,沟通指定方式服务
技术标签:数据恢复、安装配置、数据迁移、集群容灾、异常处理、其它问题
本站部分文章参考或来源于网络,如有侵权请联系站长。
数据库远程运维 Linux内核如何输出中文字符的方法示例
相关文章
- Linux内核架构:构建一个成功的操作系统(linux的内核架构图)
- Linux内核驱动开发之路(linux内核驱动开发)
- OneStep Guide:编译Linux 内核(如何编译linux内核)
- 【Linux下如何设置中文路径】(linux中文路径)
- Linux下设置文件系统的必要步骤(linux设置文件系统)
- 使用情况探究深层次:Linux 查看 CPU 使用情况(linux系统查看cpu)
- Linux下实施防御:学习保护技术(linux防止攻击)
- Linux自带浏览器带你玩转Web世界(linux内置浏览器)
- Linux中安装.gz文件的简单步骤(linux安装.gz文件)
- Linux 内核入门:玩转内核开发(linux内核入门)
- 115网盘:让Linux使用更便捷(115网盘linux)
- 开源路上,探索Linux的多元分支(linux的分支)
- Linux 内核编译过程解析(linux内核编译详解)
- 系统Linux桌面环境:最佳选择(最好的linux桌面)
- Linux分区管理:实现有效的/var空间调配(linux分区var)
- 答:Linux操作系统:提高你的工作效率(工作用linux)
- 数的限制深入了解:Linux允许的最多文件数有限制,通过优化文件系统和内核参数可提高限制,以满足更高性能需求。(linux最多文件)
- Linux 内核中的结构体深度解析(linux内核结构体)
- 如何在Linux系统中设置和管理DNS?(linux系统dns)
- 使用 Linux 连接外网的方法简介(linux外网)
- 教程尚德Linux内核视频教程:开启一段编程历程(尚德linux内核视频)
- 如何在Linux系统中判断文件类型(linux判断文件类型)
- Linux系统磁盘分区全解析:轻松实现灵活存储和管理(linux系统如何分区)
- Linux防火墙iptables学习笔记(五)linux+iptables构筑防火墙实例
- 「探究 Linux 云盘,多层次存储与数据保护」(linux云盘)
- 如何在Linux中检查内存使用情况(linux 检查内存)
- Linux驱动之分类探究(linux 驱动 分类)
- 【Linux环境下防止DDos攻击的软件准备】(linux防ddos软件)
- Linux 内核下如何打印当前时间(linux内核打印时间)
- 抵抗Linux让你躲过黑客的攻击(攻击linux命令)
- 学会使用Linux:丰富你的信息技术技能(常用 linux系统)
- Linux操作系统:到底有多么出色?(linux操作系统怎么样)
- 意思探索Linux内核:一次令人深刻的体验(linux内核是什么)