编辑
2026-04-13
记录知识
0

目录

Linux Kernel ION 内存分配器深度剖析
什么是 ION
整体架构
核心数据结构
ion_device — 全局单例
ion_buffer — 一次分配的产物
ion_heap — 堆抽象
ionheapops — C 语言多态
ionpagepool — 页面缓存池
用户态接口
IONIOCALLOC — 分配内存
IONIOCHEAP_QUERY — 查询可用 heap
分配流程详解
dma-buf 集成
attach / detach — 设备附着
begin/endcpuaccess — Cache 一致性
mmap — 用户态映射
三种 Heap 实现
System Heap — 多阶贪心分配 + 页面缓存池
分配策略
GFP 标志策略
Page Pool
安全措施
System Contig Heap — 物理连续分配
CMA Heap — 基于 CMA 区域的连续分配
三种 Heap 对比
Deferred Free 与 Shrinker 机制
Deferred Free — 延迟释放
Shrinker — 与内核内存回收框架集成
通用辅助函数(ion_heap.c)
内核映射
用户态映射
Buffer 清零
初始化顺序
debugfs 接口
设计亮点总结
版本演变
参考资料

Linux Kernel ION 内存分配器深度剖析

基于 Linux Kernel v5.4.123 源码分析,ION 位于 drivers/staging/android/ion/

什么是 ION

ION 是 Android 引入 Linux 内核的统一内存分配框架,解决一个核心问题:多个硬件设备(GPU、Camera、Display、Video 编解码器)如何零拷贝共享同一块物理内存。

在没有 ION 之前,每个设备驱动各自管理内存,跨设备传递数据需要多次拷贝。ION 提供统一的用户态接口,分配出的内存通过 Linux dma-buf 框架导出为文件描述符(fd),任何设备驱动都可以通过这个 fd 直接访问底层物理页面。

用户态程序 │ │ open("/dev/ion") │ ioctl(ION_IOC_ALLOC) → 得到 dma-buf fd │ ├── mmap(fd) ─────────────── CPU 直接读写 │ ├── 传 fd 给 GPU 驱动 ────── GPU DMA 访问(零拷贝) │ ├── 传 fd 给 Camera 驱动 ── Camera DMA 写入(零拷贝) │ └── 传 fd 给 Display 驱动 ─ Display DMA 读取(零拷贝)

整体架构

ION 采用分层 + 策略模式设计,共 6 个源文件,约 1600 行代码:

┌─────────────────────────────────────────────────────────┐ │ 用户空间 │ │ open("/dev/ion") → ioctl(ION_IOC_ALLOC) │ └────────────────────────┬────────────────────────────────┘ │ ioctl ┌────────────────────────▼────────────────────────────────┐ │ ion.c — 核心框架层(668 行) │ │ ┌──────────┐ ┌───────────┐ ┌──────────────────────┐ │ │ │/dev/ion │ │ioctl 分发 │ │dma_buf_ops 实现 │ │ │ │misc 设备 │ │ALLOC/QUERY│ │attach/map/mmap/sync │ │ │ └──────────┘ └─────┬─────┘ └──────────────────────┘ │ │ │ │ │ ion_heap_ops(虚函数表) │ │ ┌───────────────┼───────────────┐ │ │ ▼ ▼ ▼ │ │ ┌──────────┐ ┌────────────┐ ┌───────────┐ │ │ │System │ │System │ │CMA Heap │ │ │ │Heap │ │Contig Heap │ │ │ │ │ │(377 行) │ │(同文件) │ │(138 行) │ │ │ └────┬─────┘ └────────────┘ └───────────┘ │ │ │ │ │ ┌────▼──────────┐ ┌──────────────────┐ │ │ │ion_page_pool │ │ion_heap.c │ │ │ │页面缓存池 │ │通用辅助函数 │ │ │ │(155 行) │ │(315 行) │ │ │ └───────────────┘ └──────────────────┘ │ └──────────────────────────────────────────────────────────┘
文件职责
ion.c核心框架:设备注册、ioctl 分发、dma-buf 集成、buffer 生命周期
ion.h所有内部数据结构和接口声明
uapi/ion.h用户态 API:ioctl 命令、堆类型枚举、分配参数结构体
ion_heap.c堆通用操作:内核/用户映射、deferred free、shrinker
ion_page_pool.c页面缓存池:避免频繁进出 buddy allocator
ion_system_heap.cSystem Heap + System Contig Heap 实现
ion_cma_heap.cCMA Heap 实现

核心数据结构

ion_device — 全局单例

c
struct ion_device { struct miscdevice dev; // /dev/ion 字符设备 struct rw_semaphore lock; // 保护 heaps 链表 struct plist_head heaps; // 优先级排序链表,所有已注册的 heap struct dentry *debug_root; // debugfs 根目录 /sys/kernel/debug/ion/ int heap_cnt; // 已注册 heap 数量 };

全局唯一实例 internal_dev,内核启动时通过 subsys_initcall(level 4)创建。使用 plist(优先级链表)管理 heap,保证分配时按优先级遍历。

ion_buffer — 一次分配的产物

c
struct ion_buffer { struct list_head list; // 挂到 deferred free list struct ion_device *dev; struct ion_heap *heap; // 来自哪个 heap unsigned long flags; // 用户标志(ION_FLAG_CACHED) unsigned long private_flags; // 内部标志(ION_PRIV_FLAG_SHRINKER_FREE) size_t size; void *priv_virt; // heap 私有数据 struct mutex lock; int kmap_cnt; // 内核映射引用计数 void *vaddr; // 内核虚拟地址 struct sg_table *sg_table; // scatter-gather 表 —— 核心数据 struct list_head attachments; // 所有 attach 的设备列表 };

sg_table 是 ION buffer 的核心表示。 它描述了 buffer 对应的物理页面集合(可能不连续)。所有后续操作(DMA 映射、用户态 mmap、内核映射)都基于 sg_table 完成。每种 heap 在 allocate必须填充 buffer->sg_table

ion_heap — 堆抽象

c
struct ion_heap { struct plist_node node; // 优先级链表节点 struct ion_device *dev; enum ion_heap_type type; // SYSTEM / SYSTEM_CONTIG / DMA struct ion_heap_ops *ops; // 虚函数表 —— 策略模式的核心 unsigned long flags; // ION_HEAP_FLAG_DEFER_FREE unsigned int id; // 自动递增,也决定优先级 /* deferred free */ struct shrinker shrinker; // 注册到内核回收框架 struct list_head free_list; // 待回收 buffer 队列 size_t free_list_size; spinlock_t free_lock; wait_queue_head_t waitqueue; struct task_struct *task; // 后台回收线程 /* 统计 */ u64 num_of_buffers; u64 num_of_alloc_bytes; u64 alloc_bytes_wm; // 分配高水位标记 spinlock_t stat_lock; };

ion_heap_ops — C 语言多态

c
struct ion_heap_ops { int (*allocate)(heap, buffer, len, flags); // 分配物理内存,填充 sg_table void (*free)(buffer); // 释放物理内存 void*(*map_kernel)(heap, buffer); // 映射到内核虚拟地址 void (*unmap_kernel)(heap, buffer); // 解除内核映射 int (*map_user)(heap, buffer, vma); // 映射到用户虚拟地址 int (*shrink)(heap, gfp_mask, nr_to_scan); // 可选:回收缓存页面 };

不同 heap 类型实现不同的 ops,核心框架通过函数指针调用,实现接口统一、实现可替换

ion_page_pool — 页面缓存池

c
struct ion_page_pool { int high_count; // highmem 缓存页数 int low_count; // lowmem 缓存页数 struct list_head high_items; // highmem 页面链表 struct list_head low_items; // lowmem 页面链表 struct mutex mutex; gfp_t gfp_mask; // 从系统分配新页时的 GFP 标志 unsigned int order; // 页面阶数(0, 4, 8) };

用户态接口

v5.4 的 ION 只暴露两个 ioctl,相比早期版本大幅简化(移除了 handle、share、import 等概念):

ION_IOC_ALLOC — 分配内存

c
struct ion_allocation_data { __u64 len; // 请求大小(字节) __u32 heap_id_mask; // 位掩码,指定从哪些 heap 分配 __u32 flags; // ION_FLAG_CACHED 等 __u32 fd; // 输出:dma-buf 文件描述符 __u32 unused; };

用户态示例:

c
int ion_fd = open("/dev/ion", O_RDONLY); struct ion_allocation_data alloc = { .len = 1024 * 1024, // 1MB .heap_id_mask = (1 << system_heap_id), // 从 system heap 分配 .flags = ION_FLAG_CACHED, // 可缓存 }; ioctl(ion_fd, ION_IOC_ALLOC, &alloc); // alloc.fd 现在是一个 dma-buf fd // CPU 访问 void *ptr = mmap(NULL, alloc.len, PROT_READ | PROT_WRITE, MAP_SHARED, alloc.fd, 0); // 用完后 munmap(ptr, alloc.len); close(alloc.fd); // 引用计数归零时自动释放底层内存

ION_IOC_HEAP_QUERY — 查询可用 heap

因为 heap id 是动态分配的,用户态需要先查询再分配:

c
// 第一步:获取 heap 数量 struct ion_heap_query query = { .cnt = 0, .heaps = 0 }; ioctl(ion_fd, ION_IOC_HEAP_QUERY, &query); // 第二步:获取 heap 详情 struct ion_heap_data heaps[query.cnt]; query.heaps = (uintptr_t)heaps; ioctl(ion_fd, ION_IOC_HEAP_QUERY, &query); // 第三步:按 type 或 name 找到目标 heap for (int i = 0; i < query.cnt; i++) { printf("heap[%d]: name=%s type=%u id=%u\n", i, heaps[i].name, heaps[i].type, heaps[i].heap_id); }

分配流程详解

ion_alloc(len, heap_id_mask, flags) │ ├── 1. len = PAGE_ALIGN(len) // 对齐到页边界 │ ├── 2. 遍历 heaps(plist 按优先级从高到低) │ │ │ └── if ((1 << heap->id) & heap_id_mask) // mask 匹配 │ │ │ └── ion_buffer_create(heap, dev, len, flags) │ │ │ ├── kzalloc(buffer) // 分配元数据 │ ├── heap->ops->allocate() // 具体 heap 分配物理内存 │ │ │ │ │ └── 失败且有 DEFER_FREE? │ │ ├── drain freelist // 回收延迟释放的内存 │ │ └── 重试 allocate │ │ │ ├── 校验 buffer->sg_table != NULL │ └── 更新统计(num_of_buffers, alloc_bytes 等) │ ├── 3. dma_buf_export(buffer) // 包装为 dma-buf 对象 │ └── 4. dma_buf_fd(dmabuf, O_CLOEXEC) // 创建 fd 返回用户态

分配失败重试机制: 当 heap 设置了 ION_HEAP_FLAG_DEFER_FREE 时,首次分配失败不会立即报错,而是先 drain freelist(释放延迟回收队列中的 buffer),然后重试。这显著提高了内存紧张时的分配成功率。


dma-buf 集成

ION 分配出的每个 buffer 都被包装为 dma-buf,通过 dma_buf_ops 实现与内核 DMA 框架的对接。这是零拷贝跨设备共享的基础。

c
static const struct dma_buf_ops dma_buf_ops = { .attach = ion_dma_buf_attach, // 设备注册 .detach = ion_dma_buf_detatch, // 设备注销 .map_dma_buf = ion_map_dma_buf, // DMA 地址映射 .unmap_dma_buf = ion_unmap_dma_buf, // DMA 地址解映射 .mmap = ion_mmap, // 用户态 mmap .release = ion_dma_buf_release, // buffer 释放 .begin_cpu_access = ion_dma_buf_begin_cpu_access, // CPU 访问前 cache 同步 .end_cpu_access = ion_dma_buf_end_cpu_access, // CPU 访问后 cache 同步 .map = ion_dma_buf_kmap, // 内核态按页映射 .unmap = ion_dma_buf_kunmap, // 内核态解映射(空操作) };

attach / detach — 设备附着

每个设备 attach 时,ION 会 dup 一份 sg_tabledup_sg_table),因为不同设备经过 IOMMU 映射后看到的 DMA 地址不同:

同一块物理内存 (phys: 0x8000_0000) │ ├── GPU (有 IOMMU) → dma_address = 0x0010_0000 ├── Display (无 IOMMU) → dma_address = 0x8000_0000(直通) └── Camera (另一 IOMMU) → dma_address = 0xFF00_0000

物理页面共享(零拷贝),但 sg_table 元数据各自独立。

begin/end_cpu_access — Cache 一致性

在有硬件 cache 的系统上(ARM),CPU 写入的数据可能停留在 cache 中,设备通过 DMA 读到的是过时数据。ION 通过配对的 begin/end 回调解决:

begin_cpu_access(DMA_TO_DEVICE) → map buffer 到内核(kmap_get) → dma_sync_sg_for_cpu() // invalidate cache,看到设备最新写入 │ │ CPU 在此区间安全地读写 buffer │ end_cpu_access(DMA_TO_DEVICE) → dma_sync_sg_for_device() // flush cache,让设备看到 CPU 写入 → unmap(kmap_put)

mmap — 用户态映射

c
static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) { if (!(buffer->flags & ION_FLAG_CACHED)) vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); ret = buffer->heap->ops->map_user(buffer->heap, buffer, vma); }

非 cached buffer 使用 write-combine 映射,避免 cache 一致性问题;cached buffer 使用普通映射,性能更好但需要显式 sync。


三种 Heap 实现

System Heap — 多阶贪心分配 + 页面缓存池

最复杂、最重要的 heap,Android 上最常用。

分配策略

使用三个阶数的复合页(compound page),贪心选择最大可用阶数:

c
static const unsigned int orders[] = {8, 4, 0}; // order 8 = 256 页 = 1MB 大页 // order 4 = 16 页 = 64KB // order 0 = 1 页 = 4KB
分配 3MB 的过程: size_remaining = 3MB → 尝试 order 8 (1MB) ✓ → 剩余 2MB, max_order=8 → 尝试 order 8 (1MB) ✓ → 剩余 1MB, max_order=8 → 尝试 order 8 (1MB) ✓ → 剩余 0, 完成 分配 100KB 的过程: size_remaining = 100KB (对齐后 100KB) → 尝试 order 8 (1MB) 跳过(太大) → 尝试 order 4 (64KB) ✓ → 剩余 36KB, max_order=4 → 尝试 order 4 (64KB) 跳过(太大) → 尝试 order 0 (4KB) ✓ → 剩余 32KB, max_order=0 → ... 逐页分配,共 9 次

结果是一个 sg_table,包含多个不同大小的 sg entry。内存不要求物理连续,通过 scatter-gather DMA 访问。

GFP 标志策略

c
// 高阶分配(order > 4):快速失败,不回收不等待 static gfp_t high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN | __GFP_NORETRY) & ~__GFP_RECLAIM; // 低阶分配(order <= 4):允许回收等待,保证成功 static gfp_t low_order_gfp_flags = GFP_HIGHUSER | __GFP_ZERO;

高阶大页分配失败很正常(内存碎片),快速 fallback 到低阶即可。低阶分配必须成功,允许内核做内存回收。

Page Pool

每个 order 对应一个缓存池,释放的页面放回 pool 而非还给 buddy:

ion_system_heap ├── pools[0] → ion_page_pool { order=8 } // 缓存 1MB 大页 ├── pools[1] → ion_page_pool { order=4 } // 缓存 64KB 页 └── pools[2] → ion_page_pool { order=0 } // 缓存 4KB 页
分配路径: ion_page_pool_alloc(pool) → pool 有缓存?取出(O(1))→ 返回 → pool 空?alloc_pages() → 走 buddy allocator 释放路径: ion_system_heap_free(buffer) → ion_heap_buffer_zero(buffer) // 先清零(安全) → ion_page_pool_free(pool, page) // 放回 pool,不还系统

Pool 带来显著的性能提升:避免频繁进出 buddy allocator,且缓存的页面已清零、cache 状态已知,下次分配可直接使用。

安全措施

  • 分配限制:不能超过系统总内存的一半(totalram_pages() / 2
  • 释放时清零:防止数据通过复用的页面泄漏到其他进程
  • shrinker 释放跳过清零:带 ION_PRIV_FLAG_SHRINKER_FREE 标志时直接归还系统

System Contig Heap — 物理连续分配

简单直接,适用于需要物理连续且大小不大的场景:

c
static int ion_system_contig_heap_allocate(...) { int order = get_order(len); struct page *page = alloc_pages(gfp, order); // buddy allocator split_page(page, order); // 分裂为单页 // 释放 order 对齐多出来的页面 for (i = len >> PAGE_SHIFT; i < (1 << order); i++) __free_page(page + i); // sg_table 只有 1 个 entry(物理连续) sg_set_page(table->sgl, page, len, 0); }

没有 page pool,没有 deferred free,没有 shrink op。

CMA Heap — 基于 CMA 区域的连续分配

c
struct ion_cma_heap { struct ion_heap heap; struct cma *cma; // 对应一个 CMA 预留区域 };

通过 cma_for_each_area() 自动为系统中每个 CMA 区域创建一个 heap:

c
static int ion_add_cma_heaps(void) { cma_for_each_area(__ion_add_cma_heaps, NULL); } device_initcall(ion_add_cma_heaps);

CMA 区域在设备树(DTS)或内核启动参数中预留,分配出的内存保证物理连续,适用于不支持 scatter-gather DMA 的设备(如某些低端 Display 控制器)。

三种 Heap 对比

特性System HeapSystem ContigCMA Heap
物理连续不保证保证保证
内存来源buddy (全系统)buddy (全系统)CMA 预留区
Page Pool有(三阶)
Deferred Free
Shrinker
释放前清零
sg_table entries多个(散布)1 个1 个
典型用途GPU/Camera buffer小型连续 DMA大块连续 DMA

Deferred Free 与 Shrinker 机制

Deferred Free — 延迟释放

System Heap 设置了 ION_HEAP_FLAG_DEFER_FREE,buffer 释放时不立即回收:

用户 close(fd) → dma-buf 引用计数归零 → ion_dma_buf_release() → _ion_buffer_destroy() → ion_heap_freelist_add(heap, buffer) // 加入 freelist,不真正释放 → wake_up(&heap->waitqueue)

后台内核线程(SCHED_IDLE 优先级,最低调度优先级)逐个回收:

c
static int ion_heap_deferred_free(void *data) { while (true) { wait_event_freezable(heap->waitqueue, ion_heap_freelist_size(heap) > 0); // 取出第一个 buffer,真正释放 buffer = list_first_entry(&heap->free_list, ...); list_del(&buffer->list); ion_buffer_destroy(buffer); // zero + 放回 page pool } }

好处:

  • 用户态释放路径不阻塞(清零和页面归还由后台线程完成)
  • freelist 中的内存可在下次分配失败时被 drain 复用

Shrinker — 与内核内存回收框架集成

每个有 deferred free 或 shrink op 的 heap 都注册 shrinker:

c
int ion_heap_init_shrinker(struct ion_heap *heap) { heap->shrinker.count_objects = ion_heap_shrink_count; // "我有多少可回收" heap->shrinker.scan_objects = ion_heap_shrink_scan; // "请回收 N 页" return register_shrinker(&heap->shrinker); }

内存压力时的回收链:

系统内存不足 → kswapd 唤醒 → shrink_slab() 遍历所有 shrinker → ion_heap_shrink_count() │ 返回 freelist 页数 + page pool 页数 │ → ion_heap_shrink_scan(nr_to_scan) │ ├── 1. 先回收 deferred freelist │ ion_heap_freelist_shrink() │ 设置 ION_PRIV_FLAG_SHRINKER_FREE → 跳过 page pool,直接 __free_pages() │ └── 2. 不够再回收 page pool heap->ops->shrink() → ion_system_heap_shrink() → ion_page_pool_shrink(pool[0]) // order 8 → ion_page_pool_shrink(pool[1]) // order 4 → ion_page_pool_shrink(pool[2]) // order 0

ION_PRIV_FLAG_SHRINKER_FREE 标志的精妙设计:

正常释放路径: page → zero 清零 → 放回 page pool(缓存,加速下次分配) shrinker 释放路径: page → 设置 SHRINKER_FREE → 跳过 page pool → __free_pages()(归还系统)

这保证了内存压力时物理内存真正被释放回系统,而不是从一个缓存挪到另一个缓存。


通用辅助函数(ion_heap.c)

内核映射

c
void *ion_heap_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer) { // 1. 收集 sg_table 中所有页面到 pages[] 数组 for_each_sg(table->sgl, sg, table->nents, i) { for (j = 0; j < npages_this_entry; j++) *(tmp++) = page++; } // 2. 选择页保护属性 pgprot = (buffer->flags & ION_FLAG_CACHED) ? PAGE_KERNEL : pgprot_writecombine(PAGE_KERNEL); // 3. vmap 映射到连续虚拟地址 return vmap(pages, npages, VM_MAP, pgprot); }

将散布在物理内存各处的页面映射到连续的内核虚拟地址空间,让内核代码可以用普通指针访问 buffer 内容。

用户态映射

c
int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer, struct vm_area_struct *vma) { for_each_sg(table->sgl, sg, table->nents, i) { // 逐 sg entry 调用 remap_pfn_range 映射到用户 VMA remap_pfn_range(vma, addr, page_to_pfn(page), len, vma->vm_page_prot); addr += len; } }

支持 vm_pgoff 偏移,用户态可以 mmap buffer 的任意位置。

Buffer 清零

c
// 分 32 页一批处理,避免一次性大面积 vmap static int ion_heap_sglist_zero(struct scatterlist *sgl, ...) { struct page *pages[32]; for_each_sg_page(sgl, &piter, nents, 0) { pages[p++] = sg_page_iter_page(&piter); if (p == 32) { vmap → memset(0) → vunmap // 批量清零 p = 0; } } }

初始化顺序

内核启动 │ ├── subsys_initcall (level 4) │ └── ion_device_create() │ ├── kzalloc(ion_device) │ ├── misc_register("/dev/ion") // 注册字符设备 │ ├── debugfs_create_dir("ion") // 创建 debugfs 目录 │ ├── plist_head_init(&heaps) // 初始化优先级链表 │ └── internal_dev = idev // 设置全局指针 │ └── device_initcall (level 6) ├── ion_system_heap_create() │ ├── 创建 3 个 page pool (order 8/4/0) │ ├── 设置 ION_HEAP_FLAG_DEFER_FREE │ └── ion_device_add_heap() │ ├── 初始化 deferred free 线程 │ ├── 注册 shrinker │ ├── 创建 debugfs 节点 │ └── plist_add (id=0, priority=0) │ ├── ion_system_contig_heap_create() │ └── ion_device_add_heap() (id=1, priority=-1) │ └── ion_add_cma_heaps() └── cma_for_each_area(__ion_add_cma_heaps) └── ion_device_add_heap() (id=2+, priority=-2-)

subsys_initcall 先于 device_initcall,保证 internal_dev 在各 heap 注册时已就绪。


debugfs 接口

每个 heap 在 /sys/kernel/debug/ion/{heap_name}/ 下暴露:

节点权限说明
num_of_buffers0444当前存活的 buffer 数量
num_of_alloc_bytes0444当前已分配字节数
alloc_bytes_wm0444历史分配字节数高水位
{name}_shrink0644读:可回收页数;写 N:回收 N 页;写 0:全部回收

使用示例:

bash
# 查看 system heap 状态 cat /sys/kernel/debug/ion/ion_system_heap/num_of_buffers cat /sys/kernel/debug/ion/ion_system_heap/num_of_alloc_bytes # 查看可回收页面数 cat /sys/kernel/debug/ion/ion_system_heap/ion_system_heap_shrink # 手动回收所有缓存 echo 0 > /sys/kernel/debug/ion/ion_system_heap/ion_system_heap_shrink

设计亮点总结

设计手段收益
C 语言多态ion_heap_ops 虚函数表框架与实现解耦,厂商可扩展自定义 heap
零拷贝共享基于 dma-buf,每个 attach dup sg_table多设备共享物理页面,各自维护 DMA 地址
抗碎片化多阶贪心分配 (order 8→4→0)大请求尽量用大页,减少 sg entry 数量
分配加速Page Pool 缓存已清零页面避免反复进出 buddy allocator
释放不阻塞Deferred Free + SCHED_IDLE 后台线程用户态释放路径零延迟
系统级回收Shrinker 注册到内核框架内存压力时自动归还,避免 OOM
安全释放前清零 buffer 内容防止信息通过复用页面泄漏
分配容错失败时 drain freelist 重试内存紧张时提高分配成功率
简化接口只有 ALLOC 和 QUERY 两个 ioctl用户态使用简单,分配直接返回 fd
EXPORT_SYMBOLion_device_add_heap 导出SoC 厂商可通过内核模块注册私有 heap

版本演变

内核版本变化
3.3ION 进入 staging,有 client/handle/share/import 等复杂概念
4.12+逐步简化,移除 client 和 handle
5.4最终简化形态:只有 ALLOC/QUERY 两个 ioctl,分配直接返回 fd
5.6+dma-buf heaps 框架(drivers/dma-buf/heaps/)开始取代 ION
5.10+System Heap 和 CMA Heap 迁移到 dma-buf/heaps/
5.18ION 被完全移除

v5.4.123 是理解 ION 设计思想的最佳版本 — 足够成熟、足够简洁,去除了历史包袱但保留了所有核心机制。


参考资料

  • 源码路径:drivers/staging/android/ion/(6 个文件,约 1600 行)
  • UAPI 头文件:drivers/staging/android/uapi/ion.h
  • dma-buf 框架:drivers/dma-buf/dma-buf.c
  • 后继者 dma-buf heaps:drivers/dma-buf/heaps/