前面几节讲解了Binder中涉及的关键类以及远程对象调用的流程步骤,其中涉及到了数据从客户端调用流向Binder内核,再由Binder内核流向宿主进程,请求数据会经过层层封装,这样请求数据到达Binder内核时,其格式是满足Binder内核要求,这样Binder才会知道怎么解析请求数据。所以,这里涉及到Binder在调用过程中各种数据结构,下面列出了调用流程中用到的主要数据结构。
客户端调用远程服务涉及到的主要数据结构
序号 | 数据结构名称 | 说明 |
1 | flat_binder_object | IBinder对象在驱动层的描述 |
2 | binder_transaction_data | 封装请求数据以及包含的IBinder对象的信息 |
3 | binder_write_read | BINDER_WRITE_READ驱动层命令对应的封装信息 |
4 | binder_node | 在Binder驱动中记录了实体服务信息 |
5 | binder_ref | 保存实体IBinder对象的引用,用来在客户端查询实体IBinder用 |
6 | binder_proc | 和Binder驱动关联的进程信息(调用binder_open就会生成) |
flat_binder_object介绍
此结构代表了在多进程传输过程中的一个IBinder对象(远程服务对象),正是因为此结构表述了一个多进程共享的服务对象,客户端在使用远程服务对象才感觉不到多进程的存在,就像在本进程中使用远程服务对象一样,下面我们先看看 flag_binder_object结构的定义。
struct flat_binder_object {
unsigned long type; //binder类型
unsigned long flags; //同步或异步
union {
void *binder;
signed long handle;
};
void *cookie;
};
type:对象类型,取值:BINDER_TYPE_BINDER,BINDER_TYPE_WEAK_BINDER,BINDER_TYPE_HANDLE,BINDER_TYPE_WEAK_HANDLE。
flags:对象处理的标志位。
binder:如果type为BINDER_TYPE_BINDER,BINDER_TYPE_WEAK_BINDER,表示实体对象的引用;
handle:如果type为BINDER_TYPE_HANDLE,BINDER_TYPE_WEAK_HANDLE,代表远程服务对象的handle。
cookie:附加数值,存储实体对象的地址。
flag_binder_object是定义在驱动层的,在Binder的通讯里,表示一个IBinder服务对象,算是比较核心的一个数据结构。
binder_transaction_data介绍
binder_transaction_data在Binder驱动对应的处理命令为BC_TRANSACTION(用户到内核),主要用来对请求的数据进行封装,里面保存了调用的远程实体服务对应的handle,请求的数据以及请求数据里包含的对象(IBinder类型的)个数。其详细的定义如下:
struct binder_transaction_data {
union {
size_t handle; // 客户端调用,表示远程服务对应的handle
void *ptr; // 实体Binder的引用
} target;
void *cookie; // 实体Binder的地址(真实服务对象地址)
unsigned int code; // BC_TRANSACTION
unsigned int flags; // 上层调用过来的控制标记位
pid_t sender_pid; // 调用服务接口方的进程ID
uid_t sender_euid; // 调用服务接口方进程的用户ID
size_t data_size; // 请求数据的大小 (就是buffer的大小)
size_t offsets_size; // 请求数据包含的所有对象的字节数
union {
struct {
const void *buffer; // 请求数据(包含参数等信息)
const void *offsets; // 请求数据里包含的IBinder对象在buffer中的偏移信息
} ptr;
uint8_t buf[8];
} data;
};
此结构由Binder驱动进行解析处理,一个主要的功能就是对binder_transaction_data结构中的记录的flat_binder_object类型的对象进行处理(根据flat_binder_object中的type决定是否插入binder_node到调用者进程还是插入binder_ref到远程服务所在进程),所以,从这里看出flat_binder_object结构是非常重要的。
binder_write_read介绍
binder_write_read是在binder_transaction_data结构上进行的封装,最终通过ioctl系统调用发往Binder驱动是这个结构表示的数据,其对应的命令码为:BINDER_WRITE_READ,就是告诉Binder驱动完成一次写入、读取(是先写入再读取),其定义如下:
struct binder_write_read {
signed long write_size; // 需要写入的数据大小
signed long write_consumed; // 已经写入多少(内核会从write_buffer+write_consumed解析)
unsigned long write_buffer; // 写入的数据(BC_TRANSACTION + binder_transaction_data)
signed long read_size; // 读取数据缓冲区大小
signed long read_consumed; // 已经读取的数据大小
unsigned long read_buffer; // 数据读取的缓冲区
};
binder_node介绍
binder_node是在驱动层使用的数据结构,其表示一个实体IBinder,这个节点首先是插入IBinder所在的宿主进程里的,然后远程客户端调用进程会持有其引用,就是后面将要介绍的binder_ref结构信息,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;
binder_uintptr_t ptr;
binder_uintptr_t 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;
};
这里面我们主要讲解几个变量:proc、ptr、cookie、async_todo。
proc:IBinder实体所在的进程对象地址(实现IBinder服务的进程);
ptr:IBinder实体服务的引用(内核通过引用来管理对象的生命周期);
cookie:实体服务的对象地址,在调用服务的时候会直接转为BBinder;
async_todo:执行的任务队列;
binder_node结构信息插入是通过flat_binder_object的type值为BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER会插入到宿主进程的binder_proc的node里。
binder_ref介绍
binder_ref结构记录了实体Binder(binder_node)的引用信息,由服务端进程插入到客户端进程binder_proc。这个结构是作为客户端用来查询远程服务对象的binder_node用,因为这个结构有个成员变量desc,这个desc就是服务端对象在客户端的引用handle,下面是binder_ref定义。
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; // 远程服务引用对应的索引值(客户会通过引用其来找到binder_node)
int strong;
int weak;
struct binder_ref_death *death;
};
binder_proc介绍
binder_proc结构描述和Binder驱动关联的进程信息,这个结构在驱动层的binder_open函数里生成,同时保存在返回的文件句柄中,这个结构非常重要,其保存真实进程的全部信息、本进程实现的服务实体信息以及远程服务的引用信息(本进程调用的远程服务),下面看看这个结构的定义。
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;
struct vm_area_struct *vma;
struct mm_struct *vma_vm_mm;
struct task_struct *tsk;
struct files_struct *files;
struct hlist_node deferred_work_node;
int deferred_work;
void *buffer;
ptrdiff_t user_buffer_offset;
struct list_head buffers;
struct rb_root free_buffers;
struct rb_root allocated_buffers;
size_t free_async_space;
struct page **pages;
size_t buffer_size;
uint32_t buffer_free;
struct list_head todo;
wait_queue_head_t wait;
struct binder_stats stats;
struct list_head delivered_death;
int max_threads;
int requested_threads;
int requested_threads_started;
int ready_threads;
long default_priority;
struct dentry *debugfs_entry;
struct binder_context *context;
};
binder_proc结构定义的字段比较多,这里说下比较重要的几个。
threads:和本进程关联的线程信息;
nodes:本进程实现的服务信息;
refs_by_desc:使用handle索引的远程服务的引用信息(主要用查询用);
refs_by_node:使用node索引的远程服务的引用信息(主要用查询用);
从上面可以看出refs_by_desc和refs_by_node其实是复用的,只是索引的不一样,一个以handle索引,一个以node索引,这个节点在应用层其实对应的是BpBinder对象(相关知识大家可以看看我前面的文章)。
这里我们简单介绍了Binder调用过程中用到的主要数据结构,当然还有很多其他的数据结构,以后有时间再整理下,文中如有理解不当之处,欢迎大家指正,谢谢!
本系列文章均为原创,主要总结作者多年在软件行业的一些经验,和大家共同学习、进步,转载请注明出处,谢谢!