Android系列-ION內(nèi)存管理簡介
一元莫、ION相關(guān)介紹
Android的ION子系統(tǒng)的目的主要是通過在硬件設(shè)備和用戶空間之間分配和共享內(nèi)存绵载,實(shí)現(xiàn)設(shè)備之間零拷貝共享內(nèi)存羊苟。說來簡單胶惰,其實(shí)不易鹉戚。在Soc硬件中,許多設(shè)備可以進(jìn)行DMA师幕,這些設(shè)備可能有不同的能力粟按,以及不同的內(nèi)存訪問機(jī)制。
ION是Google在Android 4.0 ICS中引入霹粥,用于改善對于當(dāng)前不同的android設(shè)備灭将,有著各種不同內(nèi)存管理接口管理相應(yīng)內(nèi)存的狀況。當(dāng)前存在著各種不同的但是功能卻類似的內(nèi)存管理接口后控,例如在NVIDIA Tegra有一個(gè)“NVMAP”機(jī)制庙曙、在TI OMAP有一個(gè)“CMEM”機(jī)制、在Qualcomm MSM有一個(gè)“PMEM”機(jī)制浩淘,ION將其進(jìn)行通用化捌朴,通過其接口,可集中分配各類不同內(nèi)存(heap)张抄,同時(shí)上述三個(gè)芯片廠商也正將其內(nèi)存管理策略切換至ION上砂蔽。
另外,ION在內(nèi)核空間和用戶空間分別有一套接口欣鳖,它不僅能管理內(nèi)存察皇,還可在其clients(來自內(nèi)核的或者來自用戶空間的)之間共享內(nèi)存茴厉。
綜上泽台,ION主要功能:
- 內(nèi)存管理器:提供通用的內(nèi)存管理接口,通過heap管理各種類型的內(nèi)存矾缓。
- 共享內(nèi)存:可提供驅(qū)動(dòng)之間怀酷、用戶進(jìn)程之間、內(nèi)核空間和用戶空間之間的共享內(nèi)存嗜闻。
二蜕依、實(shí)現(xiàn)原理
在ION中,用不同heap代表不同類型的內(nèi)存,每種heap有自己的內(nèi)存分配策略样眠。
主要的heap:
- ION_HEAP_TYPE_SYSTEM: 使用vmalloc分配友瘤,這個(gè)對應(yīng)ion_heap_ops中的map_user函數(shù)
- ION_HEAP_TYPE_SYSTEM_CONTIG: 通過kmalloc分配
- ION_HEAP_TYPE_CARVEOUT: 在啟動(dòng)的時(shí)候就保留的物理上連續(xù)的內(nèi)存塊
- 另外還有兩種,這里不關(guān)注:
- ION_HEAP_TYPE_CHUNK
- ION_HEAP_TYPE_DMA: memory allocated via DMA API
每個(gè)heap中可分配若干個(gè)buffer檐束,每個(gè)client通過handle管理對應(yīng)的buffer辫秧。每個(gè)buffer只能有一個(gè)handle對應(yīng),每個(gè)用戶進(jìn)程只能有一個(gè)client被丧,每個(gè)client可能有多個(gè)handle盟戏。兩個(gè)client通過文件描述符fd(和handle有所對應(yīng),通過handle獲壬稹)柿究,通過映射方式,將相應(yīng)內(nèi)存映射黄选,實(shí)現(xiàn)共享內(nèi)存蝇摸。
三、使用方法
下面的內(nèi)容著重講述用戶空間進(jìn)程之間的內(nèi)存共享办陷。
1探入、用戶空間內(nèi)使用ION的方法
用戶空間可以使用libion庫實(shí)現(xiàn)對ion的操作,這里不講述該庫的操作方法懂诗,用戶程序直接通過ioctl和驅(qū)動(dòng)打交道蜂嗽,ion常見的ioctl命令為:
- ION_IOC_ALLOC: 分配內(nèi)存
- ION_IOC_FREE: 釋放內(nèi)存
- ION_IOC_MAP: 獲得一個(gè)只想mmap映射的內(nèi)存的文件描述符
- ION_IOC_SHARE: 創(chuàng)建一個(gè)指向共享的內(nèi)存的文件描述符
- ION_IOC_IMPORT: 引入一個(gè)共享的文件描述符
- ION_IOC_CUSTOM: 調(diào)用平臺自定義的ioctl
具體使用示例可以參見該庫的文件實(shí)現(xiàn)(system/core/lib/ion/),或如下:
(1)獲取一個(gè)ION client
需要打開ION設(shè)備:
open("/dev/ion", O_RDONLY)
這里殃恒,進(jìn)程要有訪問權(quán)限植旧,雖然是使用O_RDONLY標(biāo)記但是也返回一個(gè)可寫的內(nèi)存。返回的文件描述符號做為表示一個(gè)ION client的handle离唐。每個(gè)用戶進(jìn)程只能有一個(gè)client病附。用戶空間的client通過ioctl()系統(tǒng)調(diào)用接口和ION交互。
(2)設(shè)置獲取buffer的參數(shù)
填充結(jié)構(gòu)ion_allocation_data亥鬓,主要包含如下成員:
struct ion_allocation_data {
size_t len;
size_t align;
unsigned int heap_mask;
unsigned int flags;
struct ion_handle *handle;
};
這里 handle做為輸出 (struct ion_handle
類型)讽挟,需要填充的是其中除handle成員之外的成員(整個(gè)buffer是 struct ion_allocation_data
類型)。對于其他參數(shù)蕾哟,注意在文檔 (http://lwn.net/Articles/480055/) 中并沒有給出heap_mask搂誉,只說flags是一個(gè)比特掩碼,標(biāo)識一個(gè)或者多個(gè)將要分配所使用的ION heap(結(jié)合后面熟呛,它的解釋是錯(cuò)誤的宽档,應(yīng)該是對heap_mask的解釋),但是從源代碼中的注釋看庵朝,這些參數(shù)的含義如下:
- len:分配的大小吗冤。
- align:分配所需的對其參數(shù)又厉。
- heap_mask:待分配所使用的所有heaps的掩碼(如:ION_HEAP_SYSTEM_MASK)。
- flags:傳給heap的標(biāo)志(如:ION_FLAG_CACHED)椎瘟,ion系統(tǒng)使用低16位覆致,高16位用于各自heap實(shí)現(xiàn)使用。
具體各自取值和實(shí)現(xiàn)肺蔚,請參見ion驅(qū)動(dòng)頭文件定義和驅(qū)動(dòng)代碼篷朵。
(3)分配buffer
將設(shè)置好的buffer參數(shù)傳遞給ioctl:
int ioctl(int client_fd, ION_IOC_ALLOC, struct ion_allocation_data *allocation_data)
這里,client_fd就是剛剛打開的/dev/ioc文件描述符號婆排。分配的buffer通過返回的上述結(jié)構(gòu)的 struct ion_handle *handle
成員來引用声旺,但是這個(gè)handle并不是一個(gè)CPU訪問的地址。一個(gè)client不能有兩個(gè)handle指向同樣的buffer段只。
(4)共享buffer
buffer的handle只能通過如下調(diào)用獲取一個(gè)文件描述符號用于buffer共享:
int ioctl(int client_fd, ION_IOC_SHARE, struct ion_fd_data *fd_data);
其中fd_data如下:
struct ion_fd_data {
struct ion_handle *handle;
int fd;
}
這里腮猖, handle是輸入 ,標(biāo)示要共享buffer的handle赞枕; fd是輸出 澈缺,標(biāo)示用于共享buffer的文件描述符號。
(5)傳遞待共享的文件描述符號
在android設(shè)備中炕婶,可能會(huì)通過Binder機(jī)制將共享的文件描述符fd發(fā)送給另外一個(gè)進(jìn)程姐赡。
為了獲得被共享的buffer,第二個(gè)用戶進(jìn)程必須通過首先調(diào)用 open("/dev/icon", O_RDONLY)
獲取一個(gè)client handle柠掂,ION通過進(jìn)程ID跟蹤它的用戶空間clients项滑。 在同一個(gè)進(jìn)程中重復(fù)調(diào)用open("/dev/icon", O_RDONLY)將會(huì)返回另外一個(gè)文件描述符號,這個(gè)文件描述符號會(huì)引用內(nèi)核同樣的client結(jié)構(gòu) 涯贞。
獲取到共享文件描述符fd后枪狂,共享進(jìn)程可以通過mmap來操作共享內(nèi)存。
(6)釋放
為了釋放緩存宋渔,第二個(gè)client需要通過munmap來取消mmap的效果州疾,第一個(gè)client需要關(guān)閉通過ION_IOC_SHARE命令獲得的文件描述符號,并且使用ION_IOC_FREE如下:
int ioctl(int client_fd, ION_IOC_FREE, struct ion_handle_data *handle_data);
其中:
struct ion_handle_data {
struct ion_handle *handle;
}
命令會(huì)導(dǎo)致handle的引用計(jì)數(shù)減少1皇拣。當(dāng)這個(gè)引用計(jì)數(shù)達(dá)到0的時(shí)候严蓖,ion_handle對象會(huì)被析構(gòu),同時(shí)ION的索引數(shù)據(jù)結(jié)構(gòu)被更新氧急。
用戶進(jìn)程也可與內(nèi)核驅(qū)動(dòng)共享ION buffer颗胡。
2、內(nèi)核空間內(nèi)使用ION的方法
具體參見參考資料态蒂,這里簡略介紹杭措。
(1)獲取一個(gè)ION Client
調(diào)用如下函數(shù):
struct ion_client *ion_client_create(struct ion_device *dev,unsigned int heap_mask, const char *debug_name)
內(nèi)核中可以有多個(gè)ION clients费什,每個(gè)使用ION的driver擁有一個(gè)client钾恢。這里手素,參數(shù)dev就是對應(yīng)/dev/ion的設(shè)備,為何需要這個(gè)參數(shù)瘩蚪,目前還不確切泉懦;參數(shù)heap_mask和前面敘述一樣,用于選擇一個(gè)或多個(gè)ion heaps類型標(biāo)識堆類型疹瘦。flags參數(shù)前面說過了崩哩。
(2)共享來自用戶空間的ion buffer
用戶傳遞 ion共享文件描述符 給內(nèi)核驅(qū)動(dòng),驅(qū)動(dòng) 轉(zhuǎn)成ion_handle :
struct ion_handle *ion_import_fd(struct ion_client *client, int fd_from_user);
在許多包含多媒體中間件的智能手機(jī)中言沐,用戶進(jìn)程經(jīng)常從ion中分配buffer邓嘹,然后使用ION_IOC_SHARE命令獲取文件描述符號,然后將文件描述符號傳遞給內(nèi)核驅(qū)動(dòng)险胰。內(nèi)核驅(qū)動(dòng)調(diào)用ion_import_fd()將文件描述符轉(zhuǎn)換成ion_handle對象汹押。內(nèi)核驅(qū)動(dòng)使用ion_handle對象做為對共享buffer的client本地引用。該函數(shù)查找buffer的物理地址一確認(rèn)是否這個(gè)client是否之前分配了同樣的buffer起便,如果是棚贾,則僅增加相應(yīng)handle的引用計(jì)數(shù)。
有些硬件塊只能操作物理地址連續(xù)的buffer榆综,所以相應(yīng)的驅(qū)動(dòng)應(yīng) 對ion_handle轉(zhuǎn)換 :
int ion_phys(struct ion_client *client, struct ion_handle *handle, ion_phys_addr_t *addr, size_t *len)
若buffer的物理地址不連續(xù)妙痹,這個(gè)調(diào)用會(huì)失敗。
在處理client的調(diào)用之時(shí)鼻疮,ion始終會(huì)對input file descriptor,client,和handle arguments進(jìn)行確認(rèn)怯伊。例如:當(dāng)import一個(gè)file descriptor(文件描述符)之時(shí),ion會(huì)保證這個(gè)文件描述符確實(shí)是通過ION_IOC_SHARE命令創(chuàng)建的判沟。當(dāng)ion_phys()被調(diào)用之時(shí)震贵,ION會(huì)驗(yàn)證buffer handle是否在client允許訪問的handles列表中,若不是水评,則返回錯(cuò)誤猩系。這些驗(yàn)證機(jī)制減少了期望之外的訪問與資源泄露。
四中燥、ION 調(diào)試
關(guān)于ION debug寇甸,在 /sys/kernel/debug/ion/
提供一個(gè)debugfs 接口。
每個(gè)heap都有自己的debugfs目錄疗涉,client內(nèi)存使用狀況顯示在 /sys/kernel/debug/ion/<<heap name>>
$cat /sys/kernel/debug/ion/ion-heap-1
client pid size
test_ion 2890 16384
每個(gè)由pid標(biāo)識的client也有一個(gè)debugfs目錄/sys/kernel/debug/ion/<a id="orgtarget1"></a>
$cat /sys/kernel/debug/ion/2890
heap_name: size_in_bytes
ion-heap-1: 40960 11
五拿霉、其它資料
待更新
內(nèi)核空間
- ion驅(qū)動(dòng)路徑:
drivers/gpu/ion/*
- 頭文件:
include/linux/ion.h
- 下載路徑:https://android.googlesource.com/kernel/common.git/+/android-3.0/drivers/gpu/ion/
注:使用例如“git clone https://android.googlesource.com/kernel/common” 下載之后,目錄為空咱扣,這可能是因?yàn)榇嬖诙鄠€(gè)分支绽淘,而當(dāng)前分支為空,可以用"git branch -a"查看相應(yīng)分支闹伪,然后切換之沪铭。
用戶空間
- ion庫實(shí)現(xiàn)路徑:
system/core/libion/*
- 頭文件:
system/core/include/ion/ion.h
- 下載路徑:https://android.googlesource.com/platform/system/core/+/android-4.4.2_r2/libion/
同樣可參照上述方法下載壮池。
整體介紹:
https://wiki.linaro.org/BenjaminGaignard/ion
功能方面:
http://lwn.net/Articles/565469/
使用方面:
http://lwn.net/Articles/480055/