欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

【原创】(十六)Linux内存管理之CMA

程序员文章站 2022-04-16 08:16:06
背景 By 鲁迅 By 高尔基 说明: 1. Kernel版本:4.14 2. ARM64处理器,Contex A53,双核 3. 使用工具:Source Insight 3.5, Visio 1. 概述 ,连续内存分配器,用于分配连续的大块内存。 ,会Reserve一片物理内存区域: 1. 设备驱 ......

背景

  • read the fucking source code! --by 鲁迅
  • a picture is worth a thousand words. --by 高尔基

说明:

  1. kernel版本:4.14
  2. arm64处理器,contex-a53,双核
  3. 使用工具:source insight 3.5, visio

1. 概述

contiguous memory allocator, cma,连续内存分配器,用于分配连续的大块内存。
cma分配器,会reserve一片物理内存区域:

  1. 设备驱动不用时,内存管理系统将该区域用于分配和管理可移动类型页面;
  2. 设备驱动使用时,用于连续内存分配,此时已经分配的页面需要进行迁移;

此外,cma分配器还可以与dma子系统集成在一起,使用dma的设备驱动程序无需使用单独的cma api

2. 数据结构

内核定义了struct cma结构,用于管理一个cma区域,此外还定义了全局的cma数组,如下:

struct cma {
    unsigned long   base_pfn;
    unsigned long   count;
    unsigned long   *bitmap;
    unsigned int order_per_bit; /* order of pages represented by one bit */
    struct mutex    lock;
#ifdef config_cma_debugfs
    struct hlist_head mem_head;
    spinlock_t mem_head_lock;
#endif
    const char *name;
};

extern struct cma cma_areas[max_cma_areas];
extern unsigned cma_area_count;
  • base_pfn:cma区域物理地址的起始页帧号;
  • count:cma区域总体的页数;
  • *bitmap:位图,用于描述页的分配情况;
  • order_per_bit:位图中每个bit描述的物理页面的order值,其中页面数为2^order值;

来一张图就会清晰明了:

【原创】(十六)Linux内存管理之CMA

3. 流程分析

3.1 cma区域创建

3.1.1 方式一 根据dts来配置

之前的文章也都分析过,物理内存的描述放置在dts中,最终会在系统启动过程中,对dtb文件进行解析,从而完成内存信息注册。

cma的内存在dts中的描述示例如下图:

【原创】(十六)Linux内存管理之CMA

dtb解析过程中,会调用到rmem_cma_setup函数:

reservedmem_of_declare(cma, "shared-dma-pool", rmem_cma_setup);

【原创】(十六)Linux内存管理之CMA

3.1.2 方式二 根据参数或宏配置

可以通过内核参数或配置宏,来进行cma区域的创建,最终会调用到cma_declare_contiguous函数,如下图:

【原创】(十六)Linux内存管理之CMA

3.2 cma添加到buddy system

在创建完cma区域后,该内存区域成了保留区域,如果单纯给驱动使用,显然会造成内存的浪费,因此内存管理模块会将cma区域添加到buddy system中,用于可移动页面的分配和管理。cma区域是通过cma_init_reserved_areas接口来添加到buddy system中的。

core_initcall(cma_init_reserved_areas);

core_initcall宏将cma_init_reserved_areas函数放置到特定的段中,在系统启动的时候会调用到该函数。

【原创】(十六)Linux内存管理之CMA

3.3 cma分配/释放

  • cma分配,入口函数为cma_alloc

【原创】(十六)Linux内存管理之CMA

  • cma释放,入口函数为cma_release
    函数比较简单,直接贴上代码
/**
 * cma_release() - release allocated pages
 * @cma:   contiguous memory region for which the allocation is performed.
 * @pages: allocated pages.
 * @count: number of allocated pages.
 *
 * this function releases memory allocated by alloc_cma().
 * it returns false when provided pages do not belong to contiguous area and
 * true otherwise.
 */
bool cma_release(struct cma *cma, const struct page *pages, unsigned int count)
{
    unsigned long pfn;

    if (!cma || !pages)
        return false;

    pr_debug("%s(page %p)\n", __func__, (void *)pages);

    pfn = page_to_pfn(pages);

    if (pfn < cma->base_pfn || pfn >= cma->base_pfn + cma->count)
        return false;

    vm_bug_on(pfn + count > cma->base_pfn + cma->count);

    free_contig_range(pfn, count);
    cma_clear_bitmap(cma, pfn, count);
    trace_cma_release(pfn, pages, count);

    return true;
}

3.4 dma使用

代码参考driver/base/dma-contiguous.c,主要包括的接口有:

/**
 * dma_alloc_from_contiguous() - allocate pages from contiguous area
 * @dev:   pointer to device for which the allocation is performed.
 * @count: requested number of pages.
 * @align: requested alignment of pages (in page_size order).
 * @gfp_mask: gfp flags to use for this allocation.
 *
 * this function allocates memory buffer for specified device. it uses
 * device specific contiguous memory area if available or the default
 * global one. requires architecture specific dev_get_cma_area() helper
 * function.
 */
struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
                       unsigned int align, gfp_t gfp_mask);
 
 /**
 * dma_release_from_contiguous() - release allocated pages
 * @dev:   pointer to device for which the pages were allocated.
 * @pages: allocated pages.
 * @count: number of allocated pages.
 *
 * this function releases memory allocated by dma_alloc_from_contiguous().
 * it returns false when provided pages do not belong to contiguous area and
 * true otherwise.
 */
bool dma_release_from_contiguous(struct device *dev, struct page *pages,
                 int count);

在上述的接口中,实际调用的就是cma_alloc/cma_release接口来实现的。

整体来看,cma分配器还是比较简单易懂,也不再深入分析。

4.后记

内存管理的分析先告一段落,后续可能还会针对某些模块进一步的研究与完善。
内存管理子系统,极其复杂,盘根错节,很容易就懵圈了,尽管费了不少心力,也只能说略知皮毛。
学习就像是爬山,面对一座高山,可能会有心理障碍,但是当你跨越之后,再看到同样高的山,心理上你将不再畏惧。

接下来将研究进程管理子系统,将任督二脉打通。

未来会持续分析内核中的各类框架,并发机制等,敬请关注,一起探讨。

【原创】(十六)Linux内存管理之CMA