您的位置:首页 > 移动开发 > Android开发

android binder机制---Binder驱动

2017-06-12 20:05 274 查看

4 Binder驱动

binder机制到底是如何从代理对象找到其对应的binder实体呢?其实,在binder驱动层,还有个与之相对的结构,

叫做binder_proc。示意图如下,



Binder驱动是Android专用的,但底层的驱动架构与Linux驱动一样。binder驱动在以misc设备进行注册,作为虚拟

字符设备,没有直接操作硬件,只是对设备内存的处理。主要是驱动设备的打开 (binder_open),数据操作(binder_ioctl)。

用户态的程序调用Kernel层驱动是需要陷入内核态,进行系统调用(syscall),比如打开Binder驱动方法的调用链为: 

open-> __open() -> binder_open()。 open()为用户空间的方法,__open()便是系统调用中相应的处理方法,通过查找,

对应调用到内核binder驱动的binder_open()方法,至于其他的从用户态陷入内核态的流程也基本一致。



简单说,当用户空间调用open()方法,最终会调用binder驱动的binder_open()方法; ioctl()方法也是同理,在BInder

系列的后续文章从用户态进入内核态,都依赖于系统调用过程。

Kernel/drivers/staging/android/ 路径下的binder.c的binder_open方法如下,

static int binder_open(struct inode *nodp, struct file *filp)
{
struct binder_proc *proc;

proc = kzalloc(sizeof(*proc), GFP_KERNEL); // 为binder_proc结构体在分配kernel内存空间
if (proc == NULL)
return -ENOMEM;
get_task_struct(current);
proc->tsk = current;   //将当前线程的task保存到binder进程的tsk
INIT_LIST_HEAD(&proc->todo); //初始化todo列表
init_waitqueue_head(&proc->wait); //初始化wait队列
proc->default_priority = task_nice(current);  //将当前进程的nice值转换为进程优先级

binder_lock(__func__);   //同步锁,因为binder支持多线程访问
binder_stats_created(BINDER_STAT_PROC); //BINDER_PROC对象创建数加1
hlist_add_head(&proc->proc_node, &binder_procs); //将proc_node节点添加到binder_procs为表头的队列
proc->pid = current->group_leader->pid;
INIT_LIST_HEAD(&proc->delivered_death);
filp->private_data = proc;       //file文件指针的private_data变量指向binder_proc数据
binder_unlock(__func__); //释放同步锁

return 0;
}

创建binder_proc对象,并把当前进程等信息保存到binder_proc对象,该对象管理IPC所需的各种信息并拥有其他

结构体的根结构体;再把binder_proc对象保存到文件指针filp,以及把binder_proc加入到全局链表binder_procs。

Binder驱动中通过static HLIST_HEAD(binder_procs);,创建了全局的哈希链表binder_procs,用于保存所有的

binder_proc队列,每个进程新创建的binder_proc对象都会加入binder_procs链表中。



binder_proc结构体重要的部分如下,

struct binder_proc
{
struct hlist_node proc_node;
struct rb_root threads;
struct rb_root nodes;
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
int pid;
. . . .
};

其中的那4个rb_root域,“rb”的意思是“red black”,可见binder_proc里搞出了4个红黑树。



其中,nodes树用于记录binder实体,refs_by_desc树和refs_by_node树则用于记录binder代理。之所以会有两个

代理树,是为了便于快速查找,我们暂时只关心其中之一就可以了。threads树用于记录执行传输动作的线程信息。

  在一个进程中,有多少“被其他进程进行跨进程调用的”binder实体,就会在该进程对应的nodes树中生成多少个红

黑树节点。另一方面,一个进程要访问多少其他进程的binder实体,则必须在其refs_by_desc树中拥有对应的引用

节点。

这4棵树的节点类型是不同的,threads树的节点类型为binder_thread,nodes树的节点类型为binder_node,

refs_by_desc树和refs_by_node树的节点类型相同,为binder_ref。这些节点内部都会包含rb_node子结构,该结构

专门负责连接节点的工作,和前文的hlist_node有点儿异曲同工,这也是linux上一个常用的小技巧。我们以nodes树

为例,其示意图如下:



rb_node和rb_root的定义如下:

struct rb_node
{
unsigned long  rb_parent_color;
#define RB_RED      0
#define RB_BLACK    1
struct rb_node *rb_right;
struct rb_node *rb_left;
} __attribute__((aligned(sizeof(long))));
/* The alignment might seem pointless, but allegedly CRIS needs it */

struct rb_root
{
struct rb_node *rb_node;
};

binder_node的定义如下:

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;
};

nodes树是用于记录binder实体的,所以nodes树中的每个binder_node节点,必须能够记录下相应binder实体的

信息。因此请大家注意binder_node的ptr域和cookie域。

另一方面,refs_by_desc树和refs_by_node树的每个binder_ref节点则和上层的一个BpBinder对应,而且更重要

的是,它必须具有和“目标binder实体的binder_node”进行关联的信息。binder_ref的定义如下:

struct binder_ref
{
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;   // 注意这个node域
uint32_t desc;
int strong;
int weak;
struct binder_ref_death *death;
};

请注意那个node域,它负责和binder_node关联。另外,binder_ref中有两个类型为rb_node的域:rb_node_desc

域和rb_node_node域,它们分别用于连接refs_by_desc树和refs_by_node。也就是说虽然binder_proc中有两棵

引用树,但这两棵树用到的具体binder_ref节点其实是复用的。



上图只表示了从进程1向进程2发起跨进程传输的意思,其实反过来也是可以的,即进程2也可以通过自己的“引用

树”节点找到进程1的“实体树”节点,并进行跨进程传输。大家可以自己补充上图。

OK,现在我们可以更深入地说明binder句柄的作用了,比如进程1的BpBinder在发起跨进程调用时,向binder驱动

传入了自己记录的句柄值,binder驱动就会在“进程1对应的binder_proc结构”的引用树中查找和句柄值相符的

binder_ref节点,一旦找到binder_ref节点,就可以通过该节点的node域找到对应的binder_node节点,这个目标

binder_node当然是从属于进程2的binder_proc啦,不过不要紧,因为binder_ref和binder_node都处于binder驱

动的地址空间中,所以是可以用指针直接指向的。目标binder_node节点的cookie域,记录的其实是进程2中BBinder

的地址,binder驱动只需把这个值反映给应用层,应用层就可以直接拿到BBinder了。这就是Binder完成精确打击的

大体过程。

5,小结

1,Java层的系统服务所在进程和native的系统服务所在进程都会在进程启动时打开binder驱动。

2,一个进程中仅有一个ProcessState对象,相当于全局变量,但是有多个IPCThreadState对象。

3,打开binder驱动之后,每个进程都会将进程信息写入binder_proc结构中。

4, servicemanager进程首先通知Binder驱动程序它是守护进程,然后进入循环等待请求的到来。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息