鴻蒙輕內(nèi)核A核源碼分析系列三 物理內(nèi)存之二
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
3.1.2.3 函數(shù)OsVmPhysLargeAlloc
當(dāng)執(zhí)行到這個(gè)函數(shù)時(shí),說(shuō)明空閑鏈表上的單個(gè)內(nèi)存頁(yè)節(jié)點(diǎn)的大小已經(jīng)不能滿(mǎn)足要求,超過(guò)了第9個(gè)鏈表上的內(nèi)存頁(yè)節(jié)點(diǎn)的大小了。⑴處計(jì)算需要申請(qǐng)的內(nèi)存大小。⑵從最大的鏈表上進(jìn)行遍歷每一個(gè)內(nèi)存頁(yè)節(jié)點(diǎn)。⑶根據(jù)每個(gè)內(nèi)存頁(yè)的開(kāi)始內(nèi)存地址,計(jì)算需要的內(nèi)存的結(jié)束地址,如果超過(guò)內(nèi)存段的大小,則繼續(xù)遍歷下一個(gè)內(nèi)存頁(yè)節(jié)點(diǎn)。
⑷處此時(shí)paStart表示當(dāng)前內(nèi)存頁(yè)的結(jié)束地址,接下來(lái)paStart >= paEnd表示當(dāng)前內(nèi)存頁(yè)的大小滿(mǎn)足申請(qǐng)的需求;paStart < seg->start和paStart >= (seg->start + seg->size)發(fā)生溢出錯(cuò)誤,內(nèi)存頁(yè)結(jié)束地址不在內(nèi)存段的地址范圍內(nèi)。⑸處表示當(dāng)前內(nèi)存頁(yè)的下一個(gè)內(nèi)存頁(yè)結(jié)構(gòu)體,如果該結(jié)構(gòu)體不在空閑鏈表上,則break跳出循環(huán)。如果在空閑鏈表上,表示連續(xù)的空閑內(nèi)存頁(yè)會(huì)拼接起來(lái),滿(mǎn)足大內(nèi)存申請(qǐng)的需要。⑹表示一個(gè)或者多個(gè)連續(xù)的內(nèi)存頁(yè)的大小滿(mǎn)足申請(qǐng)需求。
- STATIC LosVmPage *OsVmPhysLargeAlloc(struct VmPhysSeg *seg, size_t nPages)
- {
- struct VmFreeList *list = NULL;
- LosVmPage *page = NULL;
- LosVmPage *tmp = NULL;
- PADDR_T paStart;
- PADDR_T paEnd;
- ⑴ size_t size = nPages << PAGE_SHIFT;
- ⑵ list = &seg->freeList[VM_LIST_ORDER_MAX - 1];
- LOS_DL_LIST_FOR_EACH_ENTRY(page, &list->node, LosVmPage, node) {
- ⑶ paStart = page->physAddr;
- paEnd = paStart + size;
- if (paEnd > (seg->start + seg->size)) {
- continue;
- }
- for (;;) {
- ⑷ paStart += PAGE_SIZE << (VM_LIST_ORDER_MAX - 1);
- if ((paStart >= paEnd) || (paStart < seg->start) ||
- (paStart >= (seg->start + seg->size))) {
- break;
- }
- ⑸ tmp = &seg->pageBase[(paStart - seg->start) >> PAGE_SHIFT];
- if (tmp->order != (VM_LIST_ORDER_MAX - 1)) {
- break;
- }
- }
- ⑹ if (paStart >= paEnd) {
- return page;
- }
- }
- return NULL;
- }
3.1.2.4 函數(shù)OsVmPhysFreeListDelUnsafe和OsVmPhysFreeListAddUnsafe
內(nèi)部函數(shù)OsVmPhysFreeListDelUnsafe用于從空閑內(nèi)存頁(yè)節(jié)點(diǎn)鏈表上刪除一個(gè)內(nèi)存頁(yè)節(jié)點(diǎn),名稱(chēng)中有Unsafe字樣,是因?yàn)楹瘮?shù)體內(nèi)并沒(méi)有對(duì)鏈表操作加自旋鎖,安全性由外部調(diào)用函數(shù)保證。⑴處進(jìn)行校驗(yàn),確保內(nèi)存段和空閑鏈表索引符合要求。⑵處獲取內(nèi)存段和空閑鏈表,⑶處空閑鏈表上內(nèi)存頁(yè)節(jié)點(diǎn)數(shù)目減1,并把內(nèi)存塊從空閑鏈表上刪除。⑷處設(shè)置內(nèi)存頁(yè)的order索引值為最大值來(lái)標(biāo)記非空閑內(nèi)存頁(yè)。
- STATIC VOID OsVmPhysFreeListDelUnsafe(LosVmPage *page)
- {
- struct VmPhysSeg *seg = NULL;
- struct VmFreeList *list = NULL;
- ⑴ if ((page->segID >= VM_PHYS_SEG_MAX) || (page->order >= VM_LIST_ORDER_MAX)) {
- LOS_Panic("The page segment id(%u) or order(%u) is invalid\n", page->segID, page->order);
- }
- ⑵ seg = &g_vmPhysSeg[page->segID];
- list = &seg->freeList[page->order];
- ⑶ list->listCnt--;
- LOS_ListDelete(&page->node);
- ⑷ page->order = VM_LIST_ORDER_MAX;
- }
和空閑鏈表上刪除對(duì)應(yīng)的函數(shù)是空閑鏈表上插入空閑內(nèi)存頁(yè)節(jié)點(diǎn)函數(shù)OsVmPhysFreeListAddUnsafe。⑴處更新內(nèi)存頁(yè)的要掛載的空閑鏈表的索引值,然后獲取內(nèi)存頁(yè)所在的內(nèi)存段seg,并獲取索引值對(duì)應(yīng)的空閑鏈表。執(zhí)行⑵把空閑內(nèi)存頁(yè)節(jié)點(diǎn)插入到空閑鏈表并更新節(jié)點(diǎn)數(shù)目。
- STATIC VOID OsVmPhysFreeListAddUnsafe(LosVmPage *page, UINT8 order)
- {
- struct VmPhysSeg *seg = NULL;
- struct VmFreeList *list = NULL;
- if (page->segID >= VM_PHYS_SEG_MAX) {
- LOS_Panic("The page segment id(%d) is invalid\n", page->segID);
- }
- ⑴ page->order = order;
- seg = &g_vmPhysSeg[page->segID];
- list = &seg->freeList[order];
- ⑵ LOS_ListTailInsert(&list->node, &page->node);
- list->listCnt++;
- }
3.1.2.5 函數(shù)OsVmPhysPagesSpiltUnsafe
函數(shù)OsVmPhysPagesSpiltUnsafe用于分割內(nèi)存塊,參數(shù)中oldOrder表示需要申請(qǐng)的內(nèi)存頁(yè)節(jié)點(diǎn)對(duì)應(yīng)的鏈表索引,newOrder表示實(shí)際申請(qǐng)的內(nèi)存頁(yè)節(jié)點(diǎn)對(duì)應(yīng)的鏈表索引。如果索引值相等,則不需要拆分,不會(huì)執(zhí)行for循環(huán)塊的代碼。由于伙伴算法中的鏈表數(shù)組中元素的特點(diǎn),即每個(gè)鏈表中的內(nèi)存頁(yè)節(jié)點(diǎn)的大小等于2的冪次方個(gè)內(nèi)存頁(yè)。在拆分時(shí),依次從高索引newOrder往低索引oldOrder遍歷,拆分一個(gè)內(nèi)存頁(yè)節(jié)點(diǎn)作為空閑內(nèi)存頁(yè)節(jié)點(diǎn)掛載到對(duì)應(yīng)的空閑鏈表上。⑴處開(kāi)始循環(huán)從高索引到低索引,索引值減1,然后執(zhí)行⑵獲取伙伴內(nèi)存頁(yè)節(jié)點(diǎn),可以看出,申請(qǐng)的內(nèi)存塊大于需求時(shí),會(huì)把后半部分的高地址部分放入空閑鏈表,保留前半部分的低地址部分。⑶處的斷言確?;锇閮?nèi)存頁(yè)節(jié)點(diǎn)索引值是最大值,表示屬于空閑內(nèi)存頁(yè)節(jié)點(diǎn)。⑷處調(diào)用函數(shù)把內(nèi)存頁(yè)節(jié)點(diǎn)放入空閑鏈表。
- STATIC VOID OsVmPhysPagesSpiltUnsafe(LosVmPage *page, UINT8 oldOrder, UINT8 newOrder)
- {
- UINT32 order;
- LosVmPage *buddyPage = NULL;
- for (order = newOrder; order > oldOrder;) {
- ⑴ order--;
- ⑵ buddyPage = &page[VM_ORDER_TO_PAGES(order)];
- ⑶ LOS_ASSERT(buddyPage->order == VM_LIST_ORDER_MAX);
- ⑷ OsVmPhysFreeListAddUnsafe(buddyPage, order);
- }
- }
這里有必要放這一張圖,直觀(guān)演示一下。假如我們需要申請(qǐng)8個(gè)內(nèi)存頁(yè)大小的內(nèi)存節(jié)點(diǎn),但是只有freeList[7]鏈表上才有空閑節(jié)點(diǎn)。申請(qǐng)成功后,超過(guò)了應(yīng)用需要的大小,需要進(jìn)行拆分。把27個(gè)內(nèi)存頁(yè)分為2份大小為26個(gè)內(nèi)存頁(yè)的節(jié)點(diǎn),第一份繼續(xù)拆分,第二份掛載到freeList[6]鏈表上。然后把第一份26個(gè)內(nèi)存頁(yè)拆分為2個(gè)25個(gè)內(nèi)存頁(yè)節(jié)點(diǎn),第一份繼續(xù)拆分,第二份掛載到freeList[5]鏈表上。依次進(jìn)行下去,最后拆分為2份2^3個(gè)內(nèi)存頁(yè)大小的內(nèi)存頁(yè)節(jié)點(diǎn),第一份作為實(shí)際申請(qǐng)的內(nèi)存頁(yè)返回,第二份掛載到freeList[3]鏈表上。如下圖紅色部分所示。

另外,函數(shù)OsVmRecycleExtraPages會(huì)調(diào)用OsVmPhysPagesFreeContiguous來(lái)回收申請(qǐng)的多余的內(nèi)存頁(yè),后文再分析。
3.2 釋放物理內(nèi)存頁(yè)接口
3.2.1 釋放物理內(nèi)存頁(yè)接口介紹
和申請(qǐng)物理內(nèi)存頁(yè)接口相對(duì)應(yīng)著,釋放物理內(nèi)存頁(yè)的接口有3個(gè),分別用于滿(mǎn)足不同的釋放內(nèi)存頁(yè)需求。函數(shù)LOS_PhysPagesFreeContiguous的傳入?yún)?shù)為要釋放物理頁(yè)對(duì)應(yīng)的內(nèi)核虛擬地址空間中的虛擬內(nèi)存地址和內(nèi)存頁(yè)數(shù)目。⑴處調(diào)用函數(shù)OsVmVaddrToPage把虛擬內(nèi)存地址轉(zhuǎn)換為物理內(nèi)存頁(yè)結(jié)構(gòu)體地址,然后⑵處把內(nèi)存頁(yè)的連續(xù)內(nèi)存頁(yè)數(shù)目設(shè)置為0。⑶處調(diào)用函數(shù)OsVmPhysPagesFreeContiguous()釋放物理內(nèi)存頁(yè)。函數(shù)LOS_PhysPageFree用于釋放一個(gè)物理內(nèi)存頁(yè),傳入?yún)?shù)為要釋放的物理頁(yè)對(duì)應(yīng)的物理頁(yè)結(jié)構(gòu)體地址。⑷處對(duì)引用計(jì)數(shù)自減,當(dāng)小于等于0,表示沒(méi)有其他引用時(shí)才進(jìn)一步執(zhí)行釋放操作。該函數(shù)同樣會(huì)調(diào)用函數(shù)OsVmPhysPagesFreeContiguous()釋放物理內(nèi)存頁(yè)。函數(shù)LOS_PhysPagesFree用于釋放掛在雙向鏈表上的多個(gè)物理內(nèi)存頁(yè),返回值為實(shí)際釋放的物理頁(yè)數(shù)目。⑸處遍歷內(nèi)存頁(yè)雙向鏈表,從鏈表上移除要釋放的內(nèi)存頁(yè)節(jié)點(diǎn)。⑹處代碼和釋放一個(gè)內(nèi)存頁(yè)的函數(shù)代碼相同。⑺處計(jì)算遍歷的內(nèi)存頁(yè)的數(shù)目,函數(shù)最后會(huì)返回該值。
- VOID LOS_PhysPagesFreeContiguous(VOID *ptr, size_t nPages)
- {
- UINT32 intSave;
- struct VmPhysSeg *seg = NULL;
- LosVmPage *page = NULL;
- if (ptr == NULL) {
- return;
- }
- ⑴ page = OsVmVaddrToPage(ptr);
- if (page == NULL) {
- VM_ERR("vm page of ptr(%#x) is null", ptr);
- return;
- }
- ⑵ page->nPages = 0;
- seg = &g_vmPhysSeg[page->segID];
- LOS_SpinLockSave(&seg->freeListLock, &intSave);
- ⑶ OsVmPhysPagesFreeContiguous(page, nPages);
- LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
- }
- ......
- VOID LOS_PhysPageFree(LosVmPage *page)
- {
- UINT32 intSave;
- struct VmPhysSeg *seg = NULL;
- if (page == NULL) {
- return;
- }
- ⑷ if (LOS_AtomicDecRet(&page->refCounts) <= 0) {
- seg = &g_vmPhysSeg[page->segID];
- LOS_SpinLockSave(&seg->freeListLock, &intSave);
- OsVmPhysPagesFreeContiguous(page, ONE_PAGE);
- LOS_AtomicSet(&page->refCounts, 0);
- LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
- }
- }
- ······
- size_t LOS_PhysPagesFree(LOS_DL_LIST *list)
- {
- UINT32 intSave;
- LosVmPage *page = NULL;
- LosVmPage *nPage = NULL;
- LosVmPhysSeg *seg = NULL;
- size_t count = 0;
- if (list == NULL) {
- return 0;
- }
- LOS_DL_LIST_FOR_EACH_ENTRY_SAFE(page, nPage, list, LosVmPage, node) {
- ⑸ LOS_ListDelete(&page->node);
- ⑹ if (LOS_AtomicDecRet(&page->refCounts) <= 0) {
- seg = &g_vmPhysSeg[page->segID];
- LOS_SpinLockSave(&seg->freeListLock, &intSave);
- OsVmPhysPagesFreeContiguous(page, ONE_PAGE);
- LOS_AtomicSet(&page->refCounts, 0);
- LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
- }
- ⑺ count++;
- }
- return count;
- }
3.2.2 釋放物理內(nèi)存頁(yè)內(nèi)部接口實(shí)現(xiàn)
3.2.2.1 函數(shù)OsVmVaddrToPage
函數(shù)OsVmVaddrToPage把虛擬內(nèi)存地址轉(zhuǎn)換為物理頁(yè)結(jié)構(gòu)體地址。⑴處調(diào)用函數(shù)LOS_PaddrQuery()把虛擬地址轉(zhuǎn)為物理地址,該函數(shù)在虛實(shí)映射部分會(huì)詳細(xì)講述。⑵處遍歷物理內(nèi)存段,如果物理內(nèi)存地址處于物理內(nèi)存段的地址范圍,則可以返回該物理地址對(duì)應(yīng)的物理頁(yè)結(jié)構(gòu)體地址。
- LosVmPage *OsVmVaddrToPage(VOID *ptr)
- {
- struct VmPhysSeg *seg = NULL;
- ⑴ PADDR_T pa = LOS_PaddrQuery(ptr);
- UINT32 segID;
- for (segID = 0; segID < g_vmPhysSegNum; segID++) {
- seg = &g_vmPhysSeg[segID];
- ⑵ if ((pa >= seg->start) && (pa < (seg->start + seg->size))) {
- return seg->pageBase + ((pa - seg->start) >> PAGE_SHIFT);
- }
- }
- return NULL;
- }
3.2.2.2 函數(shù)OsVmPhysPagesFreeContiguous
函數(shù)OsVmPhysPagesFreeContiguous()用于釋放指定數(shù)量的連續(xù)物理內(nèi)存頁(yè)。⑴處根據(jù)物理內(nèi)存頁(yè)獲取對(duì)應(yīng)的物理內(nèi)存地址。⑵處根據(jù)物理內(nèi)存地址獲取空閑內(nèi)存頁(yè)鏈表數(shù)組索引數(shù)值。⑶處獲取索引值對(duì)應(yīng)的鏈表上的內(nèi)存頁(yè)節(jié)點(diǎn)的內(nèi)存頁(yè)數(shù)目。⑷處如果要釋放的內(nèi)存頁(yè)數(shù)nPages小于當(dāng)前鏈表上的內(nèi)存頁(yè)節(jié)點(diǎn)的數(shù)目,則跳出循環(huán)執(zhí)行⑹處代碼,去釋放到小索引的雙向鏈表上。⑸處調(diào)用函數(shù)OsVmPhysPagesFree()釋放指定鏈表上的內(nèi)存頁(yè),然后更新內(nèi)存頁(yè)數(shù)量和內(nèi)存頁(yè)結(jié)構(gòu)體地址。
⑹處根據(jù)內(nèi)存頁(yè)數(shù)量計(jì)算對(duì)應(yīng)的鏈表索引,根據(jù)索引值計(jì)算鏈表上內(nèi)存頁(yè)節(jié)點(diǎn)的大小。⑺處調(diào)用函數(shù)OsVmPhysPagesFree()釋放指定鏈表上的內(nèi)存頁(yè),然后更新內(nèi)存頁(yè)數(shù)量和內(nèi)存頁(yè)結(jié)構(gòu)體地址。
- VOID OsVmPhysPagesFreeContiguous(LosVmPage *page, size_t nPages)
- {
- paddr_t pa;
- UINT32 order;
- size_t n;
- while (TRUE) {
- ⑴ pa = VM_PAGE_TO_PHYS(page);
- ⑵ order = VM_PHYS_TO_ORDER(pa);
- ⑶ n = VM_ORDER_TO_PAGES(order);
- ⑷ if (n > nPages) {
- break;
- }
- ⑸ OsVmPhysPagesFree(page, order);
- nPages -= n;
- page += n;
- }
- while (nPages > 0) {
- ⑹ order = LOS_HighBitGet(nPages);
- n = VM_ORDER_TO_PAGES(order);
- ⑺ OsVmPhysPagesFree(page, order);
- nPages -= n;
- page += n;
- }
- }
3.2.2.3 函數(shù)OsVmPhysPagesFree
函數(shù)OsVmPhysPagesFree()釋放內(nèi)存頁(yè)到對(duì)應(yīng)的空閑內(nèi)存頁(yè)鏈表。內(nèi)存頁(yè)塊釋放時(shí),會(huì)在當(dāng)前鏈表找地址連續(xù)的伙伴內(nèi)存頁(yè)塊進(jìn)行合并,然后去上一級(jí)鏈表上繼續(xù)查找是否存在連續(xù)的伙伴內(nèi)存頁(yè)塊。⑴做傳入?yún)?shù)校驗(yàn)。⑵處需要至少是倒數(shù)第二個(gè)鏈表,這樣內(nèi)存頁(yè)節(jié)點(diǎn)可以和大索引鏈表上的節(jié)點(diǎn)合并。⑶處獲取內(nèi)存頁(yè)對(duì)應(yīng)的物理內(nèi)存地址,然后后面會(huì)開(kāi)始do-while循環(huán),查找是否存在連續(xù)的內(nèi)存頁(yè)節(jié)點(diǎn)。⑷處的VM_ORDER_TO_PHYS(order)計(jì)算出鏈表索引值對(duì)應(yīng)的伙伴位圖,然后進(jìn)行異或運(yùn)算計(jì)算出伙伴內(nèi)存頁(yè)的物理內(nèi)存地址。⑸處物理地址轉(zhuǎn)換為內(nèi)存頁(yè)結(jié)構(gòu)體,進(jìn)一步判斷:如果內(nèi)存頁(yè)不存在或者不在空閑鏈表上,則跳出循環(huán)while循環(huán)。否則如果伙伴內(nèi)存節(jié)點(diǎn)存在,則執(zhí)行⑹把伙伴頁(yè)從鏈表上移除,然后索引值加1。⑺處鏈表索引加1,然后進(jìn)行邏輯與計(jì)算得到物理內(nèi)存地址。此時(shí)物理內(nèi)存地址,和合并的兩塊內(nèi)存頁(yè)塊地址連續(xù)。該內(nèi)存地址在高一級(jí)的空閑鏈表上不一定存在,存在則繼續(xù)合并,不存在則退出循環(huán)。當(dāng)索引order為8,要插入到最后一個(gè)鏈表上時(shí),或者沒(méi)有再找到可以合并的節(jié)點(diǎn)時(shí),則直接執(zhí)行⑻插入內(nèi)存頁(yè)節(jié)點(diǎn)到空閑鏈表上。
- VOID OsVmPhysPagesFree(LosVmPage *page, UINT8 order)
- {
- paddr_t pa;
- LosVmPage *buddyPage = NULL;
- ⑴ if ((page == NULL) || (order >= VM_LIST_ORDER_MAX)) {
- return;
- }
- ⑵ if (order < VM_LIST_ORDER_MAX - 1) {
- ⑶ pa = VM_PAGE_TO_PHYS(page);
- do {
- ⑷ pa ^= VM_ORDER_TO_PHYS(order);
- ⑸ buddyPage = OsVmPhysToPage(pa, page->segID);
- if ((buddyPage == NULL) || (buddyPage->order != order)) {
- break;
- }
- ⑹ OsVmPhysFreeListDel(buddyPage);
- order++;
- ⑺ pa &= ~(VM_ORDER_TO_PHYS(order) - 1);
- page = OsVmPhysToPage(pa, page->segID);
- } while (order < VM_LIST_ORDER_MAX - 1);
- }
- ⑻ OsVmPhysFreeListAdd(page, order);
- }
3.3 查詢(xún)物理頁(yè)地址接口
3.3.1 函數(shù)LOS_VmPageGet()
函數(shù)LOS_VmPageGet用于根據(jù)物理內(nèi)存地址參數(shù)計(jì)算對(duì)應(yīng)的物理內(nèi)存頁(yè)結(jié)構(gòu)體地址。⑴處遍歷物理內(nèi)存段,調(diào)用函數(shù)OsVmPhysToPage根據(jù)物理內(nèi)存地址和內(nèi)存段編號(hào)計(jì)算物理內(nèi)存頁(yè)結(jié)構(gòu)體,該函數(shù)后文再分析。⑵處如果獲取的物理內(nèi)存頁(yè)結(jié)構(gòu)體不為空,則跳出循環(huán),返回物理內(nèi)存頁(yè)結(jié)構(gòu)體指針。
- LosVmPage *LOS_VmPageGet(PADDR_T paddr)
- {
- INT32 segID;
- LosVmPage *page = NULL;
- for (segID = 0; segID < g_vmPhysSegNum; segID++) {
- ⑴ page = OsVmPhysToPage(paddr, segID);
- ⑵ if (page != NULL) {
- break;
- }
- }
- return page;
- }
繼續(xù)看下函數(shù)OsVmPhysToPage的代碼。⑴處如果參數(shù)傳入的物理內(nèi)存地址不在指定的物理內(nèi)存段的地址范圍之內(nèi)則返回NULL。⑵處計(jì)算物理內(nèi)存地址相對(duì)內(nèi)存段開(kāi)始地址的偏移值。⑶處根據(jù)偏移值計(jì)算出偏移的內(nèi)存頁(yè)的數(shù)目,然后返回物理內(nèi)存地址對(duì)應(yīng)的物理頁(yè)結(jié)構(gòu)體的地址。
- LosVmPage *OsVmPhysToPage(paddr_t pa, UINT8 segID)
- {
- struct VmPhysSeg *seg = NULL;
- paddr_t offset;
- if (segID >= VM_PHYS_SEG_MAX) {
- LOS_Panic("The page segment id(%d) is invalid\n", segID);
- }
- seg = &g_vmPhysSeg[segID];
- ⑴ if ((pa < seg->start) || (pa >= (seg->start + seg->size))) {
- return NULL;
- }
- ⑵ offset = pa - seg->start;
- ⑶ return (seg->pageBase + (offset >> PAGE_SHIFT));
- }
3.3.2 函數(shù)LOS_PaddrToKVaddr
函數(shù)LOS_PaddrToKVaddr根據(jù)物理地址獲取其對(duì)應(yīng)的內(nèi)核虛擬地址。⑴處遍歷物理內(nèi)存段數(shù)組,然后在⑵處判斷如果物理地址處于遍歷到的物理內(nèi)存段的地址范圍內(nèi),則執(zhí)行⑶,傳入的物理內(nèi)存地址相對(duì)物理內(nèi)存開(kāi)始地址的偏移加上內(nèi)核態(tài)虛擬地址空間的開(kāi)始地址就是物理地址對(duì)應(yīng)的內(nèi)核虛擬地址。
- VADDR_T *LOS_PaddrToKVaddr(PADDR_T paddr)
- {
- struct VmPhysSeg *seg = NULL;
- UINT32 segID;
- for (segID = 0; segID < g_vmPhysSegNum; segID++) {
- ⑴ seg = &g_vmPhysSeg[segID];
- ⑵ if ((paddr >= seg->start) && (paddr < (seg->start + seg->size))) {
- ⑶ return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);
- }
- }
- return (VADDR_T *)(UINTPTR)(paddr - SYS_MEM_BASE + KERNEL_ASPACE_BASE);
- }
3.4 其他函數(shù)
3.4.1 函數(shù)OsPhysSharePageCopy
函數(shù)OsPhysSharePageCopy用于復(fù)制共享內(nèi)存頁(yè)。 ⑴處進(jìn)行參數(shù)校驗(yàn), ⑵處獲取老內(nèi)存頁(yè), ⑶處獲取內(nèi)存段。⑷處如果老內(nèi)存頁(yè)引用計(jì)數(shù)為1,則把老物理內(nèi)存地址直接賦值給新物理內(nèi)存地址。⑸處如果內(nèi)存頁(yè)有多個(gè)引用,則先轉(zhuǎn)化為虛擬內(nèi)存地址,然后執(zhí)行⑹進(jìn)行內(nèi)存頁(yè)的內(nèi)容復(fù)制。⑺刷新新老內(nèi)存頁(yè)的引用計(jì)數(shù)。
- VOID OsPhysSharePageCopy(PADDR_T oldPaddr, PADDR_T *newPaddr, LosVmPage *newPage)
- {
- UINT32 intSave;
- LosVmPage *oldPage = NULL;
- VOID *newMem = NULL;
- VOID *oldMem = NULL;
- LosVmPhysSeg *seg = NULL;
- ⑴ if ((newPage == NULL) || (newPaddr == NULL)) {
- VM_ERR("new Page invalid");
- return;
- }
- ⑵ oldPage = LOS_VmPageGet(oldPaddr);
- if (oldPage == NULL) {
- VM_ERR("invalid oldPaddr %p", oldPaddr);
- return;
- }
- ⑶ seg = &g_vmPhysSeg[oldPage->segID];
- LOS_SpinLockSave(&seg->freeListLock, &intSave);
- ⑷ if (LOS_AtomicRead(&oldPage->refCounts) == 1) {
- *newPaddr = oldPaddr;
- } else {
- ⑸ newMem = LOS_PaddrToKVaddr(*newPaddr);
- oldMem = LOS_PaddrToKVaddr(oldPaddr);
- if ((newMem == NULL) || (oldMem == NULL)) {
- LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
- return;
- }
- ⑹ if (memcpy_s(newMem, PAGE_SIZE, oldMem, PAGE_SIZE) != EOK) {
- VM_ERR("memcpy_s failed");
- }
- ⑺ LOS_AtomicInc(&newPage->refCounts);
- LOS_AtomicDec(&oldPage->refCounts);
- }
- LOS_SpinUnlockRestore(&seg->freeListLock, intSave);
- return;
- }
總結(jié)
本文首先了解了物理內(nèi)存管理的結(jié)構(gòu)體,接著閱讀了物理內(nèi)存如何初始化,然后分析了物理內(nèi)存的申請(qǐng)、釋放和查詢(xún)等操作接口的源代碼。
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)