鴻蒙輕內(nèi)核A核源碼分析系列四(2) 虛擬內(nèi)存
鴻蒙輕內(nèi)核A核源碼分析系列四 虛擬內(nèi)存

上一篇了解了物理內(nèi)存,本文我們來熟悉下OpenHarmony鴻蒙輕內(nèi)核提供的虛擬內(nèi)存(Virtual memory)管理模塊。
本文中所涉及的源碼,以O(shè)penHarmony LiteOS-A內(nèi)核為例,均可以在開源站點https://gitee.com/openharmony/kernel_liteos_a 獲取。如果涉及開發(fā)板,則默認(rèn)以hispark_taurus為例。
我們首先了解了虛擬內(nèi)存管理的結(jié)構(gòu)體、相關(guān)宏定義,接著會分析內(nèi)核虛擬地址空間和用戶進(jìn)程虛擬地址空間如何初始化,然后分析虛擬內(nèi)存區(qū)間常用操作包含查找、申請和釋放等,最后分析動態(tài)內(nèi)存堆的申請、釋放接口的源代碼,并簡單介紹下內(nèi)存區(qū)間預(yù)留接口源代碼。
1、 虛擬內(nèi)存管理相關(guān)的結(jié)構(gòu)體
在文件kernel/base/include/los_vm_map.h中定義了進(jìn)程地址空間結(jié)構(gòu)體LosVmSpace,進(jìn)程地址區(qū)間結(jié)構(gòu)體LosVmMapRegion和進(jìn)程地址區(qū)間范圍結(jié)構(gòu)體LosVmMapRange。每個用戶態(tài)進(jìn)程會創(chuàng)建自己的進(jìn)程空間,內(nèi)核態(tài)會創(chuàng)建2個進(jìn)程空間,分別g_kVmSpace和g_vMallocSpace。從進(jìn)程空間申請的虛擬內(nèi)存塊使用進(jìn)程區(qū)間LosVmMapRegion來表示。每個進(jìn)程空間維護(hù)一個紅黑樹來鏈接各個進(jìn)程區(qū)間。
1.1 虛擬內(nèi)存地址空間結(jié)構(gòu)體LosVmSpace
typedef struct VmSpace { LOS_DL_LIST node; /**< 地址空間雙向鏈表 */ LosRbTree regionRbTree; /**< 地址區(qū)間的紅黑樹根節(jié)點 */ LosMux regionMux; /**< 地址區(qū)間的紅黑樹的互斥鎖 */ VADDR_T base; /**< 地址空間開始地址 */ UINT32 size; /**< 地址空間大小 */ VADDR_T heapBase; /**< 地址空間的堆開始地址heapBase */ VADDR_T heapNow; /**< 地址空間的堆開始地址heapNow */ LosVmMapRegion *heap; /**< 地址空間的地址區(qū)間 */ VADDR_T mapBase; /**< 地址空間的映射區(qū)開始地址 */ UINT32 mapSize; /**< 地址空間的映射區(qū)大小 */ LosArchMmu archMmu; /**< 地址空間的MMU結(jié)構(gòu)體 */ #ifdef LOSCFG_DRIVERS_TZDRIVER VADDR_T codeStart; /**< 用戶進(jìn)程代碼區(qū)開始地址 */ VADDR_T codeEnd; /**< 用戶進(jìn)程代碼區(qū)結(jié)束地址 */ #endif } LosVmSpace;
1.2 虛擬內(nèi)存地址區(qū)間LosVmMapRegion
typedef struct VmMapRange { VADDR_T base; /**< 虛擬內(nèi)存地址區(qū)間開始地址 */ UINT32 size; /**< 虛擬內(nèi)存地址區(qū)間大小 */ } LosVmMapRange; ...... struct VmMapRegion; typedef struct VmMapRegion LosVmMapRegion; ...... struct VmMapRegion { LosRbNode rbNode; /**< 地址區(qū)間紅黑樹節(jié)點 */ LosVmSpace *space; /**< 地址區(qū)間所在的地址空間 */ LOS_DL_LIST node; /**< 地址區(qū)間雙向鏈表 */ LosVmMapRange range; /**< 地址區(qū)間地址范圍 */ VM_OFFSET_T pgOff; /**< 地址區(qū)間頁偏移 */ UINT32 regionFlags; /**< 地址區(qū)間標(biāo)記: cow, user_wired */ UINT32 shmid; /**< 共享地址區(qū)間編號 */ UINT8 forkFlags; /**< 地址區(qū)間fork標(biāo)記: COPY, ZERO, */ UINT8 regionType; /**< 地址區(qū)間類型: ANON, FILE, DEV */ union { struct VmRegionFile { unsigned int fileMagic; struct file *file; const LosVmFileOps *vmFOps; } rf; struct VmRegionAnon { LOS_DL_LIST node; /**< 地址區(qū)間類型的雙向鏈表 */ } ra; struct VmRegionDev { LOS_DL_LIST node; /**< 地址區(qū)間類型的雙向鏈表 */ const LosVmFileOps *vmFOps; } rd; } unTypeData; };
2、 虛擬內(nèi)存相關(guān)的宏定義
文件kernel/base/include/los_vm_common.h和kernel/base/include/los_vm_zone.h定義了虛擬內(nèi)存相關(guān)的宏。對于32位系統(tǒng),虛擬進(jìn)程空間大小為4GiB,OpenHarmony鴻蒙輕內(nèi)核當(dāng)前支持32位系統(tǒng)。⑴和⑵定義了用戶進(jìn)程虛擬地址空間的開始地址和大小,⑶是用戶虛擬進(jìn)程空間的結(jié)束地址,接著定義的是用戶虛擬進(jìn)程空間的堆區(qū)、映射區(qū)的開始地址和大小。
/* user address space, defaults to below kernel space with a 16MB guard gap on either side */ #ifndef USER_ASPACE_BASE ⑴ #define USER_ASPACE_BASE ((vaddr_t)0x01000000UL) #endif #ifndef USER_ASPACE_SIZE ⑵ #define USER_ASPACE_SIZE ((vaddr_t)KERNEL_ASPACE_BASE - USER_ASPACE_BASE - 0x01000000UL) #endif ⑶ #define USER_ASPACE_TOP_MAX ((vaddr_t)(USER_ASPACE_BASE + USER_ASPACE_SIZE)) #define USER_HEAP_BASE ((vaddr_t)(USER_ASPACE_TOP_MAX >> 2)) #define USER_MAP_BASE ((vaddr_t)(USER_ASPACE_TOP_MAX >> 1)) #define USER_MAP_SIZE ((vaddr_t)(USER_ASPACE_SIZE >> 3))
內(nèi)核虛擬進(jìn)程空間的宏定義如下,⑴處定義內(nèi)核進(jìn)程地址空間開始地址和大小,⑵處定義內(nèi)核非緩存虛擬地址空間開始地址和大小,⑶處定義虛擬動態(tài)分配地址空間開始地址和大小,⑷處定義外設(shè)開始地址和大小,⑸處定義外設(shè)緩存區(qū)開始地址和大小,⑹處定義外設(shè)非緩存區(qū)開始地址和大小。
#ifdef LOSCFG_KERNEL_MMU #ifdef LOSCFG_TEE_ENABLE #define KERNEL_VADDR_BASE 0x41000000 #else #define KERNEL_VADDR_BASE 0x40000000 #endif #else #define KERNEL_VADDR_BASE DDR_MEM_ADDR #endif #define KERNEL_VADDR_SIZE DDR_MEM_SIZE #define SYS_MEM_BASE DDR_MEM_ADDR #define SYS_MEM_END (SYS_MEM_BASE + SYS_MEM_SIZE_DEFAULT) #define _U32_C(X) X##U #define U32_C(X) _U32_C(X) #define KERNEL_VMM_BASE U32_C(KERNEL_VADDR_BASE) #define KERNEL_VMM_SIZE U32_C(KERNEL_VADDR_SIZE) ⑴ #define KERNEL_ASPACE_BASE KERNEL_VMM_BASE #define KERNEL_ASPACE_SIZE KERNEL_VMM_SIZE /* Uncached vmm aspace */ ⑵ #define UNCACHED_VMM_BASE (KERNEL_VMM_BASE + KERNEL_VMM_SIZE) #define UNCACHED_VMM_SIZE DDR_MEM_SIZE ⑶ #define VMALLOC_START (UNCACHED_VMM_BASE + UNCACHED_VMM_SIZE) #define VMALLOC_SIZE 0x08000000 #ifdef LOSCFG_KERNEL_MMU ⑷ #define PERIPH_DEVICE_BASE (VMALLOC_START + VMALLOC_SIZE) #define PERIPH_DEVICE_SIZE U32_C(PERIPH_PMM_SIZE) ⑸ #define PERIPH_CACHED_BASE (PERIPH_DEVICE_BASE + PERIPH_DEVICE_SIZE) #define PERIPH_CACHED_SIZE U32_C(PERIPH_PMM_SIZE) ⑹ #define PERIPH_UNCACHED_BASE (PERIPH_CACHED_BASE + PERIPH_CACHED_SIZE) #define PERIPH_UNCACHED_SIZE U32_C(PERIPH_PMM_SIZE) #else #define PERIPH_DEVICE_BASE PERIPH_PMM_BASE #define PERIPH_DEVICE_SIZE U32_C(PERIPH_PMM_SIZE) #define PERIPH_CACHED_BASE PERIPH_PMM_BASE #define PERIPH_CACHED_SIZE U32_C(PERIPH_PMM_SIZE) #define PERIPH_UNCACHED_BASE PERIPH_PMM_BASE #define PERIPH_UNCACHED_SIZE U32_C(PERIPH_PMM_SIZE) #endif
虛擬地址空間分布示意圖如下:
3、進(jìn)程地址空間初始化
虛擬進(jìn)程空間分用戶虛擬進(jìn)程空間和內(nèi)核虛擬進(jìn)程空間,每個用戶進(jìn)程都會創(chuàng)建屬于自己的進(jìn)程空間。內(nèi)核會初始化2個進(jìn)程空間。下文詳細(xì)介紹。
3.1 內(nèi)核虛擬地址空間初始化
3.1.1 函數(shù)OsKSpaceInit
函數(shù)OsKSpaceInit()初始化內(nèi)核進(jìn)程虛擬地址空間,⑴處的函數(shù)初始化虛擬空間鏈表互斥鎖g_vmSpaceListMux,在操作內(nèi)核進(jìn)程空間時需要持有該互斥鎖。⑵處開始的函數(shù)2個函數(shù)OsKernVmSpaceInit和OsVMallocSpaceInit分別初始化內(nèi)核進(jìn)程虛擬空間g_kVmSpace和內(nèi)核動態(tài)分配進(jìn)程空間g_vMallocSpace。傳入的第2個參數(shù)由函數(shù)OsGFirstTableGet()獲取,即g_firstPageTable,這是內(nèi)核的2個進(jìn)程空間使用的一級頁表基地址,大小為0x4000字節(jié),后文在設(shè)置轉(zhuǎn)化表基地址MMU virtTtb時會使用。下文會詳細(xì)分析這2個函數(shù)。
VOID OsKSpaceInit(VOID) { ⑴ OsVmMapInit(); ⑵ OsKernVmSpaceInit(&g_kVmSpace, OsGFirstTableGet()); OsVMallocSpaceInit(&g_vMallocSpace, OsGFirstTableGet()); }
3.1.2 函數(shù)OsKernVmSpaceInit
函數(shù)OsKernVmSpaceInit()初始化內(nèi)核進(jìn)程虛擬地址空間,⑴處設(shè)置地址空間的開始地址和大小,⑵處設(shè)置地址空間映射區(qū)的開始地址和大小,對于內(nèi)核虛擬地址空間g_kVmSpace,這2個開始地址和大小是一樣的。⑶處調(diào)用通用的地址空間初始化函數(shù),后文分析此函數(shù)。
BOOL OsKernVmSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb) { ⑴ vmSpace->base = KERNEL_ASPACE_BASE; vmSpace->size = KERNEL_ASPACE_SIZE; ⑵ vmSpace->mapBase = KERNEL_VMM_BASE; vmSpace->mapSize = KERNEL_VMM_SIZE; #ifdef LOSCFG_DRIVERS_TZDRIVER vmSpace->codeStart = 0; vmSpace->codeEnd = 0; #endif ⑶ return OsVmSpaceInitCommon(vmSpace, virtTtb); }
3.1.3 函數(shù)OsVMallocSpaceInit
函數(shù)OsVMallocSpaceInit()初始化內(nèi)核堆虛擬空間,設(shè)置的虛擬地址空間和映射區(qū)地址空間的開始地址和大小也是一樣的,代碼和函數(shù)OsKernVmSpaceInit()類似,不再贅述。
BOOL OsVMallocSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb) { vmSpace->base = VMALLOC_START; vmSpace->size = VMALLOC_SIZE; vmSpace->mapBase = VMALLOC_START; vmSpace->mapSize = VMALLOC_SIZE; #ifdef LOSCFG_DRIVERS_TZDRIVER vmSpace->codeStart = 0; vmSpace->codeEnd = 0; #endif return OsVmSpaceInitCommon(vmSpace, virtTtb); }
3.2 用戶進(jìn)程虛擬地址空間初始化
3.2.1 函數(shù)OsCreateUserVmSpace
在創(chuàng)建進(jìn)程時,會調(diào)用函數(shù)OsCreateUserVmSpace()創(chuàng)建用戶進(jìn)程的虛擬地址空間。⑴為虛擬地址空間結(jié)構(gòu)體申請內(nèi)存。⑵申請一個內(nèi)存頁,并調(diào)用memset_s()初始化為0,這個內(nèi)存頁虛擬地址會作為頁表轉(zhuǎn)換基地址TTB(translation table base,ttb),虛實映射的頁表會保存在這個內(nèi)存區(qū)域。在虛實映射章節(jié),會講述為什么申請4KiB大小內(nèi)存。⑶處調(diào)用函數(shù)OsUserVmSpaceInit初始化用戶進(jìn)程虛擬地址空間。⑷處獲取虛擬地址對應(yīng)的物理頁結(jié)構(gòu)體地址。如果初始化失敗,則釋放申請的內(nèi)存。⑸處把物理頁加入虛擬空間中的MMU的頁表鏈表中,這個鏈表維護(hù)該進(jìn)程空間映射的內(nèi)存頁。
LosVmSpace *OsCreateUserVmSpace(VOID) { BOOL retVal = FALSE; ⑴ LosVmSpace *space = LOS_MemAlloc(m_aucSysMem0, sizeof(LosVmSpace)); if (space == NULL) { return NULL; } ⑵ VADDR_T *ttb = LOS_PhysPagesAllocContiguous(1); if (ttb == NULL) { (VOID)LOS_MemFree(m_aucSysMem0, space); return NULL; } (VOID)memset_s(ttb, PAGE_SIZE, 0, PAGE_SIZE); ⑶ retVal = OsUserVmSpaceInit(space, ttb); ⑷ LosVmPage *vmPage = OsVmVaddrToPage(ttb); if ((retVal == FALSE) || (vmPage == NULL)) { (VOID)LOS_MemFree(m_aucSysMem0, space); LOS_PhysPagesFreeContiguous(ttb, 1); return NULL; } ⑸ LOS_ListAdd(&space->archMmu.ptList, &(vmPage->node)); return space; }
3.2.2 函數(shù)OsUserVmSpaceInit
函數(shù)OsUserVmSpaceInit初始化用戶進(jìn)程虛擬地址空間,⑴處設(shè)置虛擬地址空間的開始地址和大小。⑵處設(shè)置虛擬空間的映射區(qū)的開始地址和大小,開始地址在虛擬空間開始地址的1/2處,大小為用戶虛擬空間大小的1/8。⑶處設(shè)置虛擬空間的堆區(qū),開始地址為虛擬空間開始地址的1/4處。
BOOL OsUserVmSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb) { ⑴ vmSpace->base = USER_ASPACE_BASE; vmSpace->size = USER_ASPACE_SIZE; ⑵ vmSpace->mapBase = USER_MAP_BASE; vmSpace->mapSize = USER_MAP_SIZE; ⑶ vmSpace->heapBase = USER_HEAP_BASE; vmSpace->heapNow = USER_HEAP_BASE; vmSpace->heap = NULL; #ifdef LOSCFG_DRIVERS_TZDRIVER vmSpace->codeStart = 0; vmSpace->codeEnd = 0; #endif return OsVmSpaceInitCommon(vmSpace, virtTtb); }
3.3 虛擬地址空間初始化的通用函數(shù)
3.3.1 函數(shù)OsVmSpaceInitCommon
函數(shù)OsVmSpaceInitCommon用于進(jìn)程虛擬地址空間的通用部分的初始化,⑴處初始化地址空間的紅黑樹根節(jié)點。⑵處初始化地址空間的地址區(qū)間操作互斥鎖。⑶處把新創(chuàng)建的地址空間掛在虛擬地址空間雙向鏈表g_vmSpaceList上。⑷處繼續(xù)調(diào)用函數(shù)OsArchMmuInit()完成地址空間MMU部分的初始化。
STATIC BOOL OsVmSpaceInitCommon(LosVmSpace *vmSpace, VADDR_T *virtTtb) { ⑴ LOS_RbInitTree(&vmSpace->regionRbTree, OsRegionRbCmpKeyFn, OsRegionRbFreeFn, OsRegionRbGetKeyFn); ⑵ status_t retval = LOS_MuxInit(&vmSpace->regionMux, NULL); if (retval != LOS_OK) { VM_ERR("Create mutex for vm space failed, status: %d", retval); return FALSE; } (VOID)LOS_MuxAcquire(&g_vmSpaceListMux); ⑶ LOS_ListAdd(&g_vmSpaceList, &vmSpace->node); (VOID)LOS_MuxRelease(&g_vmSpaceListMux); ⑷ return OsArchMmuInit(&vmSpace->archMmu, virtTtb); }
3.3.2 函數(shù)OsArchMmuInit
函數(shù)OsArchMmuInit()用于初始化虛擬地址空間的MMU,MMU在后續(xù)系列會詳細(xì)分析,此處快速了解一下即可。⑴處獲取地址空間編號,如果獲取失敗則返回FALSE。⑵初始化MMU互斥鎖,如果初始化失敗則返回FALSE。⑶處初始化內(nèi)存頁雙向鏈表。⑷處設(shè)置MMU的TTB虛擬地址。⑸處設(shè)置MMU的TTB物理地址,TTB虛擬地址基于內(nèi)核虛擬地址空間開始地址的偏移(UINTPTR)virtTtb - KERNEL_ASPACE_BASE加上物理地址就等于TTB物理地址。
BOOL OsArchMmuInit(LosArchMmu *archMmu, VADDR_T *virtTtb) { #ifdef LOSCFG_KERNEL_VM ⑴ if (OsAllocAsid(&archMmu->asid) != LOS_OK) { VM_ERR("alloc arch mmu asid failed"); return FALSE; } #endif ⑵ status_t retval = LOS_MuxInit(&archMmu->mtx, NULL); if (retval != LOS_OK) { VM_ERR("Create mutex for arch mmu failed, status: %d", retval); return FALSE; } ⑶ LOS_ListInit(&archMmu->ptList); ⑷ archMmu->virtTtb = virtTtb; ⑸ archMmu->physTtb = (VADDR_T)(UINTPTR)virtTtb - KERNEL_ASPACE_BASE + SYS_MEM_BASE; return TRUE; }
4、虛擬地址區(qū)間常用操作
虛擬地址區(qū)間操作分為查找、申請、釋放等操作。
4.1 函數(shù)LOS_RegionFind
⑴處的函數(shù)LOS_RegionFind用于在進(jìn)程虛擬地址空間內(nèi)查找并返回指定虛擬地址對應(yīng)的虛擬地址區(qū)間,兩個傳入?yún)?shù)分別是虛擬地址空間和虛擬內(nèi)存地址。該函數(shù)有個兄弟函數(shù)LOS_RegionRangeFind(),見⑶處代碼,可以用于在進(jìn)程空間內(nèi)查找并返回指定地址范圍對應(yīng)的虛擬地址區(qū)間,三個傳入?yún)?shù)分別指定指定進(jìn)程空間、虛擬內(nèi)存開始地址和地址長度(長度單位字節(jié))。這2個函數(shù)都調(diào)用函數(shù)OsFindRegion()實現(xiàn)地址區(qū)間的查找,⑵處的第3個參數(shù)為1的原因是地址區(qū)間是左閉右開區(qū)間,區(qū)間的結(jié)束地址會減1。下文會分析該函數(shù)的代碼。
⑴ LosVmMapRegion *LOS_RegionFind(LosVmSpace *vmSpace, VADDR_T addr) { LosVmMapRegion *region = NULL; (VOID)LOS_MuxAcquire(&vmSpace->regionMux); ⑵ region = OsFindRegion(&vmSpace->regionRbTree, addr, 1); (VOID)LOS_MuxRelease(&vmSpace->regionMux); return region; } ⑶ LosVmMapRegion *LOS_RegionRangeFind(LosVmSpace *vmSpace, VADDR_T addr, size_t len) { LosVmMapRegion *region = NULL; (VOID)LOS_MuxAcquire(&vmSpace->regionMux); region = OsFindRegion(&vmSpace->regionRbTree, addr, len); (VOID)LOS_MuxRelease(&vmSpace->regionMux); return region; }
IoT 任務(wù)調(diào)度 虛擬化 輕量級操作系統(tǒng) LiteOS
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。