1 kmalloc/vmalloc区别
函数 | 位置 | 特性 | 大小限制 |
---|---|---|---|
kmalloc | 物理内存映射区域 | 物理地址虚拟地址均连续 | 不能超过128K |
kzalloc | 物理内存映射区域 | 物理地址虚拟地址均连续 | 不能超过128K |
vmalloc | 虚拟内存映射区域 | 虚拟地址连续,物理地址不一定连续 | 无限制 |
vzalloc | 虚拟内存映射区域 | 虚拟地址连续,物理地址不一定连续 | 无限制 |
kzalloc只是相当于附加了 __GFP_ZERO 标志。所以它除了申请内核内存外,还会对申请到的内存内容清零。
同理,vzalloc也是一样,会对申请内存内容清零。
kmalloc函数原型:
static __always_inline void *kmalloc(size_t size, gfp_t flags);
gpf flags含义:
|– 在进程上下文,可以睡眠 GFP_KERNEL
|– 在进程上下文,不可以睡眠,如: GFP_ATOMIC
| |– 中断处理程序 GFP_ATOMIC
| |– 软中断 GFP_ATOMIC
| |– Tasklet GFP_ATOMIC
|– 用于DMA的内存,可以睡眠 GFP_DMA | GFP_KERNEL
|– 用于DMA的内存,不可以睡眠 GFP_DMA |GFP_ATOMIC
如果进程上下文允许睡眠情况下尽量用GFP_KERNEL, 如果进程上下文禁止休眠的话(如中断,taskletd等)必须用GFP_ATOMIC
vmalloc函数原型:
extern void *vmalloc(unsigned long size);
注意:vmalloc和vfree可以睡眠,因此中断上下文禁止使用。
1.1 内存释放
void kfree(const void *);
extern void vfree(const void *addr);
2 kmalloc/vmalloc内存分配原理
3 CMA
3.1 CMA概述
连续内存分配器(Contiguous Memory Allocator),简称CMA。在系统长时间运行后,内存可能碎片化,很难找到连续的物理页,CMA很好的避免了这个问题。
举个例子:
手机上1300万像素的摄像头,一个像素占用3字节,拍摄一张照片需要大约37MB内存。在系统长时间运行后,内存可能碎片化,很难找到连续的物理页,页分配器(kmalloc)和块分配器(vmalloc)很可能无法分配这么大的连续内存块。
方案1:
最开始的一种解决方案是为设备保留一块大的内存区域,比如为摄像头驱动预留一块大内存,通过ioremap来映射后作为私有内存使用,缺点是:当设备驱动不使用的时候(大多数时间手机摄像头是空闲的),内核的其他模块不能使用这块内存。
方案2:
连续内存分配器CMA很好的解决了这个问题,保留一块大的内存区域,当设备驱动不使用的时候,内核的其他模块可以使用。一般我们把这块区域定义为reserved-memory。
3.2 CMA使用方法
编译内核时需要开启以下配置宏:
(1)配置宏CONFIG_CMA,启用连续内存分配器。
(2)配置宏CONFIG_CMA_AREAS,指定CMA区域的最大数量,默认值是7。
(3)配置宏CONFIG_DMA_CMA,启用允许设备驱动分配内存的连续内存分配器
3.3 CMA的定义
CMA每个区域实际上就是一个reserved memory。CMA分两种:
- 通用的CMA区域,该区域是给整个系统分配使用的;
- 专用的CMA区域,这种是专门为单个模块定义的。
dts中CMA属性:
1. reusable:表示当前的内存区域除了被dma使用之外,还可以被内存管理子系统reuse。
2. no-map:表示是否需要创建页表映射,对于通用的内存,必须要创建映射才可以使用,共享CMA是可以作为通用内存进行分配使用的,因此必须要创建页表映射。
3. 对于共享的CMA区域,需要配置上linux,cma-default属性,标志着它是共享的CMA。
下面定义了3段区域CMA:
1.全局CMA区域,节点名称是“linux,cma”,大小是2GB,8K对齐。
2.私有CMA区域,节点名字“de_mem0” “de_mem1”,128M给GPU 2D engine使用,私有无需建立页表映射。
3.私有CMA区域,节点名字“ion”,给video pipeline使用,私有无需建立页表映射。
reserved-memory {
#address-cells = <0x2>;
#size-cells = <0x2>;
ranges;
cma_reserved: linux,cma {
compatible = "shared-dma-pool";
reusable;
size = <0x0 0x80000000>; // 2GB
alignment = <0x0 0x2000>; // 8KB
linux,cma-default;
};
de_reserved0: de_mem0 {
reg = <0x1 0x10000000 0x0 0x8000000>; // 128M, for 2de
no-map;
};
de_reserved1: de_mem1 {
reg = <0x1 0x18000000 0x0 0x8000000>; // 128M, for 2de
no-map;
};
ion_reserved: ion {
compatible = "ion-region";
size = <0x0 0x04000000>; // 64MB
};
vo_2de0 {
compatible = "sophgo,vg-lite0";
memory-region = <&de_reserved0>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 27 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "vo_2de0";
};
vo_2de1 {
compatible = "sophgo,vg-lite1";
memory-region = <&de_reserved1>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "vo_2de1";
};
2de模块中定义memory-region属性,并且把对应dts定义的cma节点de_reserved0,de_reserved1传递给该模块。
3.4 从CMA区域分配内存
struct page *page = NULL;
page = cma_alloc(dev_get_cma_area(dev),mem_size, 0, GFP_KERNEL);
dev_get_cma_area可以获取对应的cma handler,如果获取不到,比如对应模块中并未定义memory-region,那么就会返回共享的cma handler,还记的上面的 linux,cma-default属性吗,共享cma区域会被作为缺省cma来使用。
不过一般内核模块要使用CMA内存时,使用的接口依然是dma的接口:
extern void *dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle, gfp_t flag);
extern void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr_t dma_handle);
最终也会进入dma_alloc_from_contiguous调用cma_alloc分配内存。
struct page *dma_alloc_from_contiguous(struct device *dev, size_t count,
unsigned int align, bool no_warn)
{
if (align > CONFIG_CMA_ALIGNMENT)
align = CONFIG_CMA_ALIGNMENT;
return cma_alloc(dev_get_cma_area(dev), count, align, no_warn);
}
bool dma_release_from_contiguous(struct device *dev, struct page *pages,
int count)
{
return cma_release(dev_get_cma_area(dev), pages, count);
}
generic dma coherent:
对于dma framwork来说,当我们使能并且配置了CMA区域时会使用CMA进行内存分配,但是内核依然对于旧的实现方式进行了兼容,可以通过 CONFIG_HAVE_GENERIC_DMA_COHERENT 来进行配置。
定义 no-map 属性,进而这块内存就从系统中剥离出来了,无法被伙伴系统所使用,但是可以在dma核心层通过remap的形式创建页表映射来使用它。
3.5 CMA内存原理和流程
设备驱动程序不能直接使用连续内存分配器,而是调用DMA映射框架来使用连续内存分配器CMA。
3.5.1 CMA调用层次框架:
- 最底层为页分配器(以后分析),cma_alloc用来从CMA区域分配页,cma_release用来释放从CMA区域分配的页。
- 第3层为DMA映射框架专用的连续内存分配器,简称DMA专用连续内存分配器,提供的接口dma_alloc_from_contiguous用来从CMA区域分配页,接口dma_release_from_contiguous用来释放从CMA区域分配的页。
- 第4层就是DMA通用映射框架,供驱动程序调用dma_alloc_coherent和dma_alloc_noncoherent用来分配内存,接口dma_free_coherent和dma_free_noncoherent用来释放内存。
3.5.2 CMA结构体
mm/cma.h
struct cma {
unsigned long base_pfn; //该CMA区域的起始页帧号
unsigned long count; //该cma区域的页数
unsigned long *bitmap; //位图,每个位描述对应的页的分配状态,0表示空闲,1表示已分配
unsigned int order_per_bit;//位图中的每个位描述的物理页的阶数,目前取值为0,表示每个位描述一页
struct mutex lock;
const char *name;
};
mm/cma.c
struct cma cma_areas[MAX_CMA_AREAS];//定义多个CMA区域。
unsigned cma_area_count;//表示实际使用的cma区域数量
3.5.3 CMA区域初始化
linux内核首先需要解析dtb中节点“memory”,把内存块添加到memblock的memory类型,memory类型保存内存块的物理地址范围,reserved类型保存保留内存块的物理地址范围,CMA区域就属于保留内存块。
创建CMA区域的执行流程如下所示:
linux内核启动时,当调用到__reserved_mem_init_node时会调用所有使用RESERVEDMEM_OF_DECLARE声明的CMA区域。
其中全局CMA区域的初始化函数是rmem_cma_setup:
3.5.3.1 全局cma内存初始化
来看调用的cma_init_reserved_mem:
从数组cma_areas分配一个数组项,保存CMA区域的起始页帧号和页数。dts指定了属性“linux,cma-default”,那么这个CMA区域是默认的CMA区域,最后设置全局变量dma_contiguous_default_area指向这个CMA区域(默认全局CMA区域)
红色圈出了该cma区域的dts描述和dts是不是完全吻合。
如果内核参数或配置宏配置全局CMA区域,cma区域流程如下所示:
3.6 CMA内存分配
/**
* cma_alloc() - allocate pages from contiguous area
* @cma: Contiguous memory region for which the allocation is performed.
* @count: Requested number of pages.
* @align: Requested alignment of pages (in PAGE_SIZE order).
* @no_warn: Avoid printing message about failed allocation
*
* This function allocates part of contiguous memory on specific
* contiguous memory area.
*/
struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align,
bool no_warn);
函数cma_alloc负责从CMA区域分配内存,如果CMA区域中的物理页已经被页分配器分配出去(页被借走),需要把物理页迁移到其他地方。
3.7 CMA内存回收
/**
* 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 cma_alloc().
* 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);
标签:dma,15,cma,区域,CMA,memory,kmalloc,内存
From: https://www.cnblogs.com/fuzidage/p/17664807.html