android ion

1. 簡介

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管理各種類型的內存。
  • 共享內存:可提供驅動之間、用戶進程之間、內核空間和用戶空間之間的共享內存。

2. 原理

在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獲取),通過映射方式,將相應內存映射,實現共享內存。

3. 使用方法

3.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/),或如下:

3.1.1 獲取一個ION client

需要打開ION設備:

open("/dev/ion", O_RDONLY)

這裏,進程要有訪問權限,雖然是使用O_RDONLY標記但是也返回一個可寫的內存。返回的文件描述符號做爲表示一個ION client的handle。每個用戶進程只能有一個client。用戶空間的client通過ioctl()系統調用接口和ION交互。

3.1.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.1.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。

3.1.4 共享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。

3.1.5 傳遞待共享的文件描述符號

在android設備中,可能會通過Binder機制將共享的文件描述符fd發送給另外一個進程。

爲了獲得被共享的buffer,第二個用戶進程必須通過首先調用 open("/dev/icon", O_RDONLY) 獲取一個client handle,ION通過進程ID跟蹤它的用戶空間clients。 在同一個進程中重複調用open("/dev/icon", O_RDONLY)將會返回另外一個文件描述符號,這個文件描述符號會引用內核同樣的client結構 。

獲取到共享文件描述符fd後,共享進程可以通過mmap來操作共享內存。

3.1.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。

3.2 內核空間內使用ION的方法

具體參見參考資料,這裏簡略介紹。

3.2.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參數前面說過了。

3.2.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轉換 :

int ion_phys(struct ion_client *client, struct ion_handle *handle, ion_phys_addr_t *addr, size_t *len)
若buffer的物理地址不連續,這個調用會失敗。

在處理client的調用之時,ion始終會對input file descriptor,client,和handle arguments進行確認。例如:當import一個file descriptor(文件描述符)之時,ion會保證這個文件描述符確實是通過ION_IOC_SHARE命令創建的。當ion_phys()被調用之時,ION會驗證buffer handle是否在client允許訪問的handles列表中,若不是,則返回錯誤。這些驗證機制減少了期望之外的訪問與資源泄露。

4.ION 調試

關於ION debug,在 /sys/kernel/debug/ion/ 提供一個debugfs 接口。

每個heap都有自己的debugfs目錄,client內存使用狀況顯示在 /sys/kernel/debug/ion/<>

$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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章