基本原理
Android 的Ashmem是一种共享内存的机制,它基于mmap系统调用,不同进程可以将同一段物理内存映射到各自的虚拟地址控制,从而实现共享。
Ashmem与mmap的区别
mmap通过映射同一个普通文件实现进程间共享内存,普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问,不必再调用read,write等操作。进程在映射空间对共享内存的改变并不直接写回到磁盘文件中,在调用munmap后才执行此操作。可以通过调用msync实现磁盘上文件内存与共享内存区的内容一致。
Ashmem与mmap的区别在于Ashmem与cache shrinker关联起来,可以控制cache shrinker在适当时机回收这些共享内存。
Ashmem的实现
Ashmem的源代码在mm/ashmem.c中,它通过注册Cache Shrinker回收内存,通过注册misc设备提供open,mmap等接口,mmap则通过tmpfs创建文件来分配内存,tmpfs将一块内存虚拟为一个文件,这样操作共享内存就相当于操作一个文件。
Ashmem用两个结构体ashmem_area和ashmem_range来维护分配的内存,ashmem_area代表共享的内存区域,ashmem_range则将这段区域以页为单位分为多个range。
ashmem_area有个unpinned_list成员,挂在这个list上的range可以被回收。ashmem_range有一个LRU链表,在cache shrink回收一个ashmem_area的某段内存时候,是根据LRU的原则来选择哪些页面优先被回收的。
Ashmem的基本结构如下图所示。
下面依次简单分析主要函数功能:
l ashmem_init
这是module初始化函数,Ashmem是作为一个模块实现的。该函数主要功能:
n 调用kmem_cache_create分别创建struct ashmem_area和structashmem_range 的slab cache
n 调用misc_register注册 ahsmem driver
n 调用register_shrinker注册Ashmem的 CacheShrinker
l ashmem_open
标准misc设备的open函数。它调用kmem_cache_zalloc分配一个 ashmem_area,并初始化各成员变量。
l ashmem_release
做与ashmem_open相反工作,释放tmpfs文件,ashmem_area及其ashmem_range。
l ashmem_mmap
mmap操作,主要就是调用shmem_file_setup从tmpfs文件系统中创建一个文件(实际上就是一段RAM)给ashmem_area用,该文件代表着这段被共享的内存。Ashmem真正实现进程共享内存的机制是靠shmem这个linux标准机制提供的。
l ashmem_shrink
即Ashmem的cacheshrink函数。它被mm/vmscan.c::shrink_slab调用,或者被用户的 ioctl
命令调用。这个函数从LRU链表上回收指定数目的 unpinned ashmem_range。
l ashmem_ioctl
这个函数提供ioctl 接口,它实现了如下命令:
l ashmem_unpin
unpin一段内存。实现的方法很简单,就是分配一个ashmem_range,把它挂到ashmem_area -> unpinned_list上,并加到 LRU链表上。
ashmem_pin
pin一段内存,从 ashmem_area->unpinned_list上拿下这个 ashmem_range,由此可知,被unpin的range才能被回收,pin的range则不能回收。
用户接口
Ashmem驱动创建了/dev/ashmem设备文件,进程A可通过open打开该文件,用ioctl命令ASHMEM_SET_NAME和ASHMEM_SET_SIZE设置共享内存块的名字和大小,并将得到的handle传给mmap,来获得共享的内存区域,进程B通过将相同的handle传给mmap,获得同一块内存,handle在进程间的传递可通过Binder来实现。