The Android ION memory allocator

英文原文

ION heaps

ION设计的目标

为了避免内存碎片化,或者为一些有着特殊内存需求的硬件,比如GPUs、display controller以及camera等,在系统启动的时候,会为他们预留一些memory pools,这些memory pools就由ION来管理。通过ION就可以在硬件以及user space之间实现zero-copy的内存share。

ION的实现

ION通过ION heaps来展示presents它对应的memory pools。不同的Android硬件可能会要求不同的ION heaps实现,默认的ION驱动会提供如下三种不同的ION heaps实现:

  1. ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc_user()
  2. ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kzalloc

    . ION_HEAP_TYPE_CARVEOUT: carveout memory is physically contiguous and set aside at boot.

    开发者可以自己实现更多的ION heaps。比如NVIDIA就提交了一种ION_HEAP_TYPE_IOMMU的heap,这种heap带有IOMMU功能。

    不管哪一种ION heaps实现,他们都必须实现如下接口:
   struct ion_heap_ops {
int (*allocate) (struct ion_heap *heap,
struct ion_buffer *buffer, unsigned long len,
unsigned long align, unsigned long flags);
void (*free) (struct ion_buffer *buffer);
int (*phys) (struct ion_heap *heap, struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len);
struct scatterlist *(*map_dma) (struct ion_heap *heap,
struct ion_buffer *buffer);
void (*unmap_dma) (struct ion_heap *heap,
struct ion_buffer *buffer);
void * (*map_kernel) (struct ion_heap *heap,
struct ion_buffer *buffer);
void (*unmap_kernel) (struct ion_heap *heap,
struct ion_buffer *buffer);
int (*map_user) (struct ion_heap *heap, struct ion_buffer *buffer,
struct vm_area_struct *vma);
};

简单来说,接口的各个函数功能如下:

  • allocate()free()分别用来从heap中分配或者释放一个ion_buffer对象
  • 对于物理连续的内存,phys()用来得到ion_buffer对象的物理内存地址及其大小。如果heap没有提供物理连续的内存,那么它也可以不用提供这个接口。其中,ion_phys_addr_t将来会被定义在/include/linux/types.h中的phys_addr_t替代。
  • map_dma()unmap_dma()分别来用使ion_buffer对象为DMA(Direct Memory Access,直接内存存取。顾名思义,不占用cpu资源,从一个硬件存储区域把一部分连续的数据复制到另一个硬件存储区域)做好准备或者取消做好准备
  • map_kernel()unmap_kernel()分别用来把physical memory映射(map)到内核虚拟地址空间(kernel virtual address space)或者取消映射
  • map_user()用来把physical memory映射(map)到用户内存空间(user space)。为什么没有对应的unmap_user()呢?因为,这个映射用一个file descriptor来表示,当这个file descriptor关闭的时候,这个映射关系就自动取消了。

在user space使用ION

使用场景

典型的,在用户空间使用的设备访问库(user space device access libraries)一般使用ION来分配大块连续的media buffers。比如,still camera library分配一个capture buffer来供camera device使用。当这个buffer填满video data的时候,这个library就能把这块buffer传递给kernel,然后让JPEG硬编码模块来处理。

具体使用细节

在user space 的C/C++程序能够能够分配ION内存之前,它必须获得访问/dev/ion的权限。通过调用open("/dev/ion", O_RDONLY)就可获得一个以handle形式返回的file descriptor,这个file descriptor用来代表一个ION client。注意,虽然传给open一个O_RDONLY参数,但是你仍然可对这块memory进行写操作。在一个user process中最多有一个client。当有了一个client之后,就可以开始分配ION内存。为了分配内存,client必须填满下面的ion_allocation_data结构,handle除外,因为它是output参数。其他三个参数分别指明内存的大小、对齐方式以及flags。flags是一个bit mask,用来说明可以从哪些heaps中分配想要的内存。其决定顺序由系统启动时,通过ion_device_add_heap()添加的heap顺来决定。比如,ION_HEAP_TYPE_CARVEOUT是在ION_HEAP_TYPE_CONTIG之前被add的,那么如果flags = ION_HEAP_TYPE_CONTIG | ION_HEAP_TYPE_CARVEOUT,那么就是先尝试分配ION_HEAP_TYPE_CARVEOUT类型的heap,如果不行,再尝试分配ION_HEAP_TYPE_CONTIG类型的heap。()

   struct ion_allocation_data {
size_t len;
size_t align;
unsigned int flags;
struct ion_handle *handle;
}

user space通过ioctl()系统接口来与ION交互。在client填充ion_allocatoin_data结构之后,就可以通过调用int ioctl(int client_fd, ION_IOC_ALLOC, struct ion_allocation_data *allocation_data)来allocate a buffer。这个调用介绍之后,分配的buffer会通过ion_allocatoin_datahandle来返回,但是CPU不可以访问这个buffer。这个handle只可以通过调用int ioctl(int client_fd, ION_IOC_SHARE, struct ion_fd_data *fd_data);来获得一个用来share的file descriptor。这里,client_fd参数是前面通过open获得的一个对应/dev/ion file descriptor,fd_data是如下的数据结构,其handle对应ion_allocation_data::handle,是input参数;fd则是output参数,可以用来share。

当一个user process中的client分享(share)了这个fd之后,在其他user process中(当然,也可share给创建这个fd的client自己),为了获得这个shared buffer,先必须通过调用open("/dev/ion", O_RDONLY)获得一个client。(注:ION通过线程的PID来track各个client, 尤其是process中的"group leader"线程的PID。在相同的process中重复调用open("/dev/ion", O_RDONLY)只会获得指向kernel同一个client的another file descriptor)。获得client之后,然后再通过mmap()函数来把这个fd映射到address space of process(mmap函数参考1参考2)。如果要释放这个fd对应的buffer,在调用mmap()的process中,先要通过munmap()来取消mmap()的效果。然后在之前share这个fd的client中,需要通过int ioctl(int client_fd, ION_IOC_FREE, struct ion_handle_data *handle_data);来关闭这个fd对应的file descriptor。其中,ion_handle_data表示前面通过ION_IOC_ALLOC命令获得的handle,其定义如下:

     struct ion_handle_data {
struct ion_handle *handle;
}

这个ION_IOC_FREE命令会导致对应的handle的计数减1。当handle计数为0的时候,其指向的ion_handle对象就会被销毁,并且相关的ION bookkeeping数据结构也会更新。

Demo

在这个Demo中,fd在同一个client中被share使用:来源

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h> #include "/home/developer/kernel3.4/goldfish/include/linux/ion.h" void main()
{
struct ion_fd_data fd_data;
struct ion_allocation_data ionAllocData;
ionAllocData.len=0x1000;
ionAllocData.align = 0;
ionAllocData.flags = ION_HEAP_TYPE_SYSTEM; int fd=open("/dev/ion",O_RDWR);
ioctl(fd,ION_IOC_ALLOC, &ionAllocData);
fd_data.handle = ionAllocData.handle;
ioctl(fd,ION_IOC_SHARE,&fd_data);
int *p = mmap(0,0x1000,PROT_READ|PROT_WRITE,MAP_SHARED,fd_data.fd,0);
p[0]=99;
perror("test");
printf("hello all %d\n",p[0]);
}

在kernel中share ION buffer

在kernel中支持multiple clients,每一个使用ION功能的driver都可以在kernel中对应一个client。一个kernel driver通过调用struct ion_client *ion_client_create(struct ion_device *dev, unsigned int heap_mask, const char *debug_name)来获得一个ION client handle(注意,前面在user space中通过open("/dev/ion", O_RDONLY)返回的client是int类型)。dev参数是一个和/dev/ion相关的global ION deviceheap_mask参数和之前提到的ion_allocation_dataflags成员一样的含义。

当在user space中通过ION_IOC_SHARE命令得到一个buffer的file descriptor并把它传递给kernel之后,kernel driver通过调用struct ion_handle *ion_import_fd(struct ion_client *client, int fd_from_user);来把这个fd变成一个ion_handle对象,这个对象就是这个driver中对相应的buffer一个client-local reference。ion_import_fd方法会根据这个buffer的物理地址来查找:在本client中是否已经obtained一个对应此buffer的ion_handle,如果是的话,那么就可以简单的增加这个ion_handle的引用计数即可。

有些硬件只能通过physical addresses来操作physically-contiguous buffers,那么,这些对应的drivers就需要通过调用int ion_phys(struct ion_client *client, struct ion_handle *handle, ion_phys_addr_t *addr, size_t *len)来把ion_handle转变成一个physical buffer。当然,如果这个buffer不是physically contiguous,那么这个调用就会失败。

当处理一个来自client的调用时,ION会validates 输入的 file descriptor, client and handle arguments。比如ION会确保 file descriptor是由ION_IOC_SHARE命令创建的;比如当ion_phys()调用时,ION会检测这个buffer是否在这个client对应有访问权限list中,如果不是,那么就会返回错误。这样的验证机制能够减少可能的unwanted accesses以及疏忽的内存泄露。

ION通过debugfs提供可视化的debug,它通过在/sys/kernel/debug/ion下面,使用stored files来记录相应的heaps和clients,并使用symbolic names或者PIDs来标志。

比较ION和DMABUF

本节部分翻译。

  • IONDMABUF都是通过传递一个匿名file descriptor对象,给其他client一个基于引用计数的访问权限,从而达到分享内存的目的。
  • ION通过一个可分享和追踪的方式从预留的memory pool中分配内存。
  • DMABUF更多的专注于buffer导入、导出以及同步的方式来实现在NON-ARM架构上的buffer的分享。
  • ION目前只支持Android kernel
  • ION所有的user-space program都可以通过/dev/ion接口来分配ION内存。但是在Android会通过验证user和group IDs的方式来阻止对ION的非授权访问。

参考

The Android ION memory allocator

Good PDF

Integrating the ION memory allocator

ION.C

ION.H

DEMO

CSDN

Android ION内存分配的更多相关文章

  1. Android的内存分配与回收

    想写一篇关于android的内存分配和回收文章的想法来源于追查一个魅族手机图片滑动卡顿问题,我们想了很多办法还是没有避免他不停的GC,所以就打算详细的看看内存分配和GC的原理,为什么会不断的GC,GC ...

  2. 你不知道的Eclipse用法:使用Allocation tracker跟踪Android应用内存分配

    Android Tools中的DDMS带有一个很不错的跟踪内存分配的工具Allocation tracker.通过Alloction tracker,不仅知道分配了哪类对象,还可以知道在哪个线程.哪个 ...

  3. Android内存管理(5)*官方教程:Logcat内存日志各字段含义,查看当前内存快照,跟踪记录内存分配,用adb查看内存情况时各行列的含义,捕获内存快照的3种方法,如何让程序暴漏内存泄漏的方法

    Investigating Your RAM Usage In this document Interpreting Log Messages                 内存分析日志中各消息的含 ...

  4. android bitmap的内存分配和优化

    首先Bitmap在Android虚拟机中的内存分配,在Google的网站上给出了下面的一段话 大致的意思也就是说,在Android3.0之前,Bitmap的内存分配分为两部分,一部分是分配在Dalvi ...

  5. 【朝花夕拾】Android性能篇之(二)Java内存分配

    前言        在内存方面,相比于C/C++程序员,咱们java系程序员算是比较幸运的,因为对于内存的分配和回收,都交给了JVM来处理了,而不需要手动在代码中去完成.有了虚拟机内存管理机制,也就不 ...

  6. Android内存优化1 了解java内存分配 1

    开篇废话 今天我们一起来学习JVM的内存分配,主要目的是为我们Android内存优化打下基础. 一直在想以什么样的方式来呈现这个知识点才能让我们易于理解,最终决定使用方法为:图解+源代码分析. 欢迎访 ...

  7. Android性能调优篇之探索JVM内存分配

    开篇废话 今天我们一起来学习JVM的内存分配,主要目的是为我们Android内存优化打下基础. 一直在想以什么样的方式来呈现这个知识点才能让我们易于理解,最终决定使用方法为:图解+源代码分析. 欢迎访 ...

  8. Android O Bitmap 内存分配

      我们知道,一般认为在Android进程的内存模型中,heap分为两部分,一部分是native heap,一部分是Dalvik heap(实际上也是native heap的一部分).   Andro ...

  9. Android系统Bitmap内存分配原理与优化

    一.前言 笔者最近致力于vivo游戏中心稳定性维护,在分析线上异常时,发现有相当一部分是由OutOfMemory引起.谈及OOM,我们一般都会想到内存泄漏,其实,往往还有另外一个因素--图片,如果对图 ...

随机推荐

  1. [Swift]LeetCode54. 螺旋矩阵 | Spiral Matrix

    Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral or ...

  2. [Swift]LeetCode187. 重复的DNA序列 | Repeated DNA Sequences

    All DNA is composed of a series of nucleotides abbreviated as A, C, G, and T, for example: "ACG ...

  3. 开发常用的 Android 函数库

    第三方函数库(译者注:包括第三方提供的 SDK,开源函数库)以惊人的方式助力着 Android 开发,借助这些其他开发人员辛勤工作的成果,我们开发起来更轻松和快捷.目前存在成千上万的函数库,如何选择正 ...

  4. Bootstrap 字体与图标

    常用网站:icons/Font awesome/bookstrap 控制图标的大小使用 fa-lg (33%递增).fa-2x. fa-3x.fa-4x,或者 fa-5x 类 来放大图标.旋转动画 & ...

  5. OAuth 2.0 授权码请求

    关于OAuth 2.0,请参见下面这两篇文章(墙裂推荐): <OAuth 2.0> <Spring Security OAuth 2.0> 纸上得来终觉浅,绝知此事要躬行.理论 ...

  6. asp.net core系列 31 EF管理数据库架构--必备知识 反向工程

    一.   反向工程 反向工程是基于数据库架构,生成的实体类和DbContext类代码的过程,对于Visual Studio开发,建议使用PMC.对于其他开发环境,请选择.NET Core CLI工具( ...

  7. 【朝花夕拾】Lint使用篇

    工作中Lint工具使用实录及整理       AndroidStudio内置的Lint工具,对app中的代码规范带来了极大的方便.对内存泄漏.代码冗余.代码安全.国际化.代码规范等很多方面都能检测,是 ...

  8. Java开发需掌握的常用Linux命令(持续更新)

    linux命令是对Linux系统进行管理的命令.对于Linux系统来说,无论是中央处理器.内存.磁盘驱动器.键盘.鼠标,还是用户等都是文件,Linux系统管理的命令是它正常运行的核心,与之前的DOS命 ...

  9. 带着萌新看springboot源码04

    继续开头说些废话,我也不知道什么鬼,每次写着写着经常会写到其他地方去了,太容易分神了. 这次说一下springboot对于springmvc的大概整个流程,以请求动态网页为例 . 1.梳理一下spri ...

  10. Spring Boot分布式系统实践【2】-框架搭建

    前言 技术选型已经做完,那就来搭建框架了. 首先基于mvc思想,设计这套框架也是基于此,也会设计Dao层.Service层.Controller层.视图层等,同时也要考虑到dubbo的调用原理.   ...