Binder结点管理

1 Binder结点管理

在Binder驱动中,Binder结点随着进程IPC通信开始而生,随着进程IPC通信 结束而亡,换句话说,Binder结点是维系IPC通信的基础,而且一个Binder 结点也只能在完全无IPC通信的情况下被删除。本文将重点研究Binder结点 从生到死的过程。

Binder结点作为IPC通信过程中的实体,是IPC通信的媒介。在描述一个 Binder进程信息的数据结构中,与结点相关的成员变量有:

struct binder_proc {
        struct hlist_node proc_node;// list node for global binder_procs hlist
        ...
        struct rb_root nodes;
        struct rb_root refs_by_desc;
        struct rb_root refs_by_node;
        ...
};

其中,nodes记录了当前进程创建的Binder结点。而 refs_by_descrefs_by_node 都记录的是Binder结点引用,换句话说,它代表一个对其他进程 中创建的Binder结点的引用。Nodes链表的大小反映了有多少进程在向当前 进程请求服务,而结点引用则反映了当前进程在向哪些进程请求服务。

创建Binder结点的函数是 binder_new_node 。在分析此函数之前,我们先分析 一下Binder驱动中结点的数据结构定义:

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

下面分别解释上述各个成员变量的意义:

  1. debug_id : 结点的一个数字序号标记,根据全局变量 binder_last_id 来维护并分配, binder_last_id 是Binder驱动 全局变量,且单调递增的。
  2. work:类型为 BINDER_WORK_NODEbinder_work 结点,添加 到线程上的todo链表中去处理,主要处理结点的引用计数。
  3. 联合体: rb_node 代表此结点在进程全局红黑树 binder_procs 上 的一个结点。而 dead_node 代表红黑树 binder_dead_nodes 中的 一个结点。根据当前结点的状态,将决定将结点加入到哪个全局链表中。
  4. proc: 与结点相关联的 binder_proc
  5. refs: binder_ref 链表,记录当前其他进程对该Binder结点的引用。
  6. internal_strong_refs :内部强引用计数
  7. local_weak_refs :本地弱引用计数。
  8. local_strong_refs :本地强引用计数。
  9. ptr :通常指本地Binder对象的内存地址,当其是0号结点时,该指针为空。
  10. cookies: 通常指本地Binder对象的私有数据。
  11. has_strong_ref :是否拥有强引用。
  12. pending_strong_ref :对结点的强引用是否处理完成 ( BC_ACQUIRE_DONE
  13. has_weak_ref :是否拥有弱引用。
  14. pending_weak_ref :对结点的弱引用是否处理完成 ( BC_INCREFS_DONE
  15. has_async_transaction :拥有异步事务?
  16. accept_fds :是否允许传递文件描述符?
  17. min_priority :最低优先级
  18. async_todo :该结点上的异步工作队列。

1.1 新结点的创建

在函数 binder_new_node() ,创建的新结点将会加入到当前进程的结点树中, 这些结点通过红黑树的结构来维护,以本地Binder对象的内存地址的大小作 为排序依据。此函数主要是在 BR_TRANSACTION 中,为发送方创建相应的本地 结点,以便接收方能够通过该结点获取关于发送方的一些信息。当然,在调 用 BINDER_SET_CONTEXT_MGR 命令时,也会调用该函数创建一个特殊的结 点。

1.2 结点引用计数管理

结点引用计数的管理主要通过如下两个函数:

static int binder_inc_node(struct binder_node *node, int strong, int internal,
                           struct list_head *target_list)
static int binder_dec_node(struct binder_node *node, int strong, int internal)

1.3 结点引用

结点引用与结点密不可分。事实上,可以认为结点引用是结点在另一个进程 中的代理。它们之间的关系是多对一的关系,即一个结点可以对应多个结点 引用,但是结点本身只能存在于一个进程中,且结点引用与结点一般是属于 不同进程的。它的数据结构定义如下:

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

下面分别解释上述各个成员变量的意义:

  1. debug_id :同结点的 debug_id 意义一样,一个数据序号标记。
  2. rb_node_desc :代表 binder_proc 中红黑树 refs_by_desc 中的一个结点,以desc为索引,即结点引用的句柄号作为排序依据。
  3. rb_node_node :代表 binder_proc 中红黑树 refs_by_node 中的一个结点,以node为索引,即结点的内存地址作为排序依据。
  4. node_entry :作为node所拥有的结点引用链表中的一个结点
  5. proc:该结点引用相关联的 binder_proc
  6. node: 该结点引用所关联的结点
  7. desc:该结点引用的句柄号
  8. strong: 该结点引用的强引用计数
  9. weak: 该结点引用的弱引用计数
  10. death:该结点引用的死亡通知链表,主要通知它所引用的结点的死亡 消息。

系统中所有的Binder实体以及每个实体在各个进程中的引用都登记在驱动中; 驱动需要记录Binder引用 ->实体之间多对一的关系;为引用找到对应的实 体;在某个进程中为实体创建或查找到对应的引用;记录Binder的归属地 (位于哪个进程中);

函数

static struct binder_ref binder_get_ref(struct binder_proc *proc, uint32_t desc)

用于查询某个句柄号为desc的 struct binder_ref 对象,而函数

static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,  struct binder_node *node)

则是为某个结点对象创建一个结点引用对象。

1.4 结点引用计数管理

结点引用计数管理主要通过如下两个函数:

static int binder_inc_ref(struct binder_ref *ref, int strong,
                          struct list_head *target_list)
static int binder_dec_ref(struct binder_ref *ref, int strong)

binder_inc_ref 里面调用 binder_inc_node,internal 为1,直接 调用 binder_inc_node 时,internal为0。

所以, node->internal_strong_refs 统计的是 binder_ref 对远程结点的强引用。 对结点本身来说, node->internal_strong_refs 的值也反映了当前有多少个远 程结点( binder_ref )强引用指向自己。

node->local_strong_refs 则统计的是对本地结点的强引用,这个值反映了当 前进程中有多少个强引用指向自己。

Binder驱动对结点引用计数的管理

用户空间可以通过如下一个命令来增加或减少结点的引用计数:

  1. BC_INCREFS
  2. BC_ACQUIRE
  3. BC_RELEASE
  4. BC_DECREFS

IPCThreadState类中定义了如下几个相关接口:

incStrongHandle(int32_t handle)
incWeakHandle(int32_t handle)
decStrongHandle(int32_t handle)
decWeakHandle(int32_t handle)

分别会向驱动发送上述几个命令。 这个命令带的参数是结点的句柄号。 这种方法是直接改变结点的引用计数。 在驱动中也可能直接改变结点的引用计数,

binder_inc_node(target_node, 1, 0, NULL),

有一个共同点是targetlist参数都为NULL。

第二种情况是,在处理TRANSACTION期间,Binder驱动改变了传输中的结点 引用计数,然后通过如下几个命令返回给用户空间做处理:

  1. BR_ACQUIRE
  2. BR_INCREFS
  3. BR_RELEASE
  4. BR_DECREFS

其中,当将创建本进程中的某个结点的结点引用对象时,需要传入一个 target_list 参数,提交一个 BINDER_WORK_NODE 类型的 binder_work ,以处理驱动 中的结点引用计数管理,同时,以通过上述几个命令通知用户空间维护相对 应的对象强弱引用计数。

binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
                                       &thread->todo);

标识Node有强引用或弱引用,并增加引用计数。

struct binder_work {
        struct list_head entry;
        enum {
                BINDER_WORK_TRANSACTION = 1,  //代表一次进程间业务通信
                BINDER_WORK_TRANSACTION_COMPLETE,
                BINDER_WORK_NODE, //结点引用计数管理
                BINDER_WORK_DEAD_BINDER,
                BINDER_WORK_DEAD_BINDER_AND_CLEAR,
                BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
        } type;
};