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. 深入理解Spring Redis的使用 (三)、使用RedisTemplate的操作类访问Redis

    上一篇说了RedisTemplate对注解事务的支持,以及提供的序列化器. 事务需要开启enableTransactionSupport,然后使用@transactional注解,里面直接通过回调的c ...

  2. [Swift]LeetCode11. 盛最多水的容器 | Container With Most Water

    Given n non-negative integers a1, a2, ..., an , where each represents a point at coordinate (i, ai). ...

  3. [Swift]LeetCode419. 甲板上的战舰 | Battleships in a Board

    Given an 2D board, count how many battleships are in it. The battleships are represented with 'X's, ...

  4. git push每次提交都要输入用户名的解决方案

    前言 最近把代码从csdn转移到gitee后,每一次git push都需要输入用户名和密码,比较麻烦,那有没有一个办法可以去掉这个步骤呢? 原因 每次都需要输入用户名和密码是因为你采用的是https方 ...

  5. Ubuntu12.04下安装NS3.25

    实验室项目的需求,要使用到NS3做仿真,使用实验室的电脑,Ubuntu12.04,版本比较低,建议大家使用16.04,这样安装依赖文件时不会出现版本过低的问题 (没管最后也安装成功了).下面就安装步骤 ...

  6. scala调用Linux命令行

    在 scala 里面存在 调用 Linux 命令行的函数: import scala.sys.process._ 执行的方法也不难: import scala.sys.process._ /** * ...

  7. 死磕 java集合之ArrayList源码分析

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. 简介 ArrayList是一种以数组实现的List,与数组相比,它具有动态扩展的能力,因此也可 ...

  8. 使用Spring Cloud搭建服务注册中心

    我们在之前的博客中已经介绍过阿里的分布式服务框架dubbo[Linux上安装Zookeeper以及一些注意事项][一个简单的案例带你入门Dubbo分布式框架],但是小伙伴们应该也看到了,阿里的dubb ...

  9. iterm2 快捷键(转载)

    Mac 下 iterm2 的快捷键,转自:https://github.com/sumiaowen/iterm2-shortcuts iterm2-shortcuts(iterm 2 快捷键) 标签 ...

  10. CentOS 6下安装Python2.7

    安装方法 如果在CentOS上自己编译安装过python2.7,使用过程中会发现有些标准库没有安装之类的问题. 逛别人博客的时候发现,一个便捷的方法:使用RHSCL的全称是Red Hat Softwa ...