您的位置:首页 > 理论基础 > 数据结构算法

Android Binder 驱动分析 - 数据结构

2014-07-11 12:58 483 查看


binder_procs

[cpp] view
plaincopy

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
plaincopy

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的类型

[cpp] view
plaincopy

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 有相同的含义

[cpp] view
plaincopy

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传输实际内容,其定义如下

[cpp] view
plaincopy

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服务为例,支持的命令码为

[cpp] view
plaincopy

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

[cpp] view
plaincopy

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

[cpp] view
plaincopy

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结构体用于存储每一个单独的线程的信息

[cpp] view
plaincopy

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进程。

[cpp] view
plaincopy

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创建的,隶属于获得该引用的进程。

[cpp] view
plaincopy

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实体所在的进程及其它相关信息,实现数据包的路由。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: