缘由:
在应用层两次调用 open_adf 接口后,整个系统就 crash了,经过log分析发现,在 kernel 第二次ION申请内存的时候,出了问题。那什么是ION?怎么玩的?于是准备一探究竟。
什么是 ION?
ION 是google android系统在4.0后引入的一种内存管理技术。详细链接如下:
The Android ION memory allocator
用在哪些地方?
应用层和kernel层都可以使用。应用层比如 camera、display、audio等系统。同时,ION 可以在两个进程间进行内存共享。
如何用?
对于kernel层,直接调用 ION 框架中相关的函数接口就可以;对于应用层,通过访问平台提供的 ION 驱动设备节点 “/dev/ion” 来进行操作。
相关概念
ion_client
:申请内存的对象,一般一个进程代表一个 client。以应用层为例,打开一次 “/dev/ion” 节点,就产生一个 client。
ion_heap
:内存缓冲池,存放通过伙伴系统申请的各种页面。heap 在系统开机阶段由平台ION驱动调用ION框架中相关接口函数创建。为了分类管理申请的页面,heap 也是多个种类。
ion_buffer
:申请的内存页面,存放在 heap 的 pool 中。
ion_handle
:存放申请到的 buffer 的句柄
简要理解
首先,在设备树中,SPRD 的 ION 配置部分,先指定了平台上的 heap相关信息,包括 type、id、name;如下:
ion {
compatible = "sprd,ion";
#address-cells = <1>;
#size-cells = <0>;
heap@0 {
reg = <0>;
label = "system";
type = <0>;
};
heap@3 {
reg = <3>;
label = "carveout_fb";
type = <2>;
memory-region = <&fb_reserved>;
};
};
其次,在代码中,SPRD ION 驱动,一是调用ION的 ion_device_create
接口函数,创建设备驱动节点“/dev/ion”,并注册相关的 ion 操作 ioctl 文件接口:
idev = ion_device_create(&sprd_ion_ioctl);
接着,调用 ION 的 ion_heap_create
接口函数,按照 ION 设备树里面的配置,创建各种 heap。注意,此时的 heap 中是空的,并没有内存页面存在。heap 中页面,是后续慢慢的存放进去的。(根据heap的type不同,这里的过程稍微不一致,以下以 ION_HEAP_TYPE_SYSTEM
为例)。
通过 ION 申请内存的接口是 ion_alloc
。该函数会去指定的 ion_heap
中查,如果 ion_heap
的 pool 中有空闲且符合要求的页面,则直接取用该页面;如果没有,则需要通过伙伴(buddy)系统的alloc_pages
去获取新的页面,至于如何通过伙伴系统去获取内存,此处略过不表。ion_alloc 申请的内存空间,最后会将句柄放置到 ion_handle
中,返回给 ion_client
。
而在 client 不再使用当前页面时,就需要释放。释放分两种情况,一是需要立即归还给系统的页面,二是可以暂时不归还给系统的页面。如果是需要立即规划给系统的页面,则需要调用 ion_page_pool_free_immediate
函数,最后走到 __free_pages
流程上;如果是可以暂时不归还给系统的页面,则调用了 ion_page_pool_free
函数,最后将这些页面存入指定的 heap 中,并对存入的页面进行统计、归纳整理,如果下一次再有 ion alloc 的需求发出,则alloc会先查询 heap 中的页面是否符合要求,如果有符合要求的页面,就直接使用了,不再通过伙伴系统去申请新的页面。
这里需要做一个对比,就是从伙伴系统申请页面,以及站在伙伴系统的基础上,直接从 ion heap 中获取页面,它们的速度肯定是不一样的,则效率也是不一样的。毕竟,产生一次缺页中断或者重新做一次物理内存块到虚拟页面的映射,这都是需要时间的。而 ion heap 中的页面,就像一个数组一样,申请一次,每次用的时候,最多采用 memset 清零就好。
代码考察
以下考察下 ION_HEAP_TYPE_SYSTEM
类型的 alloc 和 free 的过程。
针对 ION_HEAP_TYPE_SYSTEM 类型的 type, ion_alloc 最终会调用 ion_system_heap_allocate
接口。
struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
size_t align, unsigned int heap_id_mask,
unsigned int flags)
{
buffer = ion_buffer_create(heap, dev, len, align, flags);
}
static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
struct ion_device *dev,
unsigned long len,
unsigned long align,
unsigned long flags)
{
ret = heap->ops->allocate(heap, buffer, len, align, flags);
}
上面的 allocate
注册地方如下:
static struct ion_heap_ops system_heap_ops = {
.allocate = ion_system_heap_allocate,
};
则可以继续考察 ion_system_heap_allocate
的实现(代码片段):
static int ion_system_heap_allocate(struct ion_heap *heap,
struct ion_buffer *buffer,
unsigned long size, unsigned long align,
unsigned long flags)
{
//....
unsigned long size_remaining = PAGE_ALIGN(size);
//.....
INIT_LIST_HEAD(&pages);
INIT_LIST_HEAD(&pages_from_pool);
while (size_remaining > 0) {
info = alloc_largest_available(sys_heap, buffer, size_remaining,
max_order);
if (!info)
goto free_pages;
sz = (1 << info->order) * PAGE_SIZE;
if (info->from_pool) {
pool_sz += sz;
list_add_tail(&info->list, &pages_from_pool);
} else {
int index;
for (index = 0; index < num_orders; index++) {
if (info->order == orders[index]) {
buddy_orders[index]++;
break;
}
}
buddy_sz += sz;
list_add_tail(&info->list, &pages);
}
size_remaining -= sz;
max_order = info->order;
i++;
}
//....
table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
goto free_pages;
if (sg_alloc_table(table, i, GFP_KERNEL