Cosmos的庫--C#開源操做系統學習系類五

接上文,爭取這一次打通操做系統開發的任督二脈 ~~~ 嘿嘿

庫對於你們來講都不陌生, 咱們作 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的指令。ide

總的來講,在 Cosmos.Kernel.Plugs 裏面,實現和封裝了對硬件操做的大部分功能,而後再由咱們的啓動項目(這裏爲 Cosmos.Shell.Guess )來在不一樣的時間調用,以此來搭建出供程序員使用的一個硬件環境。這個環境對外都是經過開放一些特定的類來給程序員使用,目前來看這些類還只是很底層的操做硬件的類,也許之後會在這一層上再封裝出一層 API 來。

(感受這一次本身的截圖不太給力啊……函數


IL2CPU 在編譯時會根據啓動項目的程序集加載被引入的程序集和 Plugs Plugs 項目中引入的程序集。對於里程碑 1 ,最後須要處理的程序集以下:

總共34個,須要編譯器處理的函數以下:學習


318 個,編譯完成後在 /Build/Tools/asm 目錄下生成如下文件:


由生成的目錄咱們就能很直觀的看出都編譯了那些程序集,這裏最後生成的文件爲13個,但前面咱們看到總共加載了34個程序集來進行處理,這34個程序集在編譯的過程當中會被IL2CPU有選擇地進行合併,最後輸出到上面相應的13個文件裏面。剩下的就是編譯這些彙編程序文件爲二進制文件便可,以後的內容歡迎參看該系列的第二篇文章Jui

最後,小小地揭祕一下(若是這也算是祕密的話~嘿嘿):咱們能夠看到,在Cosmos.Shell.Guess這個項目中,咱們使用的Console這個類是.net framework中定義的類,同時咱們本身定義的Console類中的方法和屬性的名稱和.net framework中定義的如出一轍,這個只要在編譯的時候制定是使用.net framework中的類仍是使用咱們本身定義的類就能夠實現跨平臺的目的,這就是跨平臺的祕密,這裏跨的是.net平臺和x86平臺,咱們也能夠本身實現arm下的Console類來實現跨arm的平臺(啊呀,誰拿磚拍的我~~)。

再小小地總結一下開發一個操做系統須要的一些前期工做


spa

到這裏我以爲操做系統開發的大體思路應該都理清楚了,剩下的就是學習硬件如何工做以及如何運行,完了以後再編寫出相應的代碼庫對相應的硬件進行操做,同時,根據教科書上所說,再實現進程的調度管理、硬件IO管理、文件系統等功能,一個基本的操做系統就算是出來了。整個開發流程就是:寫對硬件操做的代碼(這些代碼有時就被稱爲庫,在COSMOS中成爲Plug->調用對硬件操做的代碼(庫)來實現對硬件的管理,不知道這算不算是打通了操做系統開發的任督二脈(仍是我的意見,歡迎拍磚)操作系統

 

常規廣告時間:歡迎光臨小弟的淘寶話費充值小店捧場,呵呵.net

參考資料:

《程序員的自我修養鏈接、裝載與庫》

《操做系統的設計與實現》--上冊

相關文章
相關標籤/搜索