2007年7月29日星期日

操作系统内核的中神奇的代码

最近free download一个Linux kernel 2.6.22。由于工作的原因,大概有2年没有碰触Linux了,这些天又有生活无目标的感觉。花了2年时间才大概弄清楚WinCE整体的结构,基本能够理解微软的设计思想,也尝试看WinCE5的内核代码,但是有时候会很不爽,一个函数跟着跟着就丢失了。尤其是GWES,我最想知道的部分,完全没有代码。内核代码很多类似的部分,其中几乎都是C语言构成,C语言的组织基本架构就是函数,我们平时写应用程序时,也经常用函数,可是内核中的函数有时充满了玄机,有的是不归路,有的切换,有的是交错运行,对了,此时让我突然想起硬件文境切换代码,有空大家一定要看看绝对长见识。不过今天不看那些东西,我随便读,代码到哪里就说到哪里。看内核启动时,初始化终端的函数

void __init console_init(void)

{

initcall_t *call;

/* Setup the default TTY line discipline. */

(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);

/*

* set up the console device so that later boot sequences can

* inform about problems etc..

*/

call = __con_initcall_start;

while (call <>

(*call)();

call++;

}

}

__con_initcall_start这个变量你就是找遍所有C和汇编代码你也找不到它的原型。它是出现在内核编译的链接阶段,是一个编程语言中的标记,或者说是一个地址,在这里也确实表现为地址,__con_initcall_start__con_initcall_end一起前后界定了了代码段中.con_initcall.init

#define console_initcall(fn) \

static initcall_t __initcall_##fn \

__attribute_used__ __attribute__((__section__(".con_initcall.init")))=fn

搞这个macro声明了很多已赋值的函数指针,如32CPU那么就占用4个字节,console_init就是循环运行这些函数指针多对应的函数。这个架构真是太好了,很多驱动程序只要用这个macro就可以声明自己,而内核初始化部分就无须直接关心有多少console驱动程序做这样的声明,靠着强大的编译器就可以帮助你了解到。还是GCCLD厉害,微软的编译器功能太少,灵活度不高(至少微软对外放出的编译器是这样,说不定他们内部有很高级的)。不过毕竟微软不希望你利用他们的操作系统再去开发别的系统。呵呵。

所以分析内核代码是一定要想象这段代码的运行环境,否则你很难理解作者到底要干什么。文境切换代码更是如此,经过schedule函数直接就到另外一个进程去了,很神奇。还有在SMP环境下,一个函数直接会分叉,编程同时在多个CPU运行。今天就看了这点东西,以后再遇到什么好玩的东西,再和大家分享。

2007年7月28日星期六

程序员你有资格骄傲么?

我遇到过太多太多自以为是的例子,有时候包括自己。

超级解霸之父梁肇新在他书中提到程序员进阶的几个阶段,我非常同意这位大师的观点。

其中有个阶段就是学习驱动程序和理解操作系统的内核,从1990年一直有以研究linux内核所引发的研究操作系统内核的一股风潮。Ring0Ring3,虚拟内存,系统调用,进程同步,缺页等等,有一些听起来很炫的专业词汇,更有甚者,以为自己能说出这些词,自己就是高手了。

Ring0/3,如果你不懂Intel IA-32体系架构,那么请你谦虚一点。

虚拟内存,page poolpage fileswap partition,如果你没有仔细研究过当代CPU中的MMU的一些共性,当虚拟页面不在时,CPU如何响应,CPU特权级如何切换。这些都是硬件知识。虚拟内存的管理涉及ring0内核的管理,和ring3普通进程管理,他们是关系?这些你都不懂,那么请你谦虚一点。

系统调用,Intel CPUtrap 0x80是实现linux系统调用的入口,然后ring0内核再进行一次派发,最后到达实现体。windows也是类似,他们运行在同一个CPU上,不可能有太大区别。当然这里有安全机制,不可能让你这么容易就进入ring0胡作非为。如果这些你都不懂,那么你就很难想象windows如此多的DLL是干什么的,他们之间的堆叠关系,Windows丰富的API都做了些什么,在哪个ring运行,更很难想象,很多API会引发一场轰轰烈烈的ring3ring0的切换。ntDll.dll这个连接ring0ring3的使者在干什么。Glibclinuxz这个系统中的地位。如果你不懂,那么请你谦虚一点。

进程同步,进程调度算法,这个是现在的大学最喜欢教学生的部分,还会出很多颠过来倒过去的题让学生作答。这些东西没他们宣扬的那么重要。Tanenbaum教授在<>中就是这样说,“而实际系统中很重要的部分,如IO系统和文件系统又以为缺乏理论性而被忽略”。在1969年贝尔实验室第一个PDP-11系统,后来被认为是unix操作系统第一版中,当时已经可以多进程同时运行,但是multiprogramming就不可以,就是因为还没有设计出完善的文件系统,这样的问题直到1972UNIX系统被C重写时,问题才被真正解决。

缺页,现代物理内存有限,只好用虚拟内存,没进程2GB,或者4GB空间,说起来很容易,我只想问你理解么?为什么WINCE5.0进程是32MB6.02GB?微软会做什么调整?你能想象一下么?如果你是微软,那么你怎么做这个调整?createprocess底层实现中,进程空间内存分配原则就要改掉。.exe有两个模型,一个是在文件系统中的模型,一个是在进程空间的模型,他们往往会有差异,微软建立在COFF文件结构基础上的的PE文件结构是如何来说明她们的不同。Process loader在操作系统中的地位。有次引发的page on demand策略的产生。多任务不一定要实现缺页,只要进程足够小。但是多任务,而且每进程空间都是CPU所能承受的最大空间,这就必须有缺页机制。如果你都不懂,那么请你谦虚一点。

一个操作系统是在另一个操作系统里面编译完成的,你信么?还有很多很多的知识,你懂么?如果你说你都懂,好,那么我告诉你,你也没什么了不起。哦你说你可以用C+汇编实现上面东西,那么我告诉你,你同样没什么值得骄傲的,这个世界上很多人都能写自己的内核,注意我说的是“内核”,仅仅指“内核”。你能写只能说明你的编程功底很深,你能预见这些技术难题。补充一下,这些“技术难题”在现代根本不是难题,但确实是几十年前的技术难题。说明你把“古人”的解决这些问题的方法掌握了,计算机对你已经很通透。但是程序员的最高境界你仍未达到。内核只是整个操作系统中的一部分,是核心部分,注意不要被“核心”二字迷惑,它只是指地理位置的核心,并不是指重要性的核心。你可知道微软的office套件比他的XP professional贵很多。相信不用我说你也可以理解。说明微软在OFFICE上所花的心血绝对不亚于一个操作系统。从这些“高手”认为,内核最牛,可是你看office这样应用程序会比你的内核复杂,你信么?有幸您写一个能和微软这么强大的OFFICE出来,在下一定跪拜师学艺。就不说office,就说XP操作系统,内核是什么,ntoskrnl.exe一个2MB的文件,好了不用我多说,你也能很清楚掂量内核的地位。内核以外,操作系统要提供友好的界面,丰富的API,安全机制,大量可用的应用程序等等这些才是大头。一个不懂电脑的人,你和他说操作系统,他不会懂。你说对他重要么?不重要。“能用,好用”这才是重要的,你那些后台努力的骄傲就收起来吧。享受一下用户说这个东西好的感觉。

程序员最高境界不会再关心这些,什么语言?什么系统?什么内核?什么驱动?什么应用?那是一种对大局的把握。对一个庞大软件的设计能力。不过没有前一阶段的积累,你也决不可能到达这个阶段。因为假如让你来设计这个庞大系统,你会有很多自己的技术问题还没有解决,你根本不敢乱想。很多人或者公司也做大系统,为企业定制。那也许不是我说的庞大系统。你注意到,你的系统里只涉及若干东西,数据库操作,浏览器等等。我想大概oracle算是这样庞大软件了。当然office3DmaxmayaProEapache等等应该都算。他们的设计者是真正的高手。

工作经历告诉我,对硬件的控制并不是什么难事,一个驱动程序你能做,往往只是指你能做硬件控制部分,和操作系统的部分也往往是别人都写好的代码,并不是你设计的,如果这部分你也可以设计,那你也是高手,可以小骄傲一下。所以收起骄傲,停止说这个驱动简单,那个驱动简单,我看哪怕是一个完整串口驱动的设计者,也绝对是行业中的高高手。奉劝那些为windowsLinux或者WINCE写驱动程序的程序员,谦虚点,不要一搞就瞧不起做应用程序的。去看看apache之父的言谈,就知道什么才是真正的高手。

2007年7月26日星期四

Windows Mobile bus driver

Windows Mobile的驱动程序程序实际是一个树状结构。Device.exe
负责加载几乎所有的驱动程序,而驱动程序的各种信息是存在于注册表里面。在系统启动?
锥危琩evice.exe是不知道有多少驱动是要加载的。他只是找到bus driver
,然后由它继续加载所有的驱动,所以bus driver是很重要的驱动程序,是桥梁驱动。
注册表中,HKEY_LOCAL_MACHINE\Drivers的键值RootKey指示了bus driver
的位置,一般你会看到Drivers\BuiltIn。那么HKEY_LOCAL_MACHINE\Drivers\BuiltIn下
Dll说明的是bus driver的名称。
微软的Ak中是默认有个驱动的BusEnum.dll(在public下有源代码)。这个驱动会枚举
BuiltIn下的所有键值,将这些驱动都加载进device.exe。
bus driver OEM是可以替换的。你此时可能回想既然已经有现成的,为什么还要自己写?
在很多时候,SOC的许多module的clock
开关都会放在一个统一的寄存器组里,那么驱动程序在想开/关这些寄存器时,都要操作?
桓黾拇嫫髯椋庵止蚕碜试吹姆梦适强赡芤鸪逋坏摹?

每个驱动要访问这个寄存器组,他们也就需要映射这个物理空间。如果每个驱动都映射,?
驮斐尚槟饪占涞募罄朔选?
那么如何完成自定义的bus driver
?微软为我们提供了很完善的类库,使我们很容易做到。Class DefaultBusDriver 和
Class DeviceFolder就是这样基础类。详细的代码细节自己看吧。Bus driver
是也是标准的流驱动。它需要在xxx_Init中引发一系列枚举动作,将驱动加载进来。