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 共享内存机制。