《30天自制操作系統》筆記(06)——CPU的32位模式
進度回顧
上一篇中實現了啟用鼠標、鍵盤的功能。屏幕上會顯示出用戶按鍵、點擊鼠標的情況。這是通過設置硬件的中斷函數實現的,可以說硬件本身的設計就具有事件驅動的性質,所以軟件層面上才有基于事件的消息機制。
但上一篇沒有說明中斷的來龍去脈,本篇就從頭到尾描述一下CPU與此相關的設置問題。
Segment
32位的CPU使用32條地址線,能區分232=4G個內存地址。每個內存地址都有1Byte的內容。
分段,就是將4GB的內存分成很多塊(block),每一塊的起始地址都看作0來處理。有了這個功能,任何程序都可以先寫上一句"ORG 0",一個應用程序就不會占用別人的內存空間,這樣就可以同時運行多個程序。像這樣分割出來的塊,就稱為段(segment)。還有一種"分頁"的技術,這里不討論。
為了表示一個段,需要記錄以下信息:
-
段的起始地址
-
段的大小
-
段的管理屬性(禁止寫入,執行,系統專用等)
這些信息需要用8個字節保存。使用段的方式是和調色板神似的:DS是16位,理論上能夠表示216=65536個段。但由于CPU設計上的原因,低3位不能用,因此DS只能表示213=8192個段(即第0個~第8191個)。
在16位模式下,如果寫"MOV AL, [DS:EBX]",那么要計算的地址是DS*16+EBX。在32位模式下,則應該是DS表示的段(segment)的起始地址+EBX。
另外,如果寫成"MOV AL, [EBX]",則匯編器認為這等同于"MOV AL, [DS:EBX]"。這一點在16位和32位模式下是一致的。
要存儲8192個段,就需要占用8192*8=65536Byte=64KB的內存空間。這64KB的數據就稱為GDT (Global segment Descriptor Table)即"全局段號記錄表"。
將這64K的GDT整齊地排列在內存某處,再將其起始地址和有效設定個數放在CPU內被稱作GDTR的48bit寄存器中,GDT的設定就完成了。
段的起始地址、大小、管理屬性這些信息是按bit保存的,十分復雜,暫時不要理會。
IDT
當CPU遇到外部情況變化,或是內部發生某些錯誤時,會臨時切換過去處理這種突發事件。這就是中斷功能。鍵盤按鍵、鼠標按鍵、鼠標移動、除0等都會引發中斷。有了中斷機制,CPU就不需要一直查詢這些低速設備的狀態,將時間用在處理任務上。
因此,要使用鼠標鍵盤,就必須使用中斷機制,即設置IDT。
IDT(Interrupt Descriptor Table)即"中斷記錄表"。IDT記錄了0~255的中斷號與調用函數之間的對應關系。當發生了123號中斷,就會調用對應的函數。其設置方式與GDT是相似的,IDT的每一項也需要8Byte保存,這8Byte里包括中斷處理函數名(即C語言中的函數指針)。
另外,必須先設置GDT后設置IDT。原因不詳。
PIC
PIC(Programmable Interrupt Controller)即"可編程中斷控制器"。它是一個硬件芯片。
當鍵盤鼠標發生按鍵、移動時,PIC就會向CPU發送電信號,然后CPU要求PIC發送2個Byte來(其內容為"0xcd 0x??",實際上是機器語言的INT指令),CPU還真就把PIC送來的這2個Byte看作一條指令執行。其結果是調用IDT中對應的函數。
PIC的設定基本上都是固定死的幾行代碼,暫時不用理會。
進入32位模式
進入32位模式實際上很簡單,按照一定的步驟將某些寄存器(CR0等)設置為特定的值就行了。也有點繁瑣,暫時不理會。
操作系統程序被加載到內存中的什么地方才行?這個沒有特別的規定,根據自己的偏好分給OS一些內存空間就行了。不過有些內存空間放著BIOS等程序,而且大部分高地址的內存是要給應用程序使用的。因此OS程序的空間分配也不要太隨意了。下圖是HariboteOS設計的內存分布圖。
總結
本文以輕量從簡的態度簡單說明了OS啟動時要初始化的大部分東西,即GDT、IDT、PIC、32位模式。我個人認為這些都是細節,應該進行封裝。后續的內存管理、多任務才是OS設計的核心內容。
文章列表