Ashmem(Android共享內存)使用方法和原理

如下內容基於Android API Version 27(Android 8.1)Linux Kernel 3.18.0java

簡介

Ashmem即Android Shared Memory, 是Android提供的一種內存共享的機制。node

使用

Application Java層藉助MemoryFileSharedMemory,Native層藉助libc的ashmem_create_regionmmap系統調用進行使用。linux

MemoryFile和SharedMemory底層也是基於ashmem_create_region/mmapandroid

MemoryFile

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只能經過調用SharedMemory.create靜態方法或者經過Parcel反序列化的方式進行建立。安全

SharedMemory的建立者進程經過靜態方法建立,使用者進程經過Parcel反序列化來建立。函數

由於SharedMemory類實現了Parcelable,因此能夠經過binder跨進程傳輸。flex

ashmem_create_region 和 mmap

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的Pin和Unpin

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};
複製代碼

ashmem建立:(從Java層到驅動層的調用鏈)

[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_open中只是建立了一個標識ashmem的結構體,而後返回fd,並無進行實際的內存分配(不管是虛擬內存仍是物理內存)。 獲得文件描述符後,就可使用ashmem_mmap將內核中的共享內存區域映射到進程的虛擬地址空間。

ashmem_mmap

ashmem_mmap經過調用內核中shmem相關函數在tempfs建立了一個大小等於建立ashmem時傳入大小的臨時文件(因爲是內存文件,因此磁盤上不存在實際的文件),而後將文件對應的內存映射到調用mmap的進程。(注意map的是臨時文件而不是ashmem文件)

其中涉及到的shmem函數包括shmem_file_setupshmem_set_file,他們爲該臨時文件建立inode節點,將文件關聯到爲該文件配的虛擬內存,同時爲該文件設置文件本身的文件操做指針(Linux原始共享內存shmem的文件操做),併爲虛擬內存設置缺頁處理函數。這樣後續對共享內存的操做就變爲了對tempfs文件節點的操做。當首次訪問共享內存時觸發缺頁中斷處理函數併爲該虛擬內存分配實際的物理內存。

tempfs是Unix-like系統中一種基於內存的文件系統,具備極高的訪問效率。
shmem是Linux自帶的進程間通訊機制:共享內存Shared Memory
共享內存的虛擬文件記錄在/proc/<pid>/maps文件中,pid表示打開這個共享內存文件的進程ID。

ashmem_pin/ashmem_unpin

pinunpinashmemiotrl支持的兩個操做,用於共享內存的分塊使用和分塊回收,用於節省實際的物理內存。 新建立的共享內存默認都是pined的,當調用unpin時,驅動將unpined的內存區域所在的頁掛在一個unpinned_list鏈表上,後續內存回收就是基於unpinned_list鏈表進行。

ashmem驅動初始化函數ashmem_init裏調用了內核函數register_shrinker,註冊了一個內存回收回調函數ashmem_shrink,當系統內存緊張時,就會回調ashmem_shrink,由驅動自身進行適當的內存回收。驅動就是在ashmem_shrink中遍歷unpinned_list進行內存回收,以釋放物理內存。

ashmem fd的傳遞:

fd經過Binder傳遞。

Binder機制不只支持binder對象的傳遞,還支持文件描述符的傳遞。fd通過binder驅動時,binder驅動會將源進程的fd轉換成目標進程的fd,轉換過程爲:取出發送方binder數據裏的fd,經過fd找到文件對象,而後爲目標進程建立fd,將目標進程fd和文件對象進行關聯,將發送方binder數據裏的fd改成目標進程的fd,而後將數據發送給目標進程。這個過程至關於文件在目標進程又打開了一次,目標進程使用的是本身的fd,但和源進程都指向的是同一個文件。這樣源進程和目標進程就均可以map到同一片內存了。

使用場景

  • 進程間共享較大的數據,好比大致積的bitmap和較大的數據數組。
  • 向內核偷內存。因爲ashmem內存既不在java heap上也不在native heap上,對ashmem的使用可使進程得到更多的額外內存,若是過分使用會有物理內存耗盡的風險。
  • 藉助Bitmap解碼的inPurgeable屬性,在android4.x及如下系統版本中實現內存在ashmem中分配,以節省Java堆內存。好比fresco圖片加載庫針對Android4.x及如下的機型對inPurgeable屬性的使用。

總結

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…

相關文章
相關標籤/搜索