匿名共享内存模块详解 🧠一句话概括 :Ashmem(匿名共享内存)就像"进程间的公共黑板",多个进程可以同时读写同一块内存。
📚 目录 什么是匿名共享内存? 为什么需要 Ashmem? Ashmem 工作原理 Ashmem 类详解 核心操作流程 使用示例 保护标志详解 最佳实践 1. 什么是匿名共享内存? 1.1 通俗理解 想象一个办公室场景 🏢:
普通内存 :
每个员工有自己的便签本 员工A:写在自己的便签上 员工B:看不到员工A写的内容 要传递信息 → 需要复制便签共享内存 :
办公室有一块公共白板 📋 员工A:在白板上写内容 员工B:直接看白板上的内容 无需复制 → 直接共享!1.2 技术定义 Ashmem (Anonymous Shared Memory)是 Android/Linux 系统提供的匿名共享内存机制:
📁基于文件描述符 :通过 fd 访问 🔄跨进程共享 :多个进程可以映射同一块内存 🧹自动回收 :当所有引用关闭时自动释放 进程B
内核空间
进程A
映射区域B
用户空间B
Ashmem 区域 /dev/ashmem
映射区域A
用户空间A
2. 为什么需要 Ashmem? 2.1 进程间通信的挑战 进程隔离
无法直接访问
进程A 内存空间
进程B 内存空间
问题 :每个进程有独立的内存空间,无法直接访问其他进程的内存。
2.2 传统 IPC 方式的问题 方式 问题 管道/Socket 需要数据复制,大数据量效率低 消息队列 有大小限制,需要序列化 信号 只能传递简单信息
2.3 Ashmem 的优势 Ashmem
映射
映射
共享内存
进程A
进程B
0次数据复制
传统IPC
复制数据
复制数据
内核缓冲区
进程A
进程B
2次数据复制
优势 说明 零拷贝 数据无需复制,直接共享 高效 适合大数据量传输 灵活 可以设置保护标志 安全 支持权限控制
3. Ashmem 工作原理 3.1 整体架构 内核空间
用户空间
open/ioctl/mmap
dev/ashmem
Ashmem 驱动
物理内存
应用程序
Ashmem 类
3.2 关键步骤 应用程序 Ashmem类 内核 物理内存 CreateAshmem("name", size) open("/dev/ashmem") 返回 fd ioctl(SET_NAME) ioctl(SET_SIZE) MapReadAndWriteAshmem() mmap(fd, size) 分配物理内存 返回映射地址 WriteToAshmem(data) 直接写入 ReadFromAshmem() 直接读取 UnmapAshmem() munmap() CloseAshmem() close(fd) 应用程序 Ashmem类 内核 物理内存
4. Ashmem 类详解 4.1 类结构
«基类»
RefBase
Ashmem
-int memoryFd_
-int32_t memorySize_
-int flag_
-void* startAddr_
+CreateAshmem(name, size)
+Ashmem(fd, size)
+~Ashmem()
+GetAshmemFd() : int
+SetProtection(type) : bool
+GetProtection() : int
+GetAshmemSize() : int32_t
+CloseAshmem() : void
+MapAshmem(mapType) : bool
+MapReadAndWriteAshmem() : bool
+MapReadOnlyAshmem() : bool
+UnmapAshmem() : void
+WriteToAshmem(data, size, offset) : bool
+ReadFromAshmem(size, offset)
-CheckValid(size, offset, cmd) : bool
4.2 成员变量 成员 类型 说明 memoryFd_int 文件描述符 memorySize_int32_t 内存区域大小 flag_int 用户空间保护标志 startAddr_void* 映射后的起始地址
4.3 核心方法 创建 Ashmem // 静态工厂方法 static sptr< Ashmem> CreateAshmem ( const char * name, int32_t size) ;
否
是
否
是
CreateAshmem
参数有效?
返回 nullptr
AshmemCreate
open /dev/ashmem
ioctl SET_NAME
ioctl SET_SIZE
成功?
new Ashmem
返回 sptr
映射内存 bool MapAshmem ( int mapType) ; // 通用映射 bool MapReadAndWriteAshmem ( ) ; // 读写映射 bool MapReadOnlyAshmem ( ) ; // 只读映射 void UnmapAshmem ( ) ; // 取消映射 flowchart LR subgraph 映射类型 A[MapAshmem<br/>PROT_READ] --> R[只读] B[MapAshmem<br/>PROT_WRITE] --> W[只写] C[MapAshmem<br/>PROT_READ|PROT_WRITE] --> RW[读写] end读写数据 bool WriteToAshmem ( const void * data, int32_t size, int32_t offset) ; const void * ReadFromAshmem ( int32_t size, int32_t offset) ; ReadFromAshmem
否
是
否
是
有效?
检查参数
返回 nullptr
检查权限
有读权限?
返回地址指针
WriteToAshmem
否
是
否
是
有效?
检查参数
返回 false
检查权限
有写权限?
memcpy 写入
返回 true
5. 核心操作流程 5.1 完整生命周期 CreateAshmem()
MapAshmem()
Read/Write
UnmapAshmem()
MapAshmem()
CloseAshmem()
CloseAshmem()
Created
Mapped
Unmapped
Closed
5.2 内存布局 Ashmem 内存区域: ┌────────────────────────────────────────────────────────┐ │ memorySize_ 字节 │ ├────────────────────────────────────────────────────────┤ │ startAddr_ │ │ ↓ │ │ ┌──────┬──────┬──────┬──────┬──────┬──────────────────┐│ │ │ │ │ │ │ │ ││ │ │ 数据1 │ 数据2 │ 数据3 │ ... │ 数据N │ 空闲空间 ││ │ │ │ │ │ │ │ ││ │ └──────┴──────┴──────┴──────┴──────┴──────────────────┘│ │ ↑ ↑ │ │ offset=0 offset=n │ └────────────────────────────────────────────────────────┘5.3 跨进程共享流程 进程A Binder/IPC 进程B CreateAshmem("shared", 1024) MapReadAndWriteAshmem() WriteToAshmem(data) 传递 fd 接收 fd new Ashmem(fd, size) MapReadOnlyAshmem() ReadFromAshmem() 两个进程共享同一块内存! 进程A Binder/IPC 进程B
6. 使用示例 6.1 基本用法 # include "ashmem.h" # include <iostream> # include <cstring> using namespace OHOS; void BasicAshmemDemo ( ) { // 1. 创建 Ashmem 区域 sptr< Ashmem> ashmem= Ashmem :: CreateAshmem ( "MySharedMem" , 1024 ) ; if ( ashmem== nullptr ) { std:: cerr<< "创建 Ashmem 失败" << std:: endl; return ; } std:: cout<< "Ashmem FD: " << ashmem-> GetAshmemFd ( ) << std:: endl; std:: cout<< "Ashmem Size: " << ashmem-> GetAshmemSize ( ) << std:: endl; // 2. 映射到用户空间(读写模式) if ( ! ashmem-> MapReadAndWriteAshmem ( ) ) { std:: cerr<< "映射失败" << std:: endl; return ; } // 3. 写入数据 const char * message= "Hello, Ashmem!" ; if ( ashmem-> WriteToAshmem ( message, strlen ( message) + 1 , 0 ) ) { std:: cout<< "写入成功" << std:: endl; } // 4. 读取数据 const char * readData= static_cast < const char * > ( ashmem-> ReadFromAshmem ( strlen ( message) + 1 , 0 ) ) ; if ( readData) { std:: cout<< "读取到: " << readData<< std:: endl; } // 5. 取消映射 ashmem-> UnmapAshmem ( ) ; // 6. 关闭(析构时也会自动关闭) ashmem-> CloseAshmem ( ) ; } 6.2 写入结构体 # include "ashmem.h" struct UserData { int id; char name[ 32 ] ; double score; } ; void WriteStructDemo ( ) { sptr< Ashmem> ashmem= Ashmem :: CreateAshmem ( "UserData" , sizeof ( UserData) * 10 ) ; ashmem-> MapReadAndWriteAshmem ( ) ; // 写入多个结构体 for ( int i= 0 ; i< 10 ; i++ ) { UserData user; user. id= i+ 1 ; snprintf ( user. name, sizeof ( user. name) , "User%d" , i+ 1 ) ; user. score= 80.0 + i* 2 ; int offset= i* sizeof ( UserData) ; ashmem-> WriteToAshmem ( & user, sizeof ( UserData) , offset) ; } // 读取第 5 个用户 int readOffset= 4 * sizeof ( UserData) ; const UserData* user5= static_cast < const UserData* > ( ashmem-> ReadFromAshmem ( sizeof ( UserData) , readOffset) ) ; if ( user5) { std:: cout<< "ID: " << user5-> id<< std:: endl; std:: cout<< "Name: " << user5-> name<< std:: endl; std:: cout<< "Score: " << user5-> score<< std:: endl; } ashmem-> UnmapAshmem ( ) ; ashmem-> CloseAshmem ( ) ; } 6.3 跨进程共享示例 进程 A(生产者) # include "ashmem.h" # include <unistd.h> void ProducerProcess ( ) { // 创建共享内存 sptr< Ashmem> ashmem= Ashmem :: CreateAshmem ( "SharedBuffer" , 4096 ) ; ashmem-> MapReadAndWriteAshmem ( ) ; int fd= ashmem-> GetAshmemFd ( ) ; int size= ashmem-> GetAshmemSize ( ) ; // 通过某种 IPC 方式(如 Binder)将 fd 和 size 传递给进程 B // SendToProcessB(fd, size); // 写入数据 int counter= 0 ; while ( true ) { char buffer[ 64 ] ; snprintf ( buffer, sizeof ( buffer) , "Message #%d" , ++ counter) ; ashmem-> WriteToAshmem ( buffer, strlen ( buffer) + 1 , 0 ) ; std:: cout<< "生产者写入: " << buffer<< std:: endl; sleep ( 1 ) ; } } 进程 B(消费者) # include "ashmem.h" # include <unistd.h> void ConsumerProcess ( int fd, int size) { // 使用从进程 A 获取的 fd 创建 Ashmem sptr< Ashmem> ashmem= new Ashmem ( fd, size) ; // 只读映射 ashmem-> MapReadOnlyAshmem ( ) ; // 读取数据 while ( true ) { const char * data= static_cast < const char * > ( ashmem-> ReadFromAshmem ( 64 , 0 ) ) ; if ( data) { std:: cout<< "消费者读取: " << data<< std:: endl; } sleep ( 1 ) ; } } 6.4 环形缓冲区实现 # include "ashmem.h" # include <atomic> class SharedRingBuffer { public : static constexpr int BUFFER_SIZE= 4096 ; static constexpr int HEADER_SIZE= sizeof ( int ) * 2 ; // head + tail SharedRingBuffer ( const char * name) { ashmem_= Ashmem :: CreateAshmem ( name, BUFFER_SIZE+ HEADER_SIZE) ; ashmem_-> MapReadAndWriteAshmem ( ) ; // 初始化头尾指针 int zero= 0 ; ashmem_-> WriteToAshmem ( & zero, sizeof ( int ) , 0 ) ; // head ashmem_-> WriteToAshmem ( & zero, sizeof ( int ) , sizeof ( int ) ) ; // tail } bool Write ( const void * data, int size) { int * head= ( int * ) ashmem_-> ReadFromAshmem ( sizeof ( int ) , 0 ) ; int * tail= ( int * ) ashmem_-> ReadFromAshmem ( sizeof ( int ) , sizeof ( int ) ) ; int available= ( BUFFER_SIZE+ * head- * tail) % BUFFER_SIZE; if ( size> available) { return false ; // 缓冲区满 } int writePos= HEADER_SIZE+ * tail; ashmem_-> WriteToAshmem ( data, size, writePos) ; int newTail= ( * tail+ size) % BUFFER_SIZE; ashmem_-> WriteToAshmem ( & newTail, sizeof ( int ) , sizeof ( int ) ) ; return true ; } int Read ( void * buffer, int maxSize) { int * head= ( int * ) ashmem_-> ReadFromAshmem ( sizeof ( int ) , 0 ) ; int * tail= ( int * ) ashmem_-> ReadFromAshmem ( sizeof ( int ) , sizeof ( int ) ) ; int available= ( * tail- * head+ BUFFER_SIZE) % BUFFER_SIZE; int readSize= std:: min ( available, maxSize) ; if ( readSize== 0 ) { return 0 ; } int readPos= HEADER_SIZE+ * head; const void * data= ashmem_-> ReadFromAshmem ( readSize, readPos) ; memcpy ( buffer, data, readSize) ; int newHead= ( * head+ readSize) % BUFFER_SIZE; ashmem_-> WriteToAshmem ( & newHead, sizeof ( int ) , 0 ) ; return readSize; } ~ SharedRingBuffer ( ) { ashmem_-> UnmapAshmem ( ) ; ashmem_-> CloseAshmem ( ) ; } private : sptr< Ashmem> ashmem_; } ; 7. 保护标志详解 7.1 保护标志类型 // Linux 内存保护标志 # define PROT_NONE 0x0 // 不可访问 # define PROT_READ 0x1 // 可读 # define PROT_WRITE 0x2 // 可写 # define PROT_EXEC 0x4 // 可执行 7.2 两层保护机制 flowchart TB subgraph 内核层保护 K[SetProtection] K --> KR[PROT_READ] K --> KW[PROT_WRITE] K --> KRW[PROT_READ|PROT_WRITE] end subgraph 用户空间保护 U[MapAshmem] U --> UR[PROT_READ] U --> UW[PROT_WRITE] U --> URW[PROT_READ|PROT_WRITE] end KRW --> URW KRW --> UR KR --> UR Note1[用户空间权限 ≤ 内核层权限]7.3 权限组合 内核层 用户空间 结果 READ READ ✅ 可读 READ WRITE ❌ 写入失败 WRITE WRITE ✅ 可写 WRITE READ ❌ 读取失败 READ|WRITE READ ✅ 可读 READ|WRITE WRITE ✅ 可写 READ|WRITE READ|WRITE ✅ 可读写
7.4 设置保护标志 sptr< Ashmem> ashmem= Ashmem :: CreateAshmem ( "Protected" , 1024 ) ; // 设置内核层保护(只读) ashmem-> SetProtection ( PROT_READ) ; // 尝试读写映射 - 会失败,因为内核层只允许读 bool success= ashmem-> MapReadAndWriteAshmem ( ) ; // false // 只读映射 - 成功 success= ashmem-> MapReadOnlyAshmem ( ) ; // true // 获取当前保护标志 int prot= ashmem-> GetProtection ( ) ; // PROT_READ 8. 最佳实践 8.1 使用建议 ✅ 推荐做法 // 1. 使用智能指针管理 sptr< Ashmem> ashmem= Ashmem :: CreateAshmem ( "name" , size) ; // 2. 检查创建结果 if ( ashmem== nullptr ) { // 处理错误 } // 3. 检查映射结果 if ( ! ashmem-> MapReadAndWriteAshmem ( ) ) { // 处理错误 } // 4. 检查读写结果 if ( ! ashmem-> WriteToAshmem ( data, size, offset) ) { // 处理错误 } // 5. 使用完毕后取消映射 ashmem-> UnmapAshmem ( ) ; // 6. 根据需要设置合适的保护标志 ashmem-> SetProtection ( PROT_READ) ; // 只读共享 ❌ 避免的错误 // 错误1: 不检查返回值 sptr< Ashmem> ashmem= Ashmem :: CreateAshmem ( "name" , - 1 ) ; // size 无效 ashmem-> MapReadAndWriteAshmem ( ) ; // ❌ ashmem 为 nullptr // 错误2: 越界访问 ashmem-> WriteToAshmem ( data, 1024 , 900 ) ; // ❌ 900 + 1024 > size // 错误3: 权限不匹配 ashmem-> SetProtection ( PROT_READ) ; ashmem-> MapReadAndWriteAshmem ( ) ; // ❌ 映射失败 // 错误4: 未映射就读写 sptr< Ashmem> ashmem= Ashmem :: CreateAshmem ( "name" , 1024 ) ; ashmem-> WriteToAshmem ( data, size, 0 ) ; // ❌ 未映射 // 错误5: 重复映射 ashmem-> MapReadAndWriteAshmem ( ) ; ashmem-> MapReadOnlyAshmem ( ) ; // ❌ 应该先 UnmapAshmem 8.2 性能优化 场景 建议 大数据量 使用 Ashmem 避免复制 频繁读写 保持映射状态,避免反复 map/unmap 多进程读 使用只读映射,提高安全性 对齐访问 按 4 字节或 8 字节对齐,提高效率
8.3 安全建议
安全建议
最小权限原则
边界检查
同步机制
只读进程用 PROT_READ
写入进程用 PROT_WRITE
检查 offset + size <= memorySize
检查返回值
多进程写入需要同步
使用互斥锁或信号量
8.4 调试技巧 // 打印 Ashmem 状态 void DebugAshmem ( const sptr< Ashmem> & ashmem) { std:: cout<< "=== Ashmem Debug ===" << std:: endl; std:: cout<< "FD: " << ashmem-> GetAshmemFd ( ) << std:: endl; std:: cout<< "Size: " << ashmem-> GetAshmemSize ( ) << std:: endl; std:: cout<< "Protection: " << ashmem-> GetProtection ( ) << std:: endl; } // 检查系统 Ashmem 使用情况 // cat /proc/ashmem (如果可用) 📊 API 速查表 全局函数 函数 说明 返回值 AshmemCreate(name, size)创建 Ashmem 区域 fd AshmemSetProt(fd, prot)设置保护标志 0 成功,-1 失败 AshmemGetSize(fd)获取大小 size
Ashmem 类 方法 说明 返回值 CreateAshmem(name, size)创建 Ashmem sptr GetAshmemFd()获取文件描述符 int GetAshmemSize()获取大小 int32_t SetProtection(type)设置保护标志 bool GetProtection()获取保护标志 int MapAshmem(mapType)映射内存 bool MapReadAndWriteAshmem()读写映射 bool MapReadOnlyAshmem()只读映射 bool UnmapAshmem()取消映射 void WriteToAshmem(data, size, offset)写入数据 bool ReadFromAshmem(size, offset)读取数据 void* CloseAshmem()关闭 void
保护标志 标志 值 说明 PROT_NONE0x0 不可访问 PROT_READ0x1 可读 PROT_WRITE0x2 可写 PROT_EXEC0x4 可执行
🎯 总结
记住这三点 :
先创建,再映射,才能读写 用户空间权限 ≤ 内核层权限 多进程写入需要同步机制