当前位置:网站首页>匿名共享内存 ashmem
匿名共享内存 ashmem
2022-08-09 04:47:00 【ldxlz224】
概述
mmap 是 Linux 中最为大家熟悉的共享内存方式。通过打开同一个文件,并且使用 MAP_SHARED 标志来调用 mmap() 函数,两个进程就能共享一片内存空间了。但是这种方式存在一个问题,如果分配的空间有一部分不需要了,不能单独释放这些不再使用的“物理内存”,为什么是物理内存呢,因为mmap分配的是地址空间,只有当进程存取某个页面时,才会去分配实际物理内存。这些物理内存只能通过 munmap() 一次性的释放掉。如果某个页面的物理内存不需要了,想把他单独释放,传统的 mmap 时无法做到的。所以就有了 ashmem
ashmem 的作用和用法
- 为了弥补 mmap 的不足,Android开发了 ashmem 匿名共享内存机制,这种新的机制是建立在 mmap 基础上,但是 ashmem 提供了 pin 和 unpin 两个 io 操作,能够部分释放内存空间的物理内存。
- 使用 ashmem 需要打开 “dev/asheme”,并把描述符传给 mmap() 作为参数。但是要注意的是,每次 open() 打开这个设备都会得到一个不同的文件描述符,代表了一块不同的内存区域。因此如果需要多块共享内存,只要多次打开设备 “dev/asheme” 就可以了。但是 ashmem 用作共享内存是会遇到一个问题,Linux 实现共享内存是,要求两个进程同时打开一个文件,并把文件描述符传递给内核,因为文件在 Linux 中是唯一的,这样系统就能知道两个进程需要操作同一块内存。但是 asheme 只能打开 “dev/asheme” 文件,这样无法用一个文件对象表示多块共享内存,而文件描述符是一个数字,只在本进程中有效。因此也不能通过某种方式把文件描述符传递给另外一个进程中使用。
- Android 的 Binder 提供了两个进程中传递文件描述符的手段,这样 Binder 的 Service 和 client 之间就可以实现内存共享了。因此 ashmem 并不能用于任意两个进程之间的共享内存,必须是在通过 Binder 建立了联系的两个进程之间。
Android 提供了一组使用 ashmem 的函数。头文件 ashmem.h 在/system/core/include/libcutil/下,实现代码 ashmem-dev.c 位于 /system/core/libcutil/ 中。使用 ashmem 步骤如下:
- 首先创建一个共享区域,通过调用 int ashmem_create_region(const char *name, size_t size) 来完成
int ashmem_create_region(const char *name, size_t size)
{
int ret, save_errno;
// 打开设备文件 /dev/ashmem/ 返回一个文件描述符 id
int fd = __ashmem_open();
if (fd < 0) {
return fd;
}
// 判断 name 是否为 NULL 如果不为 NULL 通过 ASHMEM_NAME_LEN 来设置属性
if (name) {
char buf[ASHMEM_NAME_LEN] = {
0};
strlcpy(buf, name, sizeof(buf));
// 由 ioctl 操作 ASHMEM_SET_NAME 来设置名称
// ioctl(input/output control)是一个专用于设备输入输出操作的系统调用
ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, buf));
if (ret < 0) {
goto error;
}
}
// 通过 ioctl 设置内存大小
ret = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size));
if (ret < 0) {
goto error;
}
return fd;
error:
save_errno = errno;
close(fd);
errno = save_errno;
return ret;
}
(1)ashmem_create_region() 的主要工作是,打开设备文件 dev/ashmem 得到一个文件描述符 fd。
(2)如果 name 不为 Null 则通过 ioctl 操作 TEMP_FAILURE_RETRY 来设置名称。
(3)通过 ioctl 调用 TEMP_FAILURE_RETRY 设置内存大小。
- 得到文件描述符后,通过 mmap 分配内存。
// addr:共享内存的地址,如果为NULL,则会自动分配一块内存
// length:共享内存的长度
// prot:内存保护的一些flags(比如说:匿名,读,写权限等)
// flags:是否对其他进程可见,更新是否会传递到底层文件
// fd:文件描述符(用于对内存初始化)
// offset:偏移量(用于初始化,offset从fd哪个位置开始读取,length可以表示读取长度
void* base = mmap(0,length,prot,flags,fd,offset)
- 如果需要修改内存属性通过下面函数,prot 参数和mmap的属性一致
int ashmem_set_prot_region(int fd, int prot)
{
int ret = __ashmem_is_ashmem(fd, 1);
if (ret < 0) {
return ret;
}
return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot));
}
- 解锁部分内存调用如下函数
int ashmem_unpin_region(int fd, size_t offset, size_t len)
{
// TODO: should LP64 reject too-large offset/len?
ashmem_pin pin = {
static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
int ret = __ashmem_is_ashmem(fd, 1);
if (ret < 0) {
return ret;
}
return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_UNPIN, &pin));
}
解锁后,这部分内存会在内存不足时会被回收。
- 如果需要可以把解锁的内存再次锁住
int ashmem_pin_region(int fd, size_t offset, size_t len)
{
// TODO: should LP64 reject too-large offset/len?
ashmem_pin pin = {
static_cast<uint32_t>(offset), static_cast<uint32_t>(len) };
int ret = __ashmem_is_ashmem(fd, 1);
if (ret < 0) {
return ret;
}
return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_PIN, &pin));
}
- 如果需要获取内存块的大小,则调用下面的函数
int ashmem_get_size_region(int fd)
{
int ret = __ashmem_is_ashmem(fd, 1);
if (ret < 0) {
return ret;
}
return TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL));
}
ashmem 实现原理
andorid 5.0 上
ashamed 是建立在 Linux 已有的内存分配和共享的基础上,本身做的事情并不复杂。ashmem 的驱动的主要工作是维护一个链表,这个链表的作用就是用户 IO 操作 unpin 后的一个个节点,里面保存了里面需要解锁的内存的开始和结束地址。当系统内存不足时就会通过这个链表释放一部分内存。而比较复杂的工作,如分配地址空间,还是通过内核去完成的,所以 ashmem 的主要工作就是维护 unpinned 链表。
struct ashmem_area {
char name[ASHMEM_FULL_NAME_LEN]; /* 名字出现在 /proc/pid/maps */
struct list_head unpinned_list; /* 列表头 用户调用 pin 和 unpin 生成的*/
struct file *file; /* 用来分配虚拟空间的文件 这是通过 mmap 分配内存生成的对象和打开设备的文件不同 */
size_t size; /* 内存块的大小 */
unsigned long vm_start; /* 映射这个ashmem的vm_area的起始地址 */
unsigned long prot_mask; /* 内存块的尺寸 */
};
当用户进程调用 open() 打开设备文件时,就会创建一个 ashmem_area 对象,保存一些 ashmem 内存块的信息,这个 ashmem_area 对象的指针会保存到设备文件对象 file 的 private_data 字段中,当用户进程使用文件描述符来调用 IO 操作时,驱动就能从文件对象中得到这个 ashmem_area 对象了。
另外一个很重要的结构式 ashmem_range 记录了被解锁内存块的基本信息。ashmem_range 就是unpinned 链表的节点,记录了被解锁内存块的基本信息。当用户进程执行unpin 操作时,驱动会生成一个 ashmem_range 的节点,这个节点会挂到一个全局链表 LRU 中,当系统内存不足时,会调用驱动注册的 ashmem_shrinker() 函数来释放内存,而 ashmem_shrinker() 函数将通过 LRU 链表来找到解锁的内存并释放模块,内存被释放后 ashmem_range 将会被移除,但是还会留在进程的 unpinned_list 中,同时其内部属性 purged 会设置成 true 代表已经被释放,就不会被重复释放了。
边栏推荐
猜你喜欢

A GDAL API version must be specified. Provide a path to gdal-config using a GDAL_CONFIG environment

Pycharm社区版专业版下载安装环境配置【精细到每一个步骤】

mysql content does not exist error

杰理之ANC OFF语音没有作用【篇】

EDI对接 New York & Company案例
——内核常见调试手段(printf、dump_stack、devmem)](/img/85/7b65fb095e9cbf724118f98581991a.png)
[21天学习挑战赛——内核笔记](四)——内核常见调试手段(printf、dump_stack、devmem)

【Harmony OS】【FAQ】鸿蒙问题合集1

杰理之采用mix out eq 没有作用【篇】

浙江DAMA-CDGA/CDGP数据治理认证招生简章

The influence law of genes for disease - read the paper
随机推荐
抖音直播间带货最新玩法和运营技巧
How to choose an APS system, it is necessary to clarify these seven key factors
uboot中board_init bi_arch_number在哪
杰理之一拖二 另一台手机超距 通话会无声【篇】
特征工程实战篇
HP路由器和交换机日志分析
The influence law of genes for disease - read the paper
阿里云天池大赛赛题(机器学习)——天猫用户重复购买预测(完整代码)
OKR management process, how to implement effective dialogue, using the CFR feedback and recognition?
【HMS core】【ML kit】机器学习服务常见问题FAQ
Poly1CrossEntropyLoss的pytorch实现
mysql内容不存在的报错
【Harmony OS】【ArkUI】ets开发 基础页面布局与数据连接
【Harmony OS】【ArkUI】ets开发 创建视图与构建布局
整数倍数数列
【HMS Core】【FAQ】【AR Engine】AR Engine常见问题合集
【Harmony OS】【FAQ】Hongmeng Questions Collection 1
Efficient review of deep learning DL, CV, NLP
供应商对接Chewy的EDI需求
OKR管理过程中,如何运用CFR实现组织的高效对话、反馈和认可?