引用上篇文章作者的一幅经典图片描述,记录下读后的一点心得,没有对照源码深入分析,可能有的地方存在误解:
下面结合图1 binder通讯示例的图,总结以下三个过程:server向SMgr注册、client向SMgr获取特定名字服务的引用号、client和server通讯 在驱动中的动作和流程:
1. server项SMgr注册自己
server是拥有自己的实体binder的,注册的时候是用0号引用向SMgr发送flat_binder_object数据结构体,其中的type = BINDER_TYPE_BINDER, binder= server进程中binder对象的地址,当流经binder驱动的时候,驱动就会以binder值为索引在server进程的红黑树中搜索:
a. 如果没有搜索到,就说明这是第一次发送需要创建对应的内核binder节点,也就是要构建内核中的binder_node结构体,当然同时还需要构建binder_proc结构体来记录server进程的信息填充到binder_node.proc中,也要把flat_binder_object.binder值赋值给binder_node.ptr做备份,最后就是设置一些flag和保存cookie了,最后通过binder_node.rb_node将这个binder节点添加到server进程的一棵红黑树中。这是binder驱动对发送方所做的事情。
那么接下来binder驱动就会为接收方SMgr创建内核binder引用了,这里是第一次的情况,那么理所当然就不会存在内核binder节点了,也是要现场构建结构体binder_ref的,binder_ref.node域记录着前面刚刚创建的内核binder节点的内存地址,binder_ref.proc记录这SMgr进程对应的binder_proc(注意这是记录引用—接收方所属的进程,而不是发送方的进程),重要的还是这个引用号,它是内核驱动中动态分配的32位无符号整形数,和每个进程都有各自的文件描述符集类似,这个引用号对各个进程来说可能会不尽相同。这个接收方SMgr进程中维护这两棵红黑树:rb_node_desc, rb_node_node,分别以引用号和binder节点的内存地址为索引来将这个binder_ref挂进这两个红黑树中。最后会以binder_ref.node_entry为结点将binder_ref链接进所指向的内核binder节点的refs域中: binder_node.refs。最后会将数据包中的flat_binder_object结构体数据中的handle域设置成最新分配的引用号binder_ref.desc,将type域换成BINDER_TYPE_HANDLE。这就是驱动对接收方所做的事情和对数据包的改造。SMgr接收到注册的数据包之后,将会把引用号和server名字填进一张表中,以供后面client查询使用。
b. 如果以flat_binder_object.binder值为索引搜索到了对应的内核binder节点,说明内核中已经创建了binder节点。一个典型的例子就是,两个进程通过已知的公开binder通道建立一个私密的binder通道。当然第一次传输这种建立私密通道的数据包的时候,和a中的情形类似,会建立相对的内核binder节点和binder引用结构体,但是如果发送方再次发送了同样的要求建立私密通道的数据包时,就会出现b中的情形。可以在红黑树中找到对应的内核binder节点,接下来就会在接收方的红黑树中以内核binder节点的地址搜索对应的binder引用,看是否已建立,如果没有就建立对应的binder引用,之后对数据包进行改造即可传给接收方。
2. client向SMgr获取特定名字服务的引用号
Client要使用服务进程的功能,必须先通过SMgr来获得服务的引用句柄才能发起通信。这个索要引用的过程其实也蛮简单。Client以0号引用和server的名字为参数传递给SMgr,SMgr查表获得对应server进程在本SMgr进程中的引用号,填充数据包flat_binder_object.type = BINDER_TYPE_HANDLE, 和flat_binder_object. Handle = Smgr进程对server的引用号(对client无用)。然后返回给client,在返回数据包的过程中,binder驱动需要做一下事情:
首先根据这个handle在SMgr进程的另一棵红黑树上查找对应的内核binder引用结构体,如果没有找到,内核驱动就会认为这个handle是非法的,拒绝发送请求。可以找到的话,就以binder_ref.node(binder节点的内存地址)在接收方的进程(client)中的红黑树中搜索对应于该client的binder引用结构体,如果是第一次,那肯定不能搜索到了,这个时候就会为这个client进程线程构建一个binder_ref结构体作为binder引用,这个binder引用要添加进client进程的两颗红黑树中,同时也要添加进对应binder节点结构体binder_node.refs链表中。最后再把需要返回的给client的数据包进行改造,将新分配的引用号添加flat_binder_object. Handle中,这个时候才返回给client真正的对应于server内核binder节点的引用号。
3. client和server通讯
Client在发送数据包时,会通过将引用号填入binder_transaction_data结构体的target.handle域中来表明数据包的目的server。Binder驱动根据该引用号在放松法的红黑树中找到binder_ref结构体,进而通过其node域知道对应的内核binder节点,然后再从binder节点的peoc域知道目标binder所在的进程和其他相关的信息,实现数据包的路由。