Android Binder 驱动分析 - 数据结构

binder_procs

static HLIST_HEAD(binder_procs);
系统所有的binder_proc都在这个双向链表上。

在binder_open中每一个新创建的binder_proc都通过binder_proc->proc_node链接到这个双向链表;在binder_deferred_release函数释放binder_proc前,从binder_procs删除这个proc节点

flat_binder_object

我们把进程之间传递的数据称之为Binder对象,分为Binder实体和Binder引用,对应于数据结构flat_binder_obejct。一个transaction中可能包含有多个Binder object。

 42 /*                 
 43  * This is the flattened representation of a Binder object for transfer
 44  * between processes.  The 'offsets' supplied as part of a binder transaction
 45  * contains offsets into the data where these structures occur.  The Binder
 46  * driver takes care of re-writing the structure type and data as it moves
 47  * between processes.
 48  */     
 49 struct flat_binder_object {
 50     /* 8 bytes for large_flat_header. */
 51     unsigned long       type;
 52     unsigned long       flags;
 53 
 54     /* 8 bytes of data. */
 55     union {
 56         void        *binder;    /* local object */
 57         signed long handle;     /* remote object */
 58     };
 59 
 60     /* extra data associated with local object */
 61     void            *cookie;
 62 };

@type描述的是Binder的类型

 29 enum {          
 30     BINDER_TYPE_BINDER  = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
 31     BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
 32     BINDER_TYPE_HANDLE  = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
 33     BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
 34     BINDER_TYPE_FD      = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
 35 };  

BINDER_TYPE_BINDER表示数据就是一个Binder本地对象

BINDER_TYPE_HANDLE表示数据是一个远程handler

BINDER_TYPE_FB表示传输的数据中存储的是文件描述符

BINDER_TYPE_WEAK_BANDER

BINDER_TYPE_WEAK_HANDLE

@flags字段表示传输方式,比如同步异步,和binder_transaction_data中flags 有相同的含义

110 enum transaction_flags {
111     TF_ONE_WAY  = 0x01, /* this is a one-way call: async, no return */
112     TF_ROOT_OBJECT  = 0x04, /* contents are the component's root object */
113     TF_STATUS_CODE  = 0x08, /* contents are a 32-bit status code */
114     TF_ACCEPT_FDS   = 0x10, /* allow replies with file descriptors */
115 };

@TF_ONE_WAY表示单向传递,隐含着操作是异步的,不需要返回;

@TF_ROOT_OBJECT表示内容是一个组建的根对象,对应类型为本地对象Binder

@TF_STATUS_CODE 表示内容是一个32位的状态码,对应类型为远程对象的引用(即句柄handle)

@TF_ACCEPT_FDS表示可以接收一个文件描述符,对应的类型为文件(BINDER_TYPE_FD),即handle中存储的为文件描述符


无论是Binder引用还是Binder实体都从属于某一个进程,所以该结构不恩你个透明的在进程之间传输,必须经过驱动翻译。例如当Server把Binder实体传递给Client时,在发送数据流时binder实体通过Binder dirver,Binder driver发现flat_binder_object中的type是BINDER_TYPE_BINDER,说明binder指向的是server进程用户空间地址。把这个直接传送给client是毫无用处的,驱动必须要对数据流中的这个Binder进行修改,将type改为BINDER_TYPE_HANDLE,同时为这个Binder实体在接收进程中创建位于内核的Binder引用,并将引用号填入handle中。同样如果数据流中包含的是Binder引用(type 为BINDER_TYPE_HANDLE),那么要找到这个引用对应和的内核实体binder_node,然后用binder_node->ptr替换@binder,并且修改type为BINDER_TYPE_BINDER。这样接收进程接收到的就是一个进程本地地址。

这样做有两个好处:

1. 保证唯一性:使用Binder实体的用户空间地址并不能保证唯一性,其他server的Binder实体可能使用相同的地址

2. 保证安全性:应用程序无法随便猜测一个引用号填入target.handle来申请服务了,因为驱动还没有为application在内核中为Client进程创建相应的Binder引用,必定会被拒绝。只有经过Binder driver为Client生成的Binder引用才能使用



binder_transaction_data

Binder使用binder_transaction_data传输实际内容,其定义如下

117 struct binder_transaction_data {
118     /* The first two are only used for bcTRANSACTION and brTRANSACTION,
119      * identifying the target and contents of the transaction.
120      */
121     union {
122         size_t  handle; /* target descriptor of command transaction */
123         void    *ptr;   /* target descriptor of return transaction */
124     } target;
125     void        *cookie;    /* target object cookie */
126     unsigned int    code;       /* transaction command */
127 
128     /* General information about the transaction. */
129     unsigned int    flags;
130     pid_t       sender_pid;
131     uid_t       sender_euid;
132     size_t      data_size;  /* number of bytes of data */
133     size_t      offsets_size;   /* number of bytes of offsets */
134 
135     /* If this transaction is inline, the data immediately
136      * follows here; otherwise, it ends with a pointer to
137      * the data buffer.
138      */
139     union {
140         struct {
141             /* transaction data */
142             const void  *buffer;
143             /* offsets from buffer to flat_binder_object structs */
144             const void  *offsets;
145         } ptr;
146         uint8_t buf[8];
147     } data;
148 };

@handle 对于发送数据包的一方,该成员指明发送目的地,这里填入的是对Binder实体的引用,根据此handle,当数据到达接收方时,驱动已经将该成员修改为Binder实体,即指向Biner对象在应用进程中的指针,使用target.ptr来获取。该指针是接收发在将Binder实体传输给其他进程时提交给驱动的,驱动程序会自动将发送方填入的引用转换成接收方Binder对象的指针,故接收方直接将其当作对象指针来使用。

@ptr 对于响应方,@target联合使用ptr字段,以便找到需要处理此事件的对象。因此handle和ptr是同一件事的两种表达方式,驱动要负责handle和ptr之间的转换,当然驱动一定要维护二者之间的转换关系。

@cookie 表示target字段所附加的额外数据,发送方忽略该成员,接收方在收到数据包时,该成员存放的是接收方创建Binder实体时定义的任意数值,作为与Binder指针相关的额外信息放在驱动中。

@code 是一个命令,它描述了请求对象要执行的命令码,这是一个服务特定的字段,驱动完全不关心该成员的内容。不同的服务有不同的命令集,以servicemanager服务为例,支持的命令码为

    SVC_MGR_GET_SERVICE = 1,
    SVC_MGR_CHECK_SERVICE,
    SVC_MGR_ADD_SERVICE,
    SVC_MGR_LIST_SERVICES,

@flags 与交互相关的标志位,其中最总要的是TF_ONE_WAY位。如果该位置上表明这次交互是异步的,Server端不会返回任何数据。驱动力用该位来决定是否构建与返回相关的数据结构。

@sender_pid: @sender_euid表示进程的UID和PID,由驱动负责填入,接收方可以读取该成员获知发送方的身份

@data_size: 表示data.buffer指向的缓冲区存放数据的长度

@offsets_size:驱动一般不会关心data.buffer里面存放的数据,但是如果数据中包含binder,那么需要通过@offset来告知驱动binder在data.buffer中的偏移位置。在data.buffer中可能存在多个Binder,因此需要多个偏移位置来表示,@offset_size就是表示偏移位置占用的字节数。也就是说如果有两个offset,那么@offset_size值为8

@data.ptr.buffer:存放要发送或者接收到的数据;

@data.ptr.offsets:指向Binder偏移位置数组,该数组可以存在于data.buffer中,也可以在另外的内存空间中,并无限制。

之所以引入offsets_size和ptr.offsets这两个成员,是和Binder驱动的工作机制相关的。在Binder通信中,一个Binder实体可以发送给其他进程从而建立许多跨进程的引用;另外这些引用也可以在进程之间传递,就像java里面将一个引用赋值给另外一个引用一样。为Binder在不同进程中建立引用必须有驱动的参与,由驱动在内核创建并注册相关的数据结构后接收方才能使用该引用。而且这些引用可以是强类型,需要驱动为其维护引用计数。然而这些跨进成传递的Binder实体或引用保存在应用程序的数据包里,数据格式由用户定义。因此需要通过一种方法通知驱动,否则驱动无法从数据中提取Binder实体或引用。于是就使用数组data.ptr.offsets存放数据中每个Binder相对data.buffer的偏移量,用offsets_size表示这个数组的大小。驱动在转发数据包时会根据data.offset和offset_size将data.ptr.buffer中的Binder实体和Binder引用找出来,做相应的处理。


binder_write_read

struct binder_write_read {
    signed long write_size; /* bytes to write */
    signed long write_consumed; /* bytes consumed by driver */
    unsigned long   write_buffer;
    signed long read_size;  /* bytes to read */
    signed long read_consumed;  /* bytes consumed by driver */
    unsigned long   read_buffer;
};  

@write_size @write_consumed @write_buffer仅当这是个请求命令时有效

@read_size @read_consumed @read_buffer仅当读(返回操作结果)时有效

@write_buffer包含了一系列请求 线程执行的Binder命令

@write_size 表示写入的数据大小

@write_consumed 表示被消耗的写数据的大小

@read_buffer包含了一系列线程执行后填充的返回值

@read_size表示读取数据的大小

@read_consumed表示被消耗的读数据的大小


binder_proc

binder_proc结构体用于保存调用Binder的各个进程或者线程的信息,每当线程或者进程调用binder_open时都会创建一个新的proc结构

 282 struct binder_proc {
 283     struct hlist_node proc_node;
 284     struct rb_root threads;
 285     struct rb_root nodes;
 286     struct rb_root refs_by_desc;
 287     struct rb_root refs_by_node;
 288     int pid;
 289     struct vm_area_struct *vma;
 290     struct task_struct *tsk;
 291     struct files_struct *files;
 292     struct hlist_node deferred_work_node;
 293     int deferred_work;
 294     void *buffer;
 295     ptrdiff_t user_buffer_offset;
 296 
 297     struct list_head buffers;
 298     struct rb_root free_buffers;
 299     struct rb_root allocated_buffers;
 300     size_t free_async_space;
 301 
 302     struct page **pages;
 303     size_t buffer_size;
 304     uint32_t buffer_free;
 305     struct list_head todo;
 306     wait_queue_head_t wait;
 307     struct binder_stats stats;
 308     struct list_head delivered_death;
 309     int max_threads;
 310     int requested_threads;
 311     int requested_threads_started;
 312     int ready_threads;
 313     long default_priority;
 314     struct dentry *debugfs_entry;
 315 };

@proc_node:双向链表,所有的proc都通过这个成员链接在一起。

@threads:和这个进程相关的线程都在这棵树下

@nodes:每个进程都维护一棵红黑树,以Binder实体(binder_node)的用户空间指针作为索引。这样驱动可以通过Binder实体在用户空间的地址找到Binder实体在kernel空间的表示binder_node

@refs_by_desc:每个进程都维护一棵红黑树,以Binder ref的desc作为索引,管理这个进程的所有binder索引

@refs_by_node:每个进程都维护一棵红黑树,进程所有的binder引用,以对应的Binder实体在驱动中的内存地址为索引填入该树中

@pid:该进程的group pid

@vma:

@tsk:这个线程的task_struct结构

@files:这个结构是当前进程的打开文件表结构,在处理文件类型Binder时会用到这个结构,在下面会详细描述文件类型binder

@deffer_work_node:

@deffer_work:

@buffer:是内核为了mmap映射在kernel保留的一块内核地址空间,以便应用层地址和kernel地址能指向同一块物理内存,这样在应用层和内核之间就可以共享同一块内存。

@user_buffer_offset:是user映射地址和kernel 地址之间的偏移量,通过这个值就可以从user地址推算出相应的kernel地址,反之也可以推算出user地址。

@buffers





Binder_thread

binder_thread结构体用于存储每一个单独的线程的信息

 326 struct binder_thread {
 327     struct binder_proc *proc;
 328     struct rb_node rb_node;
 329     int pid;
 330     int looper;
 331     struct binder_transaction *transaction_stack;
 332     struct list_head todo;
 333     uint32_t return_error; /* Write failed, return error code in read buf */
 334     uint32_t return_error2; /* Write failed, return error code in read */
 335         /* buffer. Used when sending a reply to a dead process that */
 336         /* we are also waiting on */
 337     wait_queue_head_t wait;
 338     struct binder_stats stats;
 339 };

@proc字段表示当前线程属于哪一个Binder进程

@pid表示binder thread的pic

@looper 表示当前线程的状态

@transaction_stack表示要接收和发送的线程信息,其结构体为binder_transaction

@stats 用来表示Binder状态信息。


Binder 对象

Binder对象在驱动中分为两类:Binder实体,Binder引用

驱动是Binder驱动的核心,系统中所有的Binder实体以及每个实体在各个进程中的引用都登记在驱动中;驱动需要记录Binder引用和Binder实体之间的多对一关系;通过Binder引用找到对应的Binder实体;在某个进程中创建Binder实体,查找或者创建对应的Binder引用;记录Binder实体和Binder引用的归属。

驱动里的Binder是什么时候创建的呢?为了实现Binder的实名注册,系统为serverManager创建了实名注册Binder实体:binder_context_mgr_node。驱动将所有进程的0号引用都预留给了这个Binder实体,也就是说所有进程的0号Binder引用都指向了binder_context_mgr_node。接着随着应用程序(包括client和server)不断的访问实名Binder,则这个Binder_context_mgr_node的引用不断的被创建出来。而随着Server向ServerManager注册Binder,则越来越多的Binder实体被创建出来,Client向ServerManager请求实名服务,则会创建Binder实体的Binder引用。当然这个创建过程对Application来说是透明的。



binder_node

驱动中Binder实体的表示,也成为节点,隶属于提供Binder实体的进程,比如binder_context_mgr_node就隶属于ServiceManager进程。

 218 struct binder_node {
 219     int debug_id;
 220     struct binder_work work;
 221     union {
 222         struct rb_node rb_node;
 223         struct hlist_node dead_node;
 224     };
 225     struct binder_proc *proc;
 226     struct hlist_head refs;
 227     int internal_strong_refs;
 228     int local_weak_refs;
 229     int local_strong_refs;
 230     void __user *ptr;
 231     void __user *cookie;
 232     unsigned has_strong_ref:1;
 233     unsigned pending_strong_ref:1;
 234     unsigned has_weak_ref:1;
 235     unsigned pending_weak_ref:1;
 236     unsigned has_async_transaction:1;
 237     unsigned accept_fds:1;
 238     unsigned min_priority:8;
 239     struct list_head async_todo;
 240 };

@debug_id 用于调试目的,和ref的debug_id共用一个整数空间

@work

@rb_node 每个进程都维护一棵红黑树,以Binder实体在用户空间的指针,即本结构的ptr成员为索引存放该进程所有的Binder实体。这样驱动可以根据Binder实体在用户空间的指针很快找到其位于内核的节点。rb_node用于将本节点链入该红黑树中。

@dead_node 销毁时必须将rb_node从红黑树中摘除,如果本节点还有引用切断,就用dead_node将节点隔离到另外一个链表中,直到通知所有进程切断该节点的引用后,该节点才可能被销毁。

@proc 本成员指向节点所属的进程,即提供该节点的进程。

@refs 本成员是队列头,所有指向本节点的引用都连接到该队列里,这些引用可能属于不同的进程。通过该对列可以遍历指向该节点的所有引用。

@internal_strong_refs 用来实现强指针的计数器:产生一个指向本节点的强引用,该计数器则加1

@local_weak_refs 驱动为传输中的Binder设置的弱引用计数器。如果一个Binder打包在数据包中从一个进程发送到另外一个进程,驱动会为该Binder增加引用计数,知道接收进程通过BC_FREE_BUFFER通知驱动释放该数据包的数据区为止。

@local_strong_refs 驱动为传输中的Binder设置的强引用计数,同上

@ptr 指向用户空间Binder实体的指针,来自于flat_binder_object的binder成员。

@cookie 指向用户空间的附加指针,来自于flat_binder_object的cookie成员

@has_strong_ref, @pending_strong_ref, @has_weak_ref,@pending_weak_ref :这一组标志用于控制驱动与Binder实体所在进程交互式修改引用计数。

每个进程都有一棵红黑树用于存放创建好的节点,以Binder在用户空间的指针作为索引,每当在传输数据中侦测到一个代表Binder实体的flat_binder_object,先以该结构的binder指针为索引搜索红黑树;如果没有找到就创建一个新节点添加到树中。对于同一个进程来说,内存地址是惟一的,所以不会重复建设造成混乱。


binder_ref

和Binder实体一样,Binder的引用也是驱动根据传输数据的flat_binder_object创建的,隶属于获得该引用的进程。

 247 struct binder_ref {
 248     /* Lookups needed: */
 249     /*   node + proc => ref (transaction) */
 250     /*   desc + proc => ref (transaction, inc/dec ref) */
 251     /*   node => refs + procs (proc exit) */
 252     int debug_id;
 253     struct rb_node rb_node_desc;
 254     struct rb_node rb_node_node;
 255     struct hlist_node node_entry;
 256     struct binder_proc *proc;
 257     struct binder_node *node;
 258     uint32_t desc;
 259     int strong;
 260     int weak;
 261     struct binder_ref_death *death;
 262 };

@debug_id 调试用id,和binder_node的debug_id共用整数空间。

@rb_node_desc 每个进程有一棵红黑树,进程所有引用以引用号为索引填入该树中。本成员用做连接到该树的一个节点。

@rb_node_node:每个进程又有一棵红黑树,进程所有引用以节点实体在驱动中的内存地址为索引填入该树中,本成员用做连接到该树的一个节点。

@node_entry:该域将本引用作为节点链入所指向的Binder实体结构binder_node中的refs队列

@proc:本引用所述的进程

@node:本引用所指向的节点(Binder实体)

@desc:本结构的引用号

@strong:强引用计数

@weak:弱引用计数


就像一个对象有很多指针一样,同一个Binder实体可能有很多引用,不同的是这些引用可能分布在不同的进程中。和实体一样,每个进程使用红黑树存放所有进程正在使用的引用。但Binder引用可以通过两个键值索引:

所对应实体的内核地址:

注意这里使用的是驱动创建于内核中的binder_node结构的地址,而不是Binder实体在用户进程中的地址。实体在内核中的地址是惟一的,用做索引不会产生二义性;实体可能来自不同的用户进程,来自不同进程的实体地址可能相同,因此不能使用实体用户地址做索引。驱动利用该红黑树在一个进程中快速查找某个Binder实体所对应的引用(一个实体在一个应用中只建立一个索引)

引用号

引用号是驱动为引用分配的一个32位标始,在一个进程内是惟一的,而在不同的进程内可能有相同的值,这有点类似打开文件号。每个进程的打开文件号是可能重复的。引用号是要返回给应用程序的,可以看作是Binder引用在用户进程中的句柄。除了0号引用在所有进程中都保留给SMgr,其他的值由驱动在创建引用时动态分配。向Binder发送数据包时,应用程序通过将引用号填入binder_trasaction_data结构的target.handle域来表明该数据包的目的Binder。驱动根据该引用号在红黑树中找到引用的的binder_ref结构,进而通过其node域知道目标Binder实体所在的进程及其它相关信息,实现数据包的路由。






  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值