<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > ARM Linux (S3C6410架構(gòu)/2.6.35內(nèi)核)的內(nèi)存映射(三)

          ARM Linux (S3C6410架構(gòu)/2.6.35內(nèi)核)的內(nèi)存映射(三)

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

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

          本文引用地址:http://www.ex-cimer.com/article/201611/317853.htm

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

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

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

          可能正是因?yàn)轫?yè)表大小(1K)與頁(yè)大小(4K)的不匹配,也造成了內(nèi)存映射計(jì)算方面的很多麻煩。直觀地來(lái)理解,既然每一個(gè)一級(jí)頁(yè)表項(xiàng)映射的內(nèi)存空間是1M,那么在代碼中一個(gè)一級(jí)頁(yè)表項(xiàng)pgd_t的大小就應(yīng)該定義為4字節(jié),PGDIR_SIZE應(yīng)該定義為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è)字節(jié)。其實(shí)這兩個(gè)PGD仍然是互相獨(dú)立的,并沒(méi)有任何關(guān)聯(lián)。
          這給理解和計(jì)算都帶來(lái)了麻煩,但唯一的一條好處就是更好地解決了頁(yè)表大小與頁(yè)大小不匹配的問(wèn)題。因?yàn)槊績(jī)蓚€(gè)相鄰的頁(yè)表是放在一起處理的,所以干脆把兩個(gè)相鄰的PGD也定義在一起,這樣當(dāng)其中的一個(gè)被映射時(shí)也要保證另一個(gè)得到映射。
          下面看一個(gè)映射中斷向量表的實(shí)際過(guò)程,通過(guò)調(diào)用棧paging_init()->devicemaps_init()->create_mapping()->alloc_init_section()->alloc_init_pte(),最后到達(dá)了函數(shù)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ō)明一下,因?yàn)樵赟3C6410上,最多只支持內(nèi)存的二級(jí)映射即PGD->PTE->page,所以并不存在真正的PMD,即使當(dāng)它出現(xiàn)時(shí),它也與PGD相同。
          這段代碼檢查一級(jí)映射項(xiàng)PGD是不是空,如果是空的話就說(shuō)明一級(jí)映射還沒(méi)有建立過(guò),(二級(jí))頁(yè)表不存在,所以就先通過(guò)boomem來(lái)申請(qǐng)一張頁(yè)面做為頁(yè)表,有了頁(yè)表就可以填充PGD了,填充PGD的代碼__pmd_populate()在《Arm-Linux二級(jí)頁(yè)表的問(wèn)題》一篇中已經(jīng)講過(guò),不再贅述。

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

          [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)計(jì)算出這個(gè)物理地址在頁(yè)表中對(duì)應(yīng)的位置,不過(guò)需要注意的是,這里計(jì)算出的pte值指的是這個(gè)頁(yè)面所對(duì)應(yīng)的頁(yè)表項(xiàng)在硬件頁(yè)表中的位置。接下來(lái)調(diào)用set_pte_ext(),這是一個(gè)依硬件而不同的函數(shù),比如在S3C6410上,它的實(shí)現(xiàn)是armv6_set_pte_ext(),是在arch/arm/mm/proc-macros.S文件中用匯編代碼實(shí)現(xiàn)的。set_pte_ext()的作用是同時(shí)填充硬件頁(yè)表和內(nèi)核頁(yè)表。

          看一下在skyeye模擬器上運(yùn)行這個(gè)內(nèi)核的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申請(qǐng)到的中斷向量表頁(yè)的位置是0xc02aa000,這已經(jīng)是一個(gè)虛擬地址,但我們需要把它重新映射到指定地址0xffff0000去。在alloc_init_pte()中,首先確認(rèn)PGD為空,于是申請(qǐng)一頁(yè)內(nèi)存做為頁(yè)表,得到的頁(yè)面是0xc02ab000,緊挨著中斷向量表那一樣。

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

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



          評(píng)論


          相關(guān)推薦

          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();