当前位置: 首页 > news >正文

【OpenHarmony】匿名共享内存模块详解

匿名共享内存模块详解

🧠一句话概括:Ashmem(匿名共享内存)就像"进程间的公共黑板",多个进程可以同时读写同一块内存。


📚 目录

  1. 什么是匿名共享内存?
  2. 为什么需要 Ashmem?
  3. Ashmem 工作原理
  4. Ashmem 类详解
  5. 核心操作流程
  6. 使用示例
  7. 保护标志详解
  8. 最佳实践

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")返回 fdioctl(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
// 静态工厂方法staticsptr<Ashmem>CreateAshmem(constchar*name,int32_tsize);
CreateAshmem
参数有效?
返回 nullptr
AshmemCreate
open /dev/ashmem
ioctl SET_NAME
ioctl SET_SIZE
成功?
new Ashmem
返回 sptr
映射内存
boolMapAshmem(intmapType);// 通用映射boolMapReadAndWriteAshmem();// 读写映射boolMapReadOnlyAshmem();// 只读映射voidUnmapAshmem();// 取消映射
flowchart LR subgraph 映射类型 A[MapAshmem<br/>PROT_READ] --> R[只读] B[MapAshmem<br/>PROT_WRITE] --> W[只写] C[MapAshmem<br/>PROT_READ|PROT_WRITE] --> RW[读写] end
读写数据
boolWriteToAshmem(constvoid*data,int32_tsize,int32_toffset);constvoid*ReadFromAshmem(int32_tsize,int32_toffset);
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 跨进程共享流程

进程ABinder/IPC进程BCreateAshmem("shared", 1024)MapReadAndWriteAshmem()WriteToAshmem(data)传递 fd接收 fdnew Ashmem(fd, size)MapReadOnlyAshmem()ReadFromAshmem()两个进程共享同一块内存!进程ABinder/IPC进程B

6. 使用示例

6.1 基本用法

#include"ashmem.h"#include<iostream>#include<cstring>usingnamespaceOHOS;voidBasicAshmemDemo(){// 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. 写入数据constchar*message="Hello, Ashmem!";if(ashmem->WriteToAshmem(message,strlen(message)+1,0)){std::cout<<"写入成功"<<std::endl;}// 4. 读取数据constchar*readData=static_cast<constchar*>(ashmem->ReadFromAshmem(strlen(message)+1,0));if(readData){std::cout<<"读取到: "<<readData<<std::endl;}// 5. 取消映射ashmem->UnmapAshmem();// 6. 关闭(析构时也会自动关闭)ashmem->CloseAshmem();}

6.2 写入结构体

#include"ashmem.h"structUserData{intid;charname[32];doublescore;};voidWriteStructDemo(){sptr<Ashmem>ashmem=Ashmem::CreateAshmem("UserData",sizeof(UserData)*10);ashmem->MapReadAndWriteAshmem();// 写入多个结构体for(inti=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;intoffset=i*sizeof(UserData);ashmem->WriteToAshmem(&user,sizeof(UserData),offset);}// 读取第 5 个用户intreadOffset=4*sizeof(UserData);constUserData*user5=static_cast<constUserData*>(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>voidProducerProcess(){// 创建共享内存sptr<Ashmem>ashmem=Ashmem::CreateAshmem("SharedBuffer",4096);ashmem->MapReadAndWriteAshmem();intfd=ashmem->GetAshmemFd();intsize=ashmem->GetAshmemSize();// 通过某种 IPC 方式(如 Binder)将 fd 和 size 传递给进程 B// SendToProcessB(fd, size);// 写入数据intcounter=0;while(true){charbuffer[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>voidConsumerProcess(intfd,intsize){// 使用从进程 A 获取的 fd 创建 Ashmemsptr<Ashmem>ashmem=newAshmem(fd,size);// 只读映射ashmem->MapReadOnlyAshmem();// 读取数据while(true){constchar*data=static_cast<constchar*>(ashmem->ReadFromAshmem(64,0));if(data){std::cout<<"消费者读取: "<<data<<std::endl;}sleep(1);}}

6.4 环形缓冲区实现

#include"ashmem.h"#include<atomic>classSharedRingBuffer{public:staticconstexprintBUFFER_SIZE=4096;staticconstexprintHEADER_SIZE=sizeof(int)*2;// head + tailSharedRingBuffer(constchar*name){ashmem_=Ashmem::CreateAshmem(name,BUFFER_SIZE+HEADER_SIZE);ashmem_->MapReadAndWriteAshmem();// 初始化头尾指针intzero=0;ashmem_->WriteToAshmem(&zero,sizeof(int),0);// headashmem_->WriteToAshmem(&zero,sizeof(int),sizeof(int));// tail}boolWrite(constvoid*data,intsize){int*head=(int*)ashmem_->ReadFromAshmem(sizeof(int),0);int*tail=(int*)ashmem_->ReadFromAshmem(sizeof(int),sizeof(int));intavailable=(BUFFER_SIZE+*head-*tail)%BUFFER_SIZE;if(size>available){returnfalse;// 缓冲区满}intwritePos=HEADER_SIZE+*tail;ashmem_->WriteToAshmem(data,size,writePos);intnewTail=(*tail+size)%BUFFER_SIZE;ashmem_->WriteToAshmem(&newTail,sizeof(int),sizeof(int));returntrue;}intRead(void*buffer,intmaxSize){int*head=(int*)ashmem_->ReadFromAshmem(sizeof(int),0);int*tail=(int*)ashmem_->ReadFromAshmem(sizeof(int),sizeof(int));intavailable=(*tail-*head+BUFFER_SIZE)%BUFFER_SIZE;intreadSize=std::min(available,maxSize);if(readSize==0){return0;}intreadPos=HEADER_SIZE+*head;constvoid*data=ashmem_->ReadFromAshmem(readSize,readPos);memcpy(buffer,data,readSize);intnewHead=(*head+readSize)%BUFFER_SIZE;ashmem_->WriteToAshmem(&newHead,sizeof(int),0);returnreadSize;}~SharedRingBuffer(){ashmem_->UnmapAshmem();ashmem_->CloseAshmem();}private:sptr<Ashmem>ashmem_;};

7. 保护标志详解

7.1 保护标志类型

// Linux 内存保护标志#definePROT_NONE0x0// 不可访问#definePROT_READ0x1// 可读#definePROT_WRITE0x2// 可写#definePROT_EXEC0x4// 可执行

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 权限组合

内核层用户空间结果
READREAD✅ 可读
READWRITE❌ 写入失败
WRITEWRITE✅ 可写
WRITEREAD❌ 读取失败
READ|WRITEREAD✅ 可读
READ|WRITEWRITE✅ 可写
READ|WRITEREAD|WRITE✅ 可读写

7.4 设置保护标志

sptr<Ashmem>ashmem=Ashmem::CreateAshmem("Protected",1024);// 设置内核层保护(只读)ashmem->SetProtection(PROT_READ);// 尝试读写映射 - 会失败,因为内核层只允许读boolsuccess=ashmem->MapReadAndWriteAshmem();// false// 只读映射 - 成功success=ashmem->MapReadOnlyAshmem();// true// 获取当前保护标志intprot=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 状态voidDebugAshmem(constsptr<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)创建 Ashmemsptr
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可执行

🎯 总结

记住这三点

  1. 先创建,再映射,才能读写
  2. 用户空间权限 ≤ 内核层权限
  3. 多进程写入需要同步机制

http://www.cnnetsun.cn/news/17542.html

相关文章:

  • Archery数据库管理平台:5大核心功能深度解析与实战配置指南
  • 学Python有用吗?
  • Onekey终极指南:3分钟学会Steam游戏清单自动下载
  • 程序员:微软的面试流程探讨
  • 爆款!国产开源公众号AI管家ChatWiki全解析
  • 2、Linux 操作系统基础与 Bash 命令行使用指南
  • 网闸厂家排行:安全隔离技术迭代下的选型指南
  • 单北斗变形监测在水库安全中的应用与维护该如何实施?
  • Win11Debloat:终极Windows系统清理优化完整指南
  • 第一季影响设计可靠性的第一性原理(2)
  • 2025年软件工程/计算机科学与技术专业毕业设计选题推荐
  • NTFSTool:在macOS上实现NTFS磁盘完整读写的终极解决方案
  • Qwen3-Reranker-8B:重新定义文本检索效率与精度边界
  • PingFang SC woff2字体:网页中文排版的终极解决方案
  • ModernWMS开源仓库管理系统终极配置与功能详解
  • MPV播放器macOS硬件解码色彩异常终极修复指南
  • Flink自定义函数实战:从入门到精通的完整开发手册
  • 哔哩哔哩漫画下载器技术指南
  • 目标检测数据集 - 穿着服饰检测数据集下载
  • Windows系统终极清理工具:快速免费释放C盘空间
  • Spring AOP场景3——接口防抖(附带源码)
  • 黑客技术水深!普通人不要随便碰
  • AI建议的C++基础入门顺序
  • 35、使用 Ansible 部署脚本化服务器环境
  • 13、文件操作与命令全解析
  • 第32课 PCB布局的常用操作命令介绍
  • 终极修复指南:彻底解决Atmosphere固件2168-0002启动错误
  • 第一章——办公自动化之Word报告自动生成:解放双手,高效创作
  • 压电材料的d33(纵向压电应变常数)测试流程及影响因素
  • 中烟创新连续两年被认定为国家级科技型中小企业