Android进程间通信之8——Binder相关结构体简介
后面就要讲解Binder源码了,会涉及到很多struct,因此这里先铺垫一下,说几个常用的数据结构,内容如下:
1、结构体binder_work
2、结构体binder_thread
3、构体binder_stats
4、结构体binder_proc
5、结构体binder_node
6、结构体binder_ref
7、结构体binder_ref_death
8、结构体binder_state
9、结构体binder_buffer
10、结构体binder_transaction
11、结构体binder_transaction_data
12、结构体transaction_flags
13、结构体flat_binder_object
14、结构体binder_write_read
15、结构体binder_ptr_cookie
16、总结
一、结构体binder_work
1、位置
Linux的binder.c 240行
2、代码注释
binder_work代表binder驱动中进程要处理的工作项
struct binder_work {
struct list_head entry; //用于实现一个双向链表,存储的所有binder_work队列
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; //描述工作项的类型
};
二、结构体binder_thread
1、代码位置
Linux的binder.c 368行
2、代码注释
binder_thread 代表Binder线程池中的每个线程信息
struct binder_thread {
//宿主进程,即线程属于那么Binder进程
struct binder_proc *proc;
//红黑树的一个节点,binder_proc使用红黑树来组织Binder线程池中的线程
struct rb_node rb_node;
// 当前线程的PID
int pid;
// 当前线程的状态信息
int looper;
//事务堆栈,将事务封装成binder_transaction,添加到事务堆栈中,
//定义了要接收和发送的进程和线程消息
struct binder_transaction *transaction_stack;
//队列,当有来自Client请求时,将会添加到to_do队列中
struct list_head todo;
// 记录阅读buf事务时出现异常错误情况信息
uint32_t return_error; /* Write failed, return error code in read buf */
// 记录阅读事务时出现异常错误情况信息
uint32_t return_error2; /* Write failed, return error code in read */
/* buffer. Used when sending a reply to a dead process that */
/* we are also waiting on */
// 等待队列,当Binder处理事务A依赖于其他Binder线程处理事务B的情况
// 则会在sleep在wait所描述的等待队列中,直到B事物处理完毕再唤醒
wait_queue_head_t wait;
// Binder线程相关统计数据
struct binder_stats stats;
};
这里面说下上面looper对应的值
enum {
// Binder驱动 请求创建该线程,通过BC_REGISTER_LOOPER协议通知
//Binder驱动,注册成功设置此状态
BINDER_LOOPER_STATE_REGISTERED = 0x01,
//该线程是应用程序主动注册的 通过 BC_ENTER_LOOPER 协议
BINDER_LOOPER_STATE_ENTERED = 0x02,
// Binder线程退出
BINDER_LOOPER_STATE_EXITED = 0x04,
// Binder线程处于无效
BINDER_LOOPER_STATE_INVALID = 0x08,
// Binder线程处于空闲状态
BINDER_LOOPER_STATE_WAITING = 0x10,
// Binder线程处于需要返回用户控件
// 使用场景是:1、线程注册为Binder线程后,还没有准备好去处理进程间通信
//,需要返回用户空间做其他初始化准备;2、调用flush来刷新Binder线程池
BINDER_LOOPER_STATE_NEED_RETURN = 0x20
};
三、结构体binder_stats
binder_stats 代表的是Binder线程相关统计数据
1、代码位置
Linux的binder.c 173行
2、代码注释
struct binder_stats {
// 统计各个binder响应码的个数
int br[_IOC_NR(BR_FAILED_REPLY) + 1];
// 统计各个binder请求码的个数
int bc[_IOC_NR(BC_REPLY_SG) + 1];
// 统计各种obj创建的个数
int obj_created[BINDER_STAT_COUNT];
// 统计各种obj删除个数
int obj_deleted[BINDER_STAT_COUNT];
};
四、结构体binder_proc
binder_proc 代表的是一个正在使用Binder进程通信的进程,binder_proc为管理其信息的记录体,当一个进程open /dev/binder 时,Binder驱动程序会为其创建一个binder_proc结构体,用以记录进程的所有相关信息,并把该结构体保存到一个全局的hash表中
1、代码位置
Linux的binder.c 322行
2、代码注释
struct binder_proc {
/** 进程相关参数 */
//上述全局hash表中一个节点,用以标记该进程
struct hlist_node proc_node;
// 进程组ID
int pid;
// 任务控制模块
struct task_struct *tsk;
// 文件结构体数组
struct files_struct *files;
/** Binder线程池每一个Binder进程都有一个线程池,由Binder驱动来维护,Binder线程池中所有线程由一个红黑树来组织,RB树以线程ID为关键字 */
//上述红黑树的根节点
struct rb_root threads;
/** 一系列Binder实体对象(binder_node)和Binder引用对象(binder_ref) */
/** 在用户控件:运行在Server端称为Binder本地对象,运行在Client端称为Binder代理对象*/
/** 在内核空间:Binder实体对象用来描述Binder本地对象,Binder引用对象来描述Binder代理对象 */
// Binder实体对象列表(RB树),关键字 ptr
struct rb_root nodes;
// Binder引用对象,关键字 desc
struct rb_root refs_by_desc;
// Binder引用对象,关键字 node
struct rb_root refs_by_node;
// 这里有两个引用对象,是为了方便快速查找
/** 进程可以调用ioctl注册线程到Binder驱动程序中,当线程池中没有足够空闲线程来处理事务时,Binder驱动可以主动要求进程注册更多的线程到Binder线程池中 */
// Binder驱动程序最多可以请求进程注册线程的最大数量
int max_threads;
// Binder驱动每主动请求进程添加注册一个线程的时候,requested_threads+1
int requested_threads;
// 进程响应Binder要求后,requested_thread_started+1,request_threads-1,表示Binder已经主动请求注册的线程数目
int requested_threads_started;
// 进程当前空闲线程的数目
int ready_threads;
// 线程优先级,初始化为进程优先级
long default_priority;
//进程的整个虚拟地址空间
struct mm_struct *vma_vm_mm;
/** mmap 内核缓冲区*/
// mmap——分配的内核缓冲区 用户控件地址(相较于buffer)
struct vm_area_struct *vma;
// mmap——分配内核缓冲区,内核空间地址(相交于vma) 两者都是虚拟地址
void *buffer;
// mmap——buffer与vma之间的差值
ptrdiff_t user_buffer_offset;
/** buffer指向的内核缓冲区,被划分为很多小块进行性管理;这些小块保存在列表中,buffer就是列表的头部 */
// 内核缓冲列表
struct list_head buffers;
// 空闲的内存缓冲区(红黑树)
struct rb_root free_buffers;
// 正在使用的内存缓冲区(红黑树)
struct rb_root allocated_buffers;
// 当前可用来保存异步事物数据的内核缓冲区大小
size_t free_async_space;
// 对应用于vma 、buffer虚拟机地址,这里是他们对应的物理页面
struct page **pages;
// 内核缓冲区大小
size_t buffer_size;
// 空闲内核缓冲区大小
uint32_t buffer_free;
/** 进程每接收到一个通信请求,Binder将其封装成一个工作项,保存在待处理队列to_do中 */
//待处理队列
struct list_head todo;
// 等待队列,存放一些睡眠的空闲Binder线程
wait_queue_head_t wait;
// hash表,保存进程中可以延迟执行的工作项
struct hlist_node deferred_work_node;
// 延迟工作项的具体类型
int deferred_work;
//统计进程相关数据,具体参考binder_stats结构体
struct binder_stats stats;
// 表示 Binder驱动程序正在向进程发出死亡通知
struct list_head delivered_death;
// 用于debug
struct dentry *debugfs_entry;
// 连接 存储binder_node和binder_context_mgr_uid以及name
struct binder_context *context;
};
五、结构体binder_node
binder_node 代表的是Binder实体对象,每一个service组件或者ServiceManager在Binder驱动程序中的描述,Binder驱动通过强引用和弱引用来维护其生命周期,通过node找到空间的Service对象.
1、代码位置
Linux的binder.c 252行
2、代码注释
struct binder_node {
// debug调试用的
int debug_id;
struct binder_work work; //binder驱动中进程要处理的工作项
/** 每一个binder进程都由一个binder_proc来描述,binder进程内部所有Binder实体对象,
由一个红黑树来进行组织(struct rb_root nodes) ; rb_node 则对应nodes的一个节点 */
union {
//用于本节点连接红黑树
struct rb_node rb_node;
// 如果Binder 实体对象对应的进程死亡,销毁节点时需要将rb_node从红黑树中删除,
//如果本节点还没有引用切断,则用dead_node将其隔离到另一个链表中,
//直到通知所有进程切断与该节点的引用后,该节点才能销毁
struct hlist_node dead_node;
};
// 指向该Binder实体对象 对应的进程,进程由binder_proc描述
struct binder_proc *proc;
// 该 Binder实体对象可能同时被多个Client组件引用,所有指向本实体对象的引用都
//保存在这个hash队列中refs表示队列头部;这些引用可能隶属于不同进程,遍历该
//hash表能够得到这些Client组件引用了这些对象
struct hlist_head refs;
/** 引用计数
* 1、当一个Binder实体对象请求一个Service组件来执行Binder操作时。会增加该Service
* 组件的强/弱引用计数同时,Binder实体对象将会has_strong_ref与has_weak_ref置为1
*2、当一个Service组件完成一个Binder实体对象请求的操作后,Binder对象会请求减少该
* Service组件的强/弱引用计数
* 3、Binder实体对象在请求一个Service组件增加或减少强/弱引用计数的过程中,
* 会将pending_strong_ref和pending_weak_ref置为1,当Service组件完成增加
* 或减少计数时,Binder实体对象会将这两个变量置为0
*/
//远程强引用 计数
int internal_strong_refs; //实际上代表了一个binder_node与多少个binder_ref相关联
//本地弱引用技数
int local_weak_refs;
//本地强引用计数
int local_strong_refs;
unsigned has_strong_ref:1;
unsigned pending_strong_ref:1;
unsigned has_weak_ref:1;
unsigned pending_weak_ref:1;
/** 用来描述用户控件中的一个Service组件 */
// 描述用户控件的Service组件,对应Binder实体对应的Service在用户控件的(BBinder)的引用
binder_uintptr_t ptr;
// 描述用户空间的Service组件,Binder实体对应的Service在用户控件的本地Binder(BBinder)地址
binder_uintptr_t cookie;
// 异步事务处理,单独讲解
unsigned has_async_transaction:1;
struct list_head async_todo;
// 表示该Binder实体对象能否接收含有该文件描述符的进程间通信数据。当一个进程向
//另一个进程发送数据中包含文件描述符时,Binder会在目标进程中打开一个相同的文件
//故设为accept_fds为0 可以防止源进程在目标进程中打开文件
unsigned accept_fds:1;
// 处理Binder请求的线程最低优先级
unsigned min_priority:8;
};
这里说下binder_proc和binder_node关系:
可以将binder_proc理解为一个进程,而将binder_noder理解为一个Service。binder_proc里面有一个红黑树,用来保存所有在它所描述的进程里面创建Service。而每一个Service在Binder驱动里面都有一个binder_node来描述。
3、异步事物处理
异步事务处理,目的在于为同步交互让路,避免长时间阻塞发送送端
异步事务定义:(相对于同步事务)单向进程间通信要求,即不需要等待应答的进程间通信请求
Binder驱动程序认为异步事务的优先级低于同步事务,则在同一时刻,一个Binder实体对象至多只有一个异步事物会得到处理。而同步事务则无此限制。
Binder将事务保存在一个线程binder_thread的todo队列中,表示由该线程来处理该事务。每一个事务都关联Binder实体对象(union target),表示该事务的目标处理对象,表示要求该Binder实体对象对应的Service组件在制定线程中处理该事务,而如果Binder发现一个事务时异步事务,则会将其保存在目标Binder对象的async_todo的异步事务中等待处理
六、结构体binder_ref
binder_ref 代表的是Binder的引用对象,每一个Clinet组件在Binder驱动中都有一个Binder引用对象,用来描述它在内核中的状态。
1、代码位置
Linux的binder.c 281行
2、代码注释
struct binder_ref {
/* Lookups needed: */
/* node + proc => ref (transaction) */
/* desc + proc => ref (transaction, inc/dec ref) */
/* node => refs + procs (proc exit) */
//debug 调试用的
int debug_id;
/** binder_proc中使用红黑树(对应两个rb_root变量) 来存储器内部所有引用对象,
*下面的rb_node则是红黑树中的节点
*/
//Binder引用的宿主进程
struct binder_proc *proc;
//对应 refs_by_desc,以句柄desc索引 关联到binder_proc->refs_by_desc红黑树
struct rb_node rb_node_desc;
//对应refs_by_node,以Binder实体对象地址作为关键字关联到binder_proc->refs_by_node红黑树
struct rb_node rb_node_node;
/** Client通过Binder访问Service时,仅需指定一个句柄,Binder通过该desc找到对应的binder_ref,
* 再根据该binder_ref中的node变量得到binder_node(实体对象),进而找到对应的Service组件
*/
// 对应Binder实体对象中(hlist_head) refs引用对象队列中的一个节点
struct hlist_node node_entry;
// 引用对象所指向的Binder实体对象
struct binder_node *node;
// Binder引用的句柄值,Binder驱动为binder驱动引用分配一个唯一的int型整数(进程范围内唯一)
// ,通过该值可以在binder_proc->refs_by_desc中找到Binder引用,进而可以找到Binder引用对应的Binder实体
uint32_t desc;
// 强引用 计数
int strong;
// 弱引用 计数
int weak;
// 表示Service组件接受到死亡通知
struct binder_ref_death *death;
};
七、结构体binder_ref_death
binder_ref_death 一个死亡通知的结构体
我们知道Client组件无法控制它所引用的Service组件的生命周期,由于Service组件所在的进程可能意外崩溃。Client进程需要能够在它所引用的Service组件死亡时获的通知,进而进行响应。则Client进程就需要向Binder驱动注册一个用来接收死亡通知的对象地址(这里的cookie)
1、代码位置
Linux的binder.c 276行
2、代码注释
struct binder_ref_death {
//标志该通知具体的死亡类型
struct binder_work work;
// 保存负责接收死亡通知的对象地址
binder_uintptr_t cookie;
};
这里随带说一下Binder驱动向Client进程发送死亡通知的情况:
- 1、Binder驱动检测到Service组件死亡时,会找到对应Serivce实体对象(binder_node),再通过refs变量找到引用它的所有Client进程(binder_ref),再通过death变量找到Client进程向Binder注册的死亡通知接收地址;Binder将死亡通知binder_ref_death封装成工作项,添加到Client进程to_do队列中等待处理。这种情况binder_work类型为BINDER_WORK_DEAD_BINDER
- 2、Client进程向Binder驱动注册一个死亡接收通知时,如果它所引用的Service组件已经死亡,Binder会立即发送通知给Client进程。这种情况binder_work类型为BINDER_WORK_DEAD_BINDER
- 3、当Client进程向Binder驱动注销一个死亡通知时,也会发送通知,来响应注销结果
①当Client注销时,Service组件还未死亡:Binder会找到之前Client注册的binder_ref_death,当binder_work修改为BINDER_CLEAR_NOTIFICATION,并将通知按上述步骤添加到Client的to_do队列中
@当Client注销时,Service已经死亡,Binder同理将binder_work修改为WORK_DEAD_BINDER_AND_CLEAR,然后添加到todo中
八、结构体binder_state
binder_state 代表着binder设备文件的状态
1、代码位置
/frameworks/native/cmds/servicemanager/binder.c 89行
2、代码注释
struct binder_state
{
//打开 /dev/binder之后得到的文件描述符
int fd;
//mmap将设备文件映射到本地进程的地址空间,映射后的到地址空间中,映射后得到的地址空间地址,及大小。
void *mapped;
// 分配内存的大小,默认是128K
size_t mapsize;
};
九、结构体binder_buffer
binder_buffer 内核缓冲区,用以在进程间传递数据。binder驱动程序管理这个内存映射地址空间方法,即管理buffer~(buffer+buffer_size)这段地址空间的,这个地址空间被划分为一段一段来管理,每一段是结构体struct binder_buffer来描述。每一个binder_buffer通过其成员entry从低到高地址连入到struct binder_proc中的buffers表示链表中去
1、代码位置
Linux的binder.c 298行
2、代码注释
struct binder_buffer {
//entry对应内核缓冲区列表的buffers(内核缓冲区列表)
struct list_head entry; /* free and allocated entries by address */
//结合free,如果free=1,则rb_node对应free_buffers中一个节点(内核缓冲区)
//如果free!=1,则对应allocated_buffers中的一个节点
struct rb_node rb_node; /* free entry by size or allocated entry */
/* by address */
unsigned free:1;
/** Binder将事务数据保存到一个内核缓冲区(binder_transaction.buffer),然后交由Binder
* 实体对象(target_node) 处理,而target_node会将缓冲区的内容交给对应的Service组件
* (proc) 来处理,Service组件处理完事务后,若allow_user_free=1,则请求Binder释放该
* 内核缓冲区
*/
unsigned allow_user_free:1;
// 描述一个内核缓冲区正在交给那个事务transaction,用以中转请求和返回结果
struct binder_transaction *transaction;
// 描述该缓冲区正在被那个Binder实体对象使用
struct binder_node *target_node;
//表示事务时异步的;异步事务的内核缓冲区大小是受限的,这样可以保证事务可以优先放到缓冲区
unsigned async_transaction:1;
//调试专用
unsigned debug_id:29;
/**
* 存储通信数据,通信数据中有两种类型数据:普通数据与Binder对象
* 在数据缓冲区最后,有一个偏移数组,记录数据缓冲区中每一个Binder
* 对象在缓冲区的偏移地址
*/
// 数据缓冲区大小
size_t data_size;
// 偏移数组的大小(其实也是偏移位置)
size_t offsets_size;
// 用以保存通信数据,数据缓冲区,大小可变
uint8_t data[0];
// 额外缓冲区大小
size_t extra_buffers_size;
};
十、结构体binder_transaction
binder_transaction 描述Binder进程中通信过程,这个过程称为一个transaction(事务),用以中转请求和返回结果,并保存接收和要发送的进程信息
1、代码位置
Linux的binder.c 383行
2、代码注释
struct binder_transaction {
//调试调用
int debug_id;
// 用来描述的处理的工作事项,这里会将type设置为BINDER_WORK_TRANSACTION,具体结构看binder_work
struct binder_work work;
/** 源线程 */
// 源线程,即发起事务的线程
struct binder_thread *from;
// 源线程的优先级
long priority;
//源 线程的用户 ID
kuid_t sender_euid;
/** 目标线程*/
// 目标进程:处理该事务的进程
struct binder_proc *to_proc;
// 目标线程:处理该事务的线程
struct binder_thread *to_thread;
// 表示另一个事务要依赖事务(不一定要在同一个线程中)
struct binder_transaction *from_parent;
// 目标线程下一个需要处理的事务
struct binder_transaction *to_parent;
// 标志事务是同步/异步;设为1表示同步事务,需要等待对方回复;设为0异步
unsigned need_reply:1;
/* unsigned is_dead:1; */ /* not used at the moment */
/* 参考binder_buffer中解释,指向Binder为该事务分配内核缓冲区
* code与flag参见binder_transaction_data
*/
struct binder_buffer *buffer;
unsigned int code;
unsigned int flags;
/** 目标线程设置事务钱,Binder需要修改priority;修改前需要将线程原来的priority保存到
* saved_priority中,用以处理完事务回复到原来优先级
* 优先级设置:目标现场处理事务时,优先级应不低于目标Serivce要求的线程优先级,也
* 不低于源线程的优先级,故设为两者的较大值。
*/
long saved_priority;
};
十一、结构体binder_transaction_data
binder_transaction_data 代表进程间通信所传输的数据:Binder对象的传递时通过binder_transaction_data来实现的,即Binder对象实际是封装在binder_transaction_data结构体中
1、代码位置
Linux的binder.h 217行
2、代码注释
struct binder_transaction_data {
//target很重要,我下面重点介绍
/* The first two are only used for bcTRANSACTION and brTRANSACTION,
* identifying the target and contents of the transaction.
*/
union {
/* target descriptor of command transaction */
__u32 handle;
/* target descriptor of return transaction */
binder_uintptr_t ptr;
} target;
//Binder实体带有的附加数据
binder_uintptr_t cookie; /* target object cookie */
// code是一个命令,描述了请求Binder对象执行的操作,表示要对目标对象请求的命令代码
__u32 code; /* transaction command */
/* General information about the transaction. */
// 事务标志,详细看transaction_flag结构体
__u32 flags;
// 发起请求的进程PID
pid_t sender_pid;
// 发起请求的进程UID
uid_t sender_euid;
// data.buffer缓冲区的大小,data见最下面的定义;命令的真正要传输的数据就保存data.buffer缓冲区
binder_size_t data_size; /* number of bytes of data */
// data.offsets缓冲区的大小
binder_size_t offsets_size; /* number of bytes of offsets */
/* If this transaction is inline, the data immediately
* follows here; otherwise, it ends with a pointer to
* the data buffer.
*/
union {
struct {
/* transaction data */
binder_uintptr_t buffer;
/* offsets from buffer to flat_binder_object structs */
binder_uintptr_t offsets;
} ptr;
__u8 buf[8];
} data;
};
这里重点说两个共用体target和data
target
- 一个共用体target,当这个BINDER_WRITE_READ命令的目标对象是本地Binder的实体时,就用ptr来表示这个对象在本进程的地址,否则就使用handle来表示这个Binder的实体引用。
- 只有目标对象是Binder实体时,cookie成员变量才有意义,表示一些附加数据。
详解解释一下:传输的数据是一个复用数据联合体,对于BINDER类型,数据就是一个Binder本地对象。如果是HANDLE类型,这个数据就是远程Binder对象。很多人会说怎么区分本地Binder对象和远程Binder对象,主要是角度不同而已。本地对象还可以带有额外数据,保存在cookie中。
data
- 命令的真正要传输的数据就保存在data.buffer缓冲区中,前面的一成员变量都是一些用来描述数据的特征。data.buffer所表示的缓冲区数据分为两类,一类是普通数据,Binder驱动程序不关心,一类是Binder实体或者Binder引用,这需要Binder驱动程序介入处理。为什么?因为如果一个进程A传递了一个Binder实体或Binder引用给进程B,那么,Binder驱动程序就需要介入维护这个Binder实体或者引用引用计数。防止B进程还在使用这个Binder实体时,A却销毁这个实体,这样的话,B进程就会crash了。所以在传输数据时,如果数据中含有Binder实体和Binder引用,就需要告诉Binder驱动程序他们的具体位置,以便Binder驱动程序能够去维护它们。data.offsets的作用就在这里,它指定在data.buffer缓冲区中,所以Binder实体或者引用的偏移位置。
- 进程间传输的数据被称为Binder对象(Binder Object),它是一个flat_binder_object。Binder对象的传递时通过binder_transaction_data来实现的,即Binder对象实际是封装在binder_transaction_data结构体中。
十二、结构体transaction_flags
transaction_flags 描述传输方式,比如同步或者异步等
1、代码位置
Linux的binder.h 210行
2、代码注释
enum transaction_flags {
//当前事务异步,不需要等待
TF_ONE_WAY = 0x01, /* this is a one-way call: async, no return */
// 包含内容是根对象
TF_ROOT_OBJECT = 0x04, /* contents are the component's root object */
// 表示data所描述的数据缓冲区内 同时一个4bit的状态码
TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */
// 允许数据中包含文件描述
TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */
};
十三、结构体flat_binder_object
flat_binder_object 描述进程中通信过程中传递的Binder实体/引用对象或文件描述符
1、代码位置
Linux的binder.h 68行
2、代码注释
/*
* This is the flattened representation of a Binder object for transfer
* between processes. The 'offsets' supplied as part of a binder transaction
* contains offsets into the data where these structures occur. The Binder
* driver takes care of re-writing the structure type and data as it moves
* between processes.
*/
struct flat_binder_object {
struct binder_object_header hdr;
__u32 flags;
/* 8 bytes of data. */
union {
binder_uintptr_t binder; /* local object */
__u32 handle; /* remote object */
};
/* extra data associated with local object */
binder_uintptr_t cookie;
};
这个比较重要我们就一个一个来说,首先看下struct binder_object_header hdr;
里面涉及到一个结构体binder_object_header,这个结构体在Linux的binder.h 57行,代码如下:
/**
* struct binder_object_header - header shared by all binder metadata objects.
* @type: type of the object
*/
struct binder_object_header {
__u32 type;
};
flat_binder_object通过type来区分描述类型,可选的描述类型如下:
代码在
Linux的binder.h 30行
enum {
//强类型Binder实体对象
BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
//弱类型Binder实体对象
BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
// 强类型引用对象
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
// 弱类型引用对象
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
// 文件描述符
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
BINDER_TYPE_FDA = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE),
BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE),
};
当描述实体对象时,cookie表示Binder实体对应的Service在用户空间的本地Binder(BBinder)地址,binder表示Binder实体对应的当描述引用对象时,handle表示该引用的句柄值。
十四、结构体binder_write_read
binder_write_read 描述进程间通信过程中所传输的数据
1、代码位置
Linux的binder.h 165行
2、代码注释
/*
* On 64-bit platforms where user code may run in 32-bits the driver must
* translate the buffer (and local binder) addresses appropriately.
*/
struct binder_write_read {
/** 输入数据 从用户空间传输到Binder驱动程序的数据
* 数据协议代码为命令协议码,由binder_driver_command_protocol定义
*/
// 写入的大小
binder_size_t write_size; /* bytes to write */
// 记录了从缓冲区取了多少字节的数据
binder_size_t write_consumed; /* bytes consumed by driver */
// 指向一个用户控件缓冲区的地址,里面的内容即为输入数据,大小由write_size指定
binder_uintptr_t write_buffer;
/** 输出数据,从Binder驱动程序,返回给用户空间的数据
* 数据协议代码为返回协议代码,由binder_driver_return_protocol定义
*/
//读出的大小
binder_size_t read_size; /* bytes to read */
// read_buffer中读取的数据量
binder_size_t read_consumed; /* bytes consumed by driver */
// 指向一个用户缓冲区一个地址,里面保存输出的数据
binder_uintptr_t read_buffer;
};
里面涉及到两个协议,我们就在这里详细讲解下
3、binder_driver_command_protocol协议
代码在Linux的binder.h 366行
enum binder_driver_command_protocol {
/** 下面这两个命令数据类型为binder_transaction_data,是最常用到的 */
/**一个Client进程请求目标进程执行某个事务时,会使用BC_TRANSACTION请求Binder驱
*动程序将通信数据传递到Server目标进程
* 使用者:Client进程 用处:传递数据
*/
BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
/** 当Server目标进程处理完事务后,会使用BC_REPLY请求Binder将结果返回给Client源进程
* 使用者:Server进程 用处:返回数据
*/
BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
/*
* binder_transaction_data: the sent command.
*/
//当前版本not support 在Linux中的binder.c就是这么写的
BC_ACQUIRE_RESULT = _IOW('c', 2, __s32),
/*
* not currently supported
* int: 0 if the last BR_ATTEMPT_ACQUIRE was not successful.
* Else you have acquired a primary reference on the object.
*/
// 数据类型为int类型,指向Binder内部一块内核缓冲区
// 目标进程处理完源进程事务后,会使用BC_FREE_BUFFER来释放缓冲区
BC_FREE_BUFFER = _IOW('c', 3, binder_uintptr_t),
/*
* void *: ptr to transaction data received on a read
*/
//通信类型为int类型,表示binder_ref的句柄值handle
// 增加弱引用数
BC_INCREFS = _IOW('c', 4, __u32),
// 减少弱引用数
BC_DECREFS = _IOW('c', 7, __u32),
// 增加强引用数
BC_ACQUIRE = _IOW('c', 5, __u32),
// 减少强引用数
BC_RELEASE = _IOW('c', 6, __u32),
/*
* int: descriptor
*/
/** Service进程完成增加强/弱引用的计数后,会使用这两个命令通知Binder */
// 增加强引用计数后
BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie),
//增加弱引用计数后
BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie),
/*
* void *: ptr to binder
* void *: cookie for binder
*/
//当前版本不支持
BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc),
/*
* not currently supported
* int: priority
* int: descriptor
*/
// Binder驱动程序 请求进程注册一个线程到它的线程池中,新建立线程会使用
//BC_REGISTER_LOOPER来通知Binder准备就绪
BC_REGISTER_LOOPER = _IO('c', 11),
/*
* No parameters.
* Register a spawned looper thread with the device.
*/
//一个线程自己注册到Binder驱动后,会使用BC_ENTER_LOOPER通知Binder准备就绪
BC_ENTER_LOOPER = _IO('c', 12),
// 线程发送退出请求
BC_EXIT_LOOPER = _IO('c', 13),
/*
* No parameters.
* These two commands are sent as an application-level thread
* enters and exits the binder loop, respectively. They are
* used so the binder can have an accurate count of the number
* of looping threads it has available.
*/
// 进程向Binder注册一个死亡通知
BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14,
struct binder_handle_cookie),
/*
* int: handle
* void *: cookie
*/
// 进程取消之前注册的死亡通知
BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15,
struct binder_handle_cookie),
/*
* int: handle
* void *: cookie
*/
// 数据指向死亡通知binder_ref_death的地址,进程获得Service组件的死亡通知,
// 会使用该命令通知Binder其已经处理完死亡通知
BC_DEAD_BINDER_DONE = _IOW('c', 16, binder_uintptr_t),
/*
* void *: cookie
*/
BC_TRANSACTION_SG = _IOW('c', 17, struct binder_transaction_data_sg),
BC_REPLY_SG = _IOW('c', 18, struct binder_transaction_data_sg),
/*
* binder_transaction_data_sg: the sent command.
*/
};
在上述枚举命令成员中,最重要的是BC_TRANSACTION和BC_REPLY命令,被作为发送操作的命令,其数据参数都是binder_transaction_data结构体。其中前者用于翻译和解析将要被处理的事务数据,而后者则是事务处理完成之后对返回"结果数据"的操作命令。
4、binder_driver_return_protocol协议
Binder驱动的响应(返回,BR_)协议,定义了Binder命令的数据返回格式。代码在Linux的binder.h 278行
enum binder_driver_return_protocol {
// Binder驱动程序处理进程发送的请求时,发生异常,在返回BR_ERROR通知该进程
// 数据类型为int,表示错误代码
BR_ERROR = _IOR('r', 0, __s32),
/*
* int: error code
*/
// 表示通知进程成功处理了该事务
BR_OK = _IO('r', 1),
/* No parameters! */
// 与上面的 BC_ 相对应
// Client进程向Server进程发送通信请求(BC_) ,Binder使用BR_TRANSACTION通知Server
// 使用者 : Binder驱动程序 用途:通知Server
BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data),
// Server处理完 请求 使用 BC_ 通知Binder,Binder使用BR_REPLY通知Client
// 使用者:Binder驱动程序,用途:通知Client
BR_REPLY = _IOR('r', 3, struct binder_transaction_data),
/*
* binder_transaction_data: the received command.
*/
// 当前不支持
BR_ACQUIRE_RESULT = _IOR('r', 4, __s32),
/*
* not currently supported
* int: 0 if the last bcATTEMPT_ACQUIRE was not successful.
* Else the remote object has acquired a primary reference.
*/
// Binder处理请求时,发现目标进程或目标线程已经死亡,通知源进程
BR_DEAD_REPLY = _IO('r', 5),
/*
* The target of the last transaction (either a bcTRANSACTION or
* a bcATTEMPT_ACQUIRE) is no longer with us. No parameters.
*/
// Binder 接收到BC_TRANSACATION或BC_REPLY时,会返回 BR_TRANSACTION_COMPLETE通知源进程命令已经接收
BR_TRANSACTION_COMPLETE = _IO('r', 6),
/*
* No parameters... always refers to the last transaction requested
* (including replies). Note that this will be sent even for
* asynchronous transactions.
*/
// 增加弱引用计数
BR_INCREFS = _IOR('r', 7, struct binder_ptr_cookie),
// 增加强引用计数
BR_ACQUIRE = _IOR('r', 8, struct binder_ptr_cookie),
// 减少强引用计数
BR_RELEASE = _IOR('r', 9, struct binder_ptr_cookie),
// 减少弱引用计数
BR_DECREFS = _IOR('r', 10, struct binder_ptr_cookie),
/*
* void *: ptr to binder
* void *: cookie for binder
*/
//当前不支持
BR_ATTEMPT_ACQUIRE = _IOR('r', 11, struct binder_pri_ptr_cookie),
/*
* not currently supported
* int: priority
* void *: ptr to binder
* void *: cookie for binder
*/
// Binder通过源进程执行了一个空操作,用以可以替换为BR_SPAWN_LOOPER
BR_NOOP = _IO('r', 12),
/*
* No parameters. Do nothing and examine the next command. It exists
* primarily so that we can replace it with a BR_SPAWN_LOOPER command.
*/
// Binder发现没有足够的线程处理请求时,会返回BR_SPAWN_LOOPER请求增加新的新城到Binder线程池中
BR_SPAWN_LOOPER = _IO('r', 13),
/*
* No parameters. The driver has determined that a process has no
* threads waiting to service incoming transactions. When a process
* receives this command, it must spawn a new service thread and
* register it via bcENTER_LOOPER.
*/
// 当前暂不支持
BR_FINISHED = _IO('r', 14),
/*
* not currently supported
* stop threadpool thread
*/
/** Binder检测到Service组件死亡时,使用BR_DEAD_BINDER通知Client进程,Client请求
* 注销之前的死亡通知,Binder完成后,返回BR_CLEAR_DEATH_NOTIFACTION_DONE
*/
// 告诉发送方对象已经死亡
BR_DEAD_BINDER = _IOR('r', 15, binder_uintptr_t),
/*
* void *: cookie
*/
//清理死亡通知
BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, binder_uintptr_t),
/*
* void *: cookie
*/
// 发生异常,通知源进程
BR_FAILED_REPLY = _IO('r', 17),
/*
* The the last transaction (either a bcTRANSACTION or
* a bcATTEMPT_ACQUIRE) failed (e.g. out of memory). No parameters.
*/
};
5、Binder通信协议流程
单独看上面的协议可能很难理解,这里我们以一次Binder请求为过程来详细看一下Binder协议是如何通信的,就比较好理解了。这幅图的说明如下:
Binder通信过程.png
- 1 Binder是C/S架构的,通信过程牵涉到Client、Server以及Binder驱动三个角色
- 2 Client对于Server的请求以及Server对于Client的回复都需要通过Binder驱动来中转数据
- 3 BC_XXX命令是进程发送给驱动命令
- 4 BR_XXX命令是驱动发送给进程的命令
- 5 整个通信过程由Binder驱动控制
PS:这里补充说明一下,通过上面的Binder协议的说明,我们看到,Binder协议的通信过程中,不仅仅是发送请求和接收数据这些命令。同时包括了对于引用计数的管理和对于死亡通知的管理(告知一方,通讯的另外一方已经死亡)。这个功能的流程和上述的功能大致一致。
十五、结构体binder_ptr_cookie
binder_ptr_cookie 用来描述一个Binder实体对象或一个Service组件的死亡通知
1、代码位置
Linux的binder.h 257行
2、代码注释
struct binder_ptr_cookie {
binder_uintptr_t ptr;
binder_uintptr_t cookie;
};
- 当描述Binder实体对象:ptr,cookie见binder_node
- 当描述死亡通知:ptr指向一个Binder引用对象的句柄值,cookie指向接收死亡通知的对象地址
十六、总结
结构体就是这样的
如果以结构体为标的来看整个Binder传输过程则如下:
结构体为标的.png