如上图所示,Android系统匿名共享内存(以下以Ashmem代替)以Ashmem驱动为基础,在运行时库提供了cutils library来对Ashmem驱动进行访问,同时又在Framework层提供了C++和Java类接口来访问cutils。
这篇文章主要学习kernel层ashmem的基础数据结构,先来看看Ashmem到底是什么!源码版本Android 5
Ashmem驱动程序主要由ashmem.c和ashmem.h来实现,ashmem.h文件目录:kernel/include/uapi/linux/ashmem.h,ashmem.c文件目录:kernel/drivers/staging/android/ashmem.c,Ashmem驱动程序主要使用了三个结构体:ashmem_area, ashmem_range, ashmem_pin,前两个结构体定义在ashmem.c文件中,ashmem_pin定义在ashmem.h文件中:
结构体ashmem_area用来描述一块Ashmem---->
42 /**
43 * struct ashmem_area - The anonymous shared memory area
44 * @name: The optional name in /proc/pid/maps
45 * @unpinned_list: The list of all ashmem areas
46 * @file: The shmem-based backing file
47 * @size: The size of the mapping, in bytes
48 * @prot_masks: The allowed protection bits, as vm_flags
49 *
50 * The lifecycle of this structure is from our parent file's open() until
51 * its release(). It is also protected by 'ashmem_mutex'
52 *
53 * Warning: Mappings do NOT pin this structure; It dies on close()
54 */
55 struct ashmem_area {
56 char name[ASHMEM_FULL_NAME_LEN]; /* optional name in /proc/pid/maps */
57 struct list_head unpinned_list; /* list of all ashmem areas */
58 struct file *file; /* the shmem-based backing file */
59 size_t size; /* size of the mapping, in bytes */
60 unsigned long vm_start; /* Start address of vm_area
61 * which maps this ashmem */
62 unsigned long prot_mask; /* allowed prot bits, as vm_flags */
63 };
name字段解释:
结构体ashmem_area用来描述一块匿名共享内存区域,name用来保存匿名共享内存名称,同时会被写入到文件/proc/<pid>/maps中,<pid>表示创建该Ashmem的进程PID,ASHMEM_FULL_NAME_LEN表示name的长度,定义在文件头部:
38 #define ASHMEM_NAME_PREFIX "dev/ashmem/"
39 #define ASHMEM_NAME_PREFIX_LEN (sizeof(ASHMEM_NAME_PREFIX) - 1)
40 #define ASHMEM_FULL_NAME_LEN (ASHMEM_NAME_LEN + ASHMEM_NAME_PREFIX_LEN)
每一块Ashmem名称都是以dev/ashmem/为前缀,如果调用者未指定名称则:#define ASHMEM_NAME_DEF "dev/ashmem"为其默认名称,如果指定了名称长度限制为:#define ASHMEM_NAME_LEN 256 这两个定义在头文件ashmem.h中。
unpinned_list字段:
该字段用来描述一个解锁内存块列表,一块Ashmem可以被划分为多个小块,当这些小块内存处于解锁状态时,他们会被添加到所属匿名共享内存的解锁内存列表中,主要是为内存管理服务。
file、size字段:
匿名共享内存是基于linux系统的临时文件系统tmpfs来实现的,因此每一块匿名共享内存在临时文件系统tmpfs中都有一个对应的文件,这个文件大小即为匿名共享内存大小即size字段。
vm_start字段:
创建完匿名共享内存临时文件后需要将其映射到进程的地址空间,该字段表示将Ashmem临时文件映射到进程地址空间的起始位置。
prot_mask字段:
描述一块Ashmem的访问保护位,定义如下:ashmem.c
#define PROT_MASK (PROT_EXEC | PROT_READ | PROT_WRITE)
结构体ashmem_range用来描述一块处于解锁状态的内存---->
65 /**
66 * struct ashmem_range - A range of unpinned/evictable pages
67 * @lru: The entry in the LRU list
68 * @unpinned: The entry in its area's unpinned list
69 * @asma: The associated anonymous shared memory area.
70 * @pgstart: The starting page (inclusive)
71 * @pgend: The ending page (inclusive)
72 * @purged: The purge status (ASHMEM_NOT or ASHMEM_WAS_PURGED)
73 *
74 * The lifecycle of this structure is from unpin to pin.
75 * It is protected by 'ashmem_mutex'
76 */
77 struct ashmem_range {
78 struct list_head lru;
79 struct list_head unpinned;
80 struct ashmem_area *asma;
81 size_t pgstart;
82 size_t pgend;
83 unsigned int purged;
84 };
处于解锁状态的小块内存都是从一块匿名共享内存中划分出来的,他们通过unpinned成员变量链入宿主匿名共享内存的解锁内存列表unpinned_list中。一块处于解锁状态的内存宿主匿名共享内存是通过成员变量asma来描述的。
在Ashmem中,每一块处于解锁状态的内存都会通过成员变量lru链入到一个全局列表ashmem_lru_list中,这是个最近最少使用列表,定义如下:
/* LRU list of unpinned pages, protected by ashmem_mutex */
static LIST_HEAD(ashmem_lru_list);
处于解锁状态的内存都是目前不在需要使用的内存块,当系统内存不足的时候内存管理系统就会按照规则回收ashmem_lru_list中的内存块,成员变量pgstart和pgend用来描述这块处于解锁状态的内存块的起始地址和结束地址,单位是page(页)。
成员变量purged用来描述一块处于解锁状态的内存块是否已经被回收,他的值定义在头文件ashmem.h中:
12 /* Return values from ASHMEM_PIN: Was the mapping purged while unpinned? */
13 #define ASHMEM_NOT_PURGED 0 未被回收
14 #define ASHMEM_WAS_PURGED 1 已被回收
结构体ashmem_pin用来描述一块即将被锁定或者解锁的内存--->
20 struct ashmem_pin {
21 __u32 offset; /* offset into region, in bytes, page-aligned */
22 __u32 len; /* length forward from offset, in bytes, page-aligned */
23 };
offset表示描述的这块内存在其宿主匿名共享内存中的偏移值,len表示要被锁定或者解锁的内存块的大小,以字节为单位,并且对齐到页面边界的。
这三个结构体共同描述了一块Ashmem的多个状态,ashmem_area结构体即一块匿名共享内存的本体,即 告诉我们这块内存到底是什么。结构体ashmem_range描述Ashmem的解锁状态记录,提供内存管理相关便利。而ashmem_pin主要是一小块内存在被锁定或者解锁的过程中使用。