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

Binder Driver的工作流程

(2012-08-02 10:24:53)
分类: Android驱动学习笔记

Binder Driver的工作主要可以分为3类,服务注册,服务查询,服务使用。

Binder Driver的工作流程大致分为6步:

1.接收端开放监听,等待接收IPC数据

2.发送端发送IPC数据

3.发送端等待接收端的应答数据而阻塞

4.接收端接收IPC数据并传递至用户空间

5.接收端发送应答数据

6.原发送端接收应答数据并传送至用户空间

 

首先,关注服务注册阶段:

首先,服务注册阶段binder driver要在内核空间生成binder_node节点,并把节点的ref注册到Context Manager中,把node注册到服务提供者的proc中。

1.阶段:

Context Manage调用binder_open()打开binder设备,创建binder_proc结构体,然后调用binder_mmap函数,使用内存映射开辟缓存区。

生成binder_write_read结构体,这里面保存着读写数据数据的相关信息。

调用ioctl(),生成工作线程thread,把binder_write_read拷贝到内核空间,通过分析binder_write_read中wrire和read的缓冲区大小,来决定执行读操作还是写操作。执行binder_thread_read(),进入阻塞状态。

2.阶段:

同样,先执行binder_open(),和binder_mmap()函数,生成binder_proc和binder_buffer结构体。

之后从用户空间拷贝binder_write_read结构体,而具体的IPC数据内容在binder_write_read的write_buffer中,而write_buffer中的结构体形态如下图:

Binder <wbr>Driver的工作流程

BC_TRANSACTION是binder协议,前面讲过,后面就是主要要传输的RPC数据,handle是服务编号,RPC代码是具体调用的函数信息,RPC数据中第一个数据时flat_binder_object,这个结构体标示了服务的类型。

struct flat_binder_object{

unsigned long type;

}

这里主要分析两个type, 一个是BINDER_TYPE_BINDER,用来生成节点,一个是BINDER_TYPE_HANDLE,用来检索节点。

当前type为BINDER_TYPE_BINDER,这是在用户态程序启动注册服务的函数时设置的。

这里补充说明一下,就是write_buffer中不止一个IPC数据,这里只是以最简单的一个数据为准。

这里根据binder协议BC_TRANSACTION,转入传输函数段binder_transaction()。

binder_transaction主要完成传输任务:

完成传输任务主要需要三个必须的参数:发送端、目的地和数据。

static void binder_transaction(struct binder_proc *proc,

   struct binder_thread,struct binder_transaction_data *tr, int reply)

{

    struct binder_transaction *t;

    struct binder_proc *target_proc;

    struct binder_node *target_node = NULL;

    wait_queue_t *target_wait;

    ...

}

其中,binder_transaction保存数据传输所需数据,比如发送端接收端,IPC数据等等。target_proc是指向接收端的binder_proc,target_node是保存已经注册在target_proc的节点信息。target_wait用于唤醒接收端。

服务注册阶段,数据的接收方是Context Manager,因此handle值为0。Handle,就是所谓的资源号,Context Manager的资源号为0,然后根据注册的顺序,依次为1、2、3。。。也就是唯一标示服务的编号。

如果当handle=0时,直接定位全局变量binder_context_mgr_node,这是标示Context Manager的全局变量,在系统启动的时候初始化。然后:

target_node=binder_context_mgr_node

通过target_node定位target_proc:

target_proc=target_node->proc;

定位了目的地之后,然后通过binder_transaction结构体传输数据。(这个之后再讲,所有的binder传输都是这一套。)

然后在本地proc中查找,是否有自己的node。这里肯定没有,因为,我们的服务还没注册。

于是生成新节点:

node = binder_new_node(...)

然后把新节点node注册到自己proc的rb_tree中。

同时查找是否在目的端的Proc中存在ref。这里也肯定没有,因为刚刚注册,还没有注册到Context Manager端的proc中。这时,生成新的ref,注册到目的端Context Manager。

这时,这个阶段要完成的工作就基本完成了,此时把binder_transaction的工作加入到todo工作列表中,转入等待队列。

上面林林总总的说了一堆,简单来讲,阶段2中,驱动完成了在内核空间在Context Manage和Service Server端的节点node的注册,然后把生成的handle数据封装成IPC数据包,供接收端Context Manager储存服务列表。

阶段3:

当Service Server完成了写的工作之后,他的任务也完成了,就是等待Context Manager给它回复,类似于阶段1,转入等待队列。

阶段4:

在阶段1中,Context Manager通过binder_write_read结构体的read_buffer转入等待队列。当阶段2完毕之后,Context Manager从队列中唤醒,首先查找binder_transaction结构体。具体的方法,是通过todo工作列表中的binder_work结构体巧妙的查到的,简单的来说就是通过链表找到binder_work结构体,然后通过container_of()函数,扩充为binder_transaction结构体。这是内核编程常用的技巧,具体不再赘言。

获得了binder_transaction结构体,就相当于获得了通信所需的所有数据。(这个怎么通过binder_transaction传递数据,以后再说,这里只讨论工作流程中,内核中数据结构的变化)然后把binder_transaction拷贝到我们之前讨论的内存映射的区域,传送到用户空间。

同时把相应的binder_transaction结构保存在thread->transaction_stack中,以备之后发送应答数据做准备。

阶段5:

在上个阶段最后,保存了和Service Server通信的binder_transaction结构,这时,把它取出来,根据结构体中的目的地和来源地信息,设置返回数据的相关信息。然后类似于阶段2,通过binder_transaction传输回去。

阶段6:

类似于阶段4,接收完应答数据通过之前mmap映射得到的区域,把数据回送回用户空间。

 

服务检索:

这个服务检索的过程其实类似于服务注册,只是有一点不一样:

阶段2:

在这里因为没有注册任务,所以在RPC数据中少了一个重要数据结构体flat_binder_object,这样就没有注册动作,也就不会创建binder_node结构体。

阶段5:

这是服务检索的核心阶段,这里Context Manager提供了一个handle的服务标号,这是在服务注册的时候,在Context Manager用户空间登记的资源标号,这里,binder driver要完成的工作主要是如何通过这个handle找到相应的服务进程和服务节点,然后提供给发送端客户。

因为所有的服务在Context Manager的proc中都有注册,就是之前讲过的ref索引。逻辑上我们可以认为,Context Manager是所有服务的使用者。因此我们通过在Context Manager中的ref红黑树中就可以查找到需要的索引号ref。通过函数binder_get_ref(proc,handle),就可以得到。

这里得到了ref索引以后,因为客户端检索服务肯定是因为要使用这个服务,因此,根据ref索引相应的数据,在发送端客户的proc中,注册相应的ref信息。

 

服务使用

这里只是在阶段2中会有所不同:

在服务注册的时候,阶段2是根据全局变量binder_context_mgr_node确定目的节点。而这里,因为要使用的服务并不是Context Manager所提供的服务,因此handle值不等于0,因此根据相应的handle值来检索服务。因为在服务检索时,已经把要使用的服务注册到了本地proc的rb_tree中,因此通过handle查找ref索引,然后通过ref定位目标node,然后剩下的就和服务注册时一样的,把IPC数据发送给node所指的proc中。

 

 

0

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

    发评论

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

      

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

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

    新浪公司 版权所有