国内服务器
亚洲服务器
欧洲服务器
北美洲服务器
南美洲服务器
大洋洲服务器
非洲服务器

首页>>IDC资讯

Linux内核输出中文字符的案例

发表时间:2022-05-30 10:01:25

桂哥网络和大家分享了Linux内核输出汉字的案例。相信大部分人都不太了解,分享这篇文章,供大家参考。希望你看完这篇文章后收获很大。我们一起来看看。

可以在登录Linux的Windows/MacOS的SSH终端上轻松输入中文并获得中文输出,比如如下:

但是在Linux自己的虚拟终端:上显示中文几乎是不可能的

[root@localhostfont]#echo皮鞋/dev/tty 2

显示两个问号。显然,Linux内核无法识别中文。

为啥Linux内核无法识别中文?这里需要澄清一种关系:

你在远程SSH终端上的输入和显示输出的行为,都是SSH终端的宿主机完成的,比如Windows,MacOS,和Linux无关。

你在Linux本地虚拟终端,比如/dev/tty1上的输入和显示输出行为,则是由Linux内核自己处理的。

比如我用iTerm SSH连接到MacOS上的一个远程CentOS Linux,iTerm上的所有键盘输入和显示输出都是由iTerm的MacOS主机完成的。

相反,如果你在CentOS Linux的这个虚拟终端上直接输入并试图获得输出,那么输入输出一定要由Linux内核本身来处理。

基本就是这样。至于Linux内核为啥不支持中文,知道Linux内核在处理虚拟终端输入输出时是如何对待unicode逻辑的就很烦了。

反正我就是不能在这里输出中文,我也不做这个。很明显,这不是一个必然的任务,所以我只是在找乐子。

本文的目标是使Linux虚拟终端输出中文。

只要输出中文,哪怕是汉字。具体来说,就是当我在键盘敲入'A'字符时,显示器回显出来的是一个汉字。

因此,本文不打算让Linux内核大规模完备地支持中文.很多人和社区都做过这种事情,但可玩性不高。毕竟这种东西可以当私活赚钱。只要是赚钱的工作,可玩性不高,因为快。

你不需要知道又长又无聊的unicode编码和无聊的字体格式。看怎么玩。

先说效果。下面是一个816816816点阵的例子:

不太好看,所以我做了以下281628 乘以162816的点阵:

我们来谈谈这是如何实现的。

从您按下键盘上的某个键到某个字符最终显示在虚拟终端的显示器上,实际上有两种映射:

键盘和字符集的映射

把一个按键事件转换成一个字符集的代码,比如按下‘a’键,就映射到0x41。

字符集和字体的映射

将某个字符集的码字映射到一个点阵上显示。例如,0x41被映射到一个816816816的点阵,这个点阵可以被看作一个字符“a”。

Linux控制台无法识别超过0x00ff的字符集码字,因此无法处理超过0x00ff的unicode码字。如果您想让它这样做,您需要更改内核代码。

>刚才说了,修改内核代码大规模全面支持中文,这是可以赚钱的事,不但没意思,也没人会分享。

所以我尝试去修改上面的两个映射来解决问题。由于只是显示,所以我不会去修改 键盘和字符集的映射 ,因为那样仍然会碰到字符集码字超过0x00ff的处理问题。

这意味着要想显示中文,只剩下一条路,那就是修改 字符集和字体的映射

这个映射肯定是保存在内核内存或者文件系统的某个地方。我可以在当前内核的config文件里找到如下的信息:

[root@localhostfont]#cat/boot/config-3.10.0-862.11.6.el7.x86_64|grepFONT
#CONFIG_FONTSisnotset
CONFIG_FONT_8x8=y
CONFIG_FONT_8x16=y

再去看/proc/kallsyms里有什么:

[root@localhostfont]#cat/proc/kallsyms|grepfont.*8x
ffffffffb006a3e0Rfont_vga_8x8
ffffffffb006a420rfontdata_8x8
ffffffffb006ac20Rfont_vga_8x16
ffffffffb006ac60rfontdata_8x16
ffffffffb0307a10r__ksymtab_font_vga_8x16
ffffffffb03234b8r__kcrctab_font_vga_8x16
ffffffffb034246er__kstrtab_font_vga_8x16

嗯,这就是内核里保存的字体:

[root@localhostrh]#ll./drivers/video/console/font_8x*
-rw-r--r--.1rootroot95976Sep172018./drivers/video/console/font_8x16.c
-rw-r--r--.1rootroot50858Sep172018./drivers/video/console/font_8x8.c

这里不再分析这两个文件。这里仅仅是确认了一个事实, 内核在初始化的时候会使用自己的字体 ,这个时候毕竟除了内核本身,什么都没有。

问题是到了用户态,这个字体是可以被改变的,可以被改的花里胡哨的,这些个字体可不是仅仅两个8x8和8x16就能hold住的…

这个时候就需要找我们安装在发行版里面的字体文件了。我们要找到它,然后改掉里面的某个字体的形状,将其变成中文!就这么简单。

不必去搜这个字体文件安装保存在什么地方,通过执行strace setfont命令就能找到它。

[root@localhost~]#strace-F-etrace=opensetfont
...
strace:Process6276attached
[pid6276]open("/etc/ld.so.cache",O_RDONLY|O_CLOEXEC)=4
...
[pid6276]open("/lib/kbd/consolefonts/default8x16.psfu.gz",O_RDONLY|O_NOCTTY|O_NONBLOCK)=4
[pid6276]+++exitedwith0+++
---SIGCHLD{si_signo=SIGCHLD,si_code=CLD_EXITED,si_pid=6276,si_uid=0,si_status=0,si_utime=0,si_stime=0}---
+++exitedwith0+++

就是它了, /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>
unsignedcharzhaoya[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
};
intmain(intargc,char**argv)
{
inti=0;
unsignedcharbuf[16];
off_toffset=0;
ints=0;
intfd=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("Afoundat%d!
",offset);
s=1;
}
offset+=16;
}
}

直接编译执行,然后将这个default8x16.psfu作为参数set到内核即可:

[root@localhostfont]#setfont./default8x16.psfu

此时进入Linux的虚拟终端tty2,当敲键盘的大写'B'时,就会出现一个“赵”字。

虽然16×816 imes 816×8甚至8×88 imes 88×8也能做出复杂的中文点阵,但是这也太难看了。

于是我要找一个更高分辨率的font。我在Ubuntu上找到了一个高分辨率的28×1628 imes 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 imes 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"
#defineL28*2
intfd;
intmain(intargc,char**argv)
{
unsignedcharbuf[L];
off_toffset=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);
}

然后它的效果就是:

还不错。

其实本文的内容仅仅就是:

  1. 做一个蹩脚的点阵;

  2. keyboard,ascii/unicode,font之间的映射关系;

  3. 什么细节都不懂的情况下定位分析问题的方法;

  4. 越简单越好,越复杂越糟糕。

嗯,其实第三点和第四点是最重要的。

最后,如果你想知道你当前的虚拟终端支持那些字体,输入:

[root@localhostfont]#showconsolefont

就会显示:

以上是“Linux内核输出中文字符的案例”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注亿速云行业资讯频道!


上一篇 下一篇
最新文章

如何提高云服务器的安全系数

香港独立IP空间有什么优势

香港空间影响百度收录吗

为啥要租用美国VPS

VPS的缺点有哪些

香港vps作用在哪里

VPS的优点有哪些

外贸网站为啥选择美国vps

VPS能建多少个网站

VPS要如何选择位置

相关文章

美国服务器网站没法打开的缘由及解决方法

直播卖货服务器租用选择要看哪几点

日本vps服务器租用如何选择

美国云主机:稳定可靠的选择

sdwan四大场景

美国、日本和韩国服务器的区分及影响因素

服务器vps不稳定怎么解决

国外虚拟服务器可能中病毒的原因

快速韩国服务器好处有哪些

香港服务器代理ip有什么功能

X

截屏,微信识别二维码

微信号:muhuanidc

(点击微信号复制,添加好友)

打开微信

微信号已复制,请打开微信添加咨询详情!