代码改变世界

Cosmos的库--.net/C#开源操作系统学习系类五

2011-04-18 17:15  Hundre  阅读(3711)  评论(3编辑  收藏  举报


接上文,争取这一次打通操作系统开发的任督二脉~~~嘿嘿

库对于大家来说都不陌生,我们做C开发时会引入标准输入输出头文件stdio.h,做MFC开发时又会引入afx.h头文件,使用DirectX做游戏开发时又会引入DirectX的头文件等等,在头文件中有给我们定义好的函数、结构、类等等可供使用,我们写代码时直接调用即可。
在各种库中,有一部分的库是封装了对底层硬件的操作的,例如在屏幕上输入字符,使用网卡发送数据包等。因为是直接对硬件进行操作,所以这一种类型的库的代码是和硬件相关的,这就是为什么我们会有x86的标准C程序库和arm的标准C程序库,两套不同的硬件体系,需要不同的库来完成对硬件的实际操作,每一套库中,都根据不同的硬件情况,按照硬件的规格定义好数据的排列方式(这就是为什么会有int,int16,int32,int64这么多蛋疼的数据类型的原因之一,因为编译器编译后有些数据必须占有足够长的位数以符合硬件产品的规格要求),以及根据硬件的功能提供出相应的函数,然后编译器编译时就能编译出符合硬件要求的格式的数据排列方式和操作这些数据的机器码。

在库中,根据不同的硬件特点定义了只适用于特定硬件的数据结构和函数,这就是为什么我们用C++开发普通的软件的时候使用标准输入输出库(stdio.h),而用C++开发游戏软件的时候需要使用DirectX库的原因。因为DirectX的库中定义有调用显卡的特殊功能的数据结构、类或者方法,从而能充分利用显卡这个硬件来展示游戏内容。

该说点沾边的了,其实Cosmos中也定义了一些这样的库,用来提供开启CPU的分页存储、读写端口的数据等功能,在编写操作系统的过程中,我们只需要自己实现这些库中的类和函数来完成对硬件的操作。同时因为这些功能都是直接操作硬件的,所以一般的操作系统开发包里面都不会提供给用户使用,咱们现在是操作系统的开发者,所以才会使用到这些底层的东西(也就开源的操作系统能看到源代码,才会从开发者的角度看到有这么一些函数或者类……),另外从某种程度上看,做驱动程序的开发的人员也需要编写一些底层的对硬件的操作库来供应用软件的开发人员使用。

C#的项目中把程序集添加进来,就相当于C/C++里面的include头文件操作。这些对硬件操作的代码虽然都是使用C# 来编写的,但在编译的时候IL2CPU编译器会把这些操作全都转换成特定的CPU操作指令这样就可以达到使用C#来控制硬件的目的,由此可见,一套好的代码库能极大地提高我们的工作效率。但是,目前C#的对硬件进行操作的库真是少之又少啊(要不就是我太孤陋寡闻了,还没能见着这方面的东西),只能是自己来写了L

由以上看来库的历史比操作系统的历史还长哦~~~呵呵。

好了,理论到此结束,看图说话


COSMOS中,库有个特别的叫法,叫做Plugs(插件?)。

前面关于COSMOS编译的文章中说到,在COSMOS编译的时候会加载所有的Plugs并进行编译,而COSMOS所有的Plugs都放在了Cosmos.Kernel.Plugs项目中


我们看到了上文提过的CreateGDT操作,还有Console这个我们比较熟悉的类型。先看下CreateGDT中的代码

public class CreateGDT : AssemblerMethod {



                    
public override void Assemble(Assembler aAssembler) {



                               
string xFieldName = "_NATIVE_GDT_Contents";



                               
string xFieldData



                
// Null Segment



                
= "0,0,0,0,0,0,0,0"



                                   
// Code Segment



                                   
+ ", 0xFF, 0xFF, 0, 0, 0, 0x99, 0xCF, 0"



                                   
// Data Segment



                                   
+ ", 0xFF,0xFF,0,0,0,0x93,0xCF,0";



                    aAssembler.DataMembers.Add(
new KeyValuePair<string, DataMember> (aAssembler.CurrentGroup,new DataMember(xFieldName, "db", xFieldData)));



                               xFieldName 
= "_NATIVE_GDT_Pointer";



                               
//xFieldData = "0x17, (_NATIVE_GDT_Contents and 0xFFFF), (_NATIVE_GDT_Contents shr 16)";



                    aAssembler.DataMembers.Add(
new KeyValuePair<string, DataMember> (aAssembler.CurrentGroup,new DataMember(xFieldName, "dw""0x17,0,0")));



                               
new CPUx86.Move(Registers.EAX, "_NATIVE_GDT_Pointer");



                               
new CPUx86.Move("dword [_NATIVE_GDT_Pointer + 2]""_NATIVE_GDT_Contents");



                               
new Label(".RegisterGDT");



                               
new CPUNative.Lgdt(Registers.AtEAX);



                               
new CPUx86.Move(Registers.AX, "0x10");



                               
new CPUx86.Move("ds", Registers.AX);



            
new CPUx86.Move("es", Registers.AX);



            
new CPUx86.Move("fs", Registers.AX);



            
new CPUx86.Move("gs", Registers.AX);



            
new CPUx86.Move("ss", Registers.AX);



            
// Force reload of code segement

new CPUx86.JumpAlways("0x8:flush__GDT__table");

new Label("flush__GDT__table");

                    }

          }

 

 

可以看到有一些x86的指令在里面,因为这是创建x86特有的GDT这个数据结构,这里直接调用了IL2CPU提供的功能(例如CPUx86.Move)来生成x86的指令。

总的来说,在Cosmos.Kernel.Plugs里面,实现和封装了对硬件操作的大部分功能,然后再由我们的启动项目(这里为Cosmos.Shell.Guess)来在不同的时间调用,以此来搭建出供程序员使用的一个硬件环境。这个环境对外都是通过开放一些特定的类来给程序员使用,目前来看这些类还只是很底层的操作硬件的类,也许以后会在这一层上再封装出一层API来。

(感觉这一次自己的截图不太给力啊……


IL2CPU在编译时会根据启动项目的程序集加载被引入的程序集和PlugsPlugs项目中引入的程序集。对于里程碑1,最后需要处理的程序集如下:

总共34个,需要编译器处理的函数如下:


318个,编译完成后在/Build/Tools/asm目录下生成以下文件:


由生成的目录我们就能很直观的看出都编译了那些程序集,这里最后生成的文件为13个,但前面我们看到总共加载了34个程序集来进行处理,这34个程序集在编译的过程中会被IL2CPU有选择地进行合并,最后输出到上面相应的13个文件里面。剩下的就是编译这些汇编程序文件为二进制文件即可,之后的内容欢迎参看该系列的第二篇文章J

最后,小小地揭秘一下(如果这也算是秘密的话~嘿嘿):我们可以看到,在Cosmos.Shell.Guess这个项目中,我们使用的Console这个类是.net framework中定义的类,同时我们自己定义的Console类中的方法和属性的名称和.net framework中定义的一模一样,这个只要在编译的时候制定是使用.net framework中的类还是使用我们自己定义的类就可以实现跨平台的目的,这就是跨平台的秘密,这里跨的是.net平台和x86平台,我们也可以自己实现arm下的Console类来实现跨arm的平台(啊呀,谁拿砖拍的我~~)。

再小小地总结一下开发一个操作系统需要的一些前期工作


到这里我觉得操作系统开发的大致思路应该都理清楚了,剩下的就是学习硬件如何工作以及如何运行,完了之后再编写出相应的代码库对相应的硬件进行操作,同时,根据教科书上所说,再实现进程的调度管理、硬件IO管理、文件系统等功能,一个基本的操作系统就算是出来了。整个开发流程就是:写对硬件操作的代码(这些代码有时就被称为库,在COSMOS中成为Plug->调用对硬件操作的代码(库)来实现对硬件的管理,不知道这算不算是打通了操作系统开发的任督二脉(还是个人意见,欢迎拍砖)

 

常规广告时间:欢迎光临小弟的淘宝话费充值小店捧场,呵呵

参考资料:

《程序员的自我修养连接、装载与库》

《操作系统的设计与实现》--上册