Binder通信流程分析

一、Binder通信示例图

client和服务server通信必须要三个过程:

1、server之所以成为服务,必须向服务管理器SMgr注册

2、client想同server通信,必须先向服务管理器SMgr获取服务的引用号。

3、client发送数据包binder_transaction_data同server开始通讯

二、服务server向服务管理器SMgr注册自己

1、服务拥有自己Binder实体和0号引用,注册的时候,通信流程:

  服务中的0号引用->内核中的服务对应的0号引用->MSgr在内核中Binder实体->MSgr中的Binder实体。

2、注册的时候通过进程里的0号引用,向服务管理器发送Binder对象,即flat_binder_object数据结构,其中:

   flat_binder_object.type=BINDER_TYPE_BINDER  //表示传递的是Binder实体

   flat_binder_object.binder = 该服务中binder实体的地址

3、流经驱动时,驱动会根据这个binder值(binder实体的地址),在驱动内核中的服务进程相关的红黑树中进行搜索,搜索其对应内核中的Binder实体。

4、第一次注册,肯定搜索不到,因此需要创建对应的内核binder节点(内核中服务对应的binder实体),也就是要构建内核中的binder_node结构体:

   ->构建binder_proc结构体来记录server进程的信息填充到binder_node.proc中-->服务进程的信息

   ->flat_binder_object.binder值赋值给binder_node.ptr做备份---->服务进程binder实体的信息

   ->设置一些flag和保存cookie---->其他信息

   ->将该binder节点插入到内核中的服务进程相关的红黑树中,binder_node.rb_node,在红黑树中的节点信息。

   ->binder_node.refs:该节点的引用链表,向本节点的引用都链接在该队列里。这些引用可能隶属于不同的进程。通过该队列可以遍历指向该节点的所有引用。

5、内核中服务的binder实体创建完后,需要为接收方SMgr创建内核binder引用(结构体binder_ref),其中:

      binder_ref.node = 刚创建的内核binder节点的内存地址   -->引用同实体关联

      binder_ref.proc  = 本引用所属的进程,此为SMgr进程      -->引用同进程关联, 表示该引用属于哪个进程

      binder_ref.desc  = 本结构的引用号

      binder_ref.rb_node_desc =以引用号为索引添加到红黑树中的树节点信息

      binder_ref.rb_node_node=以binder节点的内存地址为索引添加到另一颗红黑树中的树节点信息

      binder_ref.node_entry :将该引用链接进所指向的内核binder_node.refs //实现引用同节点的一对多的关联

6、对数据包flat_binder_object进行改造:

     flat_binder_object.type=BINDER_TYPE_HANDLE//表示传递的是Binder引用

     flat_binder_object.handle= binder_ref.desc    //内核中binder引用的引用号

7、数据包传递给服务管理器SMgr,SMgr接收到注册的数据包之后,将会把引用号和server名字填进一张表中,以供后面client查询使用,服务server的名字、PID等信息在Binder收发数据包结构:binder_transaction_data中包含,flat_binder_object包含在它的buffer域中

注册过程总结:根据进程中的实体,在内核中建立对应的实体节点,在根据内核中的实体节点创建对应的内核SMgr的binder引用,SMgr把服务填进注册表中

三、client向SMgr获取特定名字服务的引用号

Client要使用服务进程的功能,必须先通过SMgr来获得服务的引用句柄才能发起通信。

1、client也是通过client中的0号引用、内核中的0号引用、内核中的0号实体,同SMgr服务管理器进行通信的

2、Client以0号引用和server的名字为参数传递给SMgr

3、SMgr查表获得对应server进程在本SMgr进程中的引用号,填充数据包返回给client

      flat_binder_object.type = BINDER_TYPE_HANDLE

      flat_binder_object. Handle = Smgr进程在内核中对应server的引用号,即我们注册时保存的引用号

4、数据包流经驱动时,做如下处理:

     -->根据flat_binder_object. Handle,即引用号,在红黑树中查找是否有这个引用,如果没有找到,内核驱动就会认为这个handle是非法的,拒绝发送请求。

     -->如果找到这个引用,就会获得这个引用对应的节点binder_ref.node

     -->根据这个节点和client的进程信息proc,对应binder_ref.node、binder_ref.proc ,在接收方的进程(client)中的红黑树中搜索对应于该client的binder引用结构体,如果是第一次,那肯定不能搜索到了,这个时候就会为这个client进程线程构建一个binder_ref结构体作为binder引用:

      binder_ref.node = 刚创建的内核binder节点的内存地址   -->引用同实体关联

      binder_ref.proc  = 本引用所属的进程,此为client进程      -->引用同进程关联,表示该引用属于哪个进程

      binder_ref.desc  = 本结构的引用号

      binder_ref.rb_node_desc =以引用号为索引添加到红黑树中的树节点信息

      binder_ref.rb_node_node=以binder节点的内存地址为索引添加到另一颗红黑树中的树节点信息

      binder_ref.node_entry :将该引用链接进所指向的内核binder_node.refs //实现引用同节点的一对多的关联

5、驱动对数据包改造,并返回给client

      flat_binder_object.type = BINDER_TYPE_HANDLE

      flat_binder_object. Handle = client进程在内核中对应server的引用号,即我们刚创建的引用的引用号

四、client和server通讯,结合数据包的传输、binder通信协议,以client执行server的1001号函数为例

    1、client 通过ioctl(fd, cmd, arg)函数,同服务器server通信,其中:

        cmd为BINDER_WRITE_READ:写入或读取数据

        arg为binder_write_read结构

     2、binder_write_read结构内容: 

struct binder_write_read 
{ 
signed long write_size; //写数据缓冲区大小
signed long write_consumed;
unsigned long write_buffer; //写数据缓冲区

signed long read_size;
signed long read_consumed;
unsigned long read_buffer;
};

    写数据缓冲区的内容为:写入请求数据命令+传输数据包参数(BC_TRANSACTION+binder_transaction_data)

     3、binder传输数据包binder_transaction_data结构内容:       

struct binder_transaction_data 
{ 
   union 
   {    
        //引用号,上一部中获取的服务在内核中对应该client的引用号
        //通过该引用号来表明数据包的目的server
        size_t  handle; 
        void    *ptr;   
   } target; 
   void        *cookie; 
    
    //Server端定义的公共接口函数的编号,此处取为1001
    unsigned int    code; 

    unsigned int    flags; 
    
    //client的进程信息
    pid_t       sender_pid; 
    uid_t       sender_euid; 
    
    size_t      data_size;//数据大小,此处为函数参数的大小 
    size_t      offsets_size;//不传递binder对象,不关心
 
    union 
    { 
        struct
         {  
            //1001号函数需要的参数,放在此缓存区中
            const void  *buffer; 
      
            const void  *offsets; //不传递binder对象,不关心
        } ptr; 
        uint8_t buf[8]; 
    } data; 
}; 

此处主要关心:引用号handle、要调用的函数编号code、函数需要传递的参数buffer、client进程信息sender_pid\sender_euid,对应的关键字就是:目的、调用函数及参数、源

    4、Binder驱动获取这个数据包后,根据引用号和进程信息,到红黑树中查询关于该client在内核中的对应server的引用:binder_ref结构体。进而通过其node域知道对应的内核服务的binder节点,然后再从binder节点的proc域知道目标binder所在的进程和其他相关的信息,实现数据包的路由

 

 

 

你可能感兴趣的:(Binder通信流程分析)