鸿蒙轻内核A核源码分析系列三 物理内存(1)

从本篇开始,我们分析下鸿蒙轻内核A核的内存管理部分,包括物理内存、虚拟内存、虚拟映射等部分。物理内存(Physical memory)是指通过物理内存条而获得的内存空间,相对应的概念是虚拟内存(Virtual memory)。虚拟内存使得应用进程认为它拥有一个连续完整的内存地址空间,而通常是通过虚拟内存和物理内存的映射对应着多个物理内存页。本文我们先来熟悉下OpenHarmony鸿蒙轻内核提供的物理内存(Physical memory)管理模块。

本文中所涉及的源码,以OpenHarmony LiteOS-A内核为例,均可以在开源站点 https://gitee.com/openharmony/kernel_liteos_a  获取。如果涉及开发板,则默认以hispark_taurus为例。

我们首先了解了物理内存管理的结构体,接着阅读了物理内存如何初始化,然后分析了物理内存的申请、释放和查询等操作接口的源代码。

1、物理内存结构体介绍

1.1、物理内存页LosVmPage

鸿蒙轻内核A核的物理内存采用了段页式管理,每个物理内存段被分割为物理内存页。在头文件kernel/base/include/los_vm_page.h中定义了物理内存页结构体,以及内存页数组g_vmPageArray及数组大小g_vmPageArraySize。物理内存页结构体LosVmPage可以和物理内存页一一对应,也可以对应多个连续的内存页,此时使用nPages指定内存页的数量。

typedef struct VmPage {
    LOS_DL_LIST         node;        /**< 物理内存页节点,挂在VmFreeList空闲内存页链表上 */
    PADDR_T             physAddr;    /**< 物理内存页内存开始地址*/
    Atomic              refCounts;   /**< 物理内存页引用计数 */
    UINT32              flags;       /**< 物理内存页标记 */
    UINT8               order;       /**< 物理内存页所在的链表数组的索引,总共有9个链表 */
    UINT8               segID;       /**< 物理内存页所在的物理内存段的编号 */
    UINT16              nPages;      /**< 连续物理内存页的数量 */
} LosVmPage;

extern LosVmPage *g_vmPageArray;
extern size_t g_vmPageArraySize;

在文件kernel\base\include\los_vm_common.h中定义了内存页的大小、掩码和逻辑位移值,可以看出每个内存页的大小为4KiB。

#ifndef PAGE_SIZE
#define PAGE_SIZE                        (0x1000U)
#endif
#define PAGE_MASK                        (~(PAGE_SIZE - 1))
#define PAGE_SHIFT                       (12)

1.2、物理内存段LosVmPhysSeg

在文件kernel/base/include/los_vm_phys.h中定义了物理内存段LosVmPhysSeg等几个结构体。该文件的部分代码如下所示。⑴处的宏是物理内存伙伴算法中空闲内存页节点链表数组的大小,VM_PHYS_SEG_MAX表示系统支持的物理内存段的数量。⑵处的结构体用于伙伴算法中空闲内存页节点链表数组的元素类型,除了记录双向链表,还维护链表上节点数量。⑶就是我们要介绍的物理内存段,包含开始地址,大小,内存页基地址,空闲内存页节点链表数组,LRU链表数组等成员。

⑴  #define VM_LIST_ORDER_MAX    9
    #define VM_PHYS_SEG_MAX    32

⑵  struct VmFreeList {
        LOS_DL_LIST node;   // 空闲物理内存页节点
        UINT32 listCnt;     // 空闲物理内存页节点数量
    };

⑶  typedef struct VmPhysSeg {
        PADDR_T start;            /* 物理内存段的开始地址 */
        size_t size;              /* 物理内存段的大小,bytes */
        LosVmPage *pageBase;      /* 物理内存段第一个物理内存页结构体地址 */

        SPIN_LOCK_S freeListLock; /* 伙伴算法双向链表自旋锁 */
        struct VmFreeList freeList[VM_LIST_ORDER_MAX];  /* 空闲物理内存页的伙伴双向链表 */

        SPIN_LOCK_S lruLock;  /* LRU双向链表自旋锁 */
        size_t lruSize[VM_NR_LRU_LISTS];  /* LRU大小 */
        LOS_DL_LIST lruList[VM_NR_LRU_LISTS];/* LRU双向链表 */
    } LosVmPhysSeg;

    struct VmPhysArea {
        PADDR_T start;  // 物理内存区开始地址
        size_t size;    // 物理内存区大小
    };

kernel/base/vm/los_vm_phys.c文件中定义了物理内存区数组g_physArea[],如下代码所示,其中SYS_MEM_BASEDDR_MEM_ADDR的宏名称,DDR_MEM_ADDRSYS_MEM_SIZE_DEFAULT定义在文件./device/hisilicon/hispark_taurus/sdk_liteos/board/target_config.h中,表示开发板相关的物理内存地址和大小。

STATIC struct VmPhysArea g_physArea[] = {
    {
        .start = SYS_MEM_BASE,
        .size = SYS_MEM_SIZE_DEFAULT,
    },
};

看下物理内存区VmPhysArea和物理内存段的LosVmPhysSeg区别,前者信息教少,主要记录开始地址和大小,为一块物理内存的最简单描述;后者除了物理内存块开始地址和大小,还维护物理页开始地址,空闲物理页伙伴链表,LRU链表,相应的自旋锁等信息。

上面提到了伙伴算法,先看下伙伴算法的示意图,如下。每个物理内存段都分割为一个一个的内存页,空闲的内存页挂载在空闲内存页节点链表上。共有9个空闲内存页节点链表,这些链表组成链表数组。第一个链表上的内存页节点大小为1个内存页,第二个链表上的内存页节点大小为2个内存页,第三个链表上的内存页节点大小为4个内存页,依次下去,第9个链表上的内存页节点大小为2^8个内存页。申请内存、释放内存时会操作这些空闲内存页节点链表,后文详细分析。

1.3、物理内存伙伴位图

上文提到伙伴算法,还需要了解下伙伴位图。在伙伴算法中,每个链表的索引都对应一个位图。 位图的某位对应于两个伙伴块,为1就表示其中一块忙,为0表示两块都闲或都在使用 。系统每次分配和回收伙伴块时都要对它们的伙伴位 跟1进行异或运算 。所谓异或是指刚开始时,两个伙伴块都空闲,它们的伙伴位为0,如果其中一块被使用,异或后得1;如果另一块也被使用,异或后得0;如果前面一块回收了异或后得1;如果另一块也回收了异或后得0。位图用于在释放内存页块时,判断两块内存是否属于地址连续的伙伴内存块。

在文件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值