1. 程式人生 > >【淺談】x86記憶體管理的分段分頁機制

【淺談】x86記憶體管理的分段分頁機制

最近一直在接著作業系統的課程,著手跟著os.dev上的大神的文件寫一個小型的核心,然後前期的東西自己一直在看也沒時間寫Blog,最近做到了記憶體管理這裡,看了Jamesmolloy的文件還有一些os.dev.org上的關於memorymanage的東西,總覺的還是寫點東西吧,雖是贅述,但對自己而言定是有用的。

我不想說太多關於早期8086分段的原因,只是簡單說一下這種概念。我們知道x86架構下的分段+分頁機制共同實現了我們OS的記憶體管理邏輯,當然現在很多除x86架構的CPU已經完全不需要分段(段地址+偏移地址)這種機制,因為分頁完全可以滿足我們對於記憶體的對映管理與定址。Intel只是因為早期

8086—80286時期的問題,他需要歷史保證相容,所以一直保留著分段的機制。關於分段這塊,我們更多關心的是段暫存器地址,Intel32windows程式在記憶體中的佈局採用了所謂的平坦記憶體模式,這種模式雖然只有一個段,卻同時包了含程式碼和資料。早期的16位程式,由不同的段組合而成,且每個段的地址重定位有64K的限制。386後的平坦記憶體模式下,程式無需進行地址重定位,記憶體訪問範圍達到4G寬度。其優點是,彙編程式更容易編寫,且程式碼執行速度更快。在32位程式中,所有的段暫存器依然存在,但是都被設定成了同一個值,所以段暫存器和地址重定位已經無須使用了。這簡化了我們很多的程式碼邏輯。

雖然80386

有的通用暫存器(EAX,EDI等等)被擴充倒了32位,但是其中的段暫存器(DS,ES等)仍然只有16位,顯然不可能再用16位的段暫存器直接存放4G空間需要的32位地址了,所以必須引入了一種間接辦法——將段暫存器中存放的地址換成一個索引指標,定址時不再是從段暫存器中去定址,而是先取指標,再通過該指標搜尋一個系統維護的“查詢表”讀出所需段的具體資訊。剩下的動作和傳統行為沒什麼區別,將剛剛取得的段的基地址加上偏移量便構成了一個32位地址(即,線性地址)

                  

我們知道在多使用者多工環境下,記憶體尋地工作不再是簡單地取得32位的記憶體地址就可以直接不假思索地放到地址總線上去讀寫記憶體了,此刻必須先要對需訪問的地址進行合法性檢查,看看訪問者是不是有權利去訪問它要求的地址。如果發現有非法訪問企圖,則立刻阻止

(CPU會產生一個內部異常的中斷)。系統設計師們便把屬性資訊、段的基地址和界限都糅合在一起,形成了一個新的資訊單元——段描述符號,它整整佔用了8個位元組。顯然,暫存器太小,不夠存放段描述符,所以段描述符都被統一存在專門的系統段描述符號表中(GTDLDT)儲存。其中包含了段基地址、段的大小資訊、段的屬性資訊,而且在屬性資訊裡還包含了和訪問許可權有關的資訊。最主要的描述符表是全域性描述符表(GlobalDescriptor Table,GDT),所謂全域性,意味著該表是為整個軟硬體系統服務的。在進入保護模式前,必須要定義全域性描述符表。盜用一張圖(GDTGDTR)如下:

           

    GDT23G是粒度(Granularity),用於解釋段界限的含義。當G位是“0”,段界限以位元組為單位。此時,段的擴充套件範圍是從1位元組到1兆位元組(1B~1MB),因為描述符中的界限值是20位的。相反,如果該位是“1”,那麼,段界限是以4KB為單位的。這樣,段的擴充套件範圍是從4KB4GBS位用於指定描述符的型別(DescriptorType)。當該位是“0”,表示是一個系統段;為“1”,表示是一個程式碼段或者資料段(堆疊段也是特殊的資料段)DPL表示描述符的特權級(DescriptorPrivilege Level,DPL)。這兩位用於指定段的特權級。共有4種處理器支援的特權級別,分別是0123,其中0是最高特權級別,3是最低特權級別。剛進入保護模式時執行的程式碼具有最高特權級0(可以看成是從處理器那裡繼承來的),這些程式碼通常都是作業系統程式碼,因此它的特權級別最高。每當作業系統載入一個使用者程式時,它通常都會指定一個稍低的特權級,比如3特權級。不同特權級別的程式是互相隔離的,其互訪是嚴格限制的,而且有些處理器指令(特權指令)只能由0特權級的程式來執行,為的就是安全。

   為了跟蹤全域性描述符表,處理器內部有一個48位的暫存器,稱為全域性描述符表暫存器(GDTR,下圖所示),該暫存器分為兩部分,分別是32位的線性地址和16位的邊界。32位的處理器具有32根地址線,可以訪問的地址範圍是0x000000000xFFFFFFFF,4GB記憶體。所以,GDTR32位線性基地址部分儲存的是全域性描述符表在記憶體中的起始線性地址,16位邊界部分儲存的是全域性描述符表的邊界(界限),其在數值上等於表的大小(總位元組數)減一。

              

   描述符中指定了32位的段起始地址,以及20位的段邊界。在真實模式下,段地址並非真實的實體地址,在計算實體地址時,還要左移4(乘以16)。和真實模式不同,32位保護模式下,段地址是32位的線性地址,如果未開啟分頁功能,該線性地址就是實體地址。在進入保護模式之後,處理器立即要按新的記憶體訪問模式工作,所以,必須在進入保護模式之前定義GDT。但是,由於在真實模式下只能訪問1MB的記憶體,GDT通常都定義在1MB以下的記憶體範圍中。

              

okGDT就說到這,下面我們開始啟動記憶體分頁的東西。其實也是我們所謂的真實模式和保護模式的分界。在此之前,我們必須知道CPU的一些控制暫存器。CR0CR1CR2,CR3CR4。。。。。,CR032位的暫存器,包含了一系列用於控制處理器操作模式和執行狀態的標誌位。所示,它的第1(0)是保護模式允許位(ProtectionEnable,PE是開啟保護模式大門的門把手,如果把該位置“1”,則處理器進入保護模式,按保護模式的規則開始執行。32位處理器下的6個段暫存器分為兩部分,16位和8086相同,在真實模式下,它們用於按傳統的方式定址1MB記憶體,使用方法也沒有變化。在保護模式下訪問一個段時,傳送到段選擇器的是段選擇子(上圖)。它由三部分組成,第一部分是描述符的索引號,用來在描述符表中選擇一個段描述符。TI是描述符表指示器 (TableIndicator),TI=0,表示描述符在GDT;TI=1,描述符在LDT中。LDT的知識將在後面進行介紹,它也是一個描述符表,GDT類似。上圖中的RPL是請求特權級,表示給出當前選擇子的那個程式的特權級別,正是該程式要求訪問