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

簡介

Ashmem即Android Shared Memory, 是Android提供的一種內(nèi)存共享的機(jī)制泽台。

使用

  • Java層借助MemoryFile或者SharedMemory矾缓。
  • Native層借助MemoryHeapBase或者MemoryBase嗜闻。
  • Native層直接調(diào)用libc的ashmem_create_regionmmap系統(tǒng)調(diào)用桅锄。

MemoryFile基于SharedMemoryMemoryBase基于MemoryHeapBase友瘤。 SharedMemoryMemoryHeapBase都是基于ashmem_create_region/mmap束倍。

MemoryFile

MemoryFile是對SharedMemory的包裝盟戏,官方推薦直接使用SharedMemory。

Applications should generally prefer to use {@link SharedMemory} which offers more flexible access & control over the shared memory region than MemoryFile does.

SharedMemory

SharedMemory只能通過調(diào)用SharedMemory.create靜態(tài)方法或者通過Parcel反序列化的方式進(jìn)行創(chuàng)建邮旷。 SharedMemory的創(chuàng)建進(jìn)程通過SharedMemory.create創(chuàng)建蝇摸,使用進(jìn)程通過Parcel反序列化創(chuàng)建。

因為SharedMemory類實現(xiàn)了Parcelable律歼,所以可以通過binder跨進(jìn)程傳輸蜂嗽。

MemoryBase和MemoryHeapBase

MemoryBase是對MemoryHeapBase的包裝。MemoryHeapBase對應(yīng)一塊共享內(nèi)存辱揭,使用ashmem_create_region/mmap創(chuàng)建病附,MemoryHeapBase內(nèi)部保存了共享內(nèi)存的地址和大小。通過MemoryBase可以獲取其包裝的MemoryHeapBase域庇。

MemoryBaseMemoryHeapBase都是Binder本地對象(BBinder),可以直接傳到其他進(jìn)程听皿。其他進(jìn)程分別使用IMemoryIMemoryHeap進(jìn)行跨進(jìn)程調(diào)用。

MemoryHeapBase跨進(jìn)程傳輸本質(zhì)上傳輸?shù)氖枪蚕韮?nèi)存的fd尉姨,fd在經(jīng)過binder驅(qū)動時會被轉(zhuǎn)換成目標(biāo)進(jìn)程的fd,MemoryHeapBase的客戶端代理對象BpMemoryHeap在創(chuàng)建時候會將fd映射到自己的內(nèi)存空間九府,這樣客戶端進(jìn)程在使用IMemoryHeap接口獲取到的內(nèi)存地址就是自己進(jìn)程空間的地址覆致。

ashmem_create_region 和 mmap

ashmem_create_region/mmap是SharedMemory和MemoryHeapBase的實現(xiàn)基礎(chǔ)。

int ashmem_create_region(const char *name, size_t size)

用于創(chuàng)建共享內(nèi)存儡羔,函數(shù)內(nèi)部首先通過open函數(shù)打開/dev/ashmem設(shè)備声旺,得到文件描述符后,通過調(diào)用ioctl設(shè)置fd的名稱和大小鉴扫。

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

通過binder將fd傳遞到其他進(jìn)程后澈缺,其他進(jìn)程可以通過mmap系統(tǒng)調(diào)用,將共享內(nèi)存映射到當(dāng)前進(jìn)程的地址空間莱预,之后就可以通過返回的內(nèi)存首地址進(jìn)行內(nèi)存讀寫项滑。

這樣,兩個進(jìn)程之間就實現(xiàn)了直接的內(nèi)存共享枪狂,獲得了極高的進(jìn)程間通信效率。

Ashmem的Pin和Unpin

ashmem驅(qū)動提供了兩個用于內(nèi)存管理的ioctl操作命令:pin/unpin辜限,直接通過ashmem_create_region創(chuàng)建的共享內(nèi)存默認(rèn)是pined的狀態(tài)严蓖,也就是說氧急,應(yīng)用程序不主動關(guān)閉共享內(nèi)存fd的情況下吩坝,這篇內(nèi)存會始終保留,直到進(jìn)程死亡钾恢。

如果調(diào)用unpin將共享內(nèi)存中的某段內(nèi)存解除鎖定鸳址,之后如果系統(tǒng)內(nèi)存不足稿黍,會自動釋放這部分內(nèi)存崩哩,再次使用同一段內(nèi)存前應(yīng)該先執(zhí)行pin操作,如果pin操作返回ASHMEM_WAS_PURGED酣栈,也就是說內(nèi)存已經(jīng)被回收汹押,已經(jīng)回收的內(nèi)存再次訪問會觸發(fā)缺頁中斷重新進(jìn)行物理內(nèi)存的分配,因此這段內(nèi)存里的數(shù)據(jù)已經(jīng)不是起初的那個數(shù)據(jù)了棚贾,如果仍舊當(dāng)做原始數(shù)據(jù)進(jìn)行訪問必然引發(fā)錯誤。

通過pin/unpin命令铸史,配合ashmem驅(qū)動怯伊,可以進(jìn)行簡單的內(nèi)存管理。

原理

Ashmem的核心原理主要是兩部分:驅(qū)動和fd傳遞崭篡。

驅(qū)動

Ashmem是Linux內(nèi)核中的一個misc設(shè)備猩系,對應(yīng)的設(shè)備文件是/dev/ashmem,此設(shè)備是一個虛擬設(shè)備寇甸,不存在實際文件疗涉,只在內(nèi)核驅(qū)動中對應(yīng)一個inode節(jié)點咱扣。Ashmem在驅(qū)動層是基于linux系統(tǒng)的共享內(nèi)存功能實現(xiàn)的涵防,Ashmem可以理解為只是對原生的共享內(nèi)存進(jìn)行了一層包裝,使其更方便在Android系統(tǒng)上使用壮池。

ashmem設(shè)備文件支持如下操作:

// /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創(chuàng)建:(從Java層到驅(qū)動層的調(diào)用鏈)

[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中只是創(chuàng)建了一個標(biāo)識ashmem的結(jié)構(gòu)體椰憋,然后返回fd,并沒有進(jìn)行實際的內(nèi)存分配(無論是虛擬內(nèi)存還是物理內(nèi)存)证舟。 得到文件描述符后窗骑,就可以使用ashmem_mmap將內(nèi)核中的共享內(nèi)存區(qū)域映射到進(jìn)程的虛擬地址空間。

ashmem_mmap

ashmem_mmap通過調(diào)用內(nèi)核中shmem相關(guān)函數(shù)在tempfs創(chuàng)建了一個大小等于創(chuàng)建ashmem時傳入大小的臨時文件(由于是內(nèi)存文件抵知,所以磁盤上不存在實際的文件),然后將文件對應(yīng)的內(nèi)存映射到調(diào)用mmap的進(jìn)程。(注意map的是臨時文件而不是ashmem文件)

其中涉及到的shmem函數(shù)包括shmem_file_setupshmem_set_file辛藻,他們?yōu)樵撆R時文件創(chuàng)建inode節(jié)點互订,將文件關(guān)聯(lián)到為該文件配的虛擬內(nèi)存,同時為該文件設(shè)置自己的文件操作函數(shù)(Linux共享內(nèi)存shmem的文件操作)氮墨,并為虛擬內(nèi)存設(shè)置缺頁處理函數(shù)吐葵。這樣后續(xù)對共享內(nèi)存的操作就變?yōu)榱藢empfs文件節(jié)點的操作。當(dāng)首次訪問共享內(nèi)存時觸發(fā)缺頁中斷處理函數(shù)并為該虛擬內(nèi)存分配實際的物理內(nèi)存猛铅。

tempfs是Unix-like系統(tǒng)中一種基于內(nèi)存的文件系統(tǒng)凤藏,具有極高的訪問效率堕伪。
shmem是Linux自帶的進(jìn)程間通信機(jī)制:共享內(nèi)存Shared Memory栗菜。
共享內(nèi)存的虛擬文件記錄在/proc/<pid>/maps文件中,pid表示打開這個共享內(nèi)存文件的進(jìn)程ID富俄。

ashmem_pin/ashmem_unpin

pinunpinashmemioctl支持的兩個操作而咆,用于共享內(nèi)存的分塊使用和分塊回收,用于節(jié)省實際的物理內(nèi)存暴备。 新創(chuàng)建的共享內(nèi)存默認(rèn)都是pined的,當(dāng)調(diào)用unpin時,驅(qū)動將unpined的內(nèi)存區(qū)域所在的頁掛在一個unpinned_list鏈表上玛痊,后續(xù)內(nèi)存回收就是基于unpinned_list鏈表進(jìn)行。

ashmem驅(qū)動初始化函數(shù)ashmem_init里調(diào)用了內(nèi)核函數(shù)register_shrinker混弥,注冊了一個內(nèi)存回收回調(diào)函數(shù)ashmem_shrink对省,當(dāng)系統(tǒng)內(nèi)存緊張時,就會回調(diào)ashmem_shrink蒿涎,由驅(qū)動自身進(jìn)行適當(dāng)?shù)膬?nèi)存回收。驅(qū)動就是在ashmem_shrink中遍歷unpinned_list進(jìn)行內(nèi)存回收仓手,以釋放物理內(nèi)存玻淑。

ashmem fd的傳遞:

fd通過Binder傳遞。

Binder機(jī)制不僅支持binder對象的傳遞添坊,還支持文件描述符的傳遞。fd經(jīng)過binder驅(qū)動時贬蛙,binder驅(qū)動會將源進(jìn)程的fd轉(zhuǎn)換成目標(biāo)進(jìn)程的fd,轉(zhuǎn)換過程為:取出發(fā)送方binder數(shù)據(jù)里的fd速客,通過fd找到文件對象,然后為目標(biāo)進(jìn)程創(chuàng)建fd岔擂,將目標(biāo)進(jìn)程fd和文件對象進(jìn)行關(guān)聯(lián)浪耘,將發(fā)送方binder數(shù)據(jù)里的fd改為目標(biāo)進(jìn)程的fd,然后將數(shù)據(jù)發(fā)送給目標(biāo)進(jìn)程七冲。這個過程相當(dāng)于文件在目標(biāo)進(jìn)程又打開了一次,目標(biāo)進(jìn)程使用的是自己的fd蝉稳,但和源進(jìn)程都指向的是同一個文件掘鄙。這樣源進(jìn)程和目標(biāo)進(jìn)程就都可以map到同一片內(nèi)存了。

使用場景

  • 進(jìn)程間共享體積較大的數(shù)據(jù)操漠,比如bitmap。
  • 提升進(jìn)程間傳輸數(shù)據(jù)的效率撞秋,比如ContentProvider基于共享內(nèi)存進(jìn)行數(shù)據(jù)傳送嚣鄙。
  • 借助Bitmap解碼的inPurgeable屬性,在android4.x及以下系統(tǒng)版本中實現(xiàn)內(nèi)存在ashmem中分配哑子,以節(jié)省Java堆內(nèi)存。比如fresco圖片加載庫針對Android4.x及以下的機(jī)型對inPurgeable屬性的使用剧蹂。

總結(jié)

Ashmem通過對Linux共享內(nèi)存的擴(kuò)展烦却,一方面使其使用更簡單,另一方面使其只能通過binder傳遞冒冬,增加了安全性。Ashmem在Android系統(tǒng)中起著非常重要的作用简烤,比如整個顯示系統(tǒng)App-WMS-SurfaceFlinger之間就是通過Ashmem傳遞的幀數(shù)據(jù)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末挥萌,一起剝皮案震驚了整個濱河市枉侧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌憨栽,老刑警劉巖翼虫,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異锯蛀,居然都是意外死亡次慢,警方通過查閱死者的電腦和手機(jī)翔曲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門瞳遍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人掠械,你說我怎么就攤上這事』伲” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵舔箭,是天一觀的道長。 經(jīng)常有香客問我箫章,道長镜会,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任桶至,我火速辦了婚禮扒袖,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘季率。我一直安慰自己,他們只是感情好飒泻,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布泞遗。 她就那樣靜靜地躺著,像睡著了一般史辙。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上晦毙,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天耙蔑,我揣著相機(jī)與錄音,去河邊找鬼甸陌。 笑死,一個胖子當(dāng)著我的面吹牛耻卡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播劲赠,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼霹肝!你這毒婦竟也來了塑煎?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤讯赏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后漱挎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雀哨,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年膊夹,在試婚紗的時候發(fā)現(xiàn)自己被綠了捌浩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡进统,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出螟碎,到底是詐尸還是另有隱情馋辈,我是刑警寧澤倍谜,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站答毫,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏洗搂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一撵颊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧倡勇,春花似錦嘉涌、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽警医。三九已至,卻和暖如春损敷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拗馒。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工溯街, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人呈昔。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像肝劲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子辞槐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353