一、概述
- linux内核中很多内存分配器,使用这些内存分配器,就能得到想要的内存大小和内型。内核中有像kmalloc,slab,get_free_page,mempool,vmalloc,buddy system等等各种不同的内存分配器。android下推出来了一个更强大的内存分配器,叫做ion,ion相当一个内存分配器容器,它相当于整合了上面大部分的内存分配器,使用ion可以分配到连续的物理内存,也可以分配虚拟内存,还可以分配到DMA内存等等,有兴趣的大家可以去了解下,这里暂不描述。
二、kmalloc
- kmalloc函数特性
- kmalloc函数如果不被阻塞,本身是运行很快的,也意味着kmalloc函数在获取不到空闲内存时会睡眠
- kmalloc函数所获取的内存空间清零,也就是说,分配给它的区域仍然保持这原有的数据。意味着我们需要将内存显示地清空,尤其是可能导出给用户空间或者写入设备的内存
- kmalloc函数分配的区域在物理内存也是连续的。kmalloc函数分配的是物理内存,并且是连续的。
- kmalloc是基于slab实现的,用于分配物理地址连续的小内存,__get_free_page() 分配连续的物理地址,用于整页分配
-
kmalloc函数原型
void *kmalloc(size_t size, int flags); -
kmalloc参数
-
flags参数
@)flags分配标志,它能够以多种方式控制kmalloc的行为,最常用的标志是 GFP_KERNEL,它表示内存分配是代表运行在内核空间的进程执行的。换句话说,这意味着调用它的函数正代表某个进程执行的系统调用(最终总是调用get_free_pages来实现实际的分配,这就是GFP的由来)。所以kmalloc的底层实现就是get_free_pages。
@@)使用GFP_KERNEL允许kmalloc在空闲内存较少时把当前进程转入休眠以等待一个页面。所以调用kmalloc以GFP_KERNEL标志分配内存函数必须是可重入的。
@@@)GFP_KERNEL并不总是适用,有时kmalloc是在进程上下文之外被调用的,例如在中断上下文,tasklet或者内核定时器中调用。这种情况下current进程就不应该睡眠,驱动程序则应该使用GFP_ATOMIC标志。内核通常会为原子性的分配预留一些空闲页面。使用GFP_ATOMIC标志时,kmalloc甚至会用掉最后一个空闲页面。不过如果连最后一页都没有了, 分配就返回失败。 -
flags常见类型
GFP_ATOMIC
用于在中断处理例程或其他运行于进程上下文之外的代码中分配内存,不会休眠。GFP_KERNEL
内核内存的通常分配方法,可能引起休眠。GFP_USER
用于为用户空间页分配内存,可能会休眠。上面列出的分配标志可以和下面的标志"或"起来使用。下面这些标志控制如何进行分配。
__GFP_DMA
该标志请求分配发生在可进行DMA的内存区段中。__GFP_HIGHMEM
这个标志表明要分配的内存可位于高端内存。__GFP_HIGH
这个标志标记了一个高优先级的请求,它允许为紧急情况而消耗由内核保留的一些页面。__GFP_REPEAT
__GFP_NOFAIL
__GFP_NORETRY
这三个标志告诉分配器在满足分配请求而遇到困难时应该采取何种行为。第一个表示努力再试一次。第二个表示始终不返回失败,会努力满足分配请求。最后一个表示如果请求的内存不可获得,立即返回。 -
内存区段
1)Linux内核把内存分为三个区段:可用于DMA的内存、常规内存以及高端内存。通常的内存分配都发生在常规内存区,但通过设置上面介绍过的标志也可以请求在其他区段中的分配。
2)可用于DMA的内存指存在于特别地址范围内的内存,外设可以利用这些内存执行DMA访问。外设只要做DMA映射,这里所说的外设指的是CPU核以外的外设,比如LCD控制器,USB控制器等,都是在ARM芯片内存,CPU核以外。将这些外设内存(寄存器地址)映射到DDR中
3)高端内存是32位平台为了访问(相对)大量的内存而存在的一种机制。如果不首先完成一些特殊的映射,我们就无法从内核中直接访问这些内存。当一个新页面为满足kmalloc的要求被分配时,内核会创建一个内存区段的列表以供搜索。如果指定了__GFP_DMA标志,只只有DMA区段会被搜索;如果低地址段上没有可用内存,分配就会失败。如果没有指定特殊标志,则常规区段和DMA区段都会被搜索;而如果设置了__GFP_HIGHMEM标志,则所有三个区段都会被搜索以获取一个空闲页(然而要注意的是,kmalloc不能分配高端内存) -
size参数
1)Linux内核负责管理系统物理内存,物理内存只能按页面进行分配,一页的大小一般为4KB&#x