Android之ION内存管理分析

    备注:图片中的双向箭头表示他们是链表,前后链接起来的,单向箭头表示指针指向谁。

    感兴趣可以加QQ群85486140,大家一起交流相互学习下!

  做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 的名字可以每个平台都不一样。

 

root@nanopi2:/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
root@nanopi2:/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
----------------------------------------------------
root@nanopi2:/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,我们就管不着了。

 

 

root@nanopi2:/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

 

 

 

  • 13
    点赞
  • 112
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Android ION(IPC over NETwork)是一种使用Android的Binder IPC通信机制进行进程间通信的开源库。在进行内存分析时,我们可以通过以下几个步骤来分析Android ION的内存使用情况。 首先,我们可以使用内存分析工具,如Android Studio提供的Memory Profiler,通过单击“Start Recording”按钮开始记录内存使用情况。然后,我们可以执行包含Android ION代码的操作,例如分配和释放ION内存缓冲区。 接下来,我们可以使用Memory Profiler工具来分析我们的应用程序在执行这些操作时所使用的内存。工具会提供一个内存使用情况的图表,我们可以根据这个图表来判断再分配和释放ION内存缓冲区时是否存在内存泄漏或者过度内存使用的情况。 此外,我们还可以使用Android的Debug.MemoryInfo API来获取有关Android ION内存使用情况的更详细信息。通过使用MemoryInfo对象的getMemoryStats()方法,我们可以获取关于Android ION的内存使用情况的一些统计数据,例如分配的内存缓冲区的数量、总大小等。 最后,在进行完内存分析后,我们可以根据分析结果来优化我们的应用程序。例如,我们可以检查我们的代码是否正确地释放了ION内存缓冲区,并确保我们只分配了我们实际需要的内存大小。我们还可以考虑使用更有效的算法来减少对ION内存缓冲区的需求,从而降低内存使用量。 总结来说,通过使用内存分析工具和Android的Debug.MemoryInfo API,我们可以对Android ION的内存使用情况进行分析和优化,以确保我们的应用程序能够有效地使用ION内存缓冲区,并避免内存泄漏和过度内存使用的问题。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值