Linux內(nèi)核如何輸出中文字符的方法示例
你在Windows/MacOS的登錄Linux的SSH終端上很容易輸入中文并且獲得中文輸出,比如下面這樣:
但是卻幾乎不可能將中文顯示在Linux自身的 虛擬終端 上:
[root@localhost font]# echo 皮鞋 >/dev/tty2
顯示了兩個(gè)問(wèn)號(hào),顯然Linux內(nèi)核并不能識(shí)別中文。
為什么說(shuō)是Linux內(nèi)核不能識(shí)別中文呢?這里需要理清一個(gè)關(guān)系:
- 你在遠(yuǎn)程SSH終端上的輸入和顯示輸出的行為,都是SSH終端的宿主機(jī)完成的,比如Windows,MacOS,和Linux無(wú)關(guān)。
- 你在Linux本地虛擬終端,比如/dev/tty1上的輸入和顯示輸出行為,則是由Linux內(nèi)核自己處理的。
比如,我在MacOS用iTerm SSH連接到了一個(gè)遠(yuǎn)程CentOS Linux,iTerm上的所有的鍵盤(pán)輸入,顯示器輸出行為都是iTerm的這臺(tái)MacOS宿主機(jī)完成的。
相反,如果你直接在這臺(tái)CentOS Linux的虛擬終端上輸入并且企圖獲得輸出,那么這個(gè)輸入輸出則必須由Linux內(nèi)核自身來(lái)處理。
基本上就這些。至于說(shuō)為什么Linux內(nèi)核不支持中文,那要了解Linux內(nèi)核處理虛擬終端輸入輸出時(shí)是如何對(duì)待unicode的邏輯,這要涉及一大堆的理論知識(shí),非常煩人。
反正我這里就是無(wú)法輸出中文,我也不是做這個(gè)的,顯然這不是一個(gè)必然要完成的工作任務(wù),所以,我只是玩玩。
本文的目標(biāo)就是要讓Linux的虛擬終端可以輸出中文。
僅僅是輸出中文,哪怕是一個(gè)中文漢字也好。具體來(lái)講,就是 當(dāng)我在鍵盤(pán)敲入'A'字符時(shí),顯示器回顯出來(lái)的是一個(gè)漢字。
所以說(shuō),本文并不打算 讓Linux內(nèi)核大規(guī)模完備地支持中文 ,這種事已經(jīng)有很多人和社區(qū)做了,但是可玩性并不高,畢竟這種事是可以當(dāng)私活兒賺錢(qián)的,只要是賺錢(qián)的活兒,可玩性就不高,因?yàn)橐炻铩?/p>
不需要懂冗長(zhǎng)枯燥的unicode編碼,不需要懂枯燥的font字體格式,看看怎么玩。
先展示效果吧,下面是一個(gè)
不是很好看,于是就做了下面一個(gè)
下面說(shuō)一下這是如何實(shí)現(xiàn)的。
從你敲鍵盤(pán)的某個(gè)按鍵開(kāi)始,到某個(gè)字符最終顯示在虛擬終端的顯示器上,這期間其實(shí)有兩個(gè)映射:
鍵盤(pán)和字符集的映射
將某個(gè)按鍵事件轉(zhuǎn)換為某個(gè)字符集里的某個(gè)碼,比如當(dāng)按下'A'鍵時(shí),將其映射到0x41。
字符集和字體的映射
將某個(gè)字符集的碼字映射到某個(gè)點(diǎn)陣用來(lái)顯示。比如將0x41映射到能讓人看出來(lái)是一個(gè)字符'A'的樣子的
Linux的console并不能識(shí)別超過(guò)0x00ff的字符集碼字,因此就不能處理碼字超過(guò)0x00ff的unicode,如果希望它能做到,這就要改內(nèi)核代碼了。
剛才說(shuō)了,修改內(nèi)核代碼大規(guī)模全面支持中文,這是可以賺錢(qián)的事,不但沒(méi)意思,也沒(méi)人會(huì)分享。
所以我嘗試去修改上面的兩個(gè)映射來(lái)解決問(wèn)題。由于只是顯示,所以我不會(huì)去修改 鍵盤(pán)和字符集的映射 ,因?yàn)槟菢尤匀粫?huì)碰到字符集碼字超過(guò)0x00ff的處理問(wèn)題。
這意味著要想顯示中文,只剩下一條路,那就是修改 字符集和字體的映射 !
這個(gè)映射肯定是保存在內(nèi)核內(nèi)存或者文件系統(tǒng)的某個(gè)地方。我可以在當(dāng)前內(nèi)核的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
嗯,這就是內(nèi)核里保存的字體:
[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
這里不再分析這兩個(gè)文件。這里僅僅是確認(rèn)了一個(gè)事實(shí), 內(nèi)核在初始化的時(shí)候會(huì)使用自己的字體 ,這個(gè)時(shí)候畢竟除了內(nèi)核本身,什么都沒(méi)有。
問(wèn)題是到了用戶(hù)態(tài),這個(gè)字體是可以被改變的,可以被改的花里胡哨的,這些個(gè)字體可不是僅僅兩個(gè)8x8和8x16就能hold住的…
這個(gè)時(shí)候就需要找我們安裝在發(fā)行版里面的字體文件了。我們要找到它,然后改掉里面的某個(gè)字體的形狀,將其變成中文!就這么簡(jiǎn)單。
不必去搜這個(gè)字體文件安裝保存在什么地方,通過(guò)執(zhí)行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,通過(guò)模式識(shí)別就能找到特定的字符。
我準(zhǔn)備先找到 ‘A',然后把它后面的'B'和'C'改成我的名字“趙”和“亞”。
首先我要把“趙”和“亞”字做出來(lái),形成一個(gè)點(diǎn)陣。以下是我的作品“趙”:
00000000 00000000 00100000 11111000 00100101 00100101 11111010 00100011 00111010 01100101 01100000 10011000 10000111 00000000 00000000 00000000
下面就要用這個(gè)點(diǎn)陣替換'B'的點(diǎn)陣,同時(shí)制作一個(gè)“亞”字,替換'C'的點(diǎn)陣,
在下面的站點(diǎn)可以找到該default font的對(duì)應(yīng)點(diǎn)陣圖解:
https://www.zap.org.au/software/fonts/console-fonts-distributed/psftx-centos-7.5/default8x16.psfu.large.pdf
我們就可以得到該'A'字符的點(diǎn)陣數(shù)組,然后在default8x16.psfu文件里匹配這個(gè)數(shù)組就可以了。代碼如下:
#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 }; 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; } // 簡(jiǎn)易的方法識(shí)別到'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; } }
直接編譯執(zhí)行,然后將這個(gè)default8x16.psfu作為參數(shù)set到內(nèi)核即可:
[root@localhost font]# setfont ./default8x16.psfu
此時(shí)進(jìn)入Linux的虛擬終端tty2,當(dāng)敲鍵盤(pán)的大寫(xiě)'B'時(shí),就會(huì)出現(xiàn)一個(gè)“趙”字。
雖然
于是我要找一個(gè)更高分辨率的font。我在Ubuntu上找到了一個(gè)高分辨率的
https://www.zap.org.au/software/fonts/console-fonts-distributed/psftx-debian-9.4/Lat7-VGA28x16.psf.pdf
我不需要自己做
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; // 這個(gè)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); }
然后它的效果就是:
還不錯(cuò)。
其實(shí)本文的內(nèi)容僅僅就是:
- 做一個(gè)蹩腳的點(diǎn)陣;
- keyboard,ascii/unicode,font之間的映射關(guān)系;
- 什么細(xì)節(jié)都不懂的情況下定位分析問(wèn)題的方法;
- 越簡(jiǎn)單越好,越復(fù)雜越糟糕。
嗯,其實(shí)第三點(diǎn)和第四點(diǎn)是最重要的。
最后,如果你想知道你當(dāng)前的虛擬終端支持那些字體,輸入:
[root@localhost font]# showconsolefont
就會(huì)顯示:
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Linux內(nèi)核漏洞淺析
- Linux內(nèi)核鏈表實(shí)現(xiàn)過(guò)程
- 淺談Linux內(nèi)核創(chuàng)建新進(jìn)程的全過(guò)程
- Linux內(nèi)核參數(shù)調(diào)整方法
- Linux 內(nèi)核空間與用戶(hù)空間實(shí)現(xiàn)與分析
- 簡(jiǎn)單談?wù)凩inux內(nèi)核定時(shí)器
- 一張圖看盡Linux內(nèi)核運(yùn)行原理
- Linux內(nèi)核啟動(dòng)參數(shù)詳解
- Linux內(nèi)核中紅黑樹(shù)算法的實(shí)現(xiàn)詳解
- Linux操作系統(tǒng)內(nèi)核編譯詳解
- Linux內(nèi)核模塊和驅(qū)動(dòng)的編寫(xiě)
相關(guān)文章
apache?httpd配置多個(gè)端口的實(shí)現(xiàn)方法
本文主要介紹了apache?httpd配置多個(gè)端口的實(shí)現(xiàn)方法,包含了兩種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-07-07在Linux中打開(kāi)、提取和創(chuàng)建rar文件的操作方法
RAR 是一種流行的文件壓縮格式,以其高效的壓縮算法和將大文件壓縮為較小檔案的能力而聞名,雖然 Linux 本身支持 ZIP 和 TAR 等常見(jiàn)檔案格式,但處理 RAR 文件需要額外的工具,在這篇博文中,我們將探討如何在 Linux 中打開(kāi)、提取和創(chuàng)建 RAR 文件,需要的朋友可以參考下2024-04-04win7安裝xampp提示windows找不到-n文件(安裝成功后,443端口占用,apache服務(wù)器無(wú)法正常啟動(dòng))的解
這篇文章主要介紹了win7安裝xampp提示windows找不到-n文件(安裝成功后,443端口占用,apache服務(wù)器無(wú)法正常啟動(dòng))的解決方案的相關(guān)資料,需要的朋友可以參考下2016-09-09