The Android ION memory allocator "英文原文" ION heaps ION設計的目標 為了避免記憶體碎片化,或者為一些有著特殊記憶體需求的硬體,比如GPUs、display controller以及camera等,在系統啟動的時候,會為他們預留一些memory pools ...
The Android ION memory allocator
ION heaps
ION設計的目標
為了避免記憶體碎片化,或者為一些有著特殊記憶體需求的硬體,比如GPUs、display controller以及camera等,在系統啟動的時候,會為他們預留一些memory pools,這些memory pools就由ION來管理。通過ION就可以在硬體以及user space之間實現zero-copy的記憶體share。
ION的實現
ION通過ION heaps來展示presents它對應的memory pools。不同的Android硬體可能會要求不同的ION heaps實現,預設的ION驅動會提供如下三種不同的ION heaps實現:
- ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc_user()
- ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kzalloc
. ION_HEAP_TYPE_CARVEOUT: carveout memory is physically contiguous and set aside at boot.
開發者可以自己實現更多的ION heaps。比如NVIDIA就提交了一種ION_HEAP_TYPE_IOMMU的heap,這種heap帶有IOMMU功能。
不管哪一種ION heaps實現,他們都必須實現如下介面:
struct ion_heap_ops {
int (*allocate) (struct ion_heap *heap,
struct ion_buffer *buffer, unsigned long len,
unsigned long align, unsigned long flags);
void (*free) (struct ion_buffer *buffer);
int (*phys) (struct ion_heap *heap, struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len);
struct scatterlist *(*map_dma) (struct ion_heap *heap,
struct ion_buffer *buffer);
void (*unmap_dma) (struct ion_heap *heap,
struct ion_buffer *buffer);
void * (*map_kernel) (struct ion_heap *heap,
struct ion_buffer *buffer);
void (*unmap_kernel) (struct ion_heap *heap,
struct ion_buffer *buffer);
int (*map_user) (struct ion_heap *heap, struct ion_buffer *buffer,
struct vm_area_struct *vma);
};
簡單來說,介面的各個函數功能如下:
allocate()
和free()
分別用來從heap中分配或者釋放一個ion_buffer
對象- 對於物理連續的記憶體,
phys()
用來得到ion_buffer
對象的物理記憶體地址及其大小。如果heap沒有提供物理連續的記憶體,那麼它也可以不用提供這個介面。其中,ion_phys_addr_t
將來會被定義在/include/linux/types.h中的phys_addr_t
替代。 map_dma()
和unmap_dma()
分別來用使ion_buffer
對象為DMA(Direct Memory Access,直接記憶體存取。顧名思義,不占用cpu資源,從一個硬體存儲區域把一部分連續的數據複製到另一個硬體存儲區域)做好準備或者取消做好準備map_kernel()
和unmap_kernel()
分別用來把physical memory映射(map)到內核虛擬地址空間(kernel virtual address space)或者取消映射map_user()
用來把physical memory映射(map)到用戶記憶體空間(user space)。為什麼沒有對應的unmap_user()
呢?因為,這個映射用一個file descriptor來表示,當這個file descriptor關閉的時候,這個映射關係就自動取消了。
在user space使用ION
使用場景
典型的,在用戶空間使用的設備訪問庫(user space device access libraries)一般使用ION來分配大塊連續的media buffers。比如,still camera library分配一個capture buffer來供camera device使用。當這個buffer填滿video data的時候,這個library就能把這塊buffer傳遞給kernel,然後讓JPEG硬編碼模塊來處理。
具體使用細節
在user space 的C/C++程式能夠能夠分配ION記憶體之前,它必須獲得訪問/dev/ion
的許可權。通過調用open("/dev/ion", O_RDONLY)
就可獲得一個以handle形式返回的file descriptor,這個file descriptor用來代表一個ION client。註意,雖然傳給open
一個O_RDONLY
參數,但是你仍然可對這塊memory進行寫操作。在一個user process中最多有一個client。當有了一個client之後,就可以開始分配ION記憶體。為了分配記憶體,client必須填滿下麵的ion_allocation_data
結構,handle
除外,因為它是output參數。其他三個參數分別指明記憶體的大小、對齊方式以及flags。flags是一個bit mask,用來說明可以從哪些heaps中分配想要的記憶體。其決定順序由系統啟動時,通過ion_device_add_heap()
添加的heap順來決定。比如,ION_HEAP_TYPE_CARVEOUT是在ION_HEAP_TYPE_CONTIG之前被add的,那麼如果flags = ION_HEAP_TYPE_CONTIG | ION_HEAP_TYPE_CARVEOUT
,那麼就是先嘗試分配ION_HEAP_TYPE_CARVEOUT類型的heap,如果不行,再嘗試分配ION_HEAP_TYPE_CONTIG類型的heap。()
struct ion_allocation_data {
size_t len;
size_t align;
unsigned int flags;
struct ion_handle *handle;
}
user space通過ioctl()
系統介面來與ION交互。在client填充ion_allocatoin_data
結構之後,就可以通過調用int ioctl(int client_fd, ION_IOC_ALLOC, struct ion_allocation_data *allocation_data)
來allocate a buffer。這個調用介紹之後,分配的buffer會通過ion_allocatoin_data
的handle
來返回,但是CPU不可以訪問這個buffer。這個handle
只可以通過調用int ioctl(int client_fd, ION_IOC_SHARE, struct ion_fd_data *fd_data);
來獲得一個用來share的file descriptor。這裡,client_fd
參數是前面通過open
獲得的一個對應/dev/ion
file descriptor,fd_data
是如下的數據結構,其handle
對應ion_allocation_data::handle
,是input參數;fd
則是output參數,可以用來share。
當一個user process中的client分享(share)了這個fd
之後,在其他user process中(當然,也可share給創建這個fd
的client自己),為了獲得這個shared buffer,先必須通過調用open("/dev/ion", O_RDONLY)
獲得一個client。(註:ION通過線程的PID來track各個client, 尤其是process中的"group leader"線程的PID。在相同的process中重覆調用open("/dev/ion", O_RDONLY)
只會獲得指向kernel同一個client的another file descriptor)。獲得client之後,然後再通過mmap()
函數來把這個fd
映射到address space of process(mmap函數參考1,參考2)。如果要釋放這個fd
對應的buffer,在調用mmap()
的process中,先要通過munmap()
來取消mmap()
的效果。然後在之前share這個fd
的client中,需要通過int ioctl(int client_fd, ION_IOC_FREE, struct ion_handle_data *handle_data);
來關閉這個fd
對應的file descriptor。其中,ion_handle_data
表示前面通過ION_IOC_ALLOC
命令獲得的handle
,其定義如下:
struct ion_handle_data {
struct ion_handle *handle;
}
這個ION_IOC_FREE
命令會導致對應的handle
的計數減1。當handle
計數為0的時候,其指向的ion_handle
對象就會被銷毀,並且相關的ION bookkeeping數據結構也會更新。
Demo
在這個Demo中,fd
在同一個client中被share使用:來源
#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "/home/developer/kernel3.4/goldfish/include/linux/ion.h"
void main()
{
struct ion_fd_data fd_data;
struct ion_allocation_data ionAllocData;
ionAllocData.len=0x1000;
ionAllocData.align = 0;
ionAllocData.flags = ION_HEAP_TYPE_SYSTEM;
int fd=open("/dev/ion",O_RDWR);
ioctl(fd,ION_IOC_ALLOC, &ionAllocData);
fd_data.handle = ionAllocData.handle;
ioctl(fd,ION_IOC_SHARE,&fd_data);
int *p = mmap(0,0x1000,PROT_READ|PROT_WRITE,MAP_SHARED,fd_data.fd,0);
p[0]=99;
perror("test");
printf("hello all %d\n",p[0]);
}
在kernel中share ION buffer
在kernel中支持multiple clients,每一個使用ION功能的driver都可以在kernel中對應一個client。一個kernel driver通過調用struct ion_client *ion_client_create(struct ion_device *dev, unsigned int heap_mask, const char *debug_name)
來獲得一個ION client handle(註意,前面在user space中通過open("/dev/ion", O_RDONLY)
返回的client是int
類型)。dev
參數是一個和/dev/ion
相關的global ION device,heap_mask
參數和之前提到的ion_allocation_data
的flags
成員一樣的含義。
當在user space中通過ION_IOC_SHARE
命令得到一個buffer的file descriptor並把它傳遞給kernel之後,kernel driver通過調用struct ion_handle *ion_import_fd(struct ion_client *client, int fd_from_user);
來把這個fd變成一個ion_handle
對象,這個對象就是這個driver中對相應的buffer一個client-local reference。ion_import_fd
方法會根據這個buffer的物理地址來查找:在本client中是否已經obtained一個對應此buffer的ion_handle
,如果是的話,那麼就可以簡單的增加這個ion_handle
的引用計數即可。
有些硬體只能通過physical addresses來操作physically-contiguous buffers,那麼,這些對應的drivers就需要通過調用int ion_phys(struct ion_client *client, struct ion_handle *handle, ion_phys_addr_t *addr, size_t *len)
來把ion_handle
轉變成一個physical buffer。當然,如果這個buffer不是physically contiguous,那麼這個調用就會失敗。
當處理一個來自client的調用時,ION會validates 輸入的 file descriptor, client and handle arguments。比如ION會確保 file descriptor是由ION_IOC_SHARE
命令創建的;比如當ion_phys()
調用時,ION會檢測這個buffer是否在這個client對應有訪問許可權list中,如果不是,那麼就會返回錯誤。這樣的驗證機制能夠減少可能的unwanted accesses以及疏忽的記憶體泄露。
ION通過debugfs提供可視化的debug,它通過在/sys/kernel/debug/ion下麵,使用stored files來記錄相應的heaps和clients,並使用symbolic names或者PIDs來標誌。
比較ION和DMABUF
本節部分翻譯。
- ION和DMABUF都是通過傳遞一個匿名file descriptor對象,給其他client一個基於引用計數的訪問許可權,從而達到分享記憶體的目的。
- ION通過一個可分享和追蹤的方式從預留的memory pool中分配記憶體。
- DMABUF更多的專註於buffer導入、導出以及同步的方式來實現在NON-ARM架構上的buffer的分享。
- ION目前只支持Android kernel
- ION所有的user-space program都可以通過/dev/ion介面來分配ION記憶體。但是在Android會通過驗證user和group IDs的方式來阻止對ION的非授權訪問。
參考
The Android ION memory allocator
Good PDF
Integrating the ION memory allocator
ION.C
ION.H
DEMO
CSDN