[FreeBSD]x86地址映射实例
http://www.chinaunix.net 作者:qiuhanty?
?x86地址映射實例
qiuhan
2007.8.15
今天我們通過qemu來探討freeBSD下x86地址映射。
用戶地址空間的映射:
我們以調試auditd為例
#?qgdb?auditd
(gdb)?b?main
Breakpoint?1?at?0x804b594:?file?/kerndebug/umbrella/usr.sbin/auditd/../../contrib/audit_supt/auditd/auditd.c,?line?1140.
(gdb)?target?remote?:1234
Remote?debugging?using?:1234
0xc08daf1c?in?.rtld_start?()?from?/libexec/ld-elf.so.1
(gdb)?c
Continuing.
Breakpoint?1,?main?(argc=-1077940752,?argv=0x0)
????at?/kerndebug/umbrella/usr.sbin/auditd/../../contrib/audit_supt/auditd/auditd.c:1138
1138????{
(gdb)?cpu_dump
ldtr:s=0x0050,?bs=0xc0a73ae0,?lm=0x00000087,?flag=0x0000e200
tr:s=0x0048,?bs=0xc0a73dc0,?lm=0x00000067,?flag=0xc00089a7
gdtr:base=0xc0a73a40,?limit=0x97
idtr:base=0xc0a73f20,?limit=0x7ff
dr0:0x00000000,?dr1:0x00000000,?dr2:0x00000000
dr3:0x00000000,?dr6:0x00000000,?dr7:0x00000000
cr0:0xe005003b,?cr1:0x00000000,?cr2:0x281d9028
cr3:0x079b6000,?cr4:0x00000690
(gdb)?p?$eip
$1?=?(void?(*)())?0x804b578?<main>
(gdb)?x/x?$eip
0x804b578?<main>:???????0x83e58955
這里,0x804b578是一個虛擬地址,經過段地址映射出的線性地址不變;我們主要來看線性地址到物理地址的轉換。
該地址的前10位左移2位得到0x80,加上作為頁目錄(Page-directory)基址的cr3,得到對應的頁目錄項地址
0x079b6080(physical),查看該地址內容:
(gdb)?xp/x?0x079b6080
0x79b6080:??????0x04180067
這里得到的是頁表(Page-table)的基址0x04180000.再取0x804b578的中間10位左移2位得到0x12c,相加得到
對應的頁表項地址0x0418012c,查看該地址內容:
(gdb)?xp/x?0x0418012c
0x418012c:??????0x04115425
這里得到的是頁的基址0x04115000,加上0x804b578的最后12位,得到最終的物理地址0x04115578
(gdb)?xp/x?0x04115578
0x4115578:??????0x83e58955
哈哈,內容一樣吧!
內核地址空間的映射:
用戶程序(通過系統調用或中斷)進入內核空間時,是不需要切換cr3的,很神奇吧!我們來看為什么。
繼續在剛才的環境,我們隨便查看一個內核地址0xc0a39628
(gdb)?x/x?0xc0a39628
0xc0a39628:?????0xc1c910b4
內核地址到物理地址相差一個3G,我們直接減去0xc00000000就可以了
(gdb)?xp/x?0xa39628
0xa39628:???????0xc1c910b4
但是這個地址MMU是怎么得到的呢?仍然要通過cr3,我們來手動完成這個過程。
0xc0a39628的前10位左移2位得到0xc08(這里有個技巧,直接取前3位(16進制),第3位取4的整數倍即可),加上
cr3得到0x079b6c08,查看該地址內容:
(gdb)?xp/x?0x079b6c08
0x79b6c08:??????0x008001e3
注意,這里和剛才有所不同。剛才我們沒有討論最后12位的含義,這里必須說一下.當倒數第8位(Page?size)的值
為1(即16進制的倒數第2位的值大于8?)時,以為著頁面大小為4M,這時就從先前的兩級映射退化為一級映射,頁的基址
就是0x00800000,而地址的后22位(0x239628?)均為偏移,相加就得到物理地址0xa39628.
那么有多少個這樣頁面大小為4M的映射呢?
(gdb)?xp/16x?0x079b6c00
0x79b6c00:??????0x01000063??????0x004001a3??????0x008001e3??????0x00c001e3
0x79b6c10:??????0x01004023??????0x01005063??????0x01006003??????0x01007063
0x79b6c20:??????0x01008063??????0x01009023??????0x0100a023??????0x0100b003
0x79b6c30:??????0x0100c003??????0x0100d003??????0x0100e003??????0x0100f003
原來只有3個(0x004001a3,?0x008001e3以及0x00c001e3),它們對應的地址空間為0xc0400000到0xc1000000,
即物理地址的4M--16M,共12M.還記得我們在《loader分析》中說到,kernel加載的基址就是0xc0400000,這不
是一個巧合。
為什么要用這樣頁面大小為4M的映射呢?
Intel?<System?Programming?Guide>?3.7.3解釋到,把操作系統或者可執行的內核放在大的頁面中可以減少
TLB(Translation?Lookaside?Buffer,?用于緩存頁目錄項和頁表項)?misses,?從而可以提高系統整體性能。
而且,4M和4K的頁項使用不同的TLB.
為什么用戶程序進入內核空間時,不需要切換cr3?
應為所有的可以賦給cr3的基址在0xc00偏移上的內容幾乎都是一致的。包括專門標志內核空間的IdlePTD(其值一般
為0x101e000).IdlePTD是cr3的第一次,在內核初始化化過程中一直是該值,直到啟動第一個用戶程序。在cpu_switch
會通過比較當前cr3是否等于該值來判斷是否需要切換cr3.
為了更清楚地址映射過程,我們來看一下qemu中的一段代碼:
????if?(!(env->cr[0]?&?CR0_PG_MASK))?{//頁表映射沒有使能
????????????pte?=?addr;
????????????page_size?=?4096;
????????}?else?{
????????????/*?page?directory?entry?*/
????????????//頁目錄項地址,a20_mask的值一般均為0xffffffff
????????????pde_addr?=?((env->cr[3]?&?~0xfff)?+?((addr?>>?20)?&?~3))?&?env->a20_mask;
????????????pde?=?ldl_phys(pde_addr);
????????????if?(loglevel?&?CPU_LOG_QIUHAN)?{
????????????????fprintf(logfile,?"pde_addr=0x%08x,?pde=0x%08x\n",?pde_addr,?pde);
????????????}
????????????if?(!(pde?&?PG_PRESENT_MASK))//頁表不存在
????????????????return?-1;
????????????//頁面大小是否為4M
????????????if?((pde?&?PG_PSE_MASK)?&&?(env->cr[4]?&?CR4_PSE_MASK))?{
????????????????pte?=?pde?&?~0x003ff000;?/*?align?to?4MB?*/
????????????????page_size?=?4096?*?1024;
????????????}?else?{
????????????????/*?page?directory?entry?*/
????????????????pte_addr?=?((pde?&?~0xfff)?+?((addr?>>?10)?&?0xffc))?&?env->a20_mask;
????????????????pte?=?ldl_phys(pte_addr);
????????????????if?(!(pte?&?PG_PRESENT_MASK))//頁不存在
????????????????????return?-1;
????????????????page_size?=?4096;
????????????}
????????}
????????pte?=?pte?&?env->a20_mask;
????}
????page_offset?=?(addr?&?TARGET_PAGE_MASK)?&?(page_size?-?1);
????paddr?=?(pte?&?TARGET_PAGE_MASK)?+?page_offset;
????if?(loglevel?&?CPU_LOG_QIUHAN)?{
????????fprintf(logfile,?"pte_addr=0x%08x,?pte=0x%08x,?page_offset=0x%08x,?paddr=0x%08x\n",
????????????????pte_addr,?pte,?page_offset,?paddr);
????}
????return?paddr;
讀懂這段代碼,就比較清楚了。下面是剛才尋址時打印出的調試信息,能看懂吧:
addr=0x0804b578,?cr3=0x079b6000,?a20_mask=0xffffffff
pde_addr=0x079b6080,?pde=0x04180067
pte_addr=0x0418012c,?pte=0x04115425,?page_offset=0x00000000,?paddr=0x04115000
paddr=0x04115578
addr=0xc0a39628,?cr3=0x079b6000,?a20_mask=0xffffffff
pde_addr=0x079b6c08,?pde=0x008001e3
pte_addr=0xbfbf8b98,?pte=0x008001e3,?page_offset=0x00239000,?paddr=0x00a39000
paddr=0x00a39628
這就是qemu的好處:如果你對硬件不熟悉,可以通過閱讀它的代碼或者打印調試信息來理解。我正是先讀了這段代碼才
能寫下此文的。
這樣我們就對freeBSD下x86地址映射有了一個比較清楚的認識,至于這種映射是如何建立起來的,我們下次再說吧。
總結
以上是生活随笔為你收集整理的[FreeBSD]x86地址映射实例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 苹果公司 CEO 库克:第一财季收入下滑
- 下一篇: 苹果业绩暴雷:iPhone卖不动了!库克