Android Ashmem(Anonymous Shared Memory,匿名共享内存)是一种高效的内存共享机制,广泛应用于进程间通信和数据共享。
1. Ashmem 的基本概念
Ashmem 是一种匿名共享内存机制,允许多个进程共享同一块内存区域,而无需显式的文件系统交互。它基于 Linux 内核的内存映射机制(mmap)实现,通过文件描述符(File Descriptor)传递共享内存的句柄。
2. Ashmem 的工作原理
2.1 创建共享内存区域
进程通过调用 ashmem_create_region 函数创建一个共享内存区域,并指定内存大小和名称。此时,内核会分配一块匿名内存,并返回一个文件描述符(fd),用于后续操作。
2.2 内存映射
创建共享内存后,进程使用 mmap 系统调用将这块内存映射到自身的虚拟地址空间。这样,进程就可以像访问普通内存一样访问共享内存。
2.3 共享文件描述符
通过 Binder 机制,进程可以将文件描述符(fd)传递给其他进程。接收方进程通过 mmap 将相同的共享内存区域映射到自己的地址空间,从而实现内存共享。
2.4 内存管理
Ashmem 提供了内存锁定(pin)和解锁(unpin)机制。初始状态下,共享内存是锁定的(pinned),表示内存不能被回收。当调用 ashmem_unpin_region 时,内核可以回收这部分内存,但不会改变内存的映射状态。如果进程再次访问该内存,内核会重新分配物理页面。
2.5 内存回收
当系统内存不足时,Ashmem 驱动会通过维护的 unpinned 链表,回收未锁定的内存页面。这种机制可以有效避免内存泄漏,并优化系统内存使用。
3. Ashmem 的优势
高效性:通过内存映射实现数据共享,减少了数据拷贝。安全性:共享内存是匿名的,没有文件系统路径与之关联,避免了潜在的安全风险。内存管理优化:支持内存锁定和解锁,内核可以根据需要回收内存。
4. 应用场景
跨进程通信:用于不同进程之间高效传递大量数据。多媒体处理:在音视频处理中共享数据,提高处理效率。图形渲染:共享纹理数据,提升渲染性能。
5. 注意事项
Ashmem 不会占用 Dalvik heap 和 Native heap,因此不会导致 Out of Memory(OOM)。Ashmem 的内存占用会计算到第一个创建它的进程中,其他进程不会重复计算。
Ashmem(Anonymous Shared Memory)的实现原理基于 Linux 内核的内存映射机制和虚拟文件系统(如 TMPFS)
Ashmem 实现原理
虚拟设备文件: Ashmem 使用一个虚拟设备文件 /dev/ashmem,它是一个字符设备,由内核中的 misc 设备驱动管理。当 Ashmem 驱动加载后,会在 /dev 下创建一个 ashmem 文件,用户空间可以通过打开这个文件来访问 Ashmem 服务。
创建共享内存区域: 在用户空间中,通过调用 ashmem_create_region 函数创建共享内存区域。该函数首先打开 /dev/ashmem 文件,然后通过 ioctl 设置共享内存的名称和大小。其核心代码如下:
int ashmem_create_region(const char *name, size_t size)
{
int fd, ret;
fd = open(ASHMEM_DEVICE, O_RDWR);
if (fd < 0)
return fd;
if (name) {
char buf[ASHMEM_NAME_LEN];
strlcpy(buf, name, sizeof(buf));
ret = ioctl(fd, ASHMEM_SET_NAME, buf);
if (ret < 0)
goto error;
}
ret = ioctl(fd, ASHMEM_SET_SIZE, size);
if (ret < 0)
goto error;
return fd;
error:
close(fd);
return ret;
}
这段代码通过 ioctl 调用内核空间的 Ashmem 驱动。
内核空间的 Ashmem 驱动: 在内核空间中,Ashmem 驱动通过 misc 设备框架实现。当 /dev/ashmem 被打开时,内核会调用 ashmem_open 函数,创建一个 ashmem_area 结构体,用于表示共享内存区域:
static int ashmem_open(struct inode *inode, struct file *file)
{
struct ashmem_area *asma;
int ret;
ret = nonseekable_open(inode, file);
if (unlikely(ret))
return ret;
asma = kmem_cache_zalloc(ashmem_area_cachep, GFP_KERNEL);
if (unlikely(!asma))
return -ENOMEM;
INIT_LIST_HEAD(&asma->unpinned_list);
memcpy(asma->name, ASHMEM_NAME_PREFIX, ASHMEM_NAME_PREFIX_LEN);
asma->prot_mask = PROT_MASK;
file->private_data = asma;
return 0;
}
内存映射: 用户空间通过 mmap 系统调用将共享内存映射到进程的地址空间。内核会根据 ashmem_area 结构体中的信息,将对应的内存区域映射到进程的虚拟地址空间。当进程访问映射的内存时,如果发生缺页中断,内核会调用 shmem_fault 函数来处理。
内存锁定与解锁: Ashmem 提供了 pin 和 unpin 操作,用于锁定和解锁共享内存区域。初始状态下,共享内存是锁定的,内核不会回收其物理页面。当调用 ashmem_unpin_region 时,内核可以回收未锁定的内存页面。其核心代码如下:
int ashmem_pin_region(int fd, size_t offset, size_t len)
{
struct ashmem_pin pin = { offset, len };
return ioctl(fd, ASHMEM_PIN, &pin);
}
int ashmem_unpin_region(int fd, size_t offset, size_t len)
{
struct ashmem_pin pin = { offset, len };
return ioctl(fd, ASHMEM_UNPIN, &pin);
}
Ashmem 的优势
高效性:通过内存映射实现数据共享,减少了数据拷贝。安全性:共享内存是匿名的,没有文件系统路径与之关联,避免了潜在的安全风险。内存管理优化:支持内存锁定和解锁,内核可以根据需要回收内存。