ION的设计初衷
Android为了更好的针对移动设备内存的管理,设计出了ION内存管理机制,主要是为了解决以下几个问题:
- 预留大块连续内存,比如camera,display,GPU等模块
- 避免内存随便花
- 用户控件和硬件之间实现”零拷贝”(zero-copy)的内存共享 做Android系统的,特别是跟Display,camera模块相关的
ION的官方介绍和历史由来查看下面的介绍:
https://lwn.net/Articles/480055/
ION的实现
Android系统的ION实现依赖于不同的CPU/GPU硬件,Android提供了ION的框架供CPU厂商(NXP, Qualcomm, MTK, 海思等)在自己的BSP里面实现ION机制。
默认的ION驱动会提供以下三种不同的ION heaps实现:
- ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc_user()
- ION_HEAP_TYPE_SYSTEM_CONFIG: memory allocated via kzalloc
- ION_HEAP_TYPE_CARVEOUT: carveout memory is physically contiguous and set aside at boot
CPU厂商会根据自己CPU的特性实现更多的ION heaps,比如NVIDIA提交了一种ION_HEAP_TYPE_IOMMU的heap。
不管哪一种ION heaps实现,都必须实现如下核心接口
1 | struct ion_heap_ops { |
简单概括一下
allocate()和free()分别用来从heap中分配或者释放一个ion_buffer对象- 连续的物理内存,
phys()用来得到ion_buffer对象的物理内存地址和大小 map_kernel()和unmap_kernel()分别用来把物理内存映射到内核虚拟地址空间map_user()用来把物理内存映射到用户空间,当用户空间的文件描述符被释放的时候会自动取消映射,所以没有提供类似于unmap_user()的函数
用户空间进程使用ION的情形

主要数据结构
在整个Android操作系统中,可以发现,比较好的框架,先去了解数据结构,就能猜到是如何实现的整个架构。
ion_device
ion_device是ION驱动的最基础的一个对象,用来描述一个ion设备,其实在一个Android操作系统中,一般只有一个实例存在,用来统一管理ION Heaps,由CPU厂商自己的代码来创建。
1 | struct ion_device { |
struct ion_device是ION的核心结构体,但是在用户空间是不可见的,是由CPU厂商的代码通过ion_device_create()函数分配、初始化。
- dev成员,是一个
struct miscdevice类型的杂项字符设备,用来连同内核和用户空间,open/ioctl - heaps成员,用来描述所有的
struct ion_heap实例 - clients成员,用来管理所有
struct ion_client
ion_client
struct ion_client是由ion_client_create()创建,是通过struct ion_device来创建的。
1 | struct ion_client { |
- node成员,用于将
struct ion_client实例加入到struct ion_device中ion_device::clients - device成员,指向设备的
ion_device - handles成员,是一个红黑树的根,用来管理它所拥有的handle,即
struct ion_handle实例。一个struct ion_handle实例代表一个buffer。ion_buffer是从struct ion_heap中分配的。
ion_heap
struct ion_heap表示ION中的heap,被ion_device中的ion_device::heaps所管理
1 | struct ion_heap { |
ion_handle
struct ion_handle其实就是buffer,用户永健用它来表示自己的buffer,通过ion_handle_create()函数分配获得实例
1 | struct ion_handle { |
- ref成员,是
struct kref结构体类型,在kernel中被广泛的用来表示引用计数,用来记录handle被引用的次数,当引用计数为0时自动销毁。 - client成员,指向这个handle所属的client
- buffer成员,指向真正的buffer
ion_buffer
这个结构体很重要,通过ION分配的内存就是通过它表示的。
1 | struct ion_buffer { |
- ref成员,是
struct kref结构体实例,维护了本ion_buffer的引用计数。当引用计数为 0 时会释放该 buffer,即struct ion_heap_ops::free会被调用。分配用ION_IOC_ALLOC型 ioctl 系统调用,相应的释放用ION_IOC_FREE型 ioctl 系统调用。 - size 成员,当然是本 buffer 所表示的空间的大小,用字节表示。
- priv_virt 成员,是所分配内存的虚拟地址啦,它常与
struct sg_table,或者封装它的结构,有关。它不是我们在内核中读写时所需的内核虚拟地址啦,内核虚拟地址使用 vaddr 成员来表示的。一般而言,物理内存不连续的,使用本字段;否则使用下面的 priv_phys 字段,如struct ion_heap_ops contig_heap_ops。 - priv_phys 成员,表示所分配的内存的物理地址。它适用于分配的物理内存是连续的 ion heap。这种连续的物理内存:在将其映射到用户空间时,即获取用户空间虚拟地址,可以使用
remap_pfn_range()[memory.c] 这个方便的接口;在将其映射到内核空间时,即获取内核虚拟地址,可以使用vmap()[vmalloc.c] 这个方便的接口。例子详见struct ion_heap_ops contig_heap_ops [exynos_ion.c]。priv_virt 成员和 priv_phys 成员组成了一个联合体,其实都表示地址,只不过不同的场景下具体用的不一样而已。 - kmap_cnt 成员,记录本 buffer 被映射到内核空间的次数。
- vaddr 成员,是本 buffer 对应的内核虚拟地址。当 kmap_cnt 不为 0 时有效。可以通过
ion_map_kernel()[ion.c] 来获取本 buffer 对应的内核虚拟地址。ion_map_kernel()[ion.c] 实际上调用的是相应struct ion_heap_ops::map_kernel回调函数获取相应的虚拟地址的。 - dmap_cnt 成员,记录本 buffer 被 mapped for DMA 的次数。
- sg_table 成员,是
struct sg_table结构体类型的指针。本字段与 DMA 操作有关,而且仅仅在 dmap_cnt 成员变量不为 0 时是有效的。可以通过ion_buffer_create()[ion.c] 来初始化本成员变量,该函数实际上是调用相应 ion_heap 所属的struct ion_heap_ops::map_dma回调函数获取本字段的值的。 - dirty 成员,表示 bitmask。即以位图表示本 buffer 的哪一个 page 是 dirty 的,即不能直接用于 DMA。dirty 表示 DMA 的不一致性,即 CPU 缓存中的内容与内存中的实际内容不一样。

用一张图来展示这些数据结构之间的基本关系。后面一起来看看如何在用户空间和内核空间来使用ION 共享内存机制。