如下内容基于Android API Version 27(Android 8.1)Linux Kernel 3.18.0java
Ashmem即Android Shared Memory, 是Android提供的一种内存共享的机制。node
Application Java层借助MemoryFile
和SharedMemory
,Native层借助libc的ashmem_create_region
和mmap
系统调用进行使用。linux
MemoryFile和SharedMemory底层也是基于ashmem_create_region/mmapandroid
MemoryFile是对SharedMemory的包装,官方推荐使用SharedMemory。c#
Applications should generally prefer to use {@link SharedMemory} which offers more flexible access & control over the shared memory region than MemoryFile does.数组
SharedMemory
只能经过调用SharedMemory.create
静态方法或者经过Parcel反序列化的方式进行建立。安全
SharedMemory
的建立者进程经过静态方法建立,使用者进程经过Parcel反序列化来建立。函数
由于SharedMemory
类实现了Parcelable
,因此能够经过binder跨进程传输。flex
int ashmem_create_region(const char *name, size_t size) 复制代码
用于建立共享内存,函数内部首先经过open
函数打开/dev/ashmem
设备,获得文件描述符后,经过调用ioctl
设置fd的名称和大小。spa
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
复制代码
经过binder将fd传递到其余进程后,其余进程能够经过mmap
系统调用,将共享内存映射到当前进程的地址空间,以后就能够经过返回的内存首地址进行内存读写。
这样,两个进程之间就实现了直接的内存共享,得到了极高的进程间通讯效率。
ashmem驱动提供了两个用于内存管理的ioctl
操做命令:pin/unpin
,直接经过ashmem_create_region
建立的共享内存默认是pined
的状态,也就是说,应用程序不主动关闭共享内存fd的状况下,这篇内存会始终保留,直到进程死亡。
若是调用unpin
将共享内存中的某段内存解除锁定,以后若是系统内存不足,会自动释放这部份内存,再次使用同一段内存前应该先执行pin操做,若是pin操做返回ASHMEM_WAS_PURGED
,也就是说内存已经被回收,已经回收的内存再次访问会触发缺页中断从新进行物理内存的分配,所以这段内存里的数据已经不是起初的那个数据了,若是仍旧当作原始数据进行访问必然引起错误。
经过pin/unpin
命令,配合ashmem
驱动,能够进行简单的内存管理。
Ashmem的核心原理主要是两部分:驱动和fd传递。
Ashmem是Linux内核中的一个misc设备,对应的设备文件是/dev/ashmem
,此设备是一个虚拟设备,不存在实际文件,只在内核驱动中对应一个inode节点。Ashmem在驱动层是基于linux系统的共享内存功能实现的,Ashmem能够理解为只是对原生的共享内存进行了一层包装,使其更方便在Android系统上使用。
ashmem
设备文件支持以下操做:
// /drivers/staging/android/ashmem.c
809static const struct file_operations ashmem_fops = {
810 .owner = THIS_MODULE,
811 .open = ashmem_open,
812 .release = ashmem_release,
813 .read = ashmem_read,
814 .llseek = ashmem_llseek,
815 .mmap = ashmem_mmap,
816 .unlocked_ioctl = ashmem_ioctl,
817#ifdef CONFIG_COMPAT
818 .compat_ioctl = compat_ashmem_ioctl,
819#endif
820};
复制代码
[java] android.os.SharedMemory#create
[jni] /frameworks/base/core/jni/android_os_SharedMemory.cpp#SharedMemory_create
[libc] /system/core/libcutils/ashmem-dev.c#ashmem_create_region
[driver] /drivers/staging/android/ashmem.c#ashmem_open
复制代码
ashmem_open
中只是建立了一个标识ashmem
的结构体,而后返回fd,并无进行实际的内存分配(不管是虚拟内存仍是物理内存)。 获得文件描述符后,就可使用ashmem_mmap
将内核中的共享内存区域映射到进程的虚拟地址空间。
ashmem_mmap
经过调用内核中shmem
相关函数在tempfs建立了一个大小等于建立ashmem
时传入大小的临时文件(因为是内存文件,因此磁盘上不存在实际的文件),而后将文件对应的内存映射到调用mmap的进程。(注意map的是临时文件而不是ashmem
文件)
其中涉及到的shmem
函数包括shmem_file_setup
和shmem_set_file
,他们为该临时文件建立inode节点,将文件关联到为该文件配的虚拟内存,同时为该文件设置文件本身的文件操做指针(Linux原始共享内存shmem的文件操做),并为虚拟内存设置缺页处理函数。这样后续对共享内存的操做就变为了对tempfs文件节点的操做。当首次访问共享内存时触发缺页中断处理函数并为该虚拟内存分配实际的物理内存。
tempfs
是Unix-like系统中一种基于内存的文件系统,具备极高的访问效率。
shmem
是Linux自带的进程间通讯机制:共享内存Shared Memory
。
共享内存的虚拟文件记录在/proc/<pid>/maps
文件中,pid表示打开这个共享内存文件的进程ID。
pin
和unpin
是ashmem
的iotrl
支持的两个操做,用于共享内存的分块使用和分块回收,用于节省实际的物理内存。 新建立的共享内存默认都是pined的,当调用unpin
时,驱动将unpined的内存区域所在的页挂在一个unpinned_list
链表上,后续内存回收就是基于unpinned_list
链表进行。
在ashmem
驱动初始化函数ashmem_init
里调用了内核函数register_shrinker
,注册了一个内存回收回调函数ashmem_shrink
,当系统内存紧张时,就会回调ashmem_shrink
,由驱动自身进行适当的内存回收。驱动就是在ashmem_shrink
中遍历unpinned_list
进行内存回收,以释放物理内存。
fd经过Binder传递。
Binder机制不只支持binder对象的传递,还支持文件描述符的传递。fd通过binder驱动时,binder驱动会将源进程的fd转换成目标进程的fd,转换过程为:取出发送方binder数据里的fd,经过fd找到文件对象,而后为目标进程建立fd,将目标进程fd和文件对象进行关联,将发送方binder数据里的fd改成目标进程的fd,而后将数据发送给目标进程。这个过程至关于文件在目标进程又打开了一次,目标进程使用的是本身的fd,但和源进程都指向的是同一个文件。这样源进程和目标进程就均可以map到同一片内存了。
Ashmem
经过对Linux共享内存的扩展,一方面使其使用更简单,另外一方面使其只能经过binder传递,增长了安全性。Ashmem
在Android系统中起着很是重要的做用,好比整个显示系统Activity-WindowsManagerService-SurfaceFlinger就是经过Ashmem
传递的帧数据-Surface。
参考:
blog.csdn.net/Luoshengyan…
blog.csdn.net/Luoshengyan…
blog.csdn.net/Luoshengyan…
www.jianshu.com/p/d9bc9c668…