binder驱动-------之数据结构篇2

1:binder实体在驱动中的表示(struct binder_node )

binder_node是应用空间的binder实体在内核的表示,它直属于某个特定的进程(binder_proc),其他进程对该binder实体的使用,都会挂在该binder实体的refs哈希列表中。

struct binder_node {
 int debug_id;
 struct binder_work work;
 union {
  struct rb_node rb_node;
  struct hlist_node dead_node;
 };
 struct binder_proc *proc;
 struct hlist_head refs;
 int internal_strong_refs;
 int local_weak_refs;
 int local_strong_refs;
 void __user *ptr;
 void __user *cookie;
 unsigned has_strong_ref:1;
 unsigned pending_strong_ref:1;
 unsigned has_weak_ref:1;
 unsigned pending_weak_ref:1;
 unsigned has_async_transaction:1;
 unsigned accept_fds:1;
 unsigned min_priority:8;
 struct list_head async_todo;
};

struct binder_work work;//通过这个字段来将该binder添加到todo列表中,然后会在binder_thread_read中得到处理。此时这个node->work.type类型为BINDER_WORK_NODE。

struct rb_node rb_node;//通过该字段,以该binder实例在应用空间对应的引用对象(RefBase::weakref_type)的地址为index,将该binder实体连接到该实体所属的进程(bidner_proc)的proc->nodes的红黑树下
struct binder_proc *proc;//指向该binder所属的进程

struct hlist_head refs;//所有其他进程对该binder的引用,以哈希表的形式组织在以refs为表头的哈希表下

 int internal_strong_refs;
 int local_weak_refs;
 int local_strong_refs;//以上各字段用于binder的引用管理的

void __user *ptr;//binder实体对应在应用空间的weakref_type对象(即引用对象)在应用空间的地址

void __user *cookie;//存储用户空间的Binder实体地址的指针,正是通过该字段来调用服务器端在应用空间的处理后台,见下面:

status_t IPCThreadState::executeCommand(int32_t cmd)
{
   ...  ...
    case BR_TRANSACTION:
        {
            ...  ...
             if (tr.target.ptr) {
                sp<BBinder> b((BBinder*)tr.cookie);
                const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);
                if (error < NO_ERROR) reply.setError(error);

             } else {
                const status_t error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
                if (error < NO_ERROR) reply.setError(error);
             }

 上面的cookie值就是我们binder中的cookie值,由于他本身就是服务器端bbinder对象的地址,所以取出来直接可以引用该对象的操作函数(b->transact())了。

2:binder引用在驱动中的表示(struct binder_ref)

为了实现跨进程的通讯,内核空间必须要能够依赖和维护某些信息,准确的找到发起通讯的源进程要去到的目的进程,即binder驱动要能够快速的准确的实现通讯在进程间的路由。而binder驱动实现路由所依赖的主要结构就是binder_node与binder_ref结构体。binder_node对应binder通讯的服务器,而binder_ref对应binder的客服端,而从客服端发起的通讯,要找到这个客服端对应的服务器端,就必须找到这个这次通讯的服务器端对应的binder_node在当前进程中的引用:binder_ref,如果没有这个引用就需要binder驱动负责建立这个引用。待这个引用于binder_node关系建立起来后,就会有如下关系:binder_node->refs的哈希表中包含了这个引用对象,而binder_ref->node则指向目标进程所包含的binder_node结构体。即通过binder_node->refs哈希表,可以找出系统当前所有对该binder实体有引用的客服端进程所对应的引用对象;通过binder_ref->node则可以根据当前客服端进程的引用对象我们可以找到该引用对象所对应的binder实体(binder_node)。

binder驱动中的数据路由的大致过程:

向Binder 驱动发送数据包时,应用程序通过将引用号填入binder_transaction_data结构的target.handle域中表明该数据包的目的 Binder在发送线程中的引用号。驱动根据该引用号在发送方进程的红黑树中(binder_proc->refs_by_desc)找到引用的binder_ref结构,进而通过其node域知道对应的内核binder节点,然后再从binder节点的proc域知道目标Binder实体所在的进程及其它相关信息,实现数据包的路由

需要注意的是:binder的引用是跟进程相关的,同一个binder实体在不同的进程中的引用号可能相同也可能不相同。但不同binder实体在同一个进程中的引用必须保证是唯一的。

struct binder_ref {
	/* Lookups needed: */
	/*   node + proc => ref (transaction) */
	/*   desc + proc => ref (transaction, inc/dec ref) */
	/*   node => refs + procs (proc exit) */
	int debug_id;
	struct rb_node rb_node_desc;
	struct rb_node rb_node_node;
	struct hlist_node node_entry;
	struct binder_proc *proc;
	struct binder_node *node;
	uint32_t desc;
	int strong;
	int weak;
	struct binder_ref_death *death;
};

 struct rb_node rb_node_desc;
 struct rb_node rb_node_node;//同一个进程中的所有对binder的引用,组织为两颗红黑树:一是以引用号为index,以rb_node_desc为节点,以binder_proc->refs_by_desc为根的红黑树下;一是以binder_node在内核中的地址为index,以rb_node_node为节点,以proc_node->refs_by_node为根的红黑树下,前者是为了在已知引号号的情况下,快速的找到该引用号对应的binder_ref结构体,后者是根据binder_node的地址快速找到该binder_node在当前进程中的binder_ref结构体。

struct hlist_node node_entry;//通过该节点,将系统所有对特定binder_node结构体实例的引用都以哈希表的形式组织到该binde_node->refs下面,这样便于当该binder实体对应的服务异常退出时,可以通过binder_node->refs的该字段来通知系统中所有其它对该binder有引用的进程,让他们执行相应的退出操作。

struct binder_proc *proc;//该binder引用所在的进程

struct binder_node *node;//该引用所对应的binder_node实体

uint32_t desc;//该引用对应的引用号

 int strong;
 int weak;//

 struct binder_ref_death *death;//该结构用于当该引用对应的bind_node实体已经死亡时,来通知该引用需要做的操作。

3:binder实体或引用在传输过程中的表示(struct flat_binder_object)

binder实体在驱动传输中的表示是通过struct flat_binder_object结构来实现的,然而这些跨进程传递的Binder混杂在应用程序发送的数据包里,数据格式由用户定义,如果不把它们一一标记出来告知驱动,驱动将无法从数据中将它们提取出来,因为驱动是不关心应用所传递的数据的格式和内容的,于是就使用数组data.offsets存放用户数据中每个Binder相对data.buffer的偏移量,用offsets_size表示这个数组的大小。驱动在发送数据包时会根据data.offsets和offset_size将散落于data.buffer中的Binder找出来并一一为它们创建相关的数据结构(在binder_transaction函数中,根据flat_binder_object结构体的内容创建binder_node或binder_ref等结构体,并把它插入到进程的相关树和列表中)。在数据包中传输的Binder是类型为struct flat_binder_object的结构体

上图就是如下几个结构体的关系图:

struct binder_write_read:   是binder_ioctl函数中的BINDER_WRITE_READ分支命令对应的参数

struct binder_transaction_data:是BC_TRANSACTION命令对应的参数

struct flat_binder_object:表示散落在data中的binder对象的表示

binder_transaction_data结构的offsets成员的值表示对象偏移数组的首地址,该偏移数组一般就是紧接着放在binder_transaction_data结构对应的数据段的末尾,而offeset_size成员说明这个对象偏移数组的大小。偏移数组的内容(上图中的offset1,offset2...)即为flat_binder_object对象相对于binder_transaction_data->data域的差值。

上面的这个图形象具体的说明了这些结构体之间的关联

struct flat_binder_object {
	/* 8 bytes for large_flat_header. */
	unsigned long		type;
	unsigned long		flags;

	/* 8 bytes of data. */
	union {
		void		*binder;	/* local object */
		signed long	handle;		/* remote object */
	};

	/* extra data associated with local object */
	void			*cookie;
};

unsigned long  type;//表示binder的类型,主要分为两大类:一个是BINDER_TYPE_BINDER,另一个是BINDER_TYPE_HANDLE;

void  *binder; /* local object */表示binder实体对应的服务在应用空间中的weakref_type对象的地址

signed long handle;//表示target binder实体在当前进程的引用号,最终通过当前进程的红黑树(proc->refs)来转换成对应的binder_ref结构体

void   *cookie;//表示binder实体在应用空间BBinder对象的地址,该对象只存在服务器端

以上值在应用空间的如下函数被赋值的:(frameworks/native/libs/binder/parcel.cpp)

status_t flatten_binder(const sp<ProcessState>& proc,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;
    
    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.handle = handle;
            obj.cookie = NULL;
        } else {
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = local->getWeakRefs();
            obj.cookie = local;
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = NULL;
        obj.cookie = NULL;
    }
    
    return finish_flatten_binder(binder, obj, out);
}

而binder->localBinder()的定义在如下地方那个:frameworks/native/libs/binder/biner.cpp

BBinder* BBinder::localBinder()
{
    return this;
}

可见this此处指的就是BBinder对象。

4:  binder_transaction

4.1结构体内容

struct binder_transaction {
	int debug_id;
	struct binder_work work;
	struct binder_thread *from;
	struct binder_transaction *from_parent;
	struct binder_proc *to_proc;
	struct binder_thread *to_thread;
	struct binder_transaction *to_parent;
	unsigned need_reply:1;
	/* unsigned is_dead:1; */	/* not used at the moment */

	struct binder_buffer *buffer;
	unsigned int	code;
	unsigned int	flags;
	long	priority;
	long	saved_priority;
	uid_t	sender_euid;
};

struct binder_work work;//通过该节点,将binder_transaction事物添加到目标线程的todo列表中。

struct binder_thread *from;//指示该事物所对应的源线程

struct binder_transaction *from_parent;//用来连接源线程的事物队列

struct binder_proc *to_proc;//该事物要去到的目标进程

struct binder_thread *to_thread;//该事物要去到的目标线程

struct binder_transaction *to_parent;//用来连接目的线程的事物队列

unsigned need_reply:1;//表示该事物是一个需要回复的请求

struct binder_buffer *buffer;//该事物对应的binder内存,该段内存是在target proc的内存池中分配的,并且被同时映射到目标进程的用户和内核空间

unsigned int code;//请求对应的标号,一般在应用空间的Ixxxx.cpp文件(如IMediaPlayerService.cpp文件)中定义好了

unsigned int flags;
long priority;//用于线程的迁移

需要特别注意struct binder_transaction *from_parentstruct binder_transaction *to_parent成员的区别额:由于binder_transaction事物可能同时存在于源线程和目的线程的事物栈中(thread->transaction_stack),而在源线程中的事物栈通过binder_transaction->from_parent链接;而目的线程中的事物栈通过binder_transaction->to_parent成员链接。

4.2 事物通讯过程

binder的client通过binder驱动同binder服务器端进行通讯的模型如下:

binder驱动-------之数据结构篇2_第1张图片

如上图,binder1是client段在应用空间的binder实体的表示(BpBinder),而binder2是service端在应用空间binder实体的表示(BBinder),driver指的就是binder驱动,

BC_TRANSACTION/BR_REPLY和BR_TRANSCATION/BC_REPLY命令对应的参数都是:binder_transaction_data结构。并且BR开头的命令都是内核返回给应用空间的,而BC开头的命令都是应用空间写到内核的。BC_TRANSACTION表示client端发起的一个事物请求,BR_REPLY表示client端收到该事物请求的响应,BR_TRANSCATION表示service端收到的请求,BC_REPLY表示service处理请求后发出的响应

client端大致过程:首先发送cmd:BC_TRANSACTION + arg:binder_transaction_data到内核,内核首先处理binder_thread_write然后再处理binder_thread_read,最后返回用户空间,在返回内核空间时,内核返回命令为:BR_REPLY命令。

client端在处理写过程时:在binder_thread_write--->binder_transaction函数中,包含客服端请求信息的binder_transaction_data结构会被转化成struct binder_transaction结构体,并将该结构体通过t->work.entry成员挂在目标线程(服务器对应的线程)的todo列表中,并唤醒目标线程。然后就阻塞在binder_thread_read函数。

而目标线程被唤醒起来后,在binder_thread_read函数中,从thread->todo列表中取出struct binder_transaction结构体*t,然后将该t结构转换成binder_transaction_data结构,并返回BR_TRANSACTION命令到服务器端的用户空间,而服务器端的后台线程在收到该命令后,就开始就行处理。

服务器端的所在的进程在处理完BR_TRANSACTION命令请求后,会发送BC_REPLY命令+处理请求后的响应送到内核,此时内核在binder_thread_write-->binder_transaction函数中,会将包含请求响应信息的binder_transaction_data结构转换成struct binder_transaction结构体*t,然后通过t->work.entry成员挂在client线程所在的todo列表,然后唤醒client线程。

client在binder_thread_read函数中被唤醒后,从thread->todo列表中取出struct binder_transaction结构体*t,然后将该包含请求响应信息的t结构转换成binder_transaction_data结构,并返回BR_REPLY命令到用户空间。

从而完成一次client到服务器的请求/响应全过程。

5 参考文献

在binder领域有一片文章,那是天王级别的,可以秒杀市面上所有的有关android的参考书籍,该文通篇没有代码和流程图,但却说的透切,通俗易懂。

http://blog.csdn.net/universus/article/details/6211589#t7

 

你可能感兴趣的:(android,ibinder驱动)