Jul 30

抱歉, 居然半年多不更新.  其实不是不更新啊, 而是我迁移到了 http://microcai.org 了.

 

当然, 如果有比较好的文章, 我还是会 post 到这里的.

 

多谢支持.

 

 

Dec 29

 

一直免费给 Gentoo 打工。默默的维护 overlay。
昨天,哦,是前天了,刚刚过了12点。给群里一个人帮忙安装Gentoo,陆续的帮了几次,回答了几个问题。他说麻烦我很久了,得表示一下物质感谢。
于是就捐助了我一些物质。
虽然钱财是身外物,但是一直过着 0 收入生活的我,还是很感动。在此特别留纪念。

PS:

他还给我的 qbasic 编译器提交过补丁,如果你们关注 qbasic 编译器的提交历史就能找到他。 :)

Dec 15

一直以来我都表达 python 是个糟糕语言的观点,但是没有深入的解释。

计算机一直都是“工具”,意味着我们是拿它干活的,也就是所谓的提高生产力。

 

指挥计算机干活的重要工具就是编程语言。计算机并不是训练来干活的,是编程来干活的。编程语言的效率有2个指标:编写干活指南的效率和机器人执行的效率。

 

在人力成本低于硬件的时候,人追捧的是执行效率。

人力成本越来越高的时候,人开始追求编程的效率。

人自然是希望一个语言能两头兼顾。可惜的是 python非但没有带来执行效率(这是python不追求的,所以姑且不算缺点),连它拼命牺牲执行效率希望换来的开发效率事实上也一点没有。

 

执行效率:首先,执行效率和语言本身高级不高级是没有任何关系的。执行效率的高低只关系到冗余操作的多寡。这也是“优化”的基础,去除冗余操作。

冗余操作的多寡通常有3个因素影响到:1 编译器的效率  2 程序员的水平 3 语言本身的累赘

但是衡量一个语言本身效率的,事实上应该是最后一个 "语言本身的累赘",这个才是编译器永远无法改进的,程序员水平再高也无能为力的。

不幸的是,python是一个本身的累赘非常多的语言。而累赘最少的语言,应该算是C++语言了。所有C++用到的功能,没有一个是可以在别的语言用更低的代价实现的。当然,有的语言压根就没有C++提供的功能,必须自己模拟。模拟的代价和C++提供的是一样的,水平不够的人来模拟只能获得更烂的结果。

当然 ,python是个高级语言,语法糖多点,性能烂就接受一下吧!毕竟开发效率高呢(?)

接下来我们说编程的效率。编程效率主要是受5个因素影响:语法是否自然,语义是否凝炼,文档或者或教程多不多,库是不是丰富,开发环境好不好。不用说python的文档还是很多的,但是显然没有c++多。各种粗制滥造的c++教程铺天盖地,算了,这是c++的坏处。误人子弟的教程太多。库当然是C++最丰富了。python还面临着 python3 和 python2 的分裂。

 

开发环境和库一直是C++的优势。不用说 Visual C++这种重量级公司出的IDE ,还有 kdevelop , eclipse CDT 这些免费开源的IDE。自动完成和代码提示让你写代码的时候非常轻松。相比之下 , python 就没有好的IDE了。

当然,接下来是程序员最关心的,语法是否自然,语义是否凝炼。python 的语义自然是很多的,一条语句能相当于写几千行 C 代码。这也是人常说的,语法糖多。可惜的是,C++一样有高级语法糖,而且代价很低!比python低太多了。在这一点上,python没有优势,只有劣质。python的语法糖是以牺牲性能换来的,而C++在不损失性能的同时提供了语法糖。

 

至于语法自然不,仁者见仁智者见智了。对于大多数C学过来的人来说, 自然是C++的语法简单。当然,前提是不使用模板这种高级货。真的用模板的话,模板是属于用起来简单,写起来难的语法糖。我们自然可以选择把困难留给 boost,快乐留给自己。所以这点上 C++没有输。何况python还不支持模板。

 

那么开发效率到底是 C++高还是 python呢?

差不多!

那执行效率呢? 

C++和python不是一个档次的,没法比。

 

好了,单从这点已经 python 完败。不过我想说的还不是这个。

 

如果真的有语言像python那样慢,我觉得对得起它的性能的,就必须拿出像样的功能,这个功能包括

 

语言级的并行能力,语言级的多进程能力 (等等,这不就是shell么!),语言级的SIMD能力(语言级的矩阵运算支持)

语言级的复杂数学公式计算能力  ,内置的标准各种算法(STL笑而不语,不过我要的还不止STL),描述性语义(而非指令性语义)

 

我要求的这些,是高级语言需要具备的,而 python 统统没有,连 shell 都能占上一些!

 

 

 

 

Nov 17

 

编译原理:
 
        lexical analyze -> syntax analyze  -> build Abstruct Syntax Tree -> convert 
AST to  Concreat Syntax Tree -> Generating  "register reallocate" intermediate  
language -> Generating NATIVE assembly -> native machine code -> executable 
object .
 
为了学习编译原理, 找了个简单的语言编写编译器. 我选择了 QBASIC 这个人人都会的语言.
 
QBASIC 曾经有编译器, 在 16位时代的时候. 到了 32位时代, BASIC 语言就进入脚本行列, 解释执行
了. 到64位时代嘛 .. ... 恩恩, 没有编译器了. 解释器还算是有开源的几个.
 
QBASIC 语言漂亮,优雅别致. 简单易学. 大家不用它的原因无外忽无法和C 和谐共存. 而无法和C和谐
的原因很简单: 不是编译为本机代码.
 
所以, 我决定开始写一个QBASIC编译器, 能利用现代硬件, 利用上现代意义的优化能力的编译技术来
为 QBASIC 加速!
 
QBASIC 比 Go 语言简单多了, 如果能用编译解决效率问题, 自然打败Go不在话下.
 
幸运的是我们有 LLVM . 我只需要写个前端就可以了. 生成平台汇编的事情全权交给 llvm 打理了.
So, 这就是我的想法, 经过5 天紧张的开发, 已经实现基本功能了.
 
有人说我又写一个编译器, 是个国际笑话, so , 主题就如此了.
 
 
在这里宣布这样的事情是因为觉得可能找到共同兴趣的人能一起研究开发编译器.
 
我设定了3个目标:
 
        1 实现完整的 QBASIC 语言. 至少是除了不建议使用的功能, 其他都要实现.
 
        2 实现和C的无缝集成. 我将 BASIC 的函数调用约定设定为和C语言一致来解决这个问题. 因为是本
机码, 所以使用外部库的任务和C语言一样,交给链接器完成. 使用动态链接库不需要语言和运行时本
身的任何支持. 
 
        3 进行未来方向的语言扩展.
 
 
最后, 最重要的, show me the code 
请移步访问 https://github.com/microcai/llvm-qbasic
 
 
实现方面的细节考虑:
1)      QBASIC 支持数组, 数组的下标可以在定义的时候设定起始点. 第二, 数组自动扩展
        实现办法: [1] 数组实现为一个 STL 容器. 对下标的访问由编译器自动转化为对 operator [] 的调用.
operator 函数由编译器自动生成. [2] 内置类型的operator则由运行时实现. 以便进行快速实现更好
的算法 [2] 变量离开作用域后要自动调用构析函数释放资源. 这样数组才不会导致内存泄漏.
 
2) 字符串. QBASIC 的字符串是内置类型. 字符串可以进行很多操作. 内存自动管理. 字符串可以做 + 
法和比较运算.
        实现办法: 字符串实际上为一个结构体, 由运行时库实现. 编译器自动插入对运行时的调用来完成
字符串操作. 字符串离开作用域要自动调用释放函数释放内存
 
3) PRINT/INPUT 语句. 和C不一样, BASIC 把 IO 操作内置为语言的一部分.
        到目前为止我的编译器已经实现了部分的 PRINT 指令. 对打印到屏幕的PRINT语句直接转化为对
printf的操作. 这样一个不使用字符串,不输出到文件的QBASIC程序, 编译后将只依赖 C 运行时.
        PRINT 有个可选的参数 #(数字) , 表示打印到数字指示的文件中. 这个必须由运行时来完成了.
 
 
4) 所有的运行时我都放到了  libbrt -   the qBasic RunTime library  -里了. 目前为止没有任何实现. 运
行时默认将采取静态链接的方式. 对于生成脱离运行时的程序非常有用.
 
Nov 1

传送门: http://microcai.gsalex.net/2012/10/内存管理随笔

 

思考一:

任何一个程序,只要不是 helloworld 那样的简单程序,必然会用到内存管理。内存管理是写程序不可避免的过程。C程序员最大的恶魔就是野指针和内存泄漏。这是每个C程序员的噩梦。C++继承了 C的缺陷,基本上半斤八两。而且C++还可以自由重载new操作符,给内存管理更加重了复杂性。
我常常在想,写BASH脚本的时候,我们有管理过内存么?即便是java那样的语言,内存管理也是后台进行的,并不是可避免的,俗称GC。可是写脚本的时候,真的完全没用内存管理方面的困扰。

到底是哪里出了问题呢?到底为何脚本就不用管理内存?

思考二:

能不能完全不依靠malloc/free new/delete 编写出一个 c/c++程序呢?

结果是显然的,能!

但是只有对 hello world 那样的程序才有用。最近的一项小随笔项目证实了我的想法 https://github.com/microcai/hm 。在这个程序里,我没用使用任何 malloc/free new/delete 来管理内存。

为何这个程序可以不用内存管理呢? 于是在另一个随笔项目里,我依然使用了内存管理,虽然是智能指针自动管理的,但是毕竟使用了 new 操作   https://github.com/microcai/googleproxy 。我在想,凭啥这个程序就必须使用  new 了?

思考三:

程序,说到底就是一个状态机。在 hm 程序里,我采用的是“过程化”编程,外加同步多进程的IO模型。在 googleproxy 程序里,使用的是单线程异步IO的模型。

hm里,状态机的状态就是进程的状态。一步一步执行下去,状态随之切换。cpu执行到哪一行,哪一行就是当前状态。

googleproxy里,情况有了变化,因为使用了异步,所有的状态变化都是围绕一个中心进行的: boost::io_service::run() 。 在 run() 里,通过回调来通知状态的变化。也就是说,cpu执行到哪个回调,哪个就是当前状态。

区别是什么:

在不同的状态之间,我们有数据要共享! hm 多使用局部变量,不同的状态需要共享数据,通常也处于同一代码层级!可以直接引用需要的数据!

googleproxy 里,不同的状态之间,是不同的 run() 操作的回调,需要共享数据,但是上一个状态的局部变量已经消失!上一个状态的局部变量已经消失!

要跨栈域引用共享数据,唯一的办法就是将数据创建在堆上!

简单的来说,在一个程序里,所有代码的执行路径可以归纳为从 main() 开始的一个调用树。一个函数只能引用本层和上层的局部变量,无法引用子层和兄弟层和兄弟的子层的局部变量。因为这些地方的变量都是不存在的呀!

所以,只能将共享数据创建在堆上。这样才能跨过调用树的生存周期!

这也是唯一需要堆管理的理由。

如果一个变量要进入函数的时候创建在堆上,退出的时候释放,通常的做法其实就是使用栈变量,或者是使用可变长度的stl容器。

如果返回值是对象,也不要使用指针了,直接返回对象吧!别担心临时对象拷贝开销了。

只有当跨栈域共享对象的时候,才考虑使用堆吧!
记住:即便如此,也不要使用裸指针。裸指针只用来进行直接内存访问(底层编程的时候),千万别用来引用对象。使用智能指针吧!放弃对象的拷贝开销的担心 吧! 那几百个周期的拷贝操作比 new/delete 的内存管理代码的开销相比,还是低太多里。何况有了 c++11 的 Move 语义,很多时候已经没用拷贝开销里。

 

放心大胆的脚本化C++程序吧! 实在需要跨栈共享数据的时候,使用 shared_ptr 引用吧!

Oct 12

 

曾几何时,学会了C语言。安装了VC6这个神叉IDE。

然后被迫开始CPP路程。由于C++兼容C,所以一直在以C的方式写C++。然后慢慢的开始学写C++代码。写C++代码是从MFC开始的。慢慢的,我学会了用class,感觉是个比struct好用的多的结构体。再慢慢的,我学会了继承,还有... 多重继承。

MFC 就是我的导师。开始不停的向MFC学习。既然用了c++的继承,就认识到了继承的陷阱。继承后,构造函数和构析函数的执行次序,等等。还有虚继承后的各种问题和陷阱。

一一去了解。

 

在程序结构方面,开始向 MFC 看齐。喜欢把任何操作都包装到class里。设计class的时候,开始过度设计。总考虑到某天我会需要继承它。说不定还需要多重继承。等等。明明已经设计好class,却喜欢继续添加功能。永远用不到的功能。只因为我“未来可能需要在别的程序里用,可能需要继承它。”

任何一个程序,我都为它设计对象,全局的程序对象又有子对象,子又有子,子子孙孙无穷尽也~

成员变量一多,构造函数里成员变量初始化代码就变多。然后设计构造函数重载。写更多的用不到的构造函数。

我没有意识到,我已经犯了过度设计的毛病了

 

而c++另一个强大的功能“模板”我却一无所知。当我发现了c++还有模板功能的时候,我只是简单的尝试了一下,发现模板无法在VC下很好的被自动完成。又听说模板会导致代码膨胀,诸如此类。我只小用一下模板。就丢弃了模板。继续我过度设计的c++之路。

某一天,我开始Linux之路。

当我开始写出第一个GTK程序的时候,发现了C之美。C可以写出和C++一样的对象代码。而且没有了 C++的许多陷阱。

也正因为C,我开始摒弃了过度设计。所设计的API皆以现阶段够用为限。未来怎么样,未来再添加好了。没有了c++的包袱,再也不设计class了。都是简单的函数。函数完成一个“过程”。而不再是操作一个“对象”。让我把精力放到了“过程”本身,而不会再去设计“漂亮的对象”。

之后阅读到了Linus对C++的一篇讽刺邮件。对比自身,多多感触。c++确实是个容易写出糟糕代码的语言。

从此发誓不再用c++,只以C写代码。

 

再一次拾起C++,是因为c++11的发布,有了一些新的语法糖。我虽然不再打算写c++代码,但是没打算不再读别人的c++代码。如果别人用c++11写了新的代码,我确看不懂,不是损失是什么。我决定看一看c++11。

c++11最令我着迷的新特性就是lambda。还有新的for语法。当然,boost::bind 和 boost::function 进入了 std。成为的标准的一部分。我对 std::function 的印象只限于“更好的函数指针替代”。

lambda 一用,发现非常不错。好多麻烦的 static 函数都可以被匿名的 lambda取代了。我喜欢这种让名称空间更干净的感觉。

我开始纠结lambda了。动摇了。如果只用c++兼容C的那部分,然后lambda,就是带lambda的C嘛!这样自欺欺人的开始用C++。

参与了朋友的一个小项目。他是个重度c++粉。但是因为他的关系,我开始真正的拿起了boost。他说,没有boost他就没办法写代码。

这里有个小插曲,我在Linux下一直用eclipse编程,但是也因为boost把eclipse搞崩溃了,重拾了KDevelop。结果发现KDevelop已经不再是我第一次使用的时候(Kdevelop 3.? 的样子,好多年前的尝试。)那样使用正则匹配的自动完成了,真正的和eclipse一样是通过语法解析获得的自动完成列表。这导致KDevelop的自动完成功能异常强大,和 eclipse一样智能。所不同的是,eclipse在解析boost的代码的时候会时不时的崩溃,而且eclipse花了很久很久的假死状态解析boost头文件。毕竟是java开发的,速度还是不行啊!但是KDevelop完成boost头文件的解析只需要几秒钟。第一次打开cpp文件的时候,KDevelop在后台花了及秒钟解析了boost头文件,然后打代码的时候boost的所有功能就全部都能提示出来了。和eclipse一样的自动完成,速度快了几百倍,我马上切换到了KDevelop,并卸载了eclipse。

其实之前也尝试过boost,都被假死状态的eclipse弄怕了。所以关了eclipse的index功能没了boost的语法提升瞎写过。

但是换到KDevelop后那畅快淋淋的感觉,打出 boost:: 后那完善的提示,我马上开始全身心的试用boost。

就是这一试用,让我重回了c++的怀抱。重回c++后,我发现了c++正确的用法:

  • class只能作为基础,构建比int/long/char要高级点的对象。绝不能用来构造程序本身。像MFC那样搞个 CMyApp 绝对是对c++的侮辱。也就是说,程序主体仍是C语言式的。
  • 可以用class构建string,构建bigint,用重载为bigint编写大数乘法... 这才是 class 和运算符重载的意义。绝对不能肆意滥用。
  • 必要的时候编写模板代替宏。
  • 模板更好的用法是使用boost这样现成的模板库。
  • 设计回调函数,使用 std::function/boost::function。这样可以使用 std::bind/boost::bind 绑定任意多的参数给回调函数用,避免C里的void*user_data设计。
  • 虚回调函数绝对是个傻逼设计。迫使用户使用继承并进行虚成员函数重载。
Sep 17

闲来上网,看到了 http://coolshell.cn/articles/8138.html 在反对算法面试。

他说的对也不对。他认为算法很重要,但是不应该出纯算法题目。不应该出算法题目,我赞成,但是如果说算法很重要,我就要哈哈大笑了。

算法什么也不是。

软件是一种艺术。如果艺术能用算法描述那他也就不能称为艺术了。没错,有时候确实需要算法。排序么?恩,需要一些排序算法。

 

但是就这样理解为软件为算法的推切就大错特错了。这就和把油画理解为油墨的堆砌一样可笑。

艺术需要东西去表现,对于绘画,是墨水和纸张。对于程序,是代码和机器。

算法不过是将那个驱动机器的代码进行了抽象,使得不会写程序的人也可以来凑热闹而已。

 

真正的程序,一定是一种思想。这种思想借助具体的硬件去表现。用具体的代码去驱动硬件。

但是程序本身从来也不是算法的堆砌。

 

程序是人类思想的机器描述。背下康熙字典一样写不出红楼梦。背下牛津词典也写不出莎士比亚戏剧。会各种算法也写不出好的程序。

 

唯有思想才能助你写出完美的程序。

 

UNIX就是一种思想。就算UNIX的代码最终被AT&T扼杀,UNIX的思想仍然传播下去,在Linux的土壤中再次发芽。

 

算法是轮子,是我们快速表达思想的工具。最好的优化的是思想,其次是算法,最烂的是在指令层面进行优化,当然,比没有强。

 

寄希望于算法的人,是没有思想的代码生成工具而已。

 

 

Aug 27

UNIX 说,一切都是文件。

偏偏我们每天面对的屏幕不是文件,窗口不是文件。

 

cli 程序都是从 stdin 获得输入,偏偏为何 gui  程序要从所谓的 “窗口消息” 获得呢?

“从一切都是文件” 的角度而言,这是不可取的。

我来提出一个构思:

该设计由两部分组成,一个内核实现的虚拟文件系统 X , 一个在用户空间实现的 compositor. 

compositor 是可以运行时替换的 :) 事实上没有 compositor 程序一样执行,只是没输出。

实现一个虚拟的文件系统, X, 将它挂载到  /dev/X 

然后在  X 下创建一个文件就是创建了窗口了,但是为了容纳 "子窗口" 的概念,所以窗口是一个文件夹。子文件夹就是子窗口。窗口文件夹下有个 pixmap 的文件, 就是窗口内容了。程序通过写入该文件就等于是向窗口绘制了。如果不是 pixmap 文件,可以建立一个叫 postscript 的文件,使用 postscript 描述窗口内容也行。更可以建立一个到别的窗口的 pixmap 的符号链接,表示拷贝窗口内容。还有一个 prop 的只读文件,里面存放的是窗口的各种属性。要改变属性,只能通过 ioctl 作用于代表窗口的那个文件夹,或者有 OS 自动修改。

窗口管理器可以通过操纵该目录来实现。

OpenGL 绘制,创建好窗口后,创建 pixmap 文件,然后打开它,获得一个 fd ,  使用该 fd 创建一个 OpenGL 上下文即可。建议的 API 是   XglCreateContextfd(int fd, XglConfig * config);

但是,。。。。 pixmap 的内容并不会跑到屏幕上。

要让窗口的内容跑屏幕上,你需要一个 compositor , compositor 通过读取 pixmap 文件将窗口最终合成到屏幕上。bitmap 文件的内容 compositor 可以不用读取,那样会很低效。

bitmap 的内容通常是gui程序使用 OpenGL 绘制的,pixmap 内容虽然可以被直接读取,但是这是不高效的。 compositor 通过 ioctl 可以直接获得该 pixmap 对应的 buffer 指针,然后可以用这个BUFFER指针创建 OpenGL 纹理贴图,绘制到最终的屏幕上。这中间不存在打开图像数据的来回拷贝。如果 pixmap 不是 OpenGL 绘制的,那么直接创建纹理贴图可能会失败,但是可以直接打开,进行块拷贝操作就可以了,内核已经为 pixmap 自动启用共享内存。所以开销虽然比直接创建纹理贴图大,但是也比通过 socket 传输pixmap 小很多。

compositor 使用 fnotify 文件系统通知API 获得窗口的更新情况。

如果  窗口下面没有 pixmap 文件,而是 postscript 文件,那就需要 compositor 自行对 postscript 文件进行渲染了。 如果你的显卡居然带有硬件 postscript 解释器, compositor 都可以利用呢!

窗口移动对应用程序是透明的,是 compositor 在移动窗口。

窗口pixmap不能改变大小,要改变大小,必须重新创建窗口的 pixmap 。但是在重新创建窗口前, compositor 可以选择使用原来的窗口 pixmap ,知道它任何合适的时候要求应用程序重新创建。

这个过程很简单, 直接 delete pixmap , 应用程序就必须重建它。可以是 compositor 在delete,也可以是 程序自己。

字体:

如果是 postscript 输出的程序,可以使用 postscript 字体。:) 

如果是 OpenGL 输出,则必须使用 freetype 这类程序。可以使用 OpenGL 后端的  cairo  pango 简化字体的使用。 

那么,鼠标消息呢? 键盘消息呢?

答案是,/dev/X/input . 直接打开这个文件就可以了。

但是 ... ... 如果没有 compositor  这个读取文件却没有任何数据。

为何呢? 因为这个文件的数据是 compositor 准备的。每个窗口文件夹下都有一个只写的文件(只有 compositor 组和程序所有者所在组有权限写入) , message , 这个是由 compositor 写入的。写入该文件的消息就会被程序从 /dev/X/input 读取到。 每个程序只能读取到compositor发给自己的输入。除非广播信息,那是 compositor 直接写入 /dev/X/input 的,会被每个程序读取到。

如果一个程序需要读取全局消息,比如全局快捷键,那只能和  compositor 商量去了。建议的办法是 compositor 提供 dbus 接口让程序告诉 compositor 它要获得的键盘消息。

输入法:

输入法由 compositor 提供 compositor 加载  IM MODULE 由 IM MODULE 提供输入法。

简单的输入 compositor 将结果直接反馈给程序做输入就可以了。

如果程序想有预编辑的话,必须和 compositor 商量。一般程序使用带编辑功能的 widget 而不是自己实现。该 widget 由 UI toolkit 提供。UI toolkit 可以使用IM MODULE 的形式和嵌入 compositor的 IM MODULE 通过 dbus 通信来实现复杂的预编辑功能。建议该 dbus 接口规范化,使得不同的 IM MODULE 可以相互通信。

compositor 的 IM MODULE 也可以继续和另一个真正的 IM engine 通信实现输入法呢!

 

 

 

 

 

Jul 17

Linux 内核本身的 TTY 控制台代码十分的 bugy,  当然,能工作而已。

不过,最关键的问题是不支持 国际化。 不支持矢量字体,事实上在我打补丁前,都不能显示 CJK 字符。

TTY 暴露给用户空间的接口事实上也就是 /dev/ttyXX 字符设备。 XX 从 0 到 63

如果能在用户空间实现了 tty 设备,然后由用户空间的程序绘制图形到 /dev/fb0 不也就实现了控制台了么?!而且是 user mode 的!

意味着我们可以用 FreeType2 使用矢量字体,甚至可以用 Pango 进行多国语言的排版!

但是要如何实现在用户空间这边搞 tty设备呢? tty 设备虽然最大的用处类似 pipe , 在显示设备和cli 程序的 stdout 直接建立连接,但是包行了一系列的 ioctl 来实现各种特殊功能包括 ncurses 。这就不是 pipe 能实现的。如果简单的用  /dev/pts/ 来模拟,也会有问题。 pts 和 tty 之间还是有许多不相同的地方,而且如果要在 pts 实现,那做这个 pts master 的进程就只能是 pid 1 了,因为 agettty 就是 pid 1 fork  出来的。pts 只能父子继承。

所以得寻求替代方案,幸运的是,我们有 CUSE. 由 FUSE 提供的另一种强大功能。CUSE 能在  user space 实现一切字符设备的操作功能,于是我提出实现一个 Userspace TTY Daemon, aka uttyd .

uttyd 启动的时候将原有的 /dev/tty* 全部删除,然后自己建立新的 /dev/ttyXX 节点,当然  uttyd  要在 agetty 之前启动。

然后后续运行 agetty 的时候利用的 tty 就是 uttyd 实现的了。

uttyd 在内部实现了一个支持 UTF-8 的 VT100 兼容控制台,利用 SDL 在framebuffer 上直接绘图。

 

为何用 SDL 呢? SDL 足够轻量,而且可以在 X 环境下调试,如果是在 framebuffer 下,自动的使用 /dev/fb0 而不需要修改程序。

uttyd 利用 fontconfig 进行字体配置,并使用 FreeType2 进行渲染,最后用 SDL 绘制到屏幕上。

只要你系统里安装了中文字体,你就能实现中文控制台。

 

 

Jul 16

内核里的 VT 代码非常 urgly ,buggy ,充斥着许多 hack .

更重要的是不能有unicode font

要是能在 userspace 实现 vt 就能有 unicode font 了。