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

目录

ION 内存分配器架构分析(4+1 视图)
1. 逻辑视图(Logical View)
1.1 核心抽象层次
1.2 核心类图(数据结构关系)
1.3 策略模式:ionheapops
1.4 dma-buf 桥接
2. 进程视图(Process View)
2.1 涉及的执行上下文
2.2 分配路径的锁与并发
2.3 Deferred Free 线程模型
2.4 Shrinker 回收时序
2.5 Cache 同步时序(begin/endcpuaccess)
3. 开发视图(Development View)
3.1 文件结构与职责
3.2 模块依赖关系
3.3 构建配置
3.4 初始化顺序(initcall 级别)
4. 物理视图(Physical View)
4.1 ION 在系统内存中的位置
4.2 ION buffer 的多设备共享拓扑
4.3 用户态与内核态的地址映射
5. 场景视图(+1 Scenarios)
场景 1:Camera 采集 → GPU 处理 → Display 显示
场景 2:内存压力下的分配与回收
场景 3:CPU 与设备的 Cache 同步
示例程序
总结
参考资料

ION 内存分配器架构分析(4+1 视图)

基于 Linux Kernel v5.4.123 | 源码路径:drivers/staging/android/ion/

ION 是 Android 引入 Linux 内核的统一内存分配框架,解决多硬件设备(GPU、Camera、Display、Video)之间零拷贝共享物理内存的问题。本文使用 4+1 架构视图模型对 ION 进行全面的架构分析。


1. 逻辑视图(Logical View)

逻辑视图关注系统的关键抽象、模块职责和它们之间的关系

1.1 核心抽象层次

ION 的设计分为三层,每层有清晰的职责边界:

┌─────────────────────────────────────────────────┐ │ 用户态接口层 (User Interface) │ │ /dev/ion 字符设备 + ioctl (ALLOC / QUERY) │ │ 输入: len, heap_id_mask, flags │ │ 输出: dma-buf fd │ └──────────────────────┬──────────────────────────┘ │ ┌──────────────────────▼──────────────────────────┐ │ 核心框架层 (Core Framework) │ │ ion_device — 全局单例,管理所有 heap │ │ ion_buffer — buffer 元数据 + sg_table │ │ dma_buf_ops — 与 Linux dma-buf 框架对接 │ │ deferred free — 延迟释放 + shrinker 回收 │ └──────────────────────┬──────────────────────────┘ │ ion_heap_ops (虚函数表) ┌──────────────────────▼──────────────────────────┐ │ 堆实现层 (Heap Implementations) │ │ System Heap — 散布页面 + page pool │ │ System Contig — 物理连续 buddy 分配 │ │ CMA Heap — CMA 预留区连续分配 │ │ (厂商自定义 Heap — EXPORT_SYMBOL 扩展) │ └─────────────────────────────────────────────────┘

1.2 核心类图(数据结构关系)

┌──────────────────┐ │ ion_device │ 全局单例 │──────────────────│ │ dev: miscdevice │ /dev/ion │ lock: rw_sem │ │ heaps: plist │──┐ 优先级链表 │ heap_cnt: int │ │ └──────────────────┘ │ │ 1:N ┌────────────────────┘ ▼ ┌──────────────────┐ │ ion_heap │ 堆抽象 │──────────────────│ │ type: enum │ SYSTEM / DMA / ... │ ops: heap_ops* ─┼──────┐ │ id: uint │ │ │ flags: ulong │ │ │ shrinker │ │ │ free_list │ ▼ │ task: kthread │ ┌──────────────────┐ └──────────────────┘ │ ion_heap_ops │ 虚函数表 │ │──────────────────│ │ 1:N 分配 │ allocate() │ ▼ │ free() │ ┌──────────────────┐ │ map_kernel() │ │ ion_buffer │ │ unmap_kernel() │ │──────────────────│ │ map_user() │ │ heap: ion_heap* │ │ shrink() │ │ size: size_t │ └──────────────────┘ │ flags: ulong │ │ sg_table* ──────┼───── 核心:物理页面描述 │ attachments list │──┐ │ kmap_cnt: int │ │ │ vaddr: void* │ │ └──────────────────┘ │ │ 1:N ┌───────────────┘ ▼ ┌──────────────────────────┐ │ ion_dma_buf_attachment │ 每设备一份 │──────────────────────────│ │ dev: device* │ 哪个设备 │ table: sg_table* │ dup 的 sg_table 副本 └──────────────────────────┘

1.3 策略模式:ion_heap_ops

ION 使用 C 语言函数指针实现策略模式,核心框架通过 heap->ops->allocate() 调用具体堆实现,完全不关心底层分配细节:

ion_heap_ops (接口) ┌────────────────┐ │ allocate() │ │ free() │ │ map_kernel() │ │ map_user() │ │ shrink() │ └───────┬────────┘ ┌─────────────┼─────────────┐ ▼ ▼ ▼ system_heap_ops kmalloc_ops ion_cma_ops ┌────────────┐ ┌──────────┐ ┌──────────┐ │多阶贪心分配│ │buddy整块 │ │CMA 区域 │ │+ page pool │ │分配 │ │分配 │ └────────────┘ └──────────┘ └──────────┘

新增一种堆类型只需:

  1. 定义 ion_heap_ops 结构体并实现回调
  2. 调用 ion_device_add_heap()(EXPORT_SYMBOL 导出)

框架代码零修改,完全开放-封闭。

1.4 dma-buf 桥接

ION 与 Linux dma-buf 框架的关系是**导出者(exporter)**角色:

ION (exporter) 设备驱动 (importer) ───────────── ────────────────── ion_alloc() → ion_buffer_create() → dma_buf_export(dma_buf_ops) → dma_buf_fd() → fd ──────────────→ fd 通过 IPC 传递 │ dma_buf_get(fd) dma_buf_attach() → ion_dma_buf_attach() dma_buf_map_attachment() → ion_map_dma_buf() │ 设备通过 DMA 地址访问内存(零拷贝)

ION 提供 dma_buf_ops 告诉 dma-buf 框架如何操作 ION 分配的内存(attach、map、sync、release),设备驱动只依赖 dma-buf 标准 API,不直接依赖 ION。


2. 进程视图(Process View)

进程视图关注运行时并发、线程、同步机制和时序行为

2.1 涉及的执行上下文

上下文身份做什么
用户进程ioctl(ION_IOC_ALLOC) 调用方分配/释放 buffer
deferred free kthread每个 DEFER_FREE heap 一个后台回收 freelist 中的 buffer
kswapd内核内存回收守护线程内存压力时通过 shrinker 回收 ION 缓存
设备驱动上下文GPU/Camera/Display 驱动attach、map、sync dma-buf

2.2 分配路径的锁与并发

用户进程 A 用户进程 B │ │ │ ioctl(ALLOC) │ ioctl(ALLOC) ▼ ▼ down_read(&dev->lock) ◄── rw_semaphore ──► down_read(&dev->lock) │ (读锁,可并发) │ │ 遍历 heaps │ 遍历 heaps │ heap->ops->allocate() │ heap->ops->allocate() │ └── page pool: mutex_lock │ └── page pool: mutex_lock │ (每个 pool 独立锁) │ (可能阻塞在同一 pool) │ │ │ spin_lock(&heap->stat_lock) │ spin_lock(&heap->stat_lock) │ 更新统计信息 │ 更新统计信息 │ │ up_read(&dev->lock) up_read(&dev->lock)

关键同步设计:

  • dev->lock(rw_semaphore):分配/查询用读锁(可并发),添加 heap 用写锁(互斥)
  • heap->stat_lock(spinlock):保护统计计数器,临界区极短
  • pool->mutex:每个 page pool 独立锁,不同 order 的分配不互斥
  • buffer->lock(mutex):保护 attachments 链表和 kmap_cnt

2.3 Deferred Free 线程模型

用户进程 (释放路径) deferred free kthread ───────────────── ───────────────────── close(fd) → dma_buf refcount = 0 → ion_dma_buf_release() → _ion_buffer_destroy() │ │ DEFER_FREE? │ Yes: │ spin_lock(&free_lock) │ list_add(buffer, freelist) │ free_list_size += size │ spin_unlock(&free_lock) │ wake_up(&waitqueue) ──────► wait_event_freezable() 被唤醒 │ │ │ (用户进程立即返回) │ │ spin_lock(&free_lock) │ 取出 buffer │ spin_unlock(&free_lock) │ │ │ ion_buffer_destroy() │ ├── zero 清零 buffer │ ├── 页面放回 page pool │ └── kfree(buffer)

deferred free kthread 以 SCHED_IDLE 优先级运行 — 只在系统空闲时调度,不与用户进程争 CPU。

2.4 Shrinker 回收时序

系统内存不足 │ ▼ kswapd 唤醒 │ ▼ shrink_slab() 遍历所有注册的 shrinker │ ├── ion_heap_shrink_count() │ return freelist_pages + pool_pages // "我有 2048 页可回收" │ └── ion_heap_shrink_scan(nr_to_scan=512) │ ├── 1. ion_heap_freelist_shrink() // 先回收 freelist │ 设置 ION_PRIV_FLAG_SHRINKER_FREE │ ion_buffer_destroy() │ └── free_buffer_page() │ └── __free_pages() // 直接还系统,跳过 pool │ freed = 300 页 │ └── 2. heap->ops->shrink() // 不够,再回收 page pool ion_page_pool_shrink() └── __free_pages() // pool 中取出页面还系统 freed += 212 页

2.5 Cache 同步时序(begin/end_cpu_access)

CPU cache 物理内存 设备 DMA ───────── ──────── ──────── [旧数据] 读到旧数据 begin_cpu_access(DMA_FROM_DEVICE): dma_sync_sg_for_cpu() invalidate cache ──────────────────→ [设备写入的新数据] CPU cache 失效 可见了 CPU 读写 buffer ◄──────────────────── [从物理内存加载] end_cpu_access(DMA_TO_DEVICE): dma_sync_sg_for_device() flush cache ───────────────────────→ [CPU 写入的新数据] 设备读到新数据 写回物理内存

3. 开发视图(Development View)

开发视图关注代码组织、文件结构、模块依赖和构建配置

3.1 文件结构与职责

drivers/staging/android/ ├── uapi/ │ └── ion.h # 用户态 API(ioctl 定义、堆类型枚举) │ 127 行 | 对外契约,ABI 稳定 └── ion/ ├── Kconfig # 内核配置选项 ├── Makefile # 构建规则(4 行) ├── ion.h # 内部头文件(数据结构 + 函数声明) │ 303 行 | 框架与堆实现之间的接口 ├── ion.c # 核心框架 │ 668 行 | 设备注册、ioctl、dma-buf ops、buffer 生命周期 ├── ion_heap.c # 堆通用辅助 │ 315 行 | map/unmap、deferred free、shrinker、zero ├── ion_page_pool.c # 页面缓存池 │ 155 行 | pool 分配/释放/shrink ├── ion_system_heap.c # System Heap + System Contig Heap │ 377 行 | 多阶贪心分配、page pool 集成 └── ion_cma_heap.c # CMA Heap 138 行 | CMA 区域连续分配 ───────── 总计约 1960 行(含头文件)

3.2 模块依赖关系

┌──────────────┐ │ uapi/ion.h │ 用户态 ABI └──────┬───────┘ │ include ┌──────▼───────┐ │ ion.h │ 内部接口 └──┬───┬───┬───┘ │ │ │ include ┌────────────┘ │ └────────────┐ ▼ ▼ ▼ ┌────────┐ ┌────────────┐ ┌──────────────┐ │ ion.c │ │ ion_heap.c │ │ ion_page_ │ │ │ │ │ │ pool.c │ └────────┘ └────────────┘ └──────────────┘ │ ▲ ▲ │ include │ 调用 │ 调用 │ ┌──────┴────────────────┘ │ │ │ ┌────┴─────────────┐ ┌───────────────┐ │ │ ion_system_heap.c│ │ ion_cma_heap.c│ │ └──────────────────┘ └───────────────┘ │ │ │ └──────────────┴─────────────────────┘ 都通过 ion_device_add_heap() 注册

3.3 构建配置

makefile
# Makefile obj-$(CONFIG_ION) += ion_system_heap.o ion_page_pool.o ion_heap.o ion.o obj-$(CONFIG_ION_CMA_HEAP) += ion_cma_heap.o
kconfig
# Kconfig menuconfig ION bool "Ion Memory Manager" depends on HAS_DMA && MMU select GENERIC_ALLOCATOR select DMA_SHARED_BUFFER config ION_CMA_HEAP bool "Ion CMA heap support" depends on ION && DMA_CMA

关键依赖:

  • HAS_DMA — 需要 DMA 支持
  • MMU — 需要 MMU(虚拟内存映射)
  • DMA_SHARED_BUFFER — 自动启用 dma-buf 框架
  • DMA_CMA — CMA Heap 需要 CMA 支持

3.4 初始化顺序(initcall 级别)

内核启动 → do_initcalls() │ ├── level 4: subsys_initcall │ └── ion_device_create() // 创建 /dev/ion,初始化全局 ion_device │ └── level 6: device_initcall ├── ion_system_heap_create() // 注册 System Heap (id=0) ├── ion_system_contig_heap_create() // 注册 Contig Heap (id=1) └── ion_add_cma_heaps() // 遍历 CMA 区域,注册 CMA Heap (id=2+)

subsys_initcall(level 4)先于 device_initcall(level 6),保证 internal_dev 在堆注册时已就绪。


4. 物理视图(Physical View)

物理视图关注硬件拓扑、内存布局和设备映射

4.1 ION 在系统内存中的位置

物理内存布局 (典型 ARM SoC 4GB) ┌──────────────────────────────────────┐ 0x0000_0000 │ 内核保留区 │ ├──────────────────────────────────────┤ │ │ │ ZONE_NORMAL (lowmem) │ │ ┌─────────────────────┐ │ │ │ buddy allocator │ │ │ │ ┌─────────────────┐ │ │ │ │ │ ION System Heap │ │ │ ← 从 buddy 分配 │ │ │ (散布页面) │ │ │ page pool 缓存在此 │ │ └─────────────────┘ │ │ │ │ ┌─────────────────┐ │ │ │ │ │ ION Contig Heap │ │ │ ← 从 buddy 分配(连续) │ │ └─────────────────┘ │ │ │ └─────────────────────┘ │ ├──────────────────────────────────────┤ │ ZONE_HIGHMEM │ │ (ION System Heap 也可用) │ ├──────────────────────────────────────┤ │ │ │ CMA 预留区 │ │ ┌─────────────────────┐ │ │ │ ION CMA Heap │ │ ← 从 CMA 区域分配(连续) │ │ (设备树中预留) │ │ │ └─────────────────────┘ │ │ │ ├──────────────────────────────────────┤ │ MMIO / 设备寄存器 │ └──────────────────────────────────────┘ 0xFFFF_FFFF

4.2 ION buffer 的多设备共享拓扑

┌─────────────────┐ │ 物理内存页面 │ │ page0, page1, │ │ page2, ... │ └────────┬────────┘ │ sg_table (原始) │ ┌─────────────────┼─────────────────┐ │ dup │ dup │ dup ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ sg_table │ │ sg_table │ │ sg_table │ │ (GPU 副本) │ │ (Display │ │ (Camera │ │ │ │ 副本) │ │ 副本) │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │ │ dma_map_sg dma_map_sg dma_map_sg │ │ │ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ GPU IOMMU │ │ Display │ │ Camera │ │ 页表 │ │ (直通/IOMMU)│ │ IOMMU 页表 │ │ │ │ │ │ │ │ DMA addr: │ │ DMA addr: │ │ DMA addr: │ │ 0x0010_0000 │ │ 0x8000_0000 │ │ 0xFF00_0000 │ └─────────────┘ └─────────────┘ └─────────────┘

同一块物理内存,每个设备通过各自的 IOMMU 映射得到不同的 DMA 地址。这就是为什么每个 attachment 需要 dup 独立的 sg_table。

4.3 用户态与内核态的地址映射

用户进程虚拟地址空间 内核虚拟地址空间 ┌────────────────────┐ ┌────────────────────┐ │ │ │ │ │ mmap(dma-buf fd) │ │ vmap (kmap) │ │ ┌──────────────┐ │ │ ┌──────────────┐ │ │ │ 0x7f001000 │──┼── remap_ ─┼─▶│ 0xffff8800 │ │ │ │ 用户虚拟地址 │ │ pfn_ │ │ 内核虚拟地址 │ │ │ └──────────────┘ │ range │ └──────────────┘ │ │ │ │ │ │ └────────────────────┘ └─────────┼──────────┘ │ ┌───────▼────────┐ │ 物理页面 │ │ 0x8000_1000 │ │ 0x8000_5000 │ │ 0x9000_0000 │ ← 可能不连续 └────────────────┘

5. 场景视图(+1 Scenarios)

场景视图通过端到端的用例将其他四个视图串联起来。

场景 1:Camera 采集 → GPU 处理 → Display 显示

这是 Android 上最典型的 ION 使用场景:

时间 ──────────────────────────────────────────────────────────► 用户进程 (SurfaceFlinger / CameraService) │ │ 1. open("/dev/ion") │ 2. ioctl(ION_IOC_ALLOC, {len=1920*1080*4, mask=system_heap, CACHED}) │ └── ion_alloc() │ └── ion_buffer_create() │ └── ion_system_heap_allocate() │ ├── pool_alloc(order=8) × 7 = 7MB │ └── pool_alloc(order=4) × 13 = 832KB ≈ 7.9MB │ └── dma_buf_export() → dma_buf_fd() → fd=7 │ │ 3. 将 fd=7 传给 Camera HAL(通过 Binder IPC) │ Camera HAL │ 4. dma_buf_get(fd=7) │ dma_buf_attach(camera_dev) → ion_dma_buf_attach() │ └── dup_sg_table() → camera 拥有独立 sg_table │ dma_buf_map_attachment(WRITE) → ion_map_dma_buf() │ └── dma_map_sg(camera_dev) → 获得 camera 的 DMA 地址 │ │ 5. Camera 硬件通过 DMA 将图像写入 buffer │ │ 6. dma_buf_unmap_attachment() │ 将 fd 传给 GPU 进程 │ GPU 驱动 │ 7. dma_buf_attach(gpu_dev) → 再 dup 一份 sg_table │ dma_buf_map_attachment(READ) → dma_map_sg(gpu_dev) │ └── GPU IOMMU 映射 → GPU 看到的 DMA 地址 │ │ 8. GPU 读取 buffer 做图像处理,结果写回同一 buffer │ │ 9. dma_buf_unmap_attachment() │ 将 fd 传给 Display │ Display 驱动 (HWC) │ 10. dma_buf_attach(display_dev) │ dma_buf_map_attachment(READ) → Display DMA 地址 │ │ 11. Display 控制器通过 DMA 读取 buffer,输出到屏幕 │ │ 12. dma_buf_unmap / detach(各设备依次) │ 用户进程 │ 13. close(fd=7) │ → dma_buf refcount=0 │ → ion_dma_buf_release() │ → _ion_buffer_destroy() │ └── DEFER_FREE: 加入 freelist,后台线程稍后回收

场景 2:内存压力下的分配与回收

系统状态:可用内存低,kswapd 已激活 用户进程 │ ioctl(ION_IOC_ALLOC, 2MB) │ └── ion_alloc() └── ion_buffer_create() └── heap->ops->allocate() │ ├── alloc_largest_available(order=8) │ └── pool 空 → alloc_pages(GFP_HIGHUSER | __GFP_NORETRY) │ └── 失败(内存不足,不等待回收) │ ├── alloc_largest_available(order=4) │ └── pool 空 → alloc_pages(GFP_HIGHUSER) │ └── 触发内核直接回收(direct reclaim) │ └── 成功,得到 64KB │ ├── ... 继续分配 order=0 页面填满剩余 │ └── 首次 allocate 整体失败(某个 order=0 也失败了) │ ├── 检测到 DEFER_FREE ├── ion_heap_freelist_drain(heap, 0) // 清空 freelist │ └── 释放了 3 个延迟回收的 buffer → 归还 5MB 给系统 │ └── 重试 allocate → 成功 与此同时,kswapd 线程: kswapd │ shrink_slab() │ → ion_heap_shrink_count() → 报告 1024 页可回收 │ → ion_heap_shrink_scan(512) │ ├── freelist_shrink(已空) → freed=0 │ └── pool_shrink() │ ├── pool[order=8]: 3 个 1MB 页 → __free_pages → freed=768 │ └── 够了,停止 │ └── 系统可用内存恢复到安全水位

场景 3:CPU 与设备的 Cache 同步

GPU 渲染完成,CPU 需要读取结果: 用户进程 │ │ // GPU 已经通过 DMA 写入了数据 │ │ ioctl(dmabuf_fd, DMA_BUF_IOCTL_SYNC, DMA_BUF_SYNC_START | DMA_BUF_SYNC_READ) │ └── ion_dma_buf_begin_cpu_access(DMA_FROM_DEVICE) │ ├── kmap_get() → vmap buffer 到内核 │ └── dma_sync_sg_for_cpu() │ └── invalidate CPU cache // 确保 CPU 读到设备写入的最新数据 │ │ ptr = mmap(dmabuf_fd) │ memcpy(dst, ptr, size) // CPU 安全地读取 GPU 输出 │ │ ioctl(dmabuf_fd, DMA_BUF_IOCTL_SYNC, DMA_BUF_SYNC_END | DMA_BUF_SYNC_READ) │ └── ion_dma_buf_end_cpu_access(DMA_FROM_DEVICE) │ ├── kmap_put() → kmap_cnt-- │ └── dma_sync_sg_for_device() │ └── (FROM_DEVICE 方向通常无需 flush)

示例程序

以下用户态 C 程序演示 ION 的完整使用流程:查询 heap → 分配 buffer → mmap → 读写 → 释放。

保存为 ion_example.c

c
/* * ION 用户态使用示例 * 演示:查询 heap、分配 buffer、mmap 读写、释放 * * 编译:gcc -o ion_example ion_example.c * 运行:./ion_example (需要 root 权限,且系统有 /dev/ion) * * 注意:此程序需要在带 ION 的 Android/Linux 内核上运行。 * 如果在普通 Linux PC 上,/dev/ion 不存在,程序会提示并退出。 */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/mman.h> #include <linux/ion.h> /* 需要内核头文件 */ /* 如果系统头文件中没有 ion.h,手动定义 */ #ifndef ION_IOC_ALLOC #define ION_IOC_MAGIC 'I' #define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, struct ion_allocation_data) #define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, struct ion_heap_query) struct ion_allocation_data { __u64 len; __u32 heap_id_mask; __u32 flags; __u32 fd; __u32 unused; }; #define MAX_HEAP_NAME 32 struct ion_heap_data { char name[MAX_HEAP_NAME]; __u32 type; __u32 heap_id; __u32 reserved0; __u32 reserved1; __u32 reserved2; }; struct ion_heap_query { __u32 cnt; __u32 reserved0; __u64 heaps; __u32 reserved1; __u32 reserved2; }; #define ION_FLAG_CACHED 1 #endif static const char *heap_type_name(int type) { switch (type) { case 0: return "SYSTEM"; case 1: return "SYSTEM_CONTIG"; case 2: return "CARVEOUT"; case 3: return "CHUNK"; case 4: return "DMA (CMA)"; default: return "CUSTOM"; } } int main(int argc, char *argv[]) { int ion_fd, buf_fd, ret; size_t buf_size = 4096; /* 分配 4KB */ void *mapped; /* 1. 打开 /dev/ion */ ion_fd = open("/dev/ion", O_RDONLY); if (ion_fd < 0) { perror("open /dev/ion failed (需要 ION 内核支持)"); return 1; } printf("[1] /dev/ion opened, fd=%d\n", ion_fd); /* 2. 查询可用 heap */ struct ion_heap_query query; memset(&query, 0, sizeof(query)); query.cnt = 0; query.heaps = 0; ret = ioctl(ion_fd, ION_IOC_HEAP_QUERY, &query); if (ret < 0) { perror("ION_IOC_HEAP_QUERY (get count) failed"); close(ion_fd); return 1; } printf("[2] System has %u heaps\n", query.cnt); struct ion_heap_data *heaps = calloc(query.cnt, sizeof(*heaps)); query.heaps = (__u64)(unsigned long)heaps; ret = ioctl(ion_fd, ION_IOC_HEAP_QUERY, &query); if (ret < 0) { perror("ION_IOC_HEAP_QUERY (get data) failed"); free(heaps); close(ion_fd); return 1; } unsigned int system_heap_mask = 0; for (unsigned int i = 0; i < query.cnt; i++) { printf(" heap[%u]: name=%-24s type=%-14s id=%u\n", i, heaps[i].name, heap_type_name(heaps[i].type), heaps[i].heap_id); if (heaps[i].type == 0) /* ION_HEAP_TYPE_SYSTEM */ system_heap_mask = 1 << heaps[i].heap_id; } if (!system_heap_mask) { printf("No system heap found, using mask=1\n"); system_heap_mask = 1; } free(heaps); /* 3. 分配 buffer */ struct ion_allocation_data alloc; memset(&alloc, 0, sizeof(alloc)); alloc.len = buf_size; alloc.heap_id_mask = system_heap_mask; alloc.flags = ION_FLAG_CACHED; ret = ioctl(ion_fd, ION_IOC_ALLOC, &alloc); if (ret < 0) { perror("ION_IOC_ALLOC failed"); close(ion_fd); return 1; } buf_fd = alloc.fd; printf("[3] Buffer allocated: %zu bytes, dma-buf fd=%d\n", buf_size, buf_fd); /* 4. mmap 到用户空间 */ mapped = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, buf_fd, 0); if (mapped == MAP_FAILED) { perror("mmap failed"); close(buf_fd); close(ion_fd); return 1; } printf("[4] Buffer mmap'd at %p\n", mapped); /* 5. 写入数据 */ const char *test_msg = "Hello from ION!"; memcpy(mapped, test_msg, strlen(test_msg) + 1); printf("[5] Wrote: \"%s\"\n", (char *)mapped); /* 6. 读回验证 */ char readbuf[64]; memcpy(readbuf, mapped, strlen(test_msg) + 1); printf("[6] Read back: \"%s\"\n", readbuf); printf(" Data integrity: %s\n", strcmp(test_msg, readbuf) == 0 ? "PASS" : "FAIL"); /* 7. 清理 */ munmap(mapped, buf_size); close(buf_fd); /* 释放 dma-buf → 触发 ION buffer 释放 */ close(ion_fd); printf("[7] Cleanup done. Buffer released via deferred free.\n"); return 0; }

编译和运行:

bash
# 交叉编译(ARM Android 设备) $ aarch64-linux-gnu-gcc -o ion_example ion_example.c -static $ adb push ion_example /data/local/tmp/ $ adb shell chmod +x /data/local/tmp/ion_example $ adb shell /data/local/tmp/ion_example # 或者在有 ION 的 Linux 开发板上直接编译 $ gcc -o ion_example ion_example.c $ sudo ./ion_example

预期输出:

[1] /dev/ion opened, fd=3 [2] System has 3 heaps heap[0]: name=ion_system_heap type=SYSTEM id=0 heap[1]: name=ion_system_contig_heap type=SYSTEM_CONTIG id=1 heap[2]: name=linux,cma type=DMA (CMA) id=2 [3] Buffer allocated: 4096 bytes, dma-buf fd=4 [4] Buffer mmap'd at 0x7f8a001000 [5] Wrote: "Hello from ION!" [6] Read back: "Hello from ION!" Data integrity: PASS [7] Cleanup done. Buffer released via deferred free.

注意: 如果在没有 ION 的普通 Linux PC 上运行,第一步 open("/dev/ion") 就会失败并报错 No such file or directory。ION 仅在 Android 内核或手动启用了 CONFIG_ION=y 的内核中可用。


总结

  • ION 是 Android 的统一内存分配框架,通过 /dev/ion 设备提供用户态 ioctl 接口,分配结果以 dma-buf fd 形式返回,实现多设备零拷贝共享
  • 逻辑上三层架构:用户态接口层 → 核心框架层(ion_device/ion_buffer/dma_buf_ops)→ 堆实现层(通过 ion_heap_ops 虚函数表实现策略模式)
  • 并发设计精细:分配路径用 rw_semaphore 读锁允许并发;deferred free 用 SCHED_IDLE kthread 异步回收;shrinker 注册到内核回收框架应对内存压力
  • 代码组织简洁:6 个源文件约 1960 行,职责划分清晰,ion.c 管框架,ion_heap.c 管通用辅助,各 heap 文件独立实现
  • 物理视图上,System Heap 从 buddy allocator 全局内存分配(可散布),CMA Heap 从设备树预留的连续区域分配;每个 attach 的设备 dup 独立 sg_table 以适配不同 IOMMU 映射
  • 三个关键场景展示了完整生命周期:Camera→GPU→Display 的零拷贝流水线、内存压力下的 drain-freelist 重试与 shrinker 回收、CPU-设备间的 cache 同步协议
  • 扩展性ion_device_add_heap 通过 EXPORT_SYMBOL 导出,SoC 厂商可在树外代码中注册自定义 heap,框架零修改
  • v5.4 是 ION 的最终简化形态,后续被 dma-buf heaps(5.6+)取代,5.18 中完全移除

参考资料

  • Linux Kernel v5.4.123 源码:drivers/staging/android/ion/
  • Linux dma-buf 框架:drivers/dma-buf/dma-buf.cinclude/linux/dma-buf.h
  • Philippe Kruchten, "The 4+1 View Model of Architecture", IEEE Software, 1995
  • LWN.net: "The Android ION memory allocator" (https://lwn.net/Articles/480055/)
  • 后继框架 dma-buf heaps:drivers/dma-buf/heaps/(Linux 5.6+)