class="hide-preCode-box">

4.4.2 函数OsAllocSpecificRange

函数OsAllocSpecificRange用于从虚拟地址空间中申请指定长度的内存,如果指定的虚拟地址已经被映射,则取消映射,返回值为申请到的虚拟地址。⑴处验证虚拟内存块是否在虚拟地址空间范围内。⑵处判断虚拟地址是否已经属于某个地址区间,如果不属于任何地址区间,则执行⑸返回该虚拟地址;如果属于某个地址区间,则继续执行⑶,如果地址区间标签包含VM_MAP_REGION_FLAG_FIXED_NOREPLACE,不允许替换,则返回0;如果标签包含VM_MAP_REGION_FLAG_FIXED,则调用LOS_UnMMap取消映射。如果不包含上述标签,则执行⑷,重新申请地址区间。

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) != NULL)) {
⑶      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;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

4.4.3 函数OsCreateRegion

函数OsCreateRegion用于根据虚拟地址、内存大小、地址区间标签等信息创建地址区间。⑴处为地址区间结构体申请内存,⑵处根据参数设置地址区间属性值。代码比较简单,自行阅读即可。

LosVmMapRegion *OsCreateRegion(VADDR_T vaddr, size_t len, UINT32 regionFlags, unsigned long offset)
{
⑴  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;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

4.4.4 函数OsInsertRegion

函数OsInsertRegion用于把红黑树节点插入红黑树。⑴处调用函数LOS_RbAddNode插入红黑树节点,LosVmMapRegion结构体的第一个成员是LosRbNode类型,二者可以强转。⑵处如果插入节点失败,则打印地址空间信息。代码比较简单。

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);
⑵       OsDumpAspace(region->space);
        return FALSE;
    }
    return TRUE;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

4.4.5 函数OsFindRegion

函数OsFindRegion实现根据虚拟内存地址查找地址区间。⑴处设置地址区间范围的开始地址和大小。⑵处调用函数LOS_RbGetNode()从红黑树上获取红黑树节点pstRbNode,获取成功时会继续执行⑶从红黑树节点转换为需要的地址区间。后续会有专门的系列讲解红黑树,届时再分析函数LOS_RbGetNode()

LosVmMapRegion *OsFindRegion(LosRbTree *regionRbTree, VADDR_T vaddr, size_t len)
{
    LosVmMapRegion *regionRst = NULL;
    LosRbNode *pstRbNode = NULL;
    LosVmMapRange rangeKey;
⑴  rangeKey.base = vaddr;
    rangeKey.size = len;

⑵  if (LOS_RbGetNode(regionRbTree, (VOID *)&rangeKey, &pstRbNode)) {
⑶      regionRst = (LosVmMapRegion *)LOS_DL_LIST_ENTRY(pstRbNode, LosVmMapRegion, rbNode);
    }
    return regionRst;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}">

5、VMalloc常用操作

内核动态分配虚拟地址空间操作分为申请和释放2个操作。

5.1 函数LOS_VMalloc

函数LOS_VMalloc用于从VMalloc动态分配内存堆虚拟地址空间中申请内存,参数为需要申请的字节数。
⑴处把申请的内存大小进行页对齐,并由字节数计算页数sizeCount
⑵处声明一个内存页双向链表。
⑶处申请指定数量的物理内存页并挂载到双向链表pageList上。
⑷处从动态内存分配堆进程空间g_vMallocSpace中申请虚拟内存地址区间。此时成功申请了虚拟内存和物理内存,而且页数也是一样的,下面执行
⑸循环遍历物理页双向链表上的每一个内存页进行虚实映射。
⑹处获取物理内存页的物理内存地址,然后把物理内存页的引用计数自增加1。
⑺处进行虚实映射,然后把虚拟内存地址增加一个内存页的大小,继续循环遍历。
⑻处返回申请到的虚拟地址区间的内存开始地址。虚实映射函数LOS_ArchMmuMapMMU虚实映射系列来详细讲解。

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;

⑵   LOS_DL_LIST_HEAD(pageList);
    (VOID)LOS_MuxAcquire(&space->regionMux);

⑶  count = LOS_PhysPagesAlloc(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);
⑻   return (VOID *)(UINTPTR)region->range.base;

ERROR:
    (VOID)LOS_PhysPagesFree(&pageList);
    (VOID)LOS_MuxRelease(&space->regionMux);
    return NULL;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

5.2 函数LOS_VFree

函数LOS_VFree用于释放从VMalloc动态内存堆虚拟地址空间中申请的虚拟内存,传入参数为虚拟地址。 ⑴处根据虚拟地址获取虚拟地址区间,然后执行⑵释放地址区间,其中函数LOS_RegionFree在前文已经详细讲述。

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);
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

6 其他

6.1 函数LOS_VmSpaceReserve

函数LOS_VmSpaceReserve用于在在进程空间中预留一块内存空间。⑴处先做参数校验。⑵处先判断虚拟地址和大小在指定的虚拟地址空间内。⑶处查询指定的虚拟地址的映射标签。⑷处加上标签VM_MAP_REGION_FLAG_FIXED申请一段地址区间。

STATUS_T LOS_VmSpaceReserve(LosVmSpace *space, size_t size, VADDR_T vaddr)
{
    UINT32 regionFlags = 0;

⑴  if ((space == NULL) || (size == 0) || (!IS_PAGE_ALIGNED(vaddr) || !IS_PAGE_ALIGNED(size))) {
        return LOS_ERRNO_VM_INVALID_ARGS;
    }

⑵  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, ®ionFlags);

    /* build a new region structure */
⑷  LosVmMapRegion *region = LOS_RegionAlloc(space, vaddr, size, regionFlags | VM_MAP_REGION_FLAG_FIXED, 0);

    return region ? LOS_OK : LOS_ERRNO_VM_NO_MEMORY;
}
 class="hljs-button signin active" data-title="登录复制" data-report-click="{"spm":"1001.2101.3001.4334"}"> class="hide-preCode-box">

总结

本文分析虚拟内存管理的相关源代码,首先介绍虚拟内存管理的结构体、相关宏定义,接着会分析内核虚拟地址空间和用户进程虚拟地址空间如何初始化,然后分析虚拟内存区间常用操作包含查找、申请和释放等,最后分析动态内存堆的申请、释放接口的源代码,并简单介绍下内存区间预留接口源代码。

如果大家想更加深入的学习 OpenHarmony 开发的内容,不妨可以参考以下相关学习文档进行学习,助你快速提升自己:

OpenHarmony 开发环境搭建:https://qr18.cn/CgxrRy

《OpenHarmony源码解析》:https://qr18.cn/CgxrRy

系统架构分析:https://qr18.cn/CgxrRy

OpenHarmony 设备开发学习手册:https://qr18.cn/CgxrRy

在这里插入图片描述

OpenHarmony面试题(内含参考答案):https://qr18.cn/CgxrRy

写在最后

data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/maniuT/article/details/139624526","extend1":"pc","ab":"new"}">> id="blogExtensionBox" style="width:400px;margin:auto;margin-top:12px" class="blog-extension-box"> class="blog_extension blog_extension_type2" id="blog_extension"> class="extension_official" data-report-click="{"spm":"1001.2101.3001.6471"}" data-report-view="{"spm":"1001.2101.3001.6471"}"> class="blog_extension_card_left"> class="blog_extension_card_cont"> 鸿蒙开发学习资料领取!!! class="blog_extension_card_cont_r"> 微信名片
注:本文转载自blog.csdn.net的沧海一笑-dj的文章"https://blog.csdn.net/dengjin20104042056/article/details/98792584"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接

评论记录:

未查询到任何数据!