Abstract: This paper introduces the structure of virtual memory management, related macro definition, analysis of kernel virtual address space and user process virtual address space initialization, etc.

This article is shared by Huawei cloud community “Hongmeng light kernel A core source code analysis series four (2) virtual memory”, author: Zhushy.

The source code in this article, using the OpenHarmonyLitEOS-A kernel as an example, is available on the open source site gitee.com/openharmony… To obtain. If development boards are involved, hispark_taurus is used by default.

We first understand the structure of virtual memory management, related macro definition, and then analyzes the kernel virtual address space and how to initialize the user process virtual address space, and then analysis the virtual memory interval common operations include search, application and release, etc., in the final analysis of dynamic memory heap application, release the source code of the interface, and introduces the reserved memory interval interface source code.

1, virtual memory management related architecture

In the kernel/base/include/los_vm_map.h file, the process address space structure LosVmSpace, process address range structure LosVmMapRegion and process address range structure LosVmMapRange are defined. Each user-mode process creates its own process space, and the kernels create two process Spaces, g_kVmSpace and g_vMallocSpace. Virtual memory blocks requested from process space are represented by process interval LosVmMapRegion. Each process space maintains a red-black tree to link process intervals.

1.1 Virtual memory address space structure LosVmSpace

typedef struct VmSpace { LOS_DL_LIST node; /**< address space bidirectional list */ LosRbTree regionRbTree; /**< address range red and black root node */ LosMux regionMux; /**< mutex for red-black tree of address range */ VADDR_T base; /**< Start address of address space */ UINT32 SIZE; /**< address space size */ VADDR_T heapBase; /**< address space heapBase */ VADDR_T heapNow; /**< address space heapNow */ LosVmMapRegion *heap; VADDR_T mapBase; /**< Start ADDRESS of the mapping area of the address space */ UINT32 mapSize; /**< address space mapping size */ LosArchMmu archMmu; /**< address space MMU structure */ #ifdef LOSCFG_DRIVERS_TZDRIVER VADDR_T codeStart; /**< user process code start address */ VADDR_T codeEnd; /**< user process code end address */ #endif} LosVmSpace;Copy the code

1.2 Virtual Memory Address region LosVmMapRegion

typedef struct VmMapRange { VADDR_T base; /**< Start ADDRESS of virtual memory address range */ UINT32 SIZE; /**< virtual memory address range size */} LosVmMapRange; . struct VmMapRegion; typedef struct VmMapRegion LosVmMapRegion; . struct VmMapRegion { LosRbNode rbNode; /**< address range red black tree node */ LosVmSpace *space; /**< address space */ LOS_DL_LIST node; /** / LosVmMapRange; /**< address range address range */ VM_OFFSET_T pgOff; /**< address range page offset */ UINT32 regionFlags; /**< Address range marker: COW, user_WIRED */ UINT32 SHmid; /**< UINT8 forkFlags; /** / UINT8 regionType; /**< address interval type: ANON, FILE, DEV */ union {struct VmRegionFile {unsigned int fileMagic; struct file *file; const LosVmFileOps *vmFOps; } rf; struct VmRegionAnon { LOS_DL_LIST node; /**< address range type bidirectional list */} ra; struct VmRegionDev { LOS_DL_LIST node; /**< address range type bidirectional list */ const LosVmFileOps *vmFOps; } rd; } unTypeData; };Copy the code

2, virtual memory related macro definition

The kernel/base/include/los_vm_common.h and kernel/base/include/los_vm_zone.h files define macros related to virtual memory. For 32-bit systems, the virtual process size is 4GiB, and the OpenHarmony Kernel currently supports 32-bit systems. ⑴ and ⑵ define the start address and size of the user process virtual address space, ⑶ is the end address of the user virtual process space, and then define the start address and size of the heap and mapping area of the user virtual process space.

/* 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))
Copy the code

The kernel virtual process space macro definition is as follows, (1) in definition the kernel process address space start address and size, 2 in definition kernel not cache the virtual address space start address and size, (3) dynamic allocation in definition of virtual address space start address and size, (4) in definition peripherals start address and size, 5] in definition peripherals buffer start address and size, ⑹ define the start address and size of the peripheral cache.

#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_VADDR_BASE (KERNEL_VADDR_BASE) #define KERNEL_VMM_SIZE U32_C(KERNEL_VADDR_SIZE) ⑴ 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 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_DEVICE_BASE) ⑹ #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) #endifCopy the code

The virtual address space distribution diagram is as follows:

3. Initialize the process address space

The virtual process space is divided into user virtual process space and kernel virtual process space. Each user process creates its own process space. The kernel initializes two process Spaces. Details below.

3.1 Initializing the kernel virtual address space

3.1.1 function OsKSpaceInit

The function OsKSpaceInit() initializes the kernel process virtual address space, and the function at ⑴ initializes the virtual space linked list mux_vmSPACelistmux, which is required to be held when operating on the kernel process space. The two functions OsKernVmSpaceInit and OsVMallocSpaceInit initialize g_kVmSpace and g_vMallocSpace, respectively. The second argument passed in is taken by OsGFirstTableGet(), namely g_firstPageTable, which is the level 1 page table base address used by the kernel’s two process Spaces and is 0x4000 bytes in size, which will be used later when setting the transform table base address MMU virtTtb. These two functions will be examined in detail below.

VOID OsKSpaceInit(VOID) {⑴ OsVmMapInit(); 2 OsKernVmSpaceInit (& g_kVmSpace, OsGFirstTableGet ()); OsVMallocSpaceInit(&g_vMallocSpace, OsGFirstTableGet()); }Copy the code

3.1.2 function OsKernVmSpaceInit

The function OsKernVmSpaceInit() initializes the virtual address space of the kernel process. (1) sets the start address and size of the address space, and (2) sets the start address and size of the address space mapping. ⑶ calls the generic address space initialization function, which will be examined later.

BOOL OsKernVmSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb) {⑴ vmSpace->base = KERNEL_ASPACE_BASE; vmSpace->size = KERNEL_ASPACE_SIZE; 2 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); }Copy the code

3.1.3 function OsVMallocSpaceInit

The OsVMallocSpaceInit() function initializes the kernel heap virtual space. The starting address and size of the virtual address space and the mapping area address space are the same. The code is similar to the OsKernVmSpaceInit() function.

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);
}
Copy the code

3.2 Initializing the Virtual Address space of user Processes

3.2.1 function OsCreateUserVmSpace

When a process is created, the function OsCreateUserVmSpace() is called to create a virtual address space for the user process. ⑴ Allocate memory for virtual address space structure. (2) Apply for a memory page and call memset_s() to initialize to 0. The virtual address of the memory page will be used as the translation table base address (TTB). The virtual and real mapping page table will be stored in this memory area. In the section on virtual real mapping, we will explain why 4KiB memory is required. The OsUserVmSpaceInit function is called to initialize the virtual address space of the user process. (4) obtain the corresponding physical page structure address of the virtual address. If the initialization fails, the requested memory is released. The physical pages are added to the linked list of pages of the MMU in the virtual space. The linked list maintains the memory pages of the process space mapping.

LosVmSpace *OsCreateUserVmSpace(VOID) { BOOL retVal = FALSE; ⑴ LosVmSpace *space = LOS_MemAlloc(m_aucSysMem0, sizeof(LosVmSpace)); if (space == NULL) { return NULL; } ⑵ VADDR_T * TTB = los_phyesalloccontiguous (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; } [5] LOS_ListAdd (& space - > archMmu. PtList, & (vmPage - > node)); return space; }Copy the code

3.2.2 function OsUserVmSpaceInit

The OsUserVmSpaceInit function initializes the virtual address space of the user process, and sets the starting address and size of the virtual address space at ⑴. (2) Set the start address and size of the mapping area of the virtual space. The start address is 1/2 of the start address of the virtual space, and the size is 1/8 of the size of the user virtual space. ⑶ set the heap area of virtual space, the start address is 1/4 of the start address of virtual space.

BOOL OsUserVmSpaceInit(LosVmSpace *vmSpace, VADDR_T *virtTtb) {⑴ vmSpace->base = USER_ASPACE_BASE; vmSpace->size = USER_ASPACE_SIZE; 2 vmSpace - > mapBase = USER_MAP_BASE; vmSpace->mapSize = USER_MAP_SIZE; (3) 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); }Copy the code

3.3 General function for virtual address space initialization

3.3.1 function OsVmSpaceInitCommon

The OsVmSpaceInitCommon function is used to initialize the generic portion of the process’s virtual address space, where the red-black root nodes of the address space are initialized. (2) initializes the address interval operation mutex of the address space. Hang the newly created address space on the virtual address space bidirectional list g_vmSpaceList. (4) Continue to call OsArchMmuInit() to initialize the MMU portion of the address space.

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); (3) LOS_ListAdd (& g_vmSpaceList, & vmSpace - > node); (VOID)LOS_MuxRelease(&g_vmSpaceListMux); (4) return OsArchMmuInit (& vmSpace - > archMmu, virtTtb); }Copy the code

3.3.2 rainfall distribution on 10-12 function OsArchMmuInit

The OsArchMmuInit() function is used to initialize the MMU of the virtual address space, which will be examined in detail in a later series. (1) return FALSE if (1) return FALSE if (1) return FALSE ⑵ Initialize the MMU mutex. Return FALSE if initialization fails. (3) initialize the memory page bidirectional linked list. (4) Set the TTB virtual address of MMU. Set the TTB physical address of MMU at ⑸. The TTB virtual address is based on the offset of the start address of kernel virtual address space (UINTPTR) virtttB-kernel_aspace_base plus the physical address equals to TTB physical address.

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; } (3) LOS_ListInit (& archMmu - > ptList); (4) archMmu - > virtTtb = virtTtb; ⑸ archMmu->physTtb = (VADDR_T)(UINTPTR) virtttB-kernel_aspace_base + SYS_MEM_BASE; return TRUE; }Copy the code

4. Common operations for the virtual address range

Virtual address range operations include searching for, applying for, and releasing virtual address ranges.

4.1 function LOS_RegionFind

The function LOS_RegionFind at ⑴ is used to find and return the virtual address range corresponding to the specified virtual address in the virtual address space of the process. The two parameters are the virtual address space and the virtual memory address. LOS_RegionRangeFind() ¶ This function has a sibling function LOS_RegionRangeFind(), which can be used to find and return the virtual address range corresponding to the specified address range in the process space. The three passed arguments specify the specified process space, the virtual memory start address, and the address length (length in bytes). Both functions call OsFindRegion() to find the address range. The reason why the third argument at ⑵ is 1 is that the address range is left closed and right open, and the end address of the range is reduced by 1. The code for this function is examined below.

⑴ 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; }Copy the code

4.2 function LOS_RegionAlloc

The LOS_RegionAlloc function is used to apply for an idle virtual address range from the address space. LosVmSpace*vmSpace specifies the virtual address space, and VADDR_Tvaddr specifies the virtual address. If this parameter is empty, the virtual address is applied from the mapping area. If the virtual address is not empty, the virtual address is used. If the virtual address has been mapped, the mapping is removed first. Size_tlen specifies the length of the range to apply for. UINT32regionFlags Specifies the label of the region. VM_OFFSET_Tpgoff Specifies the memory page offset.

If the virtual address specified in ⑴ is empty, the function OsAllocRange() is called to apply for memory. (2) If the specified virtual address is not empty, the OsAllocSpecificRange function is called to apply for virtual memory. The following two functions are analyzed in detail. (3) create virtual memory address range, and specify the address space of the address range as the current space vmSpace. (4) insert the address range into the red-black tree of the address space.

LosVmMapRegion *LOS_RegionAlloc(LosVmSpace *vmSpace, VADDR_T vaddr, size_t len, UINT32 regionFlags, VM_OFFSET_T pgoff) { VADDR_T rstVaddr; LosVmMapRegion *newRegion = NULL; BOOL isInsertSucceed = FALSE; /** * If addr is NULL, then the kernel chooses the address at which to create the mapping; * this is the most portable method of creating a new mapping. If addr is not NULL, * then the kernel takes it as where to place the mapping; */ (VOID)LOS_MuxAcquire(&vmSpace->regionMux); If (vaddr == 0) {⑴ rstVaddr = OsAllocRange(vmSpace, len); } else {/* if it is already mmapped here, we unmmap it */ ⑵ rstVaddr = OsAllocSpecificRange(vmSpace, vaddr, len) regionFlags); if (rstVaddr == 0) { VM_ERR("alloc specific range va: %#x, len: %#x failed", vaddr, len); goto OUT; } } if (rstVaddr == 0) { goto OUT; } ⑶ newRegion = OsCreateRegion(rstVaddr, len, regionFlags, pGOFF); if (newRegion == NULL) { goto OUT; } newRegion->space = vmSpace; ⑷ isInsertSucceed = OsInsertRegion(&vmSpace->regionRbTree, newRegion); if (isInsertSucceed == FALSE) { (VOID)LOS_MemFree(m_aucSysMem0, newRegion); newRegion = NULL; } OUT: (VOID)LOS_MuxRelease(&vmSpace->regionMux); return newRegion; }Copy the code

4.3 function LOS_RegionFree

The LOS_RegionFree function is used to free the region into the address space. (1) Verify the parameters. The parameters cannot be empty. (2) if the virtual file system macro is enabled and the address range is a valid file type, the function OsFilePagesRemove is called. (3) If shared memory is enabled and the address segment is shared, OsShmRegionFree is called to release the shared memory segment. (4) If the address range is device type, then call OsDevPagesRemove to remove the mapping, otherwise execute ⑸. All of these functions involve virtual-real mappings, which will be examined in the virtual-real mapping section. ⑹ remove the address range from the red-black tree, and release the memory occupied by the address range structure.

STATUS_T LOS_RegionFree(LosVmSpace *space, LosVmMapRegion * region) {(1) if ((space = = NULL) | | (region = = NULL) {VM_ERR (" args error, aspace % p, region % p ", space, region); return LOS_ERRNO_VM_INVALID_ARGS; } (VOID)LOS_MuxAcquire(&space->regionMux); #ifdef LOSCFG_FS_VFS ⑵ if (LOS_IsRegionFileValid(region)) {OsFilePagesRemove(space, region); } else #endif #ifdef LOSCFG_KERNEL_SHM ⑶ if (osisshmregionFree (space, region); } else if (LOS_IsRegionTypeDev(region)) {# ⑷ if (LOS_IsRegionTypeDev(region)) {#endif OsDevPagesRemove(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT); } else {⑸ OsAnonPagesRemove(&space->archMmu, region->range.base, region->range.size >> PAGE_SHIFT); } /* remove it from space */ ⑹ LOS_RbDelNode(&space->regionRbTree, &region->rbNode); /* free it */ LOS_MemFree(m_aucSysMem0, region); (VOID)LOS_MuxRelease(&space->regionMux); return LOS_OK; }Copy the code

4.4 Virtual memory internal implementation functions

4.4.1 function OsAllocRange

The OsAllocRange function is used to request a specified length of memory from the virtual address space, and returns the requested virtual address. The address interval corresponding to the start address of the mapping area is obtained from the process space. If the obtained address range is not NULL, perform ⑵ to obtain the red-black tree node of the address range and obtain the end address of the address range. The red-black tree macro pair RB_MID_SCAN and RB_MID_SCAN_END is used to loop through the red-black tree node pstRbNode and its subsequent nodes. (4) If the address range obtained by the current traversal node and the mapping area overlaps, the next node is traversed. If the length of the address range meets the requirements at ⑸, the virtual address will be returned. Otherwise, execute ⑹ updating the end address of the address range to continue traversal.

If the address range obtained from the mapping area is NULL, perform the ⑺ command. Red-black tree macro pairs RB_SCAN_SAFE and RB_SCAN_SAFE_END loop through the first tree node. The contents of the circulatory body are repeated as above and will not be repeated. ⑻ If the mapping area does not apply for a suitable virtual address, determine whether the address range behind the mapping area meets the requirements. If no suitable virtual address is obtained, 0 is returned.

VADDR_T OsAllocRange(LosVmSpace *vmSpace, size_t len) { LosVmMapRegion *curRegion = NULL; LosRbNode *pstRbNode = NULL; LosRbNode *pstRbNodeTmp = NULL; LosRbTree *regionRbTree = &vmSpace->regionRbTree; VADDR_T curEnd = vmSpace->mapBase; VADDR_T nextStart; ⑴ curRegion = LOS_RegionFind(vmSpace, vmSpace->mapBase); if (curRegion ! ⑵ pstRbNode = &curregion ->rbNode; curEnd = curRegion->range.base + curRegion->range.size; ⑶ RB_MID_SCAN(regionRbTree, pstRbNode) curRegion = (LosVmMapRegion *)pstRbNode; nextStart = curRegion->range.base; ⑷ if (nextStart < curEnd) {continue; } ⑸ ⑸ if ((nextstart-curend) >= len) {return curEnd; } else {⑹ curEnd = curRegion->range.base + curRegion->range.size; } RB_MID_SCAN_END(regionRbTree, pstRbNode) } else { /* rbtree scan is sorted, From small to big */ ⑺ RB_SCAN_SAFE(regionRbTree, pstRbNode, pstRbNodeTmp) curRegion = (LosVmMapRegion *)pstRbNode; nextStart = curRegion->range.base; if (nextStart < curEnd) { continue; } if ((nextStart - curEnd) >= len) { return curEnd; } else { curEnd = curRegion->range.base + curRegion->range.size; } RB_SCAN_SAFE_END(regionRbTree, pstRbNode, pstRbNodeTmp)} ⑻ nextStart = vmSpace->mapBase + vmSpace->mapSize; if ((nextStart >= curEnd) && ((nextStart - curEnd) >= len)) { return curEnd; } return 0; }Copy the code

4.4.2 function OsAllocSpecificRange

The OsAllocSpecificRange function is used to apply for a specified length of memory from the virtual address space. If the specified virtual address has been mapped, the mapping is cancelled and the value returned is the applied virtual address. Verify whether the virtual memory block is within the range of the virtual address space. (2) Determine whether the virtual address belongs to an address range. If it does not belong to any address range, execute ⑸ to return the virtual address; If the address range tag contains VM_MAP_REGION_FLAG_FIXED_NOREPLACE, 0 is returned; if the address range tag contains VM_MAP_REGION_FLAG_FIXED_NOREPLACE, 0 is returned; If the label contains VM_MAP_REGION_FLAG_FIXED, call LOS_UnMMap to cancel the mapping. If the above tags are not included, then execute ⑷ and re-apply for the address range.

VADDR_T OsAllocSpecificRange(LosVmSpace *vmSpace, VADDR_T vaddr, size_t len, UINT32 regionFlags) { STATUS_T status; ⑴ if (LOS_IsRangeInSpace(vmSpace, vaddr, len) == FALSE) {return 0; } ⑵ if ((LOS_RegionFind(vmSpace, vaddr)! = NULL) || (LOS_RegionFind(vmSpace, vaddr + len - 1) ! = NULL) || (LOS_RegionRangeFind(vmSpace, vaddr, len - 1) ! If ((regionFlags & VM_MAP_REGION_FLAG_FIXED_NOREPLACE)! = 0) { return 0; } else if ((regionFlags & VM_MAP_REGION_FLAG_FIXED) ! = 0) { status = LOS_UnMMap(vaddr, len); if (status ! = LOS_OK) { VM_ERR("unmmap specific range va: %#x, len: %#x failed, status: %d", vaddr, len, status); return 0; }} else {⑷ return OsAllocRange(vmSpace, len); }} ⑸ return vaddr; }Copy the code

4.4.3 function OsCreateRegion

The OsCreateRegion function is used to create an address range based on virtual addresses, memory size, address range labels, and other information. ⑴ applies for memory for the address interval structure, and (2) sets the address interval attribute value according to the parameters. The code is relatively simple, read by yourself.

LosVmMapRegion *OsCreateRegion(VADDR_T vaddr, size_t len, UINT32 regionFlags, {⑴ LosVmMapRegion *region = LOS_MemAlloc(m_aucSysMem0, sizeof(LosVmMapRegion)); if (region == NULL) { VM_ERR("memory allocate for LosVmMapRegion failed"); return region; } ⑵ region->range.base = vaddr; region->range.size = len; region->pgOff = offset; region->regionFlags = regionFlags; region->regionType = VM_MAP_REGION_TYPE_NONE; region->forkFlags = 0; region->shmid = -1; return region; }Copy the code

4.4.4 function OsInsertRegion

The OsInsertRegion function is used to insert nodes into a red-black tree. The first member of the LosVmMapRegion structure is the LosRbNode type, which can be strongly rotated. If the node fails to be inserted at (2), the address space information is printed. The code is relatively simple.

BOOL OsInsertRegion(LosRbTree *regionRbTree, LosVmMapRegion *region) {⑴ if (LOS_RbAddNode(regionRbTree, (LosRbNode *)region) == FALSE) { VM_ERR("insert region failed, base: %#x, size: %#x", region->range.base, region->range.size); 2 OsDumpAspace (region - > space); return FALSE; } return TRUE; }Copy the code

4.4.5 function OsFindRegion

The OsFindRegion function implements an address range lookup based on the virtual memory address. (1) Set the start address and size of the address range. (2) call LOS_RbGetNode() to get the pstRbNode from the red-black tree. The LOS_RbGetNode() function will be examined in a follow-up series on red-black trees.

LosVmMapRegion *OsFindRegion(LosRbTree *regionRbTree, VADDR_T vaddr, size_t len) { LosVmMapRegion *regionRst = NULL; LosRbNode *pstRbNode = NULL; LosVmMapRange rangeKey; (1) rangeKey. Base = vaddr; rangeKey.size = len; ⑵ If (LOS_RbGetNode(regionRbTree, (VOID *)& regioneKey, &pstrBNode)) {⑶ regionRst = (LosVmMapRegion *)LOS_DL_LIST_ENTRY(pstRbNode, LosVmMapRegion, rbNode); } return regionRst; }Copy the code

5. Common operations of VMalloc

The kernel dynamic allocation of virtual address space is divided into two operations: applying for and releasing virtual address space.

5.1 function LOS_VMalloc

The LOS_VMalloc function is used to allocate memory from the virtual address space of the VMalloc dynamically allocated memory heap. The parameter is the number of bytes to be allocated. Page alignment of the requested memory size, and page count by number of bytes sizeCount. (2) declare a bidirectional linked list of memory pages. (3) apply a specified number of physical memory pages and mount them to a bidirectional linked list pageList. (4) apply virtual memory address range (vmallocspace) from dynamic memory allocation heap process space. At this point, virtual memory and physical memory have been successfully applied, and the number of pages is the same, next execute ⑸ loop through each memory page on the bidirectional linked list of physical pages for virtual and real mapping. The reference count of the physical memory page is incremented by 1. Perform a virtual map at ⑺, then increase the virtual memory address by one page, and continue the loop. ⑻ return to the start memory address of the applied virtual address range. Virtual real mapping function LOS_ArchMmuMap in MMU virtual real mapping series to explain in detail.

VOID *LOS_VMalloc(size_t size) { LosVmSpace *space = &g_vMallocSpace; LosVmMapRegion *region = NULL; size_t sizeCount; size_t count; LosVmPage *vmPage = NULL; VADDR_T va; PADDR_T pa; STATUS_T ret; ⑴ size = LOS_Align(size, PAGE_SIZE); if ((size == 0) || (size > space->size)) { return NULL; } sizeCount = size >> PAGE_SHIFT; 2 LOS_DL_LIST_HEAD (pageList); (VOID)LOS_MuxAcquire(&space->regionMux); ⑶ count = los_phyesalloc (sizeCount, &pageList); if (count < sizeCount) { VM_ERR("failed to allocate enough pages (ask %zu, got %zu)", sizeCount, count); goto ERROR; } /* allocate a region and put it in the aspace list */ ⑷ region = LOS_RegionAlloc(space, 0, size, VM_MAP_REGION_FLAG_PERM_READ | VM_MAP_REGION_FLAG_PERM_WRITE, 0); if (region == NULL) { VM_ERR("alloc region failed, size = %x", size); goto ERROR; } va = region->range.base; ⑸ ⑸ while ((vmPage = LOS_ListRemoveHeadType(&pageList, LosVmPage, node)) {⑹ pa = vmPage->physAddr; LOS_AtomicInc(&vmPage->refCounts); Ret = LOS_ArchMmuMap(&space->archMmu, VA, PA, 1, region->regionFlags); if (ret ! = 1) { VM_ERR("LOS_ArchMmuMap failed! , err; %d", ret); } va += PAGE_SIZE; } (VOID)LOS_MuxRelease(&space->regionMux); Being the return (VOID *) (UINTPTR) region - > range. The base; ERROR: (VOID)LOS_PhysPagesFree(&pageList); (VOID)LOS_MuxRelease(&space->regionMux); return NULL; }Copy the code

5.2 function LOS_VFree

The LOS_VFree function is used to free the virtual memory requested from the virtual address space of the VMalloc dynamic memory heap, passing in the virtual address. (1) Get the virtual address range based on the virtual address, and then execute (2) release the address range, where LOS_RegionFree is described in detail in the previous section.

VOID LOS_VFree(const VOID *addr) { LosVmSpace *space = &g_vMallocSpace; LosVmMapRegion *region = NULL; STATUS_T ret; if (addr == NULL) { VM_ERR("addr is NULL!" ); return; } (VOID)LOS_MuxAcquire(&space->regionMux); ⑴ region = LOS_RegionFind(space, (VADDR_T)(UINTPTR)addr); if (region == NULL) { VM_ERR("find region failed"); goto DONE; } ⑵ ret = LOS_RegionFree(space, region); if (ret) { VM_ERR("free region failed, ret = %d", ret); } DONE: (VOID)LOS_MuxRelease(&space->regionMux); }Copy the code

6, other

6.1 function LOS_VmSpaceReserve

The LOS_VmSpaceReserve function is used to reserve a memory space in the process space. (1) Do parameter verification first. (2) determine that the virtual address and size are in the specified virtual address space. ⑶ query the mapping label of the specified virtual address. Label at (4)

VM_MAP_REGION_FLAG_FIXED Applies for an address range.

STATUS_T LOS_VmSpaceReserve(LosVmSpace *space, size_t size, VADDR_T vaddr) { UINT32 regionFlags = 0; (1) if ((space = = NULL) | | (size = = 0) | | (! IS_PAGE_ALIGNED(vaddr) || ! IS_PAGE_ALIGNED(size))) { return LOS_ERRNO_VM_INVALID_ARGS; } [2] the if (! LOS_IsRangeInSpace(space, vaddr, size)) { return LOS_ERRNO_VM_OUT_OF_RANGE; } /* lookup how it's already mapped */ ⑶ (VOID)LOS_ArchMmuQuery(&space->archMmu, vaddr, NULL, &regionFlags); /* build a new region structure */ ⑷ /* LosVmMapRegion */ regionFlags | VM_MAP_REGION_FLAG_FIXED, 0); return region ? LOS_OK : LOS_ERRNO_VM_NO_MEMORY; }Copy the code

7,

In this paper, virtual memory management related to the source code, first introduced the structure of the virtual memory management, related macro definition, and then analyzes the kernel virtual address space and how to initialize the user process virtual address space, and then analysis the virtual memory interval common operations include search, application and release, etc., in the final analysis of dynamic memory heap application, release the source code of the interface, And a brief introduction to the memory interval reserved interface source code. There will be more articles to share in the future. Please look forward to it. If you have any questions or suggestions, please leave a message to me. thank you

Click to follow, the first time to learn about Huawei cloud fresh technology ~