当前位置: 代码迷 >> 综合 >> Android Binder 驱动分析 - 数据结构
  详细解决方案

Android Binder 驱动分析 - 数据结构

热度:30   发布时间:2023-12-09 21:19:45.0

binder_procs

[cpp]  view plain copy
  1. 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。

[cpp]  view plain copy
  1.  42 /*                  
  2.  43  * This is the flattened representation of a Binder object for transfer 
  3.  44  * between processes.  The 'offsets' supplied as part of a binder transaction 
  4.  45  * contains offsets into the data where these structures occur.  The Binder 
  5.  46  * driver takes care of re-writing the structure type and data as it moves 
  6.  47  * between processes. 
  7.  48  */       
  8.  49 struct flat_binder_object {  
  9.  50     /* 8 bytes for large_flat_header. */  
  10.  51     unsigned long       type;  
  11.  52     unsigned long       flags;  
  12.  53   
  13.  54     /* 8 bytes of data. */  
  14.  55     union {  
  15.  56         void        *binder;    /* local object */  
  16.  57         signed long handle;     /* remote object */  
  17.  58     };  
  18.  59   
  19.  60     /* extra data associated with local object */  
  20.  61     void            *cookie;  
  21.  62 };  

@type描述的是Binder的类型

[cpp]  view plain copy
  1. 29 enum {            
  2. 30     BINDER_TYPE_BINDER  = B_PACK_CHARS('s''b''*', B_TYPE_LARGE),  
  3. 31     BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w''b''*', B_TYPE_LARGE),  
  4. 32     BINDER_TYPE_HANDLE  = B_PACK_CHARS('s''h''*', B_TYPE_LARGE),  
  5. 33     BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w''h''*', B_TYPE_LARGE),  
  6. 34     BINDER_TYPE_FD      = B_PACK_CHARS('f''d''*', B_TYPE_LARGE),  
  7. 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 有相同的含义

[cpp]  view plain copy
  1. 110 enum transaction_flags {  
  2. 111     TF_ONE_WAY  = 0x01, /* this is a one-way call: async, no return */  
  3. 112     TF_ROOT_OBJECT  = 0x04, /* contents are the component's root object */  
  4. 113     TF_STATUS_CODE  = 0x08, /* contents are a 32-bit status code */  
  5. 114     TF_ACCEPT_FDS   = 0x10, /* allow replies with file descriptors */  
  6. 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传输实际内容,其定义如下

[cpp]  view plain copy
  1. 117 struct binder_transaction_data {  
  2. 118     /* The first two are only used for bcTRANSACTION and brTRANSACTION, 
  3. 119      * identifying the target and contents of the transaction. 
  4. 120      */  
  5. 121     union {  
  6. 122         size_t  handle; /* target descriptor of command transaction */  
  7. 123         void    *ptr;   /* target descriptor of return transaction */  
  8. 124     } target;  
  9. 125     void        *cookie;    /* target object cookie */  
  10. 126     unsigned int    code;       /* transaction command */  
  11. 127   
  12. 128     /* General information about the transaction. */  
  13. 129     unsigned int    flags;  
  14. 130     pid_t       sender_pid;  
  15. 131     uid_t       sender_euid;  
  16. 132     size_t      data_size;  /* number of bytes of data */  
  17. 133     size_t      offsets_size;   /* number of bytes of offsets */  
  18. 134   
  19. 135     /* If this transaction is inline, the data immediately 
  20. 136      * follows here; otherwise, it ends with a pointer to 
  21. 137      * the data buffer. 
  22. 138      */  
  23. 139     union {  
  24. 140         struct {  
  25. 141             /* transaction data */  
  26. 142             const void  *buffer;  
  27. 143             /* offsets from buffer to flat_binder_object structs */  
  28. 144             const void  *offsets;  
  29. 145         } ptr;  
  30. 146         uint8_t buf[8];  
  31. 147     } data;  
  32. 148 };  

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

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

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

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

[cpp]  view plain copy
  1. SVC_MGR_GET_SERVICE = 1,  
  2. SVC_MGR_CHECK_SERVICE,  
  3. SVC_MGR_ADD_SERVICE,  
  4. 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

[cpp]  view plain copy
  1. struct binder_write_read {  
  2.     signed long write_size; /* bytes to write */  
  3.     signed long write_consumed; /* bytes consumed by driver */  
  4.     unsigned long   write_buffer;  
  5.     signed long read_size;  /* bytes to read */  
  6.     signed long read_consumed;  /* bytes consumed by driver */  
  7.     unsigned long   read_buffer;  
  8. };    

@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结构

[cpp]  view plain copy
  1. 282 struct binder_proc {  
  2. 283     struct hlist_node proc_node;  
  3. 284     struct rb_root threads;  
  4. 285     struct rb_root nodes;  
  5. 286     struct rb_root refs_by_desc;  
  6. 287     struct rb_root refs_by_node;  
  7. 288     int pid;  
  8. 289     struct vm_area_struct *vma;  
  9. 290     struct task_struct *tsk;  
  10. 291     struct files_struct *files;  
  11. 292     struct hlist_node deferred_work_node;  
  12. 293     int deferred_work;  
  13. 294     void *buffer;  
  14. 295     ptrdiff_t user_buffer_offset;  
  15. 296   
  16. 297     struct list_head buffers;  
  17. 298     struct rb_root free_buffers;  
  18. 299     struct rb_root allocated_buffers;  
  19. 300     size_t free_async_space;  
  20. 301   
  21. 302     struct page **pages;  
  22. 303     size_t buffer_size;  
  23. 304     uint32_t buffer_free;  
  24. 305     struct list_head todo;  
  25. 306     wait_queue_head_t wait;  
  26. 307     struct binder_stats stats;  
  27. 308     struct list_head delivered_death;  
  28. 309     int max_threads;  
  29. 310     int requested_threads;  
  30. 311     int requested_threads_started;  
  31. 312     int ready_threads;  
  32. 313     long default_priority;  
  33. 314     struct dentry *debugfs_entry;  
  34. 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结构体用于存储每一个单独的线程的信息

[cpp]  view plain copy
  1. 326 struct binder_thread {  
  2. 327     struct binder_proc *proc;  
  3. 328     struct rb_node rb_node;  
  4. 329     int pid;  
  5. 330     int looper;  
  6. 331     struct binder_transaction *transaction_stack;  
  7. 332     struct list_head todo;  
  8. 333     uint32_t return_error; /* Write failed, return error code in read buf */  
  9. 334     uint32_t return_error2; /* Write failed, return error code in read */  
  10. 335         /* buffer. Used when sending a reply to a dead process that */  
  11. 336         /* we are also waiting on */  
  12. 337     wait_queue_head_t wait;  
  13. 338     struct binder_stats stats;  
  14. 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进程。

[cpp]  view plain copy
  1. 218 struct binder_node {  
  2. 219     int debug_id;  
  3. 220     struct binder_work work;  
  4. 221     union {  
  5. 222         struct rb_node rb_node;  
  6. 223         struct hlist_node dead_node;  
  7. 224     };  
  8. 225     struct binder_proc *proc;  
  9. 226     struct hlist_head refs;  
  10. 227     int internal_strong_refs;  
  11. 228     int local_weak_refs;  
  12. 229     int local_strong_refs;  
  13. 230     void __user *ptr;  
  14. 231     void __user *cookie;  
  15. 232     unsigned has_strong_ref:1;  
  16. 233     unsigned pending_strong_ref:1;  
  17. 234     unsigned has_weak_ref:1;  
  18. 235     unsigned pending_weak_ref:1;  
  19. 236     unsigned has_async_transaction:1;  
  20. 237     unsigned accept_fds:1;  
  21. 238     unsigned min_priority:8;  
  22. 239     struct list_head async_todo;  
  23. 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创建的,隶属于获得该引用的进程。

[cpp]  view plain copy
  1. 247 struct binder_ref {  
  2. 248     /* Lookups needed: */  
  3. 249     /*   node + proc => ref (transaction) */  
  4. 250     /*   desc + proc => ref (transaction, inc/dec ref) */  
  5. 251     /*   node => refs + procs (proc exit) */  
  6. 252     int debug_id;  
  7. 253     struct rb_node rb_node_desc;  
  8. 254     struct rb_node rb_node_node;  
  9. 255     struct hlist_node node_entry;  
  10. 256     struct binder_proc *proc;  
  11. 257     struct binder_node *node;  
  12. 258     uint32_t desc;  
  13. 259     int strong;  
  14. 260     int weak;  
  15. 261     struct binder_ref_death *death;  
  16. 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实体所在的进程及其它相关信息,实现数据包的路由。

  相关解决方案