欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android binder机制---Binder驱动

程序员文章站 2024-03-23 23:42:28
...

4 Binder驱动

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

叫做binder_proc。示意图如下,

android binder机制---Binder驱动

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

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

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

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

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

android binder机制---Binder驱动

简单说,当用户空间调用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链表中。

android binder机制---Binder驱动

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个红黑树。

android binder机制---Binder驱动

其中,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树

为例,其示意图如下:

android binder机制---Binder驱动

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节点其实是复用的。

android binder机制---Binder驱动

上图只表示了从进程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驱动程序它是守护进程,然后进入循环等待请求的到来。