Ashmem 是什么?
Ashmem(Anonymous Shared Memory 匿名共享内存),是在 Android 的内存管理中提供的一种机制。它基于mmap系统调用,不同的进程可以将同一段物理内存空间映射到各自的虚拟空间,从而实现共享。
mmap机制
mmap系统调用是将一个打开的文件映射到进程的用户空间,mmap系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。
mmap 函数原型:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
* addr: *指定为文件描述符fd应被映射到的进程空间的起始地址。它通常被指定为一个空指针,这样告诉内核自己去选择起始地址。一般默认为NULL
* length: *是映射到调用进程地址空间中的字节数,从被映射文件开头offset个字节处开始算
* prot: *负责保护内存映射区的保护。常用值是代表读写访问的PROT_READ | PROT_WRITE.当然还包括数据的执行(PROT_EXEC)、数据不可访问(PROT_NONE)
** flag: **flags常用值有MAP_SHARED或MAP_PRIVATE这两个标志必须选一个,并可以选上MAP_FIXED。如果指定了,那么调用进程对被映射数据所做的修改只对该进程可见,而不该变其底层支撑对象。如果指定了,那么调用进程对被映射数据所作的修改对于共享该对象的所有进程都可见,而且确实改变了其底层支撑对象
* fd: *参数fd为映射文件的描述符,offset为文件的起点,默认为0
* offset: *偏移量
ashmem 在 mmap 上的改进
ashmem通过内核驱动提供了辅助内核的内存回收算法机制(pin/unpin)
什么是pin和unpin呢?
具体来讲,就是当你使用Ashmem分配了一块内存,但是其中某些部分却不会被使用时,那么就可以将这块内存unpin掉。unpin后,内核可以将它对应的物理页面回收,以作他用。你也不用担心进程无法对unpin掉的内存进行再次访问,因为回收后的内存还可以再次被获得(通过缺页handler),因为unpin操作并不会改变已经 mmap的地址空间。
Ashmem 的定义
- 我们先来看一下部分 ashmem 实现的头文件(ashmem.h)
#define ASHMEM_NAME_LEN 256
//定义设备名称
#define ASHMEM_NAME_DEF "dev/ashmem"
/* 从 ASHMEM_PIN 返回的值: 判断是否要清除 */
#define ASHMEM_NOT_PURGED 0
#define ASHMEM_WAS_PURGED 1
/*从 ASHMEM_GET_PIN_STATUS 返回的值: 是 pinned 还是 unpined */
#define ASHMEM_IS_UNPINNED 0
#define ASHMEM_IS_PINNED 1
struct ashmem_pin {
__u32 offset; /* 偏移量 */
__u32 len; /* 从偏移开始的长度 */
};
Ashmem 是怎么实现的?
下面我们开始按照 Ashmem 的实现代码来看看它是怎么样工作的(ashmem.c)
我们先来看一下两个结构体ashmem_area
和ashmem_range
:
/*
* ashmem_area - anonymous shared memory area
* Lifecycle: From our parent file's open() until its release()
* Locking: Protected by `ashmem_mutex'
* Big Note: Mappings do NOT pin this structure; it dies on close()
*/
struct ashmem_area {
char name[ASHMEM_FULL_NAME_LEN]; /* 用于/proc/pid/maps中的一个标识名称(可选) */
struct list_head unpinned_list; /* 所有 ashmem 共享内存区域列表 */
struct file *file; /* ashmem 支持的文件 */
size_t size; /* 区域字节大小 */
unsigned long prot_mask; /* 内存映射区的保护 */
};
我们可以看到 ashmem_area
定义了一个内存共享区域,它的生命周期是从文件打开open()
到它被释放release()
,并且支持原子性
/*
* ashmem_range - represents an interval of unpinned (evictable) pages
* Lifecycle: From unpin to pin
* Locking: Protected by `ashmem_mutex'
*/
struct ashmem_range {
struct list_head lru; /* LRU 列表 */
struct list_head unpinned; /* unpinned 列表 */
struct ashmem_area *asma; /* 关联的 ashmem 区域 */
size_t pgstart; /* 开始页面 */
size_t pgend; /* 结束页面 */