SimpleOS-内存管理

整体设计方案

内存管理模块整体方案如下:

img

物理内存池划分如下:

img

虚拟内存池划分如下:

img

数据结构

//物理内存池
struct pool{
    struct bitmap pool_bitmap;  //物理内存池位图
    uint32_t phy_addr_start;    //管理空间起始地址
    uint32_t pool_size;         //管理空间长度

    struct lock lock;           //进程申请物理内存时需要上锁
};

//虚拟内存池
struct virtual_addr{
    struct bitmap vaddr_bitmap; //虚拟内存池位图
    uint32_t vaddr_start;       //管理空间起始地址
};

/* 内存块 */
struct mem_block {
    struct list_elem free_elem;
};

/* 内存块描述符 */
struct mem_block_desc {
    uint32_t block_size;		 // 内存块大小
    uint32_t blocks_per_arena;	 // 本arena中可容纳此mem_block的数量.
    struct list free_list;	 // 目前可用的mem_block链表
};

/* 内存仓库arena元信息 */
struct arena {
    struct mem_block_desc* desc;	 // 此arena关联的mem_block_desc
    uint32_t cnt;
    bool large;		                // large为ture时,cnt表示的是页框数。否则cnt表示空闲mem_block数量
};

img

内存池提供以页为单位的内存空间
对于每一页内存空间都由元信息arena来组织,并将剩余空间切分成小块
每一个元信息arena组织的内存仓库 都按照块的大小有着相应的mem_block_desc
由mem_block_desc中的free_list来串起所有同一大小的空闲内存块

函数表

  • kernel/memory.c

      //-------------------------------------内存系统初始化相关函数---------------------------
    
      /*
      @brief: 初始化内存相关数据结构(内存池、内存仓库、内存块描述符)
      @param: 无
      @retval:无
      */
      void mem_init();
    
      /*
      @brief: 内存池初始化(物理内核内存池、物理用户内存池、虚拟内核内存池)
      @param: all_mem:物理内存总量
      @retval:无
      */
      static void mem_pool_init(uint32_t all_mem);
    
      /*
      @brief: 初始化内存块描述符数组(管理7种不同的内存块描述符(16、32、64、128、256、512、1024))
      @param: desc_array:要初始化的内存块描述符数组
      @retval:无
      */
      void block_desc_init(struct mem_block_desc* desc_array);
    
      //-------------------------------------内存系统初始化相关函数---------------------------
    
      //-------------------------------------内存分配相关函数---------------------------
    
      /*
      @brief: 分配pg_cnt页的内存空间
      @param: pf:内存池类型标识符(内核/用户)
              pg_nct:申请分配的页数
      @retval:成功返回起始虚拟地址,失败返回NULL
      */
      void* malloc_page(enum pool_flags pf,uint32_t pg_cnt);
    
      /*
      @brief: 向虚拟内存池申请pg_cnt页的空间
      @param: pf:内存池类型标识符(内核/用户)
              pg_nct:申请分配的页数
      @retval:成功返回起始虚拟地址,失败返回NULL
      */
      static void* vaddr_get(enum pool_flags pf,uint32_t pg_cnt);
    
      /*
      @brief: 向物理内核/用户内存池申请1页空间,
      @param: m_pool:申请的物理内存池(用户/内核)
      @retval:成功返回地址起点,失败返回-1
      */
      static void* palloc(struct pool* m_pool);
    
      /*
      @brief: 建立从虚拟地址到物理地址的映射(以页为单位)(建立相应的页表/页目录)
      @param: _vaddr:虚拟地址
              _page_phyaddr:物理地址
      @retval:无
      */
      static void page_table_add(void* _vaddr,void* _page_phyaddr);
    
      /*
      @brief: 给内核分配pg_cnt页内存,
      @param: 略
      @retval:成功则返回虚拟地址,失败返回NULL
      */
      void* get_kernel_pages(uint32_t pg_cnt);
    
      /*
      @brief: 给用户分配pg_cnt页内存,
      @param: 略
      @retval:成功则返回虚拟地址,失败返回NULL
      */
      void* get_user_pages(uint32_t pg_cnt);
    
      /*
      @brief: 在堆(即内存池)中申请size字节内存(灵活申请)
      @param: 略
      @retval:无
      */
      void* sys_malloc(uint32_t size);
    
      /*
      @brief: 给指定虚拟地址分配一页内存
      @param: pf:内存池表示符
              vaddr:指定虚拟地址
      @retval:成功则返回虚拟地址,失败返回NULL
      */
      void* get_a_page(enum pool_flags pf,uint32_t vaddr);
      //-------------------------------------内存分配相关函数---------------------------
                  
      //-------------------------------------内存回收相关函数---------------------------
    
      /*
      @brief: 释放 内核/用户 内存池种以虚拟地址vaddr为起点的cnt个物理页框 
      @param: pf:内存池标识符
              _vaddr:要回收的虚拟地址
              pg_cnt:要回收的页数
      @retval:无
      */
      void mfree_page(enum pool_flags pf, void* _vaddr, uint32_t pg_cnt);
    
    
      /*
      @brief: 将1张物理页回收到物理内存池,实质就是清除物理内存池中位图的位
      @param: pg_pyh_addr:要回收页的物理地址
      @retval:无
      */
      void pfree(uint32_t pg_phy_addr);
    
      /*
      @brief: 将以_vaddr起始的连续pg_cnt个虚拟页回收到虚拟内存池,实质就是清除虚拟内存池中位图的位
      @param: pf:内存池标识符
              _vaddr:要回收的虚拟地址
              pg_cnt:要回收的页数
      @retval:无
      */
      static void vaddr_remove(enum pool_flags pf, void* _vaddr, uint32_t pg_cnt)
    
      /*
      @brief: 解除页表中虚拟地址vaddr的映射,实质是将vaddr对应的pte存在位置0
      @param: 略
      @retval:无
      */
      static void page_table_pte_remove(uint32_t vaddr);
    
      /*
      @brief: 回收ptr指向的内存块(内存块大小由arena指出)
      @param: 略
      @retval:无
      */
      void sys_free(void* ptr);
    
      //-------------------------------------内存回收相关函数---------------------------
    
      //------------------------------工具函数-----------------------------------------
    
      /*
      @brief: 返回虚拟地址映射的物理地址 
      @param: 略
      @retval:略
      */
      uint32_t addr_v2p(uint32_t vaddr);
    
      /*
      @brief: 返回arena中第idx个内存块的地址
      @param: 略
      @retval:略
      */
      static struct mem_block* arena2block(struct arena* a, uint32_t idx);
    
      /*
      @brief: 返回内存块b所在的arena地址
      @param: 略
      @retval:略
      */
      static struct arena* block2arena(struct mem_block* b);
    
      /*
      @brief: 得到虚拟地址vaddr对应的pte指针
      @param: 略
      @retval:略
      */
      uint32_t* pte_ptr(uint32_t vaddr);
    
      /*
      @brief: 得到虚拟地址vaddr对应的pde指针
      @param: 略
      @retval:略
      */
      uint32_t* pde_ptr(uint32_t vaddr)
    
      //------------------------------工具函数-----------------------------------------
    

关键函数说明

  • kernel/memory.c/内存分配相关函数

    img

  • kernel/memory.c/内存回收相关函数

    img

  • kernel/memory.c/page_table_add()

    img

  • kernel/memory.c/sys_malloc()

    img

背景知识

pool-virtual_addr

arena-mem_block_desc

工具图表