linux內核學習筆記:內存尋址

linux內核學習筆記:內存尋址


        內存地址分爲三種:邏輯地址,線性地址,物理地址。在分段的CPU結構中,程序中引用的地址都是邏輯地址,邏輯地址經過分段單元成爲線性地址。然後經過分頁單元成爲物理地址,物理地址就是硬件電路尋址的實際地址。如果CPU體系結構不支持分段,那麼邏輯地址等於物理地址。一般RSIC指令的CPU都不支持分段,如arm。複雜指令的CPU支持分段,如x86。
一.分段
        1.硬件中的分段:因爲x86體系結構是分段的,所以程序是由很多段組成。程序的邏輯地址有段與偏移量組成。實現這種分段機制有特殊的硬件來完成,主要有段寄存cs, ss, ds, es, fs, gs,全局段描述符寄存器gdtr與局部段描述符寄存器ldtr。前三個段寄存器有固定的用途:cs 代碼段寄存器。ss 棧段寄存器。 ds數據段寄存器。邏輯地址由16位段選擇符和32位偏移量組成,段寄存器裝有段選擇符,段選擇符由索引號與特權級。當段選擇符裝入段寄存器時,硬件單元就會按照裏面的索引號,根據是全局段還是局部段從gdtr或ldtr寄存器段描述符的初始地址,算出線性地址。在這期間還進行相應的權限檢查。
        2.linux中的分段:linux的設計目標就是簡單實用就好,而分段單元使得程序變得複雜。所以Linux不喜歡分段。但在在x86平臺上必須分段,所以linux採用一種巧妙的方法,在用戶態與內核態使用四個段,分別是用戶代碼段,用戶數據段,內核代碼段,內核數據段。而且段描述符的基地址都是0,這樣換算下來的邏輯地址等於線性地址。分段的唯一用途就是可以檢查權限。
二. 分頁
        1. 硬件中的分頁:分頁使得線性地址轉換成物理地址,還有一個關鍵的任務就是進行線性地址訪問權限的檢查。爲了方便,線性地址分爲相應的組:稱爲頁。而物理地址也相應的分爲組:稱爲頁框。頁的大小由處理器決定。有些處理器支持不同大小的也,如s3c2440。x86支持4k大小的也。分頁根據頁的大小分爲常規分頁與擴展分頁。擴展分頁允許頁的大小爲4M而不是4k。常規分頁採用三級頁表,32位線性地址分爲三個部分,最高十位是目錄中間十位爲頁表,最後12位偏移量。在打開硬件分頁功能前,首先應該初始話頁表,然後把頁目錄的物理地址存放在cr3寄存器中,線性地址轉換成物理地址是這樣進行的。首先分頁單元取線性地址的最高十位,以它爲索引找到頁目錄項,然後取頁目錄中的base字段加上線性地址的中間十位找到頁表項,然後加上線性地址的最後12位偏移量就是物理地址。當然這其中與做相應的權限檢查。
        2. linux中的分頁:爲了保持可移植性,無論硬件採用幾級分頁,linux都採用的四級分頁:頁全局目錄,頁上級目錄,頁中間目錄,頁表。如果硬件是兩極分頁,linux採用中間兩極分頁全是0的方式消除中間的分頁。linux的進程很大程度上依賴於分頁技術。不同的進程分配不同的地址空間,有效的減少了尋址錯誤。分頁技術是虛擬內存機制的基礎。linux提供了一些宏來簡化頁表的操作。
三. 物理內存佈局
        在系統初始化的時候,linux必須對物理內存做一定的映射,指定那些物理內存可以用,那些不可以用。內核保留的頁框爲:不可用的物理地址的頁框範圍,這個也就是沒有安裝物理內存的地址。另外一個是含有內核代碼和已初始化數據結構的頁框。由於硬件的原因,linux一般安裝在物理地址的0x00100000開始的地方,但也不是絕對的,如mini2440,物理地址開始就是0x30000000,所以內核安裝在0x30008000起始地址處。在啓動內核的早期階段,內核訪問BIOS來確定物理內存的大小,有的體系結構通過啓動代碼傳過來的內核參數來了解物理內存的大小。隨後內核運行一些函數來初始化內存映射,初始化內核的一些表量來描述內存的物理佈局。
四. 進程頁表
        進程的線性地址空間分爲兩部分:
(1) 從0x00000000到0xbfffffff的線性地址,無論進程運行在內核態還是用戶態都可以尋址
(2) 從0xc0000000到0xffffffff的線性地址,只有內核態的進程才能尋址。
        這也就是說,在內核代碼的線性地址都是大於0xc0000000的,但是可以尋址小於0xc0000000的地址,而用戶態的代碼只能尋址小於0xc0000000的線性地址。。也全局目錄小於0xc0000000的地址,具體的頁目錄項依賴具體的進程,但是大於0xc0000000的頁目錄項都是相同的都等於內核頁全局目錄。
五. 內核頁表
            內核維持着一個自己使用的頁表,即內核頁全局目錄,這個頁全局目錄的最高目錄項,爲其他普通進程提供參考模型。在linux啓動的最初階段,CPU還沒有開啓分頁功能,所以線性地址就等於物理地址。在開啓分頁功能之前,內核必須初始化頁全局目錄以及頁表。這分爲兩個階段:
(1)臨時內核頁表
        臨時頁全局目錄存放在swappper_pg_dir變量中,臨時頁表在gp0變量出存放。緊接着在內核未初始化的數據段後面。假如內核使用的全部內存可以存放在8M的空間裏,因爲一個頁表可以映射4M的地址,所以8M的空間需要兩個頁表,也就是需要兩個頁目錄項。分頁第一階段的目的是實模式下與保護模式下都可以訪問這8M的地址。所以內核需要將0x00000000到0x007fffff的線性地址與從0xc0000000到0xc07fffff的線性地址都映射到從0x00000000到0x007fffff的物理地址。
(2)當RAM小於896M的最終內核頁表
        內核幾乎會映射全部的物理地址,但是內核的線性地址空間限制在0xc0000000到0xffffffff的1G的空間內,所以如果物理地址大於1G那麼就映射不來了。所以要採用一些特殊的方法來實現。預留128M的線性地址空間就是做臨時映射的。所以內核只固定線性映射896M的線性地址,這部分的線性地址與物理地址是線性的映射關係。
(3) 當RAM大於896M小於4G之間的最終內核頁表
        這部分的物理地址大小已經超過了內核的線性地址大小。在線性地址的最高128M,內核可以採用固定映射或者臨時映射來對高於1G內存的動態映射。這種映射不是線性關係,可以任意映射。
六. 總結
        在不同的平臺上linux的物理空間分佈有很大的不同,分頁與分段的實現也有很大的不同。但是linux爲了實現可移植性,努力做到之間的差距達到最小。對內存尋址,分頁分段的學習有助於理解linux內存管理與linux進程的相關知識。是理解linux內核的基礎。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章