<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 嵌入式系統 > 設計應用 > ARM Linux (S3C6410架構/2.6.35內核)的內存映射(三)

ARM Linux (S3C6410架構/2.6.35內核)的內存映射(三)

作者: 時(shí)間:2016-11-09 來(lái)源:網(wǎng)絡(luò ) 收藏
這里記錄一下Linux內核做二級內存映射的過(guò)程,以中斷向量表的映射過(guò)程為例。

S3C6410架構下,Linux采用的是粗粒度小頁(yè)內存管理方式,即內存段(section)的大小為1M,而頁(yè)(page)的大小為4K。在第一級內存映射中,每一個(gè)PGD項覆蓋1M的內存區域;如果有二級內存映射的話(huà),每一個(gè)PTE項覆蓋4K的內存區域。

本文引用地址:http://dyxdggzs.com/article/201611/317853.htm

下面我們來(lái)看一下二級內存映射表的設計。如果段的大小是1M而頁(yè)的大小是4K的話(huà),那么每一張二級映射表即頁(yè)表中就需要有1M/4K=256個(gè)表項。而不論是PGD還是PTE,每一個(gè)表項的大小是4字節,即一個(gè)長(cháng)整形數的大小。一張頁(yè)表的大小為256*4=1024/1K字節,所以,頁(yè)表的大小與頁(yè)的大小并不能對并,一張4K大小的內存頁(yè)可以存得下4張這樣的頁(yè)表。Linux采用了這樣一種設計來(lái)存放頁(yè)表:(文件arch/arm/include/asm/pgalloc.h)

在一張4K大小的內存頁(yè)中,存放了4張不同的頁(yè)表,它們依次是:第一張頁(yè)表的ARM版本(也被叫做硬件版本),第二張(與第一張表的虛擬空間是連續的)頁(yè)表的ARM版本,第一張頁(yè)表的內核版本(也被叫做Linux版本),第二張頁(yè)表的內核版本。同一張表的內核版本與ARM版本不是連續存放,而是間隔開(kāi)的。

頁(yè)表為什么會(huì )有內核版本和硬件版本的區分呢?因為內核需要的一些信息(比如dirty、access等)在A(yíng)RM需要的頁(yè)表信息中沒(méi)有,所以L(fǎng)inux需要另外一份滿(mǎn)足自己需要的映射表。

可能正是因為頁(yè)表大小(1K)與頁(yè)大小(4K)的不匹配,也造成了內存映射計算方面的很多麻煩。直觀(guān)地來(lái)理解,既然每一個(gè)一級頁(yè)表項映射的內存空間是1M,那么在代碼中一個(gè)一級頁(yè)表項pgd_t的大小就應該定義為4字節,PGDIR_SIZE應該定義為1M,但事實(shí)不是這樣:

[c]#define PGDIR_SHIFT 21#define PGDIR_SIZE (1UL << PGDIR_SHIFT)typedef unsigned long pgd_t[2];[/c] 

PGDIR_AIZE被定義為2M,而pgd_t被定義為8個(gè)字節。其實(shí)這兩個(gè)PGD仍然是互相獨立的,并沒(méi)有任何關(guān)聯(lián)。
這給理解和計算都帶來(lái)了麻煩,但唯一的一條好處就是更好地解決了頁(yè)表大小與頁(yè)大小不匹配的問(wèn)題。因為每?jì)蓚€(gè)相鄰的頁(yè)表是放在一起處理的,所以干脆把兩個(gè)相鄰的PGD也定義在一起,這樣當其中的一個(gè)被映射時(shí)也要保證另一個(gè)得到映射。
下面看一個(gè)映射中斷向量表的實(shí)際過(guò)程,通過(guò)調用棧paging_init()->devicemaps_init()->create_mapping()->alloc_init_section()->alloc_init_pte(),最后到達了函數alloc_init_pte(),這段代碼包含了我的注釋和打?。ㄒ訹Michael]開(kāi)關(guān)):

[c]static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,unsigned long end, unsigned long pfn,const struct mem_type *type){pte_t *pte;printk(MICHAEL_DBG "alloc_init_pte()n");if (pmd_none(*pmd)) {pte = alloc_bootmem_low_pages(2 * PTRS_PER_PTE * sizeof(pte_t));printk(MICHAEL_DBG "pmd is still blank, pte = 0x%xn", pte);printk(MICHAEL_DBG "will populate pmdn");__pmd_populate(pmd, __pa(pte) | type->prot_l1);}pte = pte_offset_kernel(pmd, addr);do {void *linux_pte = (void *)pte;void *hw_pte = linux_pte - 2048;printk(MICHAEL_DBG "pmd has been populated, pte = 0x%x, pfn = 0x%x, pfn_pte = 0x%xn", pte, pfn, pfn_pte(pfn, __pgprot(type->prot_pte)));printk(MICHAEL_DBG "before set_pte_ext(): hw_pte = 0x%x, *hw_pte = 0x%x, linux_pte = 0x%x, *linux_pte = 0x%xn", hw_pte, *((unsigned int*)hw_pte), linux_pte, *((unsigned int *)linux_pte));set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);printk(MICHAEL_DBG "after set_pte_ext(): hw_pte = 0x%x, *hw_pte = 0x%x, linux_pte = 0x%x, *linux_pte = 0x%xn", hw_pte, *((unsigned int*)hw_pte), linux_pte, *((unsigned int *)linux_pte));pfn  ;} while (pte  , addr  = PAGE_SIZE, addr != end);}[/c] 

先看前面一段(去掉了注釋和打印):

[c]if (pmd_none(*pmd)) {pte = alloc_bootmem_low_pages(2 * PTRS_PER_PTE * sizeof(pte_t));__pmd_populate(pmd, __pa(pte) | type->prot_l1);}[/c] 

先說(shuō)明一下,因為在S3C6410上,最多只支持內存的二級映射即PGD->PTE->page,所以并不存在真正的PMD,即使當它出現時(shí),它也與PGD相同。
這段代碼檢查一級映射項PGD是不是空,如果是空的話(huà)就說(shuō)明一級映射還沒(méi)有建立過(guò),(二級)頁(yè)表不存在,所以就先通過(guò)boomem來(lái)申請一張頁(yè)面做為頁(yè)表,有了頁(yè)表就可以填充PGD了,填充PGD的代碼__pmd_populate()在《Arm-Linux二級頁(yè)表的問(wèn)題》一篇中已經(jīng)講過(guò),不再贅述。

這一段執行完之后,頁(yè)表有了,但頁(yè)表還是空的,下面要給指定的表項填充內容:

[c]pte = pte_offset_kernel(pmd, addr);do {set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);pfn  ;} while (pte  , addr  = PAGE_SIZE, addr != end);}[/c] 

addr是需要映射的頁(yè)面的物理地址,pte_offset_kernel(pmd, addr)計算出這個(gè)物理地址在頁(yè)表中對應的位置,不過(guò)需要注意的是,這里計算出的pte值指的是這個(gè)頁(yè)面所對應的頁(yè)表項在硬件頁(yè)表中的位置。接下來(lái)調用set_pte_ext(),這是一個(gè)依硬件而不同的函數,比如在S3C6410上,它的實(shí)現是armv6_set_pte_ext(),是在arch/arm/mm/proc-macros.S文件中用匯編代碼實(shí)現的。set_pte_ext()的作用是同時(shí)填充硬件頁(yè)表和內核頁(yè)表。

看一下在skyeye模擬器上運行這個(gè)內核的log:
vectors = 0xc02aa000
init_mm.pgd = 0xc0004000, addr = 0xffff0000, pgd_index() = 0x7ff, PGDIR_SHIFT = 21
alloc_init_pte()
pmd is still blank, pte = 0xc02ab000
will populate pmd
__pmd_populate():
&pmdp[0] = 0xc0007ff8, pmdp[0] = 0x502ab021
&pmdp[1] = 0xc0007ffc, pmdp[1] = 0x502ab421
pmd has been populated, pte = 0xc02abfc0, pfn = 0x502aa, pfn_pte = 0x502aa34b
before set_pte_ext(): hw_pte = 0xc02ab7c0, *hw_pte = 0x0, linux_pte = 0xc02abfc0, *linux_pte = 0x0
after set_pte_ext(): hw_pte = 0xc02ab7c0, *hw_pte = 0x502aa02a, linux_pte = 0xc02abfc0, *linux_pte = 0x502aa34b

我們通過(guò)bootmem申請到的中斷向量表頁(yè)的位置是0xc02aa000,這已經(jīng)是一個(gè)虛擬地址,但我們需要把它重新映射到指定地址0xffff0000去。在alloc_init_pte()中,首先確認PGD為空,于是申請一頁(yè)內存做為頁(yè)表,得到的頁(yè)面是0xc02ab000,緊挨著(zhù)中斷向量表那一樣。

接下來(lái)填充PGD。我們要映射的的目標虛擬地址是0xffff0000,它在PGD表中的序號是0xffff0000/1M=0xfff,每個(gè)PGD占4字節,而PGD表的開(kāi)始位置是0xc0004000,所以0xffff0000所對應的PGD的位置是0xc0004000 ((0xffff0000/1M) * 4) =0xc0007ffc。它是相鄰兩個(gè)PGD中的第二個(gè),前一個(gè)PGD在0xc0007ff8。真給PGD的內容呢,先看前一項0xc0007ff8,向這里填的內容是(頁(yè)表基地址 屬性),新得到的頁(yè)表地址為0xc02ab000,物理地址為0x502ab000,加上屬性值0x21,所以向0xc0007ff8填充的PGD內容為0x502ab021。那么相應地,相鄰的下一張頁(yè)表的基地址要加上256*4=1024/1K,即0x400,所以向一個(gè)PGD(0xc0007ffc)填充的內容為0x502ab021 0x400 =0x502ab421。

現在頁(yè)表有了,下面要做的就是填充指定的頁(yè)表項。目標虛擬地址0xffff0000在Linux頁(yè)表中表項的地址是0xc02abfc0,這是由pte_offset_kernel(pmd, addr)計算出來(lái)的,然后調用set_pte_ext()寫(xiě)入頁(yè)表項,匯編代碼的細節這里先不深究,只看寫(xiě)入的內容。最后兩行打印分別是調用set_pte_ext()前后硬件頁(yè)表和內核頁(yè)表的內容,可以看到兩張表里的內容都已經(jīng)填好:

before set_pte_ext(): hw_pte = 0xc02ab7c0, *hw_pte = 0x0, linux_pte = 0xc02abfc0, *linux_pte = 0x0
after set_pte_ext(): hw_pte = 0xc02ab7c0, *hw_pte = 0x502aa02a, linux_pte = 0xc02abfc0, *linux_pte = 0x502aa34b



評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>