目录

1 什么是ION

2 ION中不同 type 的 heap

3 ION分配(以system heap为例)

3.1 ion_alloc()

3.2 ion_system_heap_allocate()

3.3 alloc_largest_available()

3.4 alloc_buffer_page()

3.5 ion_page_pool_alloc()

3.6 ion_page_pool_remove()

3.7 ion_page_pool_alloc_pages()

4 释放内存(system heap)到pool

4.1 free_buffer_page()

4.2 返还到pool中

4.3  释放内存到 buddy

4.3.1 ion_page_pool_shrink()

5 小结


1 什么是ION

  • ION具体不知道是什么的缩写,只知道是android系统上google引入的内存管理方式,为了实现用户与内核间数据共享时零拷贝。多用于多媒体,比如camera和display,graphic。
  • ION是一个内存管理器,管理不同type的内存堆(heap),而不同的type的内存又通过不同的内存分配器来分配,比如cma、kmalloc、vmalloc。

2 ION中不同 type 的 heap

enum ion_heap_type {ION_HEAP_TYPE_SYSTEM,ION_HEAP_TYPE_SYSTEM_CONTIG,ION_HEAP_TYPE_CARVEOUT,ION_HEAP_TYPE_CHUNK,ION_HEAP_TYPE_DMA,ION_HEAP_TYPE_CUSTOM, /*
};
  • ION_HEAP_TYPE_SYSTEM:头文件中说是通过vmalloc分配,代码中看是直接通过alloc_pages分配的,对应文件ion_system_heap.c
  • ION_HEAP_TYPE_SYSTEM_CONTIG:通过kmalloc进行分配,对应文件ion_system_heap.c
  • ION_HEAP_TYPE_DMA:从代码中看是对接的cma分配器,对应文件ion_cma_heap.c
  • ION_HEAP_TYPE_CARVEOUT:对应文件ion_carveout_heap.c
  • ION_HEAP_TYPE_CHUNK:对应文件ion_chunk_heap.c

3 ION分配(以system heap为例)

  • 用户层打开/dev/ion,并通过ioctl调用传递分配内存需要的参数,主要是:
struct ion_allocation_data {__u64 len; //需要分配的字节数__u32 heap_id_mask; //需要从哪个heap中分配,heap id是在每个heap添加到ion dev时自动增长的,从0开始。__u32 flags; //__u32 fd; //分配后的内存转换成dma-buf的fd文件句柄__u32 unused;
};

内核中 ioctl 调用 ion_alloc 函数进行分配:

long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
....省略
union ion_ioctl_arg data;
....省略switch (cmd) {case ION_IOC_ALLOC:{int fd;//调用ion_alloc,传入用户层参数。分配成功则返回dma_buf转换后的fd到用户层。fd = ion_alloc(data.allocation.len,data.allocation.heap_id_mask,data.allocation.flags);if (fd < 0)return fd;//将fd赋值给用户层data.allocation.fd = fd;break;}case ION_IOC_HEAP_QUERY:ret = ion_query_heaps(&data.query);break;default:return -ENOTTY;}
....省略return ret;
}

3.1 ion_alloc()

  • 从ion dev设备中查找用户想从哪个heap中分配内存
  • 分配成功后,将ion_buffer转换并导出成dma_buf,再讲dma_buf转换成文件fd
int ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
{struct ion_device *dev = internal_dev;struct ion_buffer *buffer = NULL;struct ion_heap *heap;DEFINE_DMA_BUF_EXPORT_INFO(exp_info);int fd;struct dma_buf *dmabuf;pr_debug("%s: len %zu heap_id_mask %u flags %x\n", __func__,len, heap_id_mask, flags);/** len是用户传递过来的长度,这里需要页对齐。少于一个page的大小就按一个page给,比如传递过来是5字节* 经过page_align后,返回的值就是4096.*/len = PAGE_ALIGN(len);if (!len)return -EINVAL;down_read(&dev->lock);/** ion设备的heaps链表中,链接了ion设备等的所有heaps,比如system heap、cma heap、carveout heap等* 通过遍历所有的heap,找到匹配上用户传递过来的参数中指定的heap(比如heap id),主要就是heap id。heap id在每个heap添加到ion dev时自动分配的。* 表示中哪个heap去分配*/plist_for_each_entry(heap, &dev->heaps, node) {/* if the caller didn't specify this heap id */if (!((1 << heap->id) & heap_id_mask))continue;/** 找到了用户需要的heap,比如system heap*/buffer = ion_buffer_create(heap, dev, len, flags);if (!IS_ERR(buffer))break;}up_read(&dev->lock);if (!buffer)return -ENODEV;if (IS_ERR(buffer))return PTR_ERR(buffer);//将分配到的buffer导出为dma buf格式exp_info.ops = &dma_buf_ops;exp_info.size = buffer->size;exp_info.flags = O_RDWR;exp_info.priv = buffer; //保留ion_buffer//导出为dma bufdmabuf = dma_buf_export(&exp_info);if (IS_ERR(dmabuf)) {_ion_buffer_destroy(buffer);return PTR_ERR(dmabuf);}//将dma buf转换成file fd文件句柄,返回给用户空间fd = dma_buf_fd(dmabuf, O_CLOEXEC);if (fd < 0)dma_buf_put(dmabuf);return fd;
}

假如选中的heap为system heap(ION_HEAP_TYPE_SYSTEM),那么接下来会调用它的allocate函数:

static struct ion_heap_ops system_heap_ops = {.allocate = ion_system_heap_allocate,.free = ion_system_heap_free,.map_kernel = ion_heap_map_kernel,.unmap_kernel = ion_heap_unmap_kernel,.map_user = ion_heap_map_user,.shrink = ion_system_heap_shrink,
};

3.2 ion_system_heap_allocate()

  • system heap中管理着两类pool,分别是cache和uncache,而每一类又分为不同order,总共3个order,8/4/0.
  • 分配前,会先将size转换成页对齐大小,比如5字节,那么对齐后就是4096字节
  • 分配时,会从三种order中选中最接近分配要求但是不超过size的order。比如分配18页,那么会分配1个order为4,2个order为1的页。
  • 分配成功后,会将分配后的离散的物理页通过sg_table组织起来,具体是将page地址存放在scatterlist->page_link中,同时将page彻底从buddy系统中脱离出来。
static int ion_system_heap_allocate(struct ion_heap *heap,struct ion_buffer *buffer,unsigned long size,unsigned long flags)
{struct ion_system_heap *sys_heap = container_of(heap,struct ion_system_heap,heap);struct sg_table *table;struct scatterlist *sg;struct list_head pages;struct page *page, *tmp_page;int i = 0;unsigned long size_remaining = PAGE_ALIGN(size);unsigned int max_order = orders[0];if (size / PAGE_SIZE > totalram_pages / 2)return -ENOMEM;INIT_LIST_HEAD(&pages);//假如传递过来的size为4096(在ion_alloc经过对齐了),再次经过页面对齐,此时的size_remaining也为4096while (size_remaining > 0) {//分配内存,system heap中管理着两类pool,根据pool中连续page页的order数,又分为6个pool,分别是order为//8、 4、 0,定义在int orders[] = {8, 4, 0};中page = alloc_largest_available(sys_heap, buffer, size_remaining,max_order);if (!page)goto free_pages;//分配成功,将page页加入到pages list中list_add_tail(&page->lru, &pages);size_remaining -= PAGE_SIZE << compound_order(page);max_order = compound_order(page);i++;}//将分配成功的page放入sg_table中table = kmalloc(sizeof(*table), GFP_KERNEL);if (!table)goto free_pages;//总共分配了多少次(i次)连续的内存块,那么就分配i个scatterlist用来关联物理bufferif (sg_alloc_table(table, i, GFP_KERNEL))goto free_table;sg = table->sgl;//将内存块与scatterlist关联起来,比如上面分配了一个16页,2个一页,总共3个物理连续的内存buffer,//将他们分别放入scatterlist的page_link中,实际是将page结构体的地址存放在其中。//接着将page->lru从前面的pages链表中删除。此时的page就只有sg中能找到了,在buddy中已经找不到了。list_for_each_entry_safe(page, tmp_page, &pages, lru) {sg_set_page(sg, page, PAGE_SIZE << compound_order(page), 0);sg = sg_next(sg);list_del(&page->lru);}//最后将sg_table赋值,其中就包含刚分配的内存页buffer->sg_table = table;return 0;free_table:kfree(table);
free_pages:list_for_each_entry_safe(page, tmp_page, &pages, lru)free_buffer_page(sys_heap, buffer, page);return -ENOMEM;
}

3.3 alloc_largest_available()

找到匹配分配大小的order,并调用 alloc_buffer_page 实际去分配。

static struct page *alloc_largest_available(struct ion_system_heap *heap,struct ion_buffer *buffer,unsigned long size,unsigned int max_order)
{struct page *page;int i;//system heap中有三种order大小的内存块,分别是8、4、0,对应2^8页...//此处找到小于分配size的最大order,比如size是18*4K(18个page),那么这里就会选择到order 4 ,先分配一个16个page返回,//再次进入该函数,size变成2*4k(2个page),会选择order为0,分配一个page返回。//第三次进入该函数,size变成1*4k(1个page),此时会选择order为0,分配一个page返回。for (i = 0; i < NUM_ORDERS; i++) {if (size < order_to_size(orders[i]))continue;if (max_order < orders[i])continue;//实际分配:先从pool中分配,如果没有就从buddy中分配。page = alloc_buffer_page(heap, buffer, orders[i]);if (!page)continue;return page;}return NULL;
}

3.4 alloc_buffer_page()

  • 实际分配时,根据用户空间传递过来的flags决定是从cached还是uncached中分配(实际我也没太明白这两个的区别)。
  • 对应的pool具体是哪个order,之前已经比较出来。
static struct page *alloc_buffer_page(struct ion_system_heap *heap,struct ion_buffer *buffer,unsigned long order)
{bool cached = ion_buffer_cached(buffer);struct ion_page_pool *pool;struct page *page;//system heap自己定义了两个pool,一个cached一个uncached//每个pool对应不同order,当前是8/4/0三种if (!cached)pool = heap->uncached_pools[order_to_index(order)];elsepool = heap->cached_pools[order_to_index(order)];//从pool中分配内存,分配时先从high链表中分配,没有就从low中分配,还没有就从buddy中分配。page = ion_page_pool_alloc(pool);return page;
}

3.5 ion_page_pool_alloc()

  • 首先从pool中的high内存链表中分配,如果没有就从low链表中分配
  • 再者,如果当前pool中都没有内存,那么就从buddy中分配
struct page *ion_page_pool_alloc(struct ion_page_pool *pool)
{struct page *page = NULL;BUG_ON(!pool);mutex_lock(&pool->mutex);//分配时,先从pool中分配//pool中又分为highmem和lowmem//先从high中去分配if (pool->high_count)page = ion_page_pool_remove(pool, true);else if (pool->low_count)page = ion_page_pool_remove(pool, false);mutex_unlock(&pool->mutex);//如果没有分配成功,那么就从伙伴系统中分配。//但是这里分配后并没有加入到pool中。if (!page)page = ion_page_pool_alloc_pages(pool);return page;
}

3.6 ion_page_pool_remove()

如果pool中有内存,那么就从pool的对应链表中获取page,获取后将page从链表中移除。

static struct page *ion_page_pool_remove(struct ion_page_pool *pool, bool high)
{struct page *page;//如果是从high中申请,那么就从high_items链表中拿出page(可能是order为8/4/0),并且high_count减1.//这里可以看到,page被加入到high_items或者low_items中,是通过page->lruif (high) {BUG_ON(!pool->high_count);page = list_first_entry(&pool->high_items, struct page, lru);pool->high_count--;} else {BUG_ON(!pool->low_count);page = list_first_entry(&pool->low_items, struct page, lru);pool->low_count--;}//将page从low或者high链表中移除。list_del(&page->lru);return page;
}

3.7 ion_page_pool_alloc_pages()

如果pool中没有的话,就调用alloc_pages() 从 buddy 中分配内存

static void *ion_page_pool_alloc_pages(struct ion_page_pool *pool)
{struct page *page = alloc_pages(pool->gfp_mask, pool->order);if (!page)return NULL;return page;
}

4 释放内存(system heap)到pool

释放内存,会调用free接口,将ion_buffer中通过sg_table组织起来的page加到pool中。当然如果在分配时,已经指定了flags

static void ion_system_heap_free(struct ion_buffer *buffer)
{struct ion_system_heap *sys_heap = container_of(buffer->heap,struct ion_system_heap,heap);struct sg_table *table = buffer->sg_table;struct scatterlist *sg;int i;/* zero the buffer before goto page pool */if (!(buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE))ion_heap_buffer_zero(buffer);//遍历ion_buffer中通过sg_table组织的page,并将这些page返回到pool中,并不直接给buddy。for_each_sg(table->sgl, sg, table->nents, i)free_buffer_page(sys_heap, buffer, sg_page(sg));sg_free_table(table);kfree(table);
}

4.1 free_buffer_page()

通过是否设置 ION_PRIV_FLAG_SHRINKER_FREE 标志来确认释放时直接给 buddy 还是先放到pool 中

static void free_buffer_page(struct ion_system_heap *heap,struct ion_buffer *buffer, struct page *page)
{struct ion_page_pool *pool;unsigned int order = compound_order(page);bool cached = ion_buffer_cached(buffer);/* go to system *///如果指定了ION_PRIV_FLAG_SHRINKER_FREE标志,则直接返还给buddyif (buffer->private_flags & ION_PRIV_FLAG_SHRINKER_FREE) {__free_pages(page, order);return;}//一般来说都是返还给pool中,直到系统内存不足时,//kswapd回收内存调用shrink回调,将pool中的内存回收到buddy中if (!cached)pool = heap->uncached_pools[order_to_index(order)];elsepool = heap->cached_pools[order_to_index(order)];//返还到pool中ion_page_pool_free(pool, page);
}

4.2 返还到pool中

ion_page_pool_free()->ion_page_pool_add()函数,返还到pool中。

  • 返还给pool,也是先确定具体返还到的order、high或low
  • 返还给pool后,最终要回到buddy中,需要通过系统回收接口 shrink 进行主动回收
static int ion_page_pool_add(struct ion_page_pool *pool, struct page *page)
{mutex_lock(&pool->mutex);//判断page是否为highmem中分配的if (PageHighMem(page)) {list_add_tail(&page->lru, &pool->high_items);pool->high_count++;} else {list_add_tail(&page->lru, &pool->low_items);pool->low_count++;}mutex_unlock(&pool->mutex);return 0;
}

4.3  释放内存到 buddy

  • 已经在pool中的page,通过系统内存回收 shrink 机制回到 buddy
  • ion_system_heap_shrink()函数:
static int ion_system_heap_shrink(struct ion_heap *heap, gfp_t gfp_mask,int nr_to_scan)
{struct ion_page_pool *uncached_pool;struct ion_page_pool *cached_pool;struct ion_system_heap *sys_heap;int nr_total = 0;int i, nr_freed;int only_scan = 0;sys_heap = container_of(heap, struct ion_system_heap, heap);if (!nr_to_scan)only_scan = 1;//根据不同的order遍历for (i = 0; i < NUM_ORDERS; i++) {uncached_pool = sys_heap->uncached_pools[i];cached_pool = sys_heap->cached_pools[i];if (only_scan) { //扫描nr_total += ion_page_pool_shrink(uncached_pool,gfp_mask,nr_to_scan);nr_total += ion_page_pool_shrink(cached_pool,gfp_mask,nr_to_scan);} else { //回收nr_freed = ion_page_pool_shrink(uncached_pool,gfp_mask,nr_to_scan);nr_to_scan -= nr_freed;nr_total += nr_freed;if (nr_to_scan <= 0)break;nr_freed = ion_page_pool_shrink(cached_pool,gfp_mask,nr_to_scan);nr_to_scan -= nr_freed;nr_total += nr_freed;if (nr_to_scan <= 0)break;}}return nr_total;
}

4.3.1 ion_page_pool_shrink()

根据传入的pool以及回收的数量,在指定的pool中扫描回收
回收时主要就是将page从pool链表中拿下来,然后调用系统free_pages接口,将page挂到buddy的free_area中。

int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,int nr_to_scan)
{int freed = 0;bool high;if (current_is_kswapd())high = true;elsehigh = !!(gfp_mask & __GFP_HIGHMEM);//只扫描,返回page数量if (nr_to_scan == 0)return ion_page_pool_total(pool, high);//回收//先将low的内存进行回收,如果没有low就从high中回收//回收的内存达到要求的nr_to_scan后停止,或者把pool中的全部回收完了也停止while (freed < nr_to_scan) {struct page *page;mutex_lock(&pool->mutex);if (pool->low_count) {page = ion_page_pool_remove(pool, false);} else if (high && pool->high_count) {page = ion_page_pool_remove(pool, true);} else {mutex_unlock(&pool->mutex);break;}mutex_unlock(&pool->mutex);ion_page_pool_free_pages(pool, page);freed += (1 << pool->order);}//返回回收的数量return freed;
}

5 小结

最后,system heap的大致分配流程到此,其中我觉得比较重要的是,ion分配的页已经不在buddy,而是由自己来做管理。同时里面涉及到通过sg_table来管理离散的物理内存,并且最终ion_alloc函数中将ion_buffer转换成dma_buf,同时使用dma_buf的fd作为返回给用户的文件句柄。

linux内存管理之 ION 内存管理器浅析Ⅰ(system heap)相关推荐

  1. linux内存管理之 ION 内存管理器浅析Ⅱ(system contig heap)

    目录 1 system contig heap 与 system heap 2 system contig heap创建 3 system contig heap内存分配 4 system conti ...

  2. android内存地址分配,Android ION内存分配

    ION设计的目标 为了避免内存碎片化,或者者为少量有着特殊内存需求的硬件,比方GPUs.display controller以及camera等,在系统启动的时候,会为他们预留少量memory pool ...

  3. android ion内存统计,android ion 内存泄漏排查

    1.查看各个进程的ION :/sys/kernel/debug/ion/heaps # cat system-heap cat system-heap client              pid ...

  4. android之ION内存管理器(1)-- 简介

    by JHJ(jianghuijun211@gmail.com) 为什么需要ION 回顾2011年末[2],LWN审查了android kernel patch[3],以期望将这些patch合并到ke ...

  5. Linux驱动学习--android中的内存管理机制ION(一)--简单介绍

    目录 一.引言 二.ION的介绍及使用 ------> ION介绍 ------> ION的使用 ------> HEAP种类 三.接口分析 ------> 主要数据结构 -- ...

  6. ION内存管理器介绍

    1. ION介绍 ION是google在Android4.0为了解决内存碎片化管理而引入的通用内存管理器,用来支持不同的内存分配机制,如CARVOUT(PMEM),物理连续内存(kmalloc),虚拟 ...

  7. Linux下ion内存,ION内存管理剖析.doc

    1.android之ION内存管理器(1)-- 简介 为什么需要ION 2011年末[2],LWN审查了android kernel patch[3],以期望将这些patch合并到kernel主线中. ...

  8. Linux下ion内存,android内存管理-ION/PMEM

    ION debug ION 在/sys/kernel/debug/ion/ 提供一个debugfs 接口. 每个heap都有自己的debugfs目录,client内存使用状况显示在/sys/kerne ...

  9. 【Linux 内核】Linux 内核体系架构 ( 进程调度 | 内存管理 | 中断管理 | 设备管理 | 文件系统 )

    文章目录 一.进程调度 二.内存管理 三.中断管理 四.设备管理 五.文件系统 一.进程调度 进程调度 : 进程 是 系统中 进行 资源分配 的 基本单位 ; 每个进程 在 运行时 , 都 感觉自己占 ...

最新文章

  1. 使用Bochs调试Linux内核初级入门
  2. u-boot 源码分析讲解
  3. 基于ssm框架和freemarker的商品销售系统
  4. 一个DEMO让你彻底理解线程池
  5. mac笔记本修改 mysql 的密码
  6. LeetCode(976)——三角形的最大周长(JavaScript)
  7. 百度悄然发布Deep Vioce 3
  8. Oracle10g安装在RHEL AS 3
  9. widevine level1测试视频的生成方法
  10. matlab排序函数 下标,[转载]MATLAB中的排序函数
  11. 提取网页内容-Python
  12. 【软件测试基础】控制用例粒度:测试点的组合和拆分
  13. 如何挑选微信第三方开发商
  14. 从五个方面来讲一下平面设计颜色搭配知识——黎乙丙
  15. 出界的路径数----迭代问题与计算思维
  16. ★「C++游戏」BattleOfPhantom:大乱斗游戏升级版
  17. 苹果ANCS协议分析
  18. Material Design 之Style(三)
  19. PHP个人逍遥商城系统源码 可商用版
  20. 英语读书笔记-Book Lovers Day 11

热门文章

  1. 趋势交易法之区间跨度
  2. 01【刘立刚图形学笔记】_图形学整体概述
  3. python根据图片网址下载图片
  4. KBEngine warring项目源码阅读(二) 登录和baseapp的负载均衡
  5. 新媒体运营教程:完整的用户增长5步方案!
  6. 启科量子可视化量子编程——QuComposer
  7. 艺术源于生活而高于生活,IT编程亦是如此
  8. D3.js树图tree 组织机构分布图(基于vue)
  9. MyBatis(九):MyBatis类型处理器(TypeHandler)详解
  10. TFT LCD屏接口芯片-通达LT7381(SSD1963)