binder-驱动数据结构

之前的分析中,可以了解到 handle 在整个过程中非常重要,因为他代表的就是服务的引用

比如进程A调用进程B的 X 服务,那么handle就是进程B提供 X 服务的引用

既然他是一个引用,那么必然就会有一个结构体来表示

他就是 binder_ref

它属于内核代码 所以需要去linux源码去寻找

https://github.com/torvalds/linux/blob/master/drivers/android/binder.c

binder_ref

1
2
3
4
5
6
7
8
9
10
11
12
struct binder_ref {
int debug_id;
struct rb_node rb_node_desc; // 关联到binder_proc->refs_by_desc红黑树中
struct rb_node rb_node_node; // 关联到binder_proc->refs_by_node红黑树中
struct hlist_node node_entry; // 关联到binder_node->refs哈希表中
struct binder_proc *proc; // 该Binder引用所属的Binder进程
struct binder_node *node; // 该Binder引用对应的Binder实体
uint32_t desc; // 描述
int strong;
int weak;
struct binder_ref_death *death;
};

desc是Binder引用的描述,实际上它就是Binder驱动为该Binder分配的一个唯一的int型整数。通过该desc,可以在binder_proc->refs_by_desc中找到该Binder引用,进而可以找到该Binder引用所引用的Binder实体等信息

驱动程序根据handle找到进程B中服务的引用,那么引用中就必然会有一个结构体指向服务,就是 binder_ref 结构体中的 binder_node

binder_node

X 服务的结构体是 binder_node ,也就是表示服务的结构体,也是描述Binder实体的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct binder_node {
int debug_id;
struct binder_work work;
union {
struct rb_node rb_node; // 如果这个Binder实体还在使用,则将该节点链接到proc->nodes中。
struct hlist_node dead_node; // 如果这个Binder实体所属的进程已经销毁,而这个Binder实体又被其它进程所引用,则这个Binder实体通过dead_node进入到一个哈希表中去存放
};
struct binder_proc *proc; // 该binder实体所属的Binder进程
struct hlist_head refs; // 该Binder实体的所有Binder引用所组成的链表
int internal_strong_refs;
int local_weak_refs;
int local_strong_refs;
void __user *ptr; // Binder实体在用户空间的地址(为Binder实体对应的Server在用户空间的本地Binder的引用)
void __user *cookie; // Binder实体在用户空间的其他数据(为Binder实体对应的Server在用户空间的本地Binder自身)
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;
};

系统里边通过 binder_proc 来表示进程B,也就是目标进程

可以在 binder_node 中找到 binder_proc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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;
struct list_head waiting_threads;
int pid;
struct task_struct *tsk;
struct hlist_node deferred_work_node;
int deferred_work;
bool is_dead;

struct list_head todo;
struct binder_stats stats;
struct list_head delivered_death;
int max_threads;
int requested_threads;
int requested_threads_started;
int tmp_ref;
long default_priority;
struct dentry *debugfs_entry;
struct binder_alloc alloc;
struct binder_context *context;
spinlock_t inner_lock;
spinlock_t outer_lock;
};

关于这些解释,大可不必现在就记清楚,你可以学得差不多了,再回头来复习

  • nodes,它是包含该进程内的所有Binder实体所组成的红黑树。nodes成员和binder_node->rb_node关联到一棵红黑树,从而将binder_proc和binder_node关联起来。
  • refs_by_desc,它是包含该进程内的所有Binder引用所组成的红黑树。refs_by_desc成员和binder_ref->rb_node_desc关联到一棵红黑树,从而将binder_proc和binder_ref关联起来。
  • refs_by_node,它是包含该进程内的所有Binder引用所组成的红黑树。refs_by_node成员和binder_ref->rb_node_node关联到一棵红黑树,从而将binder_proc和binder_ref关联起来。
  • buffer,它是该进程内核虚拟内存的起始地址。而user_buffer_offset,则是该内核虚拟地址和进程虚拟地址之间的差值。在Binder驱动中,将进程的内核虚拟地址和进程虚拟地址映射到同一物理页面,该user_buffer_offset则是它们之间的差值;这样,已知其中一个,就可以根据差值算出另外一个。
  • todo是该进程的待处理事务队列,而wait则是等待队列。它们的作用是实现进程的等待/唤醒。例如,当Server进程的wait等待队列为空时,Server就进入中断等待状态;当某Client向Server发送请求时,就将该请求添加到Server的todo待处理事务队列中,并尝试唤醒Server等待队列上的线程。如果,此时Server的待处理事务队列不为空,则Server被唤醒后;唤醒后,则取出待处理事务进行处理,处理完毕,则将结果返回给Client

binder_buffer

binder_buffer是描述Binder进程所管理的每段内存的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct binder_buffer {
struct list_head entry; // 和binder_proc->buffers关联到同一链表,从而使Binder进程对内存进行管理。
struct rb_node rb_node; // 和binder_proc->free_buffers或binder_proc->allocated_buffers关联到同一红黑树,从而对已有内存和空闲内存进行管理。

unsigned free:1; // 空闲与否的标记
unsigned allow_user_free:1;
unsigned async_transaction:1;
unsigned debug_id:29;

struct binder_transaction *transaction;

struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
uint8_t data[0];
};

binder_buffer是Binder驱动的私有结构体,它定义在drivers/staging/android/binder.c中。Binder驱动将Binder进程的内存分成一段一段进行管理,而这每一段则是使用binder_buffer来描述的

binder_thread

在真实场景中,会有多个client发送数据给server,也就是多个A发送数据给进程B,那么进程B就会创建出多个线程来给client提供服务,那么进程B中肯定有一个结构体来管理这些线程

就是struct rb_root threads 他是一个红黑树,在树下面会挂有多个线程,每个线程使用的结构体 binder_thread 来表示

1
2
3
4
5
6
7
8
9
10
11
12
13
struct binder_thread {
struct binder_proc *proc; // 线程所属的Binder进程
struct rb_node rb_node; // 红黑树节点,关联到红黑树binder_proc->threads中。
int pid; // 进程id
int looper; // 线程状态。可以取BINDER_LOOPER_STATE_REGISTERED等值
struct binder_transaction *transaction_stack; // 正在处理的事务栈
struct list_head todo; // 待处理的事务链表
uint32_t return_error; /* Write failed, return error code in read buf */
uint32_t return_error2; /* Write failed, return error code in read */

wait_queue_head_t wait; // 等待队列
struct binder_stats stats; // 保存一些统计信息
};

先梳理下上面的流程

  • 根据 handle 找到 binder_ref
  • 根据 binder_ref 找到 binder_node
  • 根据 binder_node 找到 binder_proc

所以在内核态中,内核肯定是要创建这些结构体

  1. server传入一个flat_binder_object给驱动(在service_manager启动时,会创建flat_binder_object)
  2. 内核中根据flat_binder_object为每个服务创建binder_node
    • binder_node.desc = server进程
  3. server_manager创建binder_ref,并把binder_node引用给binder_ref
    • binder_ref.desc = 1,2,3…..
  4. 在用户态会创建一个服务链表(红黑树),链表中有 name 和 handle
  5. client向servermanager查询服务,传一个名称给sservermanager
  6. servermanager 返回一个handle 给驱动程序
  7. 驱动程序在 servermanager 的 binder_ref 红黑树中根据handle找到binder_ref
  8. 再根据binder_ref.node 找到 binder_node
  9. 给client创建新的binder_ref指向上一步中的 binder_node ,他的 desc 从 1 开始
  10. 驱动返回 desc 给 client,即 handle

在这个过程中,会有很多binder_ref,在server中,他的desc的值是由注册服务的顺序决定的,对于client,是根据他获取服务的顺序决定的,所以binder_ref是和进程相关联的

binder_state

1
2
3
4
5
6
struct binder_state
{
int fd; // 文件节点"/dev/binder"的句柄
void *mapped; // 映射内存的起始地址
unsigned mapsize; // 映射内存的大小
};

binder_state定义在frameworks/native/cmds/servicemanager/binder.c中,它是ServiceManager用来描述打开的”/dev/binder”的信息结构体

svcinfo

1
2
3
4
5
6
7
8
9
struct svcinfo
{
struct svcinfo *next; // 下一个"服务的信息"
void *ptr; // 服务在Binder驱动中的Binder引用的描述
struct binder_death death;
int allow_isolated;
unsigned len; // 服务的名称长度
uint16_t name[0]; // 服务的名称
};

svcinfo定义在frameworks/native/cmds/servicemanager/service_manager.c中
它是ServiceManager守护进程的私有结构体
svcinfo是保存”注册到ServiceManager中的服务”的相关信息的结构体。它是一个单链表,在ServiceManager守护进程中的svclist是保存注册到ServiceManager中的服务的链表,它就是struct info类型。svcinfo中的next是指向下一个服务的节点,而ptr是该服务在Binder驱动中Binder引用的描述。name则是服务的名称

Parcel

Parcel是描述Binder通信信息的结构体,定义在frameworks/native/include/binder/Parcel.h中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Parcel {
public:
...

// 获取数据(返回mData)
const uint8_t* data() const;
// 获取数据大小(返回mDataSize)
size_t dataSize() const;
// 获取数据指针的当前位置(返回mDataPos)
size_t dataPosition() const;

private:
...

status_t mError;
uint8_t* mData; // 数据
size_t mDataSize; // 数据大小
size_t mDataCapacity; // 数据容量
mutable size_t mDataPos; // 数据指针的当前位置
size_t* mObjects; // 对象在mData中的偏移地址
size_t mObjectsSize; // 对象个数
size_t mObjectsCapacity; // 对象的容量

...
}

数据传输过程

数据复制过程