加载中…
个人资料
Steven天天Helloworld
Steven天天Helloworld
  • 博客等级:
  • 博客积分:0
  • 博客访问:4,491
  • 关注人气:4
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
正文 字体大小:

Binder Driver内数据结构布局

(2012-08-02 08:48:13)
标签:

it

分类: Android驱动学习笔记

Binder Driver内的各种数据结构繁复,这几天连蒙带猜,终于有了一点感觉,写个blog记录一下,很可能哪里说得不对,希望指正。

首先,对Binder数据结构的讨论,就从所谓的“Binder Driver的根结构”---binder_proc结构体开始讨论。

binder_proc结构体是在进程打开binder driver的时候创建的,也就是调用binder_open()的时候,反过来讲就是所有打开binder_proc的进程,在Binder Driver中都有一个binder_proc的数据镜像,这个数据镜像保存着进程与binder_driver交互的各种信息。所有的binder_proc结构体在内核中的组织方法是双向链表。

binder_proc中的数据主要分成几部分:

1.提供服务和使用服务的相关信息

2.用户和内核间内存映射的相关信息

3.等待队列

4.工作队列

5.工作线程相关信息

6.Binder状态和优先级

首先头两个,1和2项的信息比较麻烦,放到后面介绍,先说说比较简单的等待队列、工作队列、工作线程相关信息。

等待队列:

Binder是一个IPC工具,因此,客户端和服务端在发送和接受数据的时候肯定会阻塞情况,比如服务端监听来着Binder的请求时,要阻塞,当请求过来时,从等待队列唤醒,再比如当客户端通过Binder发送完IPC请求后,要阻塞等待服务端的返回数据,因此等待队列数据:

weit_queue_head_t wait;

就是用来保证唤醒队列时的队列信息。

 

工作队列:

首先,工作队列类似于一个工作列表,它在binder driver中的调用方式类似于如下伪代码实现:

while(1){

...

work=list_first_entry(work_queue);

根据work信息,执行相应操作;

....

}

实际的实现当然比这个伪代码复杂,但是,逻辑是一样的。

工作队列在binder_proc中的存在形式是:

struct list_head todo;

熟悉linux的人肯定知道,这是一个链表。

通过todo可以获得binder_work结构

binder_work结构体定义如下:

struct binder_work{

   struct list_head entry;

   enum{

   ...

   }type;

}

type中的枚举类型是标示工作模式的。然后通过container_of()函数调用,可以获得binder_transaction结构体。这个结构体是binder driver用来传送数据所用的结构体,记录了数据从什么地方来(数据的发送线程),到什么地方去(接受进程和线程)。以及数据buffer在内核空间和用户空间的逻辑地址。(这一句可能有点晕,先放一下,一会说完内核和用户的内存映射就好懂了,现在知道这是传送数据的buffer就行了)

总结一下,工作队列是一个由binder_work组成的链表,每一个binder_work保存着与工作相关的具体信息(binder_transaction结构体)。进程的任务就是不停的循环处理队列中的工作。

 

工作线程:

工作线程(threads)就是进程中,真正干活的伙计。就是所谓的“劳者一份”。当要进行ioctl()调用的时候(就是真正要干活,进行数据传输的时候),就会调用binder_get_thread()函数,获得一个线程来为自己干活(这里好像涉及到线程池机制,还没看懂,如果谁看懂了,欢迎讨论)。根据任务的种类(具体就是看看是读(read)还是写(write)),来调用binder_thread_read()或者binder_thread_write()。而具体的binder driver的数据交换,是在这两个函数中完成的。因此,工作线程就是进程中负责通信的线程,具体的数据如下:

struct rb_root threads;

int max_threads:

int request_threads;

int request_threads_started;

int ready_threads;

 

下面讨论最重要的两个数据,服务相关数据,和内存映射相关数据:

服务相关数据:

struct rb_root nodes;

struct rb_root refs_by_desc;

struct rb_root refs_by_node;

首先要介绍另一个重要数据结构“binder_node”,binder_node是在服务注册的时候生成的,它代表的是一个服务。

然而,一个进程提供的,不止一个服务,多个服务通过nodes的红黑树(rb_tree)组织起来。所以,nodes红黑树维护的是进程所提供的服务集合。

另一个结构binder_ref数据结构:

struct binder_ref{

struct binder_proc *proc;

struct binder_node *node;

uint32_t desc;

}

这个结构体是在服务检索的时候生成的(唯一的例外在Context Manager中,这个特殊情况后面再分析),也就是说,这个ref索引节点表征的是一个进程使用的服务,ref中的proc是ref拥有进程的binder_proc结构,node就是正在使用的服务节点。desc是服务编号,用来标识服务。

 

内存映射相关信息

这里首先要讨论的是binder_mmap()函数,这个函数通过mmap()的调用,把内核中的一个区域和用户空间的一个区域映射在了一起,映射到了同一个物理页面。这也是binder效率高的原因。一般我们在内核态使用用户数据,或者内核态把数据传送给用户空间都使用copy_from_user()或者cop_to_user()。binder用binder_mmap()函数,使内核空间的一段内存和用户空间一段内存映射到了同一段物理内存上,当需要传输数据的时候,只要把内核空间的内存地址加上相对与用户空间的地址偏移,就行了。(用户使用高3G的数据空间,内核是低1G的数据空间)

那么在binder_proc中维护的内存映射相关信息如下:

struct wm_area_struct *vma;

struct task_struct *tsk;

struct files_struct *files;

void *buffer;

ptrdiff_t user_buffer_offset;

struct list_head buffers;

struct rb_root free_buffers;

struct rb_root allocated_buffers;

struct page **pages;

size_t buffer_size;

uint32_t buffer_free;

上面标示的数据还是要提一下,就是,binder在传送RPC数据的时候才使用mmap区域,而RPC代码,Binder协议等等都是在用户空间设置好,然后copy_from_user()的。因为RPC数据的大小不定,要根据具体的RPC代码和服务node,因此所有的空闲buffer,用红黑树rb_tree组织起来,按需分配。

0

阅读 评论 收藏 转载 喜欢 打印举报/Report
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 电话:4000520066 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有