第二章 ?內存尋址
內存地址
內存地址分為三種:邏輯地址(logical ? address)(段+偏移量)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 線性地址(linear ? ? address)(共32位 可以表示高達4G 地址)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 物理地址(physical address)(用于內存芯片級內存單元尋址) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
?地址轉換關系:邏輯地址------>線性地址------>物理地址
段選擇符和段寄存器
邏輯地址由段標識符和段偏移量組成
段標識符總共16位 ? ?稱為段選擇符
段選擇符見下圖:
80x86中的段寄存器有:cs(code segment)代碼段寄存器
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?ss(stack segment)棧段寄存器
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ds(data segment) 數據段寄存器
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? es fs gs ?等等
段描述符
段描述符包含8個字節的段信息(其中包含段的首字節的線性地址)
段描述符放在全局描述表(Global Descriptor Table,GDT)和局部描述表(Local Descriptor Table,LDT)中
GDT:整個系統只有一個 ?
LDT:如果進程需要創建自己的段,就可以創建自己的LDT
GDT所在主存的地址和大小放在gdtr控制寄存器中,當前正在被使用的LDT地址和大小放在ldtr控制寄存器中
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2015.10.15
快速段描述
不可編程寄存器:不能由程序員設置的寄存器,可以存放8字節的段描述符,每當一個段選擇符被選定時, 相應的段描述符就被裝入不可編程寄存器中。?
?邏輯地址和線性地址的轉換 ? ? ?
linux中的分段
linux中更喜歡分頁,因為RISC對分段的支持有限,采用分頁使可移植性得到加強
用戶態和內核態程序都是用同一段對指令和數據進行尋址
四個主要的Linux段的段描述符字段的值:
段 ? ? ? ? ? ? ? ? ? ? ? ?BASE ? ? ? ? ? ? ? ? ? ?G ? ? ? Limit ? ? ? ? ? ? S ? ? ? ? Type ? ? ?DPL ? ? D/B ? ? ? ? ? ? P ? ? ? ??
用戶代碼段 ? ? ? ?0x00000000 ? ? ? ? 1 ? ? ? 0xfffff ? ? ? ? ? ? ?1 ? ? ? ? 10 ? ? ? ? ?3 ? ? ? ? ? 1 ? ? ? ? ? ? ? ? 1
用戶數據段? ? ? ? 0x00000000 ? ? ? ? 1 ? ? ? 0xfffff ? ? ? ? ? ? ?1 ? ? ? ? 2 ? ? ? ? ? ?3 ? ? ? ? ? 1 ? ? ? ? ? ? ? ? ?1
內核代碼段? ? ? ? 0x00000000 ? ? ? ? 1 ? ? ? 0xfffff ? ? ? ? ? ? ?1 ? ? ? ? 10 ? ? ? ? ?0 ? ? ? ? ? 1 ? ? ? ? ? ? ? ? ?1
內核數據段? ? ? ? 0x00000000 ? ? ? ? 1 ? ? ? 0xfffff ? ? ? ? ? ? ?1 ? ? ? ? 2 ? ? ? ? ? ?0 ? ? ? ? ? 1 ? ? ? ? ? ? ? ? ?1
相應的段選擇符由__USER_CS ?__USER_DS ? ? __KENERL_CS ?__KENERL_DS定義?
linux GDT和LDT
在單處理器的系統中,GDT只有一個?
在多處理器的系統中,GDT有多個,每個處理器有自己的一個GDT
所有GDT都存放在cpu_gdt_table數組中,所有GDT的地址和大小則存儲在cpu_gdt_descr數組中
對于LDT,大多數用戶態的linux程序不使用局部描述表,這樣內核就定義了一個缺省的LDT供大多
數進程共享
硬件中的分頁
分頁單元把線性地址轉換為物理地址
線性地址被分成以固定長度為單位的組,稱為頁(page),一般4KB
分頁單元把所有RAM分為固定長度的頁框(page frame),一般4KB
頁:一個數據塊,存放在一個頁框或者磁盤中
頁框:物理頁
把線性地址映射到物理地址的數據結構成稱為頁表(page table)
常規分頁
二級分頁的32位線性地址劃分:
Directory(目錄):最高10位
Table(頁表):最高10位
Offset(偏移量):最低12位
線性地址的轉換分兩步完成,每一布都基于一種轉換表,第一種轉換表稱為頁目錄表(page directory ),第二種轉換表稱為頁表(page table)
采用二級結構主要為了減少每個頁表所需的RAM的數量
正在使用的頁目錄項的物理地址存放于cr3中
常規分頁下線性地址向物理地址的轉換:
頁表項的字段:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Present (1則指的頁在主存中,0則不再主存中)
? ? ? ? ? ? ? ? ? ? ? ? ? ? 包含頁框物理地址的最高20位(如果字段指向頁目錄,則相應頁框含有一個頁表,如果字段指向頁表,則相應的頁框就是一頁數據)
? ? ? ? ? ? ? ? ? ? ? ? ? ? Accessed(每當分頁單元對相應的頁框進行尋址時就設置這個標志)
? ? ? ? ? ? ? ? ? ? ? ? ? ? Dirty(每當對一個頁框進行寫操作時設置此標志)
? ? ? ? ? ? ? ? ? ? ? ? ? ? Read/Write(含有頁或者頁表的存取權限)
? ? ? ? ? ? ? ? ? ? ? ? ? ? User/Supervisor(含有訪問頁或者頁表所需的特權級)
? ? ? ? ? ? ? ? ? ? ? ? ? ? Pcd和PWT(控制硬件高速緩存處理頁或者頁表的方式)
? ? ? ? ? ? ? ? ? ? ? ? ? ? Page Size(只能應用與頁目錄項,為1則頁目錄項指的時2MB或者4MB的頁框)
? ? ? ? ? ? ? ? ? ? ? ? ? ? Global ?(只應用于頁表項,用來防止常用頁從高速緩存中刷新出去)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2015.10.16
擴展分頁
從Pentium模型開始,80x86微處理器引入擴展分頁(extended page),它允許頁框的大小位4MB而不是4KB
此時的32位線性地址結構:
?Directory(目錄):最高10位
?Offset(偏移量):其余22位
擴展分頁下線性地址向物理地址轉換:
處理器所支持的RAM容量受連接到地址總線的地址管腳數限制。早期Intel處理器從80386到Pentium使用32位物理地址。從理論上講,這樣的系統上可以安裝高達4GB的RAM;而實際上,由于用戶進程線性地址空間的需要,內核不能對1GB以上的RAM進行尋址。
為了擴展32位80x86結構所支持的RAM容量,Intel通過在它的處理器上把管腳數從32增加到36來滿足這些需求。此時,Intel所有的處理器現在的尋址能力為2^36=64GB
Intel通過引入一種叫作物理地址擴展的機制來使用增加的物理地址
通過設置cr4控制寄存器來激活PAE機制
激活PAE機制后分頁的特點:
1.一個頁表的頁表項從1024個變為原來的一半512個
2.引入一個叫作頁目錄指針表(Page Directory Pointer Table,PDPT)的頁表新級別,它由4個64位表項組成
3.cr3控制寄存器包含一個27位的頁目錄指針表基地址字段,因為PDPT按32字節對齊
4.當把線性地址映射到4KB的頁時(頁目錄項中的PS標志清零),32位線性地址按下列方式解釋
? ?cr3:指向一個PDPT
? ?位31-30:指向PDPT四個中的一個
? ?位29-21:指向頁目錄中512個的一個
? ?位20-12:指向頁表中的512個的一個
? ?位11-0:4KB頁中的偏移量
5.當把線性地址映射到2MB的頁時(頁目錄項中的PS標志為1),32位線性地址按下列方式解釋
? cr3:指向一個PDPT
? 位31-30:指向PDPT四個中的一個
? 位29-21:指向頁目錄中512個的一個
? 位20-0:4MB頁中的偏移量
linux中的分頁
Linux 采用了一種同時適用于32位和64位系統的普通分因為模型
直到內核2.6.10,Linux 采用三級分頁模型
內核2.6.11開始,Linux 采用四級分頁模型:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 頁全局目錄(Page Global Directory)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 頁上級目錄(Page Upper Directory)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 頁中級目錄(Page Middle Directory)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 頁表(Page Table) ? ?
對于沒有激活PAE的32位系統,采用兩級頁表(頁全局目錄+頁表)
對于啟用了PAE的32位系統,采用三級頁表(頁全局目錄+頁中間目錄+頁表)
對于64位系統,采用三級頁表還是四級頁表由具體內核決定
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2015.10.17
線性地址字段
PAGE_SHIFT:指定Offset字段的位數,在80x86處理器上為12
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?PAGE_SIZE為2^12=4KB
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?PAGE_MASK為0xfffff000,用于屏蔽Offset的所有位
PMD_SHIFT:指定Offste字段和Table字段的總位數,既頁中間目錄項可以映射的區域大小的對數
? ? ? ? ? ? ? ? ? ? ? ? ? PAGE_MASK用于屏蔽Offset和Table的所有位
? ? ? ? ? ? ? ? ? ? ? ? ? 當未激活PAE時,PMD_SHIFT為22,PMD_SIZE為2^22=4MB
? ? ? ? ? ? ? ? ? ? ? ? ? 當激活PAE時,PMD_SHIFT為21,PMD_SIZE為2^21=2MB
PUD_SHIFT:確定頁上級目錄項能映射的區域的大小的對數
? ? ? ? ? ? ? ? ? ? ? ? 在80x86處理器中,PUD_SHIFT和PMD_SHIFT相同,PUD_SIZE為4MB或者2MB ? ??
PGDIR_SHIFT:確定頁全局目錄項能映射的區域的大小的對數
PTRS_PER_PTE,PTRS_PER_PMD,PTRS_PER_PUD,PTRS_PER_PGD:用于計算頁表 ?頁中間目錄 頁上級目錄和頁全局目錄表中表項的個數。當PAE禁用時,它們產生的值分別為1024,1,1,1024。當PAE激活時,產生的值分別時512,512,1,4。
頁表處理 ? ? ? ? ??
pte_t,pmd_t,pud_t和pgd_t分別描述頁表項 頁中間目錄項 頁上級目錄項和頁全局目錄項的格式
當PAE激活時,它們都是64位的數據類型,否則都是32位的數據類型。pgprot_t是另一個64位
(PAE激活時)或者32位(PAE禁止時)的數據類型,它表示與一個單獨表項相關的保護標志。
_ _pte _ _pmd ? _ _pud ?_ _pgd 和 _ _ pgprot可以把無符號整數轉換為相應的數據類型
pte_val pmd_val pud_val pgd_val 和pgprot_val則執行相反的轉換?
內核提供了許多宏和函數用于讀或者修改頁表表項:
1.如果相應的表項值為0,則宏pte_none pmd_none pud_none pgd_none返回值為1,否則返回0
2.宏pte_clear pmd_clear pud_clear 和 pgd_clear清除相應頁表的一個表項,由此禁止進程使用由該表項映射的線性地址。ptep_get_and_clear()函數用于清除一個頁表項并返回前一個值
3.set_pte set_pmd set_pud 和 set_pgd 向一個頁表項寫入指定的值
4.如果a 和 b 兩個頁表項指向同一頁并且指定相同的訪問優先級,那么pte_same(a,b)返回1,否則返回 0
5.如果頁中間項e指向一個大型頁(4MB或者2MB),那么pmd_larger(e)返回 1,否則返回 0
物理內存布局
一般來說Llinux 內核安裝在RAM中的物理地址0x00100000開始的地方,即從第2MB開始
從第2MB開始的原因:
1.頁框0由Bios使用
2.物理地址從0x000a0000到0x000fffff的范圍通常留給Bios例程,即680KB到1MB
3.第1MB的其它頁框為其他特定計算機保留
進程頁表
進程的線性空間分成兩部分:
1.從0x00000000到0xbfffffff的線性地址,無論進程運行在用戶態還是內核態都可以尋址(前3GB)
2.從0xc0000000到0xffffffff的線性地址,只有內核態的進程可以尋址(第4GB)
宏PAGE_OFFSET 產生的值為0xc0000000,這就是進程在線性地址空間的偏移量,也是內核生存空間的開始之處
內核頁表
內核維持著一組自己使用的頁表,駐留在所謂的主內核頁全局目錄(master kenerl Page Global?Directory)中
臨時內核列表
在此階段PAE未被激活,所以頁上級目錄和頁中級目錄相當于頁全局目錄,在此不再提及
臨時頁全局目錄放在swapper_pg_dir變量中,臨時頁表在pg0變量處開始存放
為簡單起見,我們假設內核使用的段 臨時頁表和128KB的內存范圍能容納于RAM前8MB空間里。
分頁第一個階段的目標時允許在實模式和保護模式下都能很容易地對8MB尋址。
因此,內核必須建立一個映射,把從0x00000000到0x007fffff的線性空間和0xc0000000到0xc07fffff的線性地址映射到從0x00000000到0x007fffff的物理地址。還句話說,內核在初始化的第一階段,可以通過與物理地址相同的線性地址或者通過0xc0000000開始的8mb線性地址對RAM的前8MB僅進行尋址
固定映射的線性地址
固定映射的線性地址(fix-mapped linear address)基本上時一種類似于0xffffc000這樣的常量線性地址,其對應的物理地址可以以任意方式建立。因此,每個固定映射的線性地址都映射一個物理內存的頁框
? 2015.10.18.22:55