1. 程式人生 > >Android之ION記憶體管理分析

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的結構體。如下所示:

/**
 * 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;
};
len:我們要申請的記憶體大小,是頁對齊的,如果應用層沒有做頁對齊,那麼在核心driver裡面也會做頁對齊的。

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是258
root         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