Android之ION記憶體管理分析
備註:圖片中的雙向箭頭表示他們是連結串列,前後連結起來的,單向箭頭表示指標指向誰。
做Camera都快2年了,對buffer流轉,buffer queue 等一些細節方面,還是不太明白。雖然也知道怎麼用,但是不知道更深層次的工作機制,內心有點忐忑不安。所以決定拿一個週末好好研究了一下ION。下面就對這個週末先做個筆記吧,如果你發現其中有錯誤,歡迎指正出來,大家一起共同進步,學習。
ION是google在Android4.0 為了解決記憶體碎片管理而引入的通用記憶體管理器,在面向程式設計師程式設計方面,它和ashmem很相似。但是終究還是ION更勝一籌。
一、ION常用的資料結構
<1>struct ion_allocation_data
一般開發平臺上都會有一個ion封裝庫,我們在開發時,只需要知道怎麼呼叫介面申請到buffer就行了。同其它裝置驅動一樣,ion 裝置驅動具有open,close,ioctl等標準系統呼叫。我們在使用ioctl(fd, ION_IOC_ALLOC, &data)申請buffer時,都需要傳入一個struct ion_allocation_data的結構體。如下所示:
len:我們要申請的記憶體大小,是頁對齊的,如果應用層沒有做頁對齊,那麼在核心driver裡面也會做頁對齊的。/** * struct ion_allocation_data - metadata passed from userspace for allocations * @len: size of the allocation * @align: required alignment of the allocation * @heap_id_mask: mask of heap ids to allocate from * @flags: flags passed to heap * @handle: pointer that will be populated with a cookie to use to * refer to this allocation * * Provided by userspace as an argument to the ioctl */ struct ion_allocation_data { size_t len; size_t align; unsigned int heap_id_mask; unsigned int flags; ion_user_handle_t handle; };
align:對齊標示,如上所示,一般都是頁對齊(4K)
heap_id_mask:用這個來標示我們要在哪個heap上申請buffer
flags:這個就是我們傳入的一些標誌位,包括我們要在哪個heap 上申請buffer和是否使用cache。
handle:儲存buffer的控制代碼(ion_handle),其實就是一個整型值。這個值還不程序間共享,只能當前程序訪問。因為這是一個虛擬地址。
這個結構體是用來申請buffer是感測kernel的一些配置引數,並將申請成功的buffer的handle,存放到handle域,其實這裡ion_user_handle_t 是一個整型變數。
typedef int ion_user_handle_t;
<2>struct ion_fd_data
如下面註釋所說,當我們想程序間共享這個buffer時,就需要使用ioctl(fd, ION_IOC_SHARE, &fd_data)來得到這個buffer的唯一id,其中fd_data就是struct ion_fd_data
/**
* struct ion_fd_data - metadata passed to/from userspace for a handle/fd pair
* @handle: a handle
* @fd: a file descriptor representing that handle
*
* For ION_IOC_SHARE or ION_IOC_MAP userspace populates the handle field with
* the handle returned from ion alloc, and the kernel returns the file
* descriptor to share or map in the fd field. For ION_IOC_IMPORT, userspace
* provides the file descriptor and the kernel returns the handle.
*/
struct ion_fd_data {
ion_user_handle_t handle;
int fd;
};
handle:指向這個buffer的ion_handle,當前程序可以使用這個訪問buffer
fd:當我們想共享這個buffer時,使用ION_IOC_SHARE kernel就會給我們返回一個唯一標識這個buffer的fd,並儲存到fd域中,具體使用場景,後面我們會介紹。
<3>struct ion_handle_data
這個其實就是記錄當前buffer的控制代碼的結構體,只不過又封裝了一下,如名字就知道是提供給user的。
/**
* struct ion_handle_data - a handle passed to/from the kernel
* @handle: a handle
*/
struct ion_handle_data {
ion_user_handle_t handle;
};
handle:當前程序中表示這個buffer的控制代碼
<4>struct on_device
/**
* struct ion_device - the metadata of the ion device node
* @dev: the actual misc device
* @buffers: an rb tree of all the existing buffers
* @buffer_lock: lock protecting the tree of buffers
* @lock: rwsem protecting the tree of heaps and clients
* @heaps: list of all the heaps in the system
* @user_clients: list of all the clients created from userspace
*/
struct ion_device {
struct miscdevice dev;
struct rb_root buffers;
struct mutex buffer_lock;
struct rw_semaphore lock;
struct plist_head heaps;
long (*custom_ioctl) (struct ion_client *client, unsigned int cmd,
unsigned long arg);
struct rb_root clients;
struct dentry *debug_root;
};
上面裡面有很多熟悉的面孔,我們就挑幾個比較重要的來解釋一下
buffers:用來記錄使用者申請的所有ion_buffer的紅黑樹。
heaps:ion裝置建立的記憶體堆,這個可以由使用者自定義,我在開發過程中,他們喜歡給一些裝置reserer一些buffer,已保證實體地址連續
clients:核心建立的所有ion_client物件都會連結到這個紅黑樹上
<5>struct ion_client
每一個申請buffer的程序都會有至少包含一個ion_client物件。它表示的就是buffer的使用者。/**
* struct ion_client - a process/hw block local address space
* @node: node in the tree of all clients
* @dev: backpointer to ion device
* @handles: an rb tree of all the handles in this client
* @idr: an idr space for allocating handle ids
* @lock: lock protecting the tree of handles
* @name: used for debugging
* @task: used for debugging
*
* A client represents a list of buffers this client may access.
* The mutex stored here is used to protect both handles tree
* as well as the handles themselves, and should be held while modifying either.
*/
struct ion_client {
struct rb_node node;
struct ion_device *dev;
struct rb_root handles;
struct idr idr;
struct mutex lock;
const char *name;
struct task_struct *task;
pid_t pid;
struct dentry *debug_root;
};
node:用來將當前ion_client,連結進ion_dev client紅黑樹中的node。可以參考上面ion_device結構體。
handles: 當前client申請的ion_buffer紅黑樹
idr:有idr空間分配的唯一標識這個client的id
name:ion_client的名字,一般是程序id加上是當前程序第幾個client的序列號,即$(process_id) + num
taksk:當前程序的任務控制塊
pid:程序的程序id
<6>struct ion_handle
我們申請到的buffer就是用這個結構體表示的。
/**
* ion_handle - a client local reference to a buffer
* @ref: reference count
* @client: back pointer to the client the buffer resides in
* @buffer: pointer to the buffer
* @node: node in the client's handle rbtree
* @kmap_cnt: count of times this client has mapped to kernel
* @id: client-unique id allocated by client->idr
*
* Modifications to node, map_cnt or mapping should be protected by the
* lock in the client. Other fields are never changed after initialization.
*/
struct ion_handle {
struct kref ref;
struct ion_client *client;
struct ion_buffer *buffer;
struct rb_node node;
unsigned int kmap_cnt;
int id;
};
是用來存放buffer的,從結構體中,我們可以發現有buffer的使用者和buffer的指標,還有一個一個非常關鍵的
ref:當前ion_buffer引用計數
client:當前ion_buffer屬於哪個client所有
buffer:指向buffer記憶體塊的地址
id:唯一表示ion_buffer的整型變數,通過核心的idr機制加上這個id就可以在其它程序中訪問到這塊buffer。而且上層在傳下來的struct ion_allocation_data結構體中也只是儲存了這個id。
二、程序、client、buffer的關係
閱讀kernel的ion裝置驅動時,你會發現程序、ion_client、buffer他們三個的關係還是比較"亂"的。在ion裝置結構體中有ion_client,ion_buffer,ion_heap紅黑樹。它會把系統中所有的ion_client,ion_buffer,ion_heap串聯起來。如下所示的結構。
在核心中預設會建立下面幾種heap,其中用的最多要屬於system heap和carveout heap(名字可以自己定義的)
system heap:分配的物理頁面可能是離散的,只是虛擬地址連續而已。
carveout heap:分配的物理頁面是連續的,因為這些記憶體是提前預留的。
kernel剛起來的時,會根據解析裝置樹,找到ion裝置節點的配置,然後依次找到各個heap的id,type,地址區間,其中system heap地址區間是沒有資料的。但是我所看的三星4418的程式碼不是使用dts方式,而是直接在標頭檔案中配置的,如下所示,其中最後的nxp_device_ion變數是全域性的,對ion裝置是可見的,ion裝置probe建立各種heap時就會直接拿過來用了。
void __init nxp_ion_set_platdata(void)
{
struct ion_platform_data *pdata;
pdata = kzalloc(sizeof(struct ion_platform_data), GFP_KERNEL);
pdata->heaps = kzalloc(5 * sizeof(struct ion_platform_heap), GFP_KERNEL);
if (pdata) {
pdata->nr = 3;
pdata->heaps[0].type = ION_HEAP_TYPE_SYSTEM;
pdata->heaps[0].name = "ion_noncontig_heap";
pdata->heaps[0].id = ION_HEAP_TYPE_SYSTEM;
pdata->heaps[1].type = ION_HEAP_TYPE_SYSTEM_CONTIG;
pdata->heaps[1].name = "ion_contig_heap";
pdata->heaps[1].id = ION_HEAP_TYPE_SYSTEM_CONTIG;
pdata->heaps[2].type = ION_HEAP_TYPE_NXP_CONTIG;
pdata->heaps[2].name = "nxp_contig_heap";
pdata->heaps[2].id = ION_HEAP_TYPE_NXP_CONTIG;
nxp_device_ion.dev.platform_data = pdata;
}
}
下面就是nexell寫的針對三星4418的平臺ion裝置驅動,下面省略號都是一些檢查指標有效性的程式碼,這裡為了程式碼看起來簡潔,就刪掉了。這裡只是介紹一下大概的流程,如果你想深入研究的話,可以查閱原始碼。static int nxp_ion_probe(struct platform_device *pdev)
{
struct ion_platform_data *pdata = pdev->dev.platform_data; //上面的程式碼可以看到這個平臺數據在kernel起來的最初期,已經設定過了。
int error;
int i;
struct ion_device *ion_dev;
struct ion_heap **heaps;
........
ion_dev = ion_device_create(nxp_ion_custom_ioctl); //設定/dev/ion裝置,並將客戶nexell的ionctl註冊到ion core。
........
heaps = kzalloc(sizeof(struct ion_heap *) * pdata->nr, GFP_KERNEL);
.......
for (i = 0; i < pdata->nr; i++) {
struct ion_platform_heap *heap_data = &pdata->heaps[i];
heaps[i] = _ion_heap_create(heap_data); //這裡會建立各種heap,我們就不細說了。
.......
}
s_num_heaps = pdata->nr;
s_heaps = heaps;
g_ion_nxp = ion_dev;
s_nxp_ion_dev = &pdev->dev;
platform_set_drvdata(pdev, g_ion_nxp);
printk("%s success!!!\n", __func__);
return 0;
.......
return error;
}
系統各種heap建立的過程中,會給每一個heap分配一個記憶體池,這裡我們已system_heap為例子來說一下。他們分被是64K,16K,1K的記憶體池,其中每一個pool都包含了高地址和低地址的雙向連結串列。static const unsigned int orders[] = {8, 4, 0};
static const int num_orders = ARRAY_SIZE(orders);
下面是建立記憶體池的函式,系統在第一次建立時,pool各個域都是初始值。因為system heap本來就是系統隨機分配的,只有在ion buffer釋放時,才會將對應的物理頁放到對應的pool中,這樣的話下次系統分配ion buffer時,不需要從系統那邊申請了,直接在pool中查詢是否有合適的buffer,有的話,直接就拿過來用了,沒有的話才會從系統那邊在申請。struct ion_page_pool *ion_page_pool_create(gfp_t gfp_mask, unsigned int order)
{
struct ion_page_pool *pool = kmalloc(sizeof(struct ion_page_pool),
GFP_KERNEL);
if (!pool)
return NULL;
pool->high_count = 0;
pool->low_count = 0;
INIT_LIST_HEAD(&pool->low_items);//低地址連結串列
INIT_LIST_HEAD(&pool->high_items);//高地址連結串列
pool->gfp_mask = gfp_mask;
pool->order = order; //order分別是對應的大小等級,即8,4,0
mutex_init(&pool->mutex);
plist_node_init(&pool->list, order);
return pool;
}
系統中各種heap和pool的如下所示,值得我們注意的是system heap有3個pool,cma_heap沒有pool,carveout_heap,chunk_heap只有一條pool,其中用的最多的就是system_heap和chunk_heap.
接下來我們瞭解一下ion_client和buffer的關係。當我們開啟ion裝置時,會建立一個ion_client物件。如下程式碼中所見的那樣,至於內部如何建立的,這裡我們就不介紹了。
static int ion_open(struct inode *inode, struct file *file)
{
struct miscdevice *miscdev = file->private_data;
struct ion_device *dev = container_of(miscdev, struct ion_device, dev);
struct ion_client *client;
pr_debug("%s: %d\n", __func__, __LINE__);
client = ion_client_create(dev, "user");//建立ion_client
if (IS_ERR(client))
return PTR_ERR(client);
file->private_data = client;
return 0;
}
程序,ion_client,buffer的組織結構如下圖這樣。程序之間共享buffer時,也是通過binder機制將共享ion_buffer傳送給對應的程序,然後對應的程序在根據這個fd將該塊buffer對映進自己的程序中。驗證效果:
下面是在公司手機上抓的log,最後一側都是ion_client的名字,其中最後一個欄位中,前半部是程序id,後面的序號是當前第幾個此開啟的ion裝置。
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 1265-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 1265-1
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 1299-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 1299-1
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 208-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 208-1
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 208-2
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 208-3
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 208-4
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 734-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 883-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 883-1
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 906-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 906-1
用ps命令可以看到cameraserver程序,android7.0上camera已經從mediaserver程序中獨立出一個cameraserver程序,注意下面cameraserver程序id是258root 256 1 1005760 65120 poll_sched acd51634 S zygote
audioserver 257 1 32436 6336 binder_thr ac6ed58c S /system/bin/audioserver
cameraserver 258 1 52468 6160 binder_thr b5b6658c S /system/bin/cameraserver
drm 259 1 42844 11348 binder_thr b55d858c S /system/bin/drmserver
這裡我打開了camera後,發現多了很多ion_client,其中258開始的ion_client有15塊。-rw-rw-r-- 1 root root 0 2012-01-01 08:01 1265-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 1265-1
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 1299-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 1299-1
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 1844-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 1844-1
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 208-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 208-1
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 208-2
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 208-3
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 208-4
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-1
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-2
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-3
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-4
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-5
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-6
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-7
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-8
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-9
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-10
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-11
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-12
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-13
-rw-rw-r-- 1 root root 0 2012-01-01 08:03 258-14
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 734-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 883-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 883-1
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 906-0
-rw-rw-r-- 1 root root 0 2012-01-01 08:01 906-1
三、ION buffer分配及共享
1.申請buffer(ION_OPEN)
buffer申請步驟還是很簡單的,申請一個ion_buffer,然後申請ion_handle來盛放ion_buffer.
struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
size_t align, unsigned int heap_id_mask,
unsigned int flags)
{
struct ion_handle *handle;
struct ion_device *dev = client->dev;
struct ion_buffer *buffer = NULL;
struct ion_heap *heap;
int ret;
..........
............
len = PAGE_ALIGN(len);//申請大小要對齊
down_read(&dev->lock);
plist_for_each_entry(heap, &dev->heaps, node) {//根據flag標誌位,判斷從哪一個heap分配buffer
/* if the caller didn't specify this heap id */
if (!((1 << heap->id) & heap_id_mask))
continue;
buffer = ion_buffer_create(heap, dev, len, align, flags);//找到對應的heap就開始從heap分配buffer
if (!IS_ERR(buffer))
break;
}
up_read(&dev->lock);
..........
handle = ion_handle_create(client, buffer);//建立ion_handle,儲存client,buffer各種指標
/*
* ion_buffer_create will create a buffer with a ref_cnt of 1,
* and ion_handle_create will take a second reference, drop one here
*/
ion_buffer_put(buffer);
......
mutex_lock(&client->lock);
ret = ion_handle_add(client, handle);//將建立的ion_handle加入到client的紅黑樹中
mutex_unlock(&client->lock);
if (ret) {
ion_handle_put(handle);
handle = ERR_PTR(ret);
}
return handle;
下面是申請buffer的實現細節,其中我刪除一些我們不必瞭解的程式碼,如果想了解的話,可自行研究原始碼。static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
struct ion_device *dev,
unsigned long len,
unsigned long align,
unsigned long flags)
{
struct ion_buffer *buffer;
struct sg_table *table;
struct scatterlist *sg;
struct timeval time;
int i, ret;
buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL);
if (!buffer)
return ERR_PTR(-ENOMEM);
buffer->heap = heap;//buffer是從哪個heap分配的buffer
buffer->flags = flags; //申請buffer時,用的flag,這一flag標誌buffer從哪個heap分配
kref_init(&buffer->ref); //引用技術器初始化
if (heap->flags & ION_HEAP_FLAG_DEFER_FREE) {
bool cached = ion_buffer_cached(buffer);
ion_heap_freelist_drain(heap, cached, len);
}
ret = heap->ops->allocate(heap, buffer, len, align, flags); //這裡已system_heap為例,下面清看程式碼
if (ret) {
if (!(heap->flags & ION_HEAP_FLAG_DEFER_FREE))
goto err2;
ion_heap_freelist_drain(heap, -1, 0);
ret = heap->ops->allocate(heap, buffer, len, align,
flags);
if (ret)
goto err2;
}
buffer->dev = dev;
buffer->size = len;
.....
ion_buffer_add(dev, buffer);//將上面申請的buffer新增到ion_client的buffer連結串列中
mutex_unlock(&dev->buffer_lock);
......
}
下面這段程式碼才是真正申請buffer的函式,之前我們已經介紹過了system_heap存在3種poll,256k,16k,1k的,所以這裡為了能將申請的buffer連結到對應poll上,系統在申請buffer時,也是按著這些大小一次一次的分配的,並將他們用連結串列連線起來。例如加入我們申請1M記憶體,它會分配1M/256K=4個,這4個瑣碎的buffer,在釋放buffer時都會放到256k大小的那個poll上。
static int ion_system_heap_allocate(struct ion_heap *heap,
struct ion_buffer *buffer,
unsigned long size, unsigned long align,
unsigned long flags)
{
struct ion_system_heap *sys_heap = container_of(heap,
struct ion_system_heap,
heap);
struct sg_table *table;
struct scatterlist *sg;
int ret;
struct list_head pages;
struct page_info *info, *tmp_info;
int i = 0;
long size_remaining = PAGE_ALIGN(size);//將申請的buffer大小頁對齊。
unsigned int max_order = orders[0]; //注意max_order = 8 ;
INIT_LIST_HEAD(&pages);
while (size_remaining > 0) {
info = alloc_largest_available(sys_heap, buffer, size_remaining, max_order);//每次申請最大的記憶體,
if (!info) //申請的優先順序256K->16K->1K
goto err;
list_add_tail(&info->list, &pages);//每次申請到的page首地址都會新增到info->list連結串列中
size_remaining -= (1 << info->order) * PAGE_SIZE; //注意這裡
max_order = info->order;//重新分配大小權值,
i++;
}
table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
goto err;
//......省去一些我們不必瞭解的,具體清參考原始碼
return -ENOMEM;
}
分配buffer時,先分配大塊開始分配,例如我們想分配1M的記憶體,那麼1M/256 = 4塊。那麼如果我們想分配1M+16K的空間,那麼就分配4塊256K和1塊16K的就行了。下面我們來見見alloc_largest_available()的真面目。
static struct page_info *alloc_largest_available(struct ion_system_heap *heap,
struct ion_buffer *buffer,
unsigned long size,
unsigned int max_order)
{
struct page *page;
struct page_info *info;
int i;
for (i = 0; i < num_orders; i++) { //num_orders = 3 分別是8,4,0
if (size < order_to_size(orders[i]))//申請的size小於當前權值對應的mem_size,就會對比第二權值
continue;
if (max_order < orders[i])
continue;
page = alloc_buffer_page(heap, buffer, orders[i]);//分配物理頁面
if (!page)
continue;
info = kmalloc(sizeof(struct page_info), GFP_KERNEL);
info->page = page;
info->order = orders[i];//記錄當前使用的權值,供上一級呼叫處使用。
return info;
}
return NULL;
}
開始建立儲存ion_buffer的ion_handle,其實它的工作很簡單,static struct ion_handle *ion_handle_create(struct ion_client *client,
struct ion_buffer *buffer)
{
struct ion_handle *handle;
handle = kzalloc(sizeof(struct ion_handle), GFP_KERNEL);
if (!handle)
return ERR_PTR(-ENOMEM);
kref_init(&handle->ref); //ion_handle的引用計數初始化
RB_CLEAR_NODE(&handle->node);
handle->client = client;
ion_buffer_get(buffer);
ion_buffer_add_to_handle(buffer);//這個地方很值得推敲,可以看到一個ion_buffer可以對應多個ion_handle,不知道這樣理解正確否。
handle->buffer = buffer; //記錄ion_buffer
return handle;
}
2.buffer共享
在kernel ion driver中可以發現,上層應用通過系統呼叫將我們想share的ion_handle傳下來(其實就是對應idr),然後通過這個handle找到真正的ion_handle。根據ion_buffer在dma buffer中找到其對應的fd,然後其它程序通過這個fd就可以找到對應的buffer了。
case ION_IOC_SHARE:
case ION_IOC_MAP:
{
struct ion_fd_data data;
struct ion_handle *handle;
if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
return -EFAULT;
handle = ion_uhandle_get(client, (int)data.handle);//根據之前儲存的id,找到對應的ion_handle
data.fd = ion_share_dma_buf_fd(client, handle);//在dma buffer中查詢buffer,並返回buffer的唯一fd
if (copy_to_user((void __user *)arg, &data, sizeof(data)))
return -EFAULT;
if (data.fd < 0)
return data.fd;
break;
}
四、Debug方法
在/sys/kernel/debug/ion/ 目錄下有Ion除錯檔案。可以通過這個地方,來檢視自己申請的那個buffer有沒有建立成功。現在手上有nanopi2 的開發板,我們進去看看。能夠發現nxp_contig_heap,當然這個heap 的名字可以每個平臺都不一樣。
[email protected]:/sys/kernel/debug/ion # ls -l
-rw-rw-r-- root root 0 1970-01-01 00:00 1
-rw-rw-r-- root root 0 2016-01-01 08:06 107
-rw-rw-r-- root root 0 2016-01-01 08:06 339
-rw-rw-r-- root root 0 2016-01-01 08:07 504
-rw-rw-r-- root root 0 1970-01-01 00:00 ion_contig_heap
-rw-rw-r-- root root 0 1970-01-01 00:00 ion_noncontig_heap
-rw-rw-r-- root root 0 1970-01-01 00:00 nxp_contig_heap
[email protected]:/sys/kernel/debug/ion # cat ion
ion_contig_heap ion_noncontig_heap
at ion_contig_heap <
client pid size
----------------------------------------------------
----------------------------------------------------
orphaned allocations (info is from last known client):
----------------------------------------------------
total orphaned 0
total 0
----------------------------------------------------
[email protected]:/sys/kernel/debug/ion # cat nxp_contig_heap
client pid size
----------------------------------------------------
nxp-fb 1 11059200
----------------------------------------------------
orphaned allocations (info is from last known client):
surfaceflinger 107 3686400 0 1
surfaceflinger 107 3440640 0 1
surfaceflinger 107 3440640 0 1
surfaceflinger 107 13836288 0 1
surfaceflinger 107 3686400 0 1
surfaceflinger 107 3686400 0 1
----------------------------------------------------
total orphaned 31776768
total 42835968
----------------------------------------------------
上面是還沒開啟camera時的狀態,可以看到media server 程序還沒申請ion buffer開啟camera之後就大不一樣了。下面可以看到不光mediaserver申請了那麼多buffer,而且surfaceflinger程序也申請了很多的buffer,這是由於要顯示camera的資料。不過看到nanopi2開發板關掉camera後,mediaserver程序分配的buffer也沒有釋放,這是他們的bug,我們就管不著了。
[email protected]:/sys/kernel/debug/ion # cat nxp_contig_heap
client pid size
----------------------------------------------------
nxp-fb 1 11059200
----------------------------------------------------
orphaned allocations (info is from last known client):
surfaceflinger 107 131072 0 1
mediaserver 112 466944 0 1
mediaserver 112 118784 0 1
mediaserver 112 466944 0 1
mediaserver 112 118784 0 1
mediaserver 112 118784 0 1
mediaserver 112 118784 0 1
mediaserver 112 118784 0 1
mediaserver 112 466944 0 1
mediaserver 112 118784 0 1
mediaserver 112 118784 0 1
mediaserver 112 466944 0 1
mediaserver 112 118784 0 1
surfaceflinger 107 3686400 0 1
surfaceflinger 107 245760 0 1
surfaceflinger 107 245760 0 1
surfaceflinger 107 245760 0 1
surfaceflinger 107 733184 0 1
surfaceflinger 107 3686400 0 1
surfaceflinger 107 131072 0 1
surfaceflinger 107 13836288 0 1
surfaceflinger 107 245760 0 1
surfaceflinger 107 3686400 0 1
surfaceflinger 107 733184 0 1
surfaceflinger 107 733184 0 1
surfaceflinger 107 3686400 0 1
surfaceflinger 107 131072 0 1
surfaceflinger 107 733184 0 1
surfaceflinger 107 733184 0 1
surfaceflinger 107 733184 0 1
----------------------------------------------------
total orphaned 37175296
total 48234496
----------------------------------------------------
android5.1 camera服務依然還是在media server程序中,下面能夠看到mediaserver程序的pid時112,和上面的吻合。
drm 111 1 23224 3976 ffffffff b6ef9708 S /system/bin/drmserver
media 112 1 200540 11444 ffffffff b6ec0708 S /system/bin/mediaserver
install 113 1 9412 684 c05388ec b6f3a29c S /system/bin/installd