如下內容基於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…