一、ION 相關介紹
Android 的 ION 子系統的目的主要是通過在硬件設備和用戶空間之間分配和共享內存,實現設備之間零拷貝共享內存。
說來簡單,其實不易。在Soc硬件中,許多設備可以進行DMA,這些設備可能有不同的能力,以及不同的內存訪問機制。
ION是Google在Android 4.0 ICS中引入,用于改善對于當前不同的android設備,有著各種不同內存管理接口管理相應內存的狀況。
當前存在著各種不同的但是功能卻類似的內存管理接口,例如在NVIDIA Tegra有一個“NVMAP”機制、在TI OMAP有一個“CMEM”機制、在Qualcomm MSM有一個“PMEM”機制,ION將其進行通用化,通過其接口,可集中分配各類不同內存(heap),同時上述三個芯片廠商也正將其內存管理策略切換至ION上。
另外,ION在內核空間和用戶空間分別有一套接口,它不僅能管理內存,還可在其clients(來自內核的或者來自用戶空間的)之間共享內存。
綜上,ION主要功能:
- 內存管理器:提供通用的內存管理接口,通過heap管理各種類型的內存。
- 共享內存:可提供驅動之間、用戶進程之間、內核空間和用戶空間之間的共享內存。
二、實現原理
在ION中,用不同heap代表不同類型的內存,每種heap有自己的內存分配策略。
主要的heap:
- ION_HEAP_TYPE_SYSTEM: 使用vmalloc分配,這個對應ion_heap_ops中的map_user函數
- ION_HEAP_TYPE_SYSTEM_CONTIG: 通過kmalloc分配
- ION_HEAP_TYPE_CARVEOUT: 在啟動的時候就保留的物理上連續的內存塊
- 另外還有兩種,這里不關注:
- ION_HEAP_TYPE_CHUNK
- ION_HEAP_TYPE_DMA: memory allocated via DMA API
每個heap中可分配若干個buffer,每個client通過handle管理對應的buffer。每個buffer只能有一個handle對應,每個用戶進程只能有一個client,每個client可能有多個handle。
兩個client通過文件描述符fd(和handle有所對應,通過handle獲?。?,通過映射方式,將相應內存映射,實現共享內存。
三、使用方法
下面的內容著重講述用戶空間進程之間的內存共享。
1、用戶空間內使用ION的方法
用戶空間可以使用libion庫實現對ion的操作,這里不講述該庫的操作方法,用戶程序直接通過ioctl和驅動打交道,ion常見的ioctl命令為:
- ION_IOC_ALLOC: 分配內存
- ION_IOC_FREE: 釋放內存
- ION_IOC_MAP: 獲得一個只想mmap映射的內存的文件描述符
- ION_IOC_SHARE: 創建一個指向共享的內存的文件描述符
- ION_IOC_IMPORT: 引入一個共享的文件描述符
- ION_IOC_CUSTOM: 調用平臺自定義的ioctl
具體使用示例可以參見該庫的文件實現(system/core/lib/ion/),或如下:
sharedMemoryION.txz
(1)獲取一個ION client
需要打開ION設備:
open("/dev/ion", O_RDONLY)
這里,進程要有訪問權限,雖然是使用O_RDONLY標記但是也返回一個可寫的內存。返回的文件描述符號做為表示一個ION client的handle。每個用戶進程只能有一個client。用戶空間的client通過ioctl()系統調用接口和ION交互。
(2)設置獲取buffer的參數
填充結構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成員之外的成員(整個buffer是 struct ion_allocation_data
類型)。
對于其他參數,注意在文檔 (http://lwn.net/Articles/480055/) 中并沒有給出heap_mask,只說flags是一個比特掩碼,標識一個或者多個將要分配所使用的ION heap(結合后面,它的解釋是錯誤的,應該是對heap_mask的解釋),但是從源代碼中的注釋看,這些參數的含義如下:
- len:分配的大小。
- align:分配所需的對其參數。
- heap_mask:待分配所使用的所有heaps的掩碼(如:ION_HEAP_SYSTEM_MASK)。
- flags:傳給heap的標志(如:ION_FLAG_CACHED),ion系統使用低16位,高16位用于各自heap實現使用。
具體各自取值和實現,請參見ion驅動頭文件定義和驅動代碼。
(3)分配buffer
將設置好的buffer參數傳遞給ioctl:
int ioctl(int client_fd, ION_IOC_ALLOC, struct ion_allocation_data *allocation_data)
這里,client_fd就是剛剛打開的/dev/ioc文件描述符號。分配的buffer通過返回的上述結構的 struct ion_handle *handle
成員來引用,但是這個handle并不是一個CPU訪問的地址。一個client不能有兩個handle指向同樣的buffer。
(4)共享buffer
buffer的handle只能通過如下調用獲取一個文件描述符號用于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是輸入 ,標示要共享buffer的handle;fd是輸出 ,標示用于共享buffer的文件描述符號。
(5)傳遞待共享的文件描述符號
在android設備中,可能會通過Binder機制將共享的文件描述符fd發送給另外一個進程。
為了獲得被共享的buffer,第二個用戶進程必須通過首先調用 open("/dev/icon", O_RDONLY)
獲取一個client handle,ION通過進程ID跟蹤它的用戶空間clients。
在同一個進程中重復調用open("/dev/icon", O_RDONLY)將會返回另外一個文件描述符號,這個文件描述符號會引用內核同樣的client結構 。
獲取到共享文件描述符fd后,共享進程可以通過mmap來操作共享內存。
(6)釋放
為了釋放緩存,第二個client需要通過munmap來取消mmap的效果,第一個client需要關閉通過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;
}
命令會導致handle的引用計數減少1。當這個引用計數達到0的時候,ion_handle對象會被析構,同時ION的索引數據結構被更新。
用戶進程也可與內核驅動共享ION buffer。
2、內核空間內使用ION的方法
具體參見參考資料,這里簡略介紹。
(1)獲取一個ION Client
調用如下函數:
struct ion_client *ion_client_create(struct ion_device *dev,unsigned int heap_mask, const char *debug_name)
內核中可以有多個ION clients,每個使用ION的driver擁有一個client。這里,參數dev就是對應/dev/ion的設備,為何需要這個參數,目前還不確切;參數heap_mask和前面敘述一樣,用于選擇一個或多個ion heaps類型標識堆類型。flags參數前面說過了。
(2)共享來自用戶空間的ion buffer
用戶傳遞 ion共享文件描述符 給內核驅動,驅動 轉成ion_handle :
struct ion_handle *ion_import_fd(struct ion_client *client, int fd_from_user);
在許多包含多媒體中間件的智能手機中,用戶進程經常從ion中分配buffer,然后使用ION_IOC_SHARE命令獲取文件描述符號,然后將文件描述符號傳遞給內核驅動。內核驅動調用ion_import_fd()將文件描述符轉換成ion_handle對象。
內核驅動使用ion_handle對象做為對共享buffer的client本地引用。該函數查找buffer的物理地址一確認是否這個client是否之前分配了同樣的buffer,如果是,則僅增加相應handle的引用計數。
有些硬件塊只能操作物理地址連續的buffer,所以相應的驅動應 對ion_handle轉換 :
struct ion_handle *ion_import_fd(struct ion_client *client, int fd_from_user);
若buffer的物理地址不連續,這個調用會失敗。
在處理client的調用之時,ion始終會對input file descriptor,client,和handle arguments進行確認。例如:當import一個file descriptor(文件描述符)之時,ion會保證這個文件描述符確實是通過ION_IOC_SHARE命令創建的。
當ion_phys()被調用之時,ION會驗證buffer handle是否在client允許訪問的handles列表中,若不是,則返回錯誤。這些驗證機制減少了期望之外的訪問與資源泄露。
四、ION 調試
關于ION debug,在 /sys/kernel/debug/ion/
提供一個debugfs 接口。
每個heap都有自己的debugfs目錄,client內存使用狀況顯示在 /sys/kernel/debug/ion/<<heap name>>
$cat /sys/kernel/debug/ion/ion-heap-1
client pid size
test_ion 2890 16384
每個由pid標識的client也有一個debugfs目錄/sys/kernel/debug/ion/
$cat /sys/kernel/debug/ion/2890
heap_name: size_in_bytes
ion-heap-1: 40960 11
五、其它資料
待更新
內核空間
- ion驅動路徑:
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” 下載之后,目錄為空,這可能是因為存在多個分支,而當前分支為空,可以用"git branch -a"查看相應分支,然后切換之。
用戶空間
- ion庫實現路徑:
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/
其它參考:
- http://blog.csdn.net/melody_lu123/article/details/7556820
- http://blog.csdn.net/crazyjiang/article/details/7927260