最近突然看到一个博客,是讲Binder原理的,以前看深入理解Android I的时候看过,那时候就看不懂。感觉这个还有点意思,就看了好几天,发现越看越不懂,然后看老罗的博客,发现也不是太懂,现在想根据书上的东西好好梳理下Binder。
感觉里面应该重点掌握的应是bind_node是注册的服务在内核中的代表,bind_ref是客户端连接在驱动的代表。bind_buffer内核缓冲区,通过mmap之后可以在用户空间操作,然后在内核也可以访问,bind_proc是进程的代表,客户端和服务端都有,对上面的进行管理,未完,待续,等看完了艺术探索过来更新。
handle, RPC代码和RPC数据保存在binder_transaction_data的结构体中,属于RPC范畴.
binder_write_read 等于binder_transaction_data(用户空间数据)加上binder协议,是IPC数据范畴.
binder_write_read中的write_size是发送ipc数据时使用,read_size是接收数据时使用.
flat_binder_object在内核中传输的时候,会判断type的类型,来判断是否创建binder节点(服务注册的时候存在)
进程间通信根据Client和Server的状态设置该值
struct binder_work {//用户查找binder_transaction结构体
struct list_head entry; //
enum {
BINDER_WORK_TRANSACTION = 1, //获取ipc数据
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER, //Binder驱动检测到Service组件死亡时,找到Binder实体对象中的refs,找到引用它的client进程和Client进程主动注册死亡通知发现Service组件已死亡两种情况
BINDER_WORK_DEAD_BINDER_AND_CLEAR, //Client进程注销死亡通知时,相应的Service组件已死亡,binder驱动找到之前注册的binder_ref_death结构体,并修改它work
BINDER_WORK_CLEAR_DEATH_NOTIFICATION, //Client进程注销一个死亡通知,相应的Service组件没有死亡,Binder驱动程序会找到之前注册的,一个binder_ref_death结构体,并且将它的work修改为此, 然后将该结构体封装成一个工作项添加到Client进程的todo队列中
} type; //工作项的类型
};
binder实体对象 Service中的Binder在内核中的代表
struct binder_node {
int debug_id;
struct binder_work work;//引用计数发生变化时 BINDER_WORK_NODE,并且将它添加到响应进程的todo队列中
union {
struct rb_node rb_node;
struct hlist_node dead_node;
};
struct binder_proc *proc; //指向service进程
struct hlist_head refs; //所有的Client的binder引用binder_ref->node
int internal_strong_refs;
int local_weak_refs;
int local_strong_refs;
void __user *ptr; //指向Service组件内部的一个引用计数 weakref_impl 弱引用计数
void __user *cookie; //指向Service组件的地址
unsigned has_strong_ref:1; //请求Service组件时 1 结束 0
unsigned pending_strong_ref:1; // 请求的时候为1,service增加后为0
unsigned has_weak_ref:1; //请求Service组件时 1 结束 0
unsigned pending_weak_ref:1;
unsigned has_async_transaction:1; //每一个事务都关联一个binder实体对象
unsigned accept_fds:1; //是否接收含有文件描述符的进程间通信数据 防止源进程在目标进程中打开
unsigned min_priority:8; //线程优先级
struct list_head async_todo; //异步事务队列
};
Service组件,在驱动中的binder_node,binder_ref都维护引用计数
描述Client组件的死亡通知在驱动中的代表,
struct binder_ref_death {
struct binder_work work; //见第一个数据结构
void __user *cookie; //保存Client负责接收死亡通知对象的地址
};
Binder驱动程序决定向客户端进程发送一个Service组件死亡通知时。会将binder_ref_death结构体封装成一个工作项。加到Client进程的todo队列中 ,Client进程在处理这个工作项,会通过binder_ref_death结构体成员变量的work来区是哪一种情况,见第一个结构体中的枚举值
描述一个Binder引用对象 Client进程中的Binder引用在驱动中的代表
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; //hash列表中的节点 对应与binder_node->refs
struct binder_proc *proc; //Binder引用对象的宿主进程
struct binder_node *node; //Binder引用对象所引用的Binder实体对象
uint32_t desc; //在Client进程的用户空间,Binder引用对象是使用一个句柄值来描述的,BInder驱动程序就可以通过该句柄值找到对于的,Binder引用对象即是binder_ref
int strong;
int weak;
struct binder_ref_death *death; //Client进程注册的死亡通知保存的地方
};
进程对应内核缓冲区 缓冲区中的事务会随着请求发送变化
struct binder_buffer {
struct list_head entry; /* free and allocated entries by addesss */ //内核缓冲区列表的一个节点
struct rb_node rb_node; /* free entry by size or allocated entry */
/* by address */ //free 为1 表示为空闲内核缓冲区中的一个节点
unsigned free:1; //1表示内核缓冲区是空闲的 不会分配物理页面的
unsigned allow_user_free:1;//Service组件处理完后发现为1,Service组件请求Binder驱动释放该内核缓冲区
unsigned async_transaction:1; //为1表示异步事务
unsigned debug_id:29;
struct binder_transaction *transaction; //每一个事务都关联一个目标Binder实体对象
struct binder_node *target_node; //目标的binder节点
size_t data_size; //数据缓冲区的大小
size_t offsets_size; //偏移数组 记录了每一个Binder对象再数据缓冲区中的位置
uint8_t data[0]; //指向大小可变的数据缓冲区,用来保存通信数据的 可保存普通数据和Binder对象 Binder驱动程序只关心Binder对象
};
进程调用open /dev/binder的时候创建 将它保存在全局的hash 进程在驱动中的代表
struct binder_proc {
struct hlist_node proc_node; //是hash列表中的节点
struct rb_root threads; // 拥有binder_thread结构体的红黑树的根
struct rb_root nodes; //binder实体对象的红黑树的根 以ptr作为关键字
struct rb_root refs_by_desc;
struct rb_root refs_by_node; //带有binder_ref 结构体红黑树的根,使用binder_node结构体区分binder_ref
int pid; //创建binder_proc结构体的进程的pid
struct vm_area_struct *vma; //内核缓冲区地址 用户空间地址 在应用程序内部使用
struct task_struct *tsk; //任务控制块 进程相关
struct files_struct *files; //文件结构体数组
struct hlist_node deferred_work_node; //进程延迟执行的工作项
int deferred_work; //延迟工作项的具体内容
void *buffer; //内核缓冲区的地址 内核空间地址 大块空间 划分为Binder_buffer小空间 接收ipc数据的结构体指针
ptrdiff_t user_buffer_offset; //内核缓冲区中 用户空间地址和内核空间地址的差值 接收的ipc数据的内核和用户的地址偏移量,将接收到的ipc数据传递到用户空间
struct list_head buffers; //指向指向该列表的头部 为接收ipc数据而分配的binder_buffer结构体列表
struct rb_root free_buffers; //已分配物理页面 接收ipc数据后,要释放的binder_buffer结构体列表 根据RPC数据的大小分配binder_buffer结构体的data
struct rb_root allocated_buffers;
size_t free_async_space; //保存异步事务数据缓冲区的大小
struct page **pages; //对于的物理页面 数组中每个元素指向一个物理页面
size_t buffer_size; //mmap后内核缓冲区的大小 内核中开辟的buffer大小
uint32_t buffer_free; //空闲内核缓冲区的大小
struct list_head todo; //把待处理请求封装成一个工作项,加如到待处理工作项队列 进程从待机状态唤醒后要做的事情 和binder_work联系一块
wait_queue_head_t wait; //空闲binder线程会睡眠在等待队列里面 让进程进入待机状态
struct binder_stats stats; //统计进程数据的,进程见请求的次数
struct list_head delivered_death; //死亡通知封装成一个工作项保存在所描述的一个队列中,进程收到后会删除
int max_threads;
int requested_threads; //驱动主动请求进程注册一个线程时加1,进程响应后减1
int requested_threads_started;//驱动程序主动请求进程注册的数目,进程响应后加1
int ready_threads; //当前空闲的Binder线程数目
long default_priority; //宿主进程的优先级
struct dentry *debugfs_entry;
};
deferred_work的可能取值
enum binder_deferred_state {
BINDER_DEFERRED_PUT_FILES = 0x01, //进程关闭文件描述符
BINDER_DEFERRED_FLUSH = 0x02, //唤醒线程检查进程是否有新的工作项需要处理
BINDER_DEFERRED_RELEASE = 0x04, //不再使用binder进程间通信机制,驱动释放它分配的资源,释放进程结构体,binder实体对象
};
binder驱动会为内核缓冲区分配文件描述符,进程可以通过文件描述符把内核缓冲区映射到自己的地址空间
binder线程池中的一个线程,线程注册到binder驱动时,驱动会创建该结构体
struct binder_thread {
struct binder_proc *proc; //指向宿主进程
struct rb_node rb_node; //一个节点
int pid; //线程ID
int looper; //状态
struct binder_transaction *transaction_stack; //一个事务交给一个线程处理时,事务封装成结构体 处理谁,谁放在最前端 查找另一端接收进程
struct list_head todo; //Client进程的请求
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 */
wait_queue_head_t wait; //等待依赖的线程处理结束
struct binder_stats stats; //接收到进程间通信请求的次数
};
线程的状态
enum {
BINDER_LOOPER_STATE_REGISTERED = 0x01, //收到用户线程发送BC_register_looper
BINDER_LOOPER_STATE_ENTERED = 0x02, //表明准备就绪BC_ENTER_looper
BINDER_LOOPER_STATE_EXITED = 0x04,
BINDER_LOOPER_STATE_INVALID = 0x08,
BINDER_LOOPER_STATE_WAITING = 0x10, //binder线程处于空闲状态
BINDER_LOOPER_STATE_NEED_RETURN = 0x20 //初始化状态
};
线程是应用程序主动注册的,那么它通过BC_ENTER_looper来通知binder驱动
描述进程间通信过程
struct binder_transaction {
int debug_id;
struct binder_work work; //binder驱动为目标线程创建一个事务后(由驱动负责)设置BINDER_WORK_TRANSACTION,并添加到目标线程的todo队列中
struct binder_thread *from; //发起事务的线程,成为源线程
struct binder_transaction *from_parent; //
struct binder_proc *to_proc; //负责处理该事务的进程
struct binder_thread *to_thread; //目标线程
struct binder_transaction *to_parent; //处理完本事务,然后返回父事务
unsigned need_reply:1; //为1 表示是同步事务
/* unsigned is_dead:1; */ /* not used at the moment */
struct binder_buffer *buffer; //binder驱动程序为该事务分配的一块内存缓冲区
unsigned int code;
unsigned int flags;
long priority; //线程优先级
long saved_priority; //保存原来的线程优先级
uid_t sender_euid; //线程用户ID
};
应用程序通过IO命令和驱动交互
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
进程间通信
struct binder_write_read {
signed long write_size; /* bytes to write */ 缓冲区大小
signed long write_consumed; /* bytes consumed by driver */ 驱动从缓冲区处理的字节
unsigned long write_buffer; //描述输入数据 从用户空间传输到binder驱动程序的数据 指向一个用户缓冲区地址
signed long read_size; /* bytes to read */
signed long read_consumed; /* bytes consumed by driver */
unsigned long read_buffer; //指向一个用户空间缓冲区的地址
};
全部是进程发送给binder驱动
enum BinderDriverCommandProtocol {
BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
BC_FREE_BUFFER = _IOW('c', 3, int), //指向内核缓冲区
BC_INCREFS = _IOW('c', 4, int), //binder引用对象的句柄值 弱引用
BC_ACQUIRE = _IOW('c', 5, int), //增加 强引用计数
BC_RELEASE = _IOW('c', 6, int),
BC_DECREFS = _IOW('c', 7, int), //弱引用
BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie),
BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie),
BC_REGISTER_LOOPER = _IO('c', 11), //驱动请求进程注册线程到驱动
BC_ENTER_LOOPER = _IO('c', 12), //线程主动注册
BC_EXIT_LOOPER = _IO('c', 13), //线程要退出时
BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_ptr_cookie), //注册
BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_ptr_cookie), //清空
BC_DEAD_BINDER_DONE = _IOW('c', 16, void *), //指向一个死亡通知结构体binder_ref_death的地址
//Client用命令协议码通知binder驱动程序处理完Service组件的死亡通知了
};
源进程使用命令协议代码BC_TRANSACTION请求binder驱动将通信数据传递到目标进程
目标进程处理完源进程的请求操作之后使用命令协议码BC_REPLY请求驱动将结果传递给源进程
返回协议代码
enum BinderDriverReturnProtocol {
BR_ERROR = _IOR('r', 0, int), //驱动处理请求时出错
BR_OK = _IO('r', 1), //成功处理
BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data), //通知Server进程来处理该进程间通信请求
BR_REPLY = _IOR('r', 3, struct binder_transaction_data),//server处理完请求之后,驱动以此协议返回Client进程
BR_DEAD_REPLY = _IO('r', 5), //发现目标进程或线程已经死亡 驱动会返回这个协议代码
BR_TRANSACTION_COMPLETE = _IO('r', 6),//当binder驱动收到进程发来的BC_TRANSACTION或是BC_REPLY,驱动返回此码,告知进程已收到
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),
BR_NOOP = _IO('r', 12), //通知应用进程执行了一个空操作
BR_SPAWN_LOOPER = _IO('r', 13),//发现进程没有足够的空闲Binder线程来处理进程间通信请求,通知该进程增加一个线程到Binder线程池中
BR_DEAD_BINDER = _IOR('r', 15, void *),//当驱动检测到Service组件死亡事件时,通知Client进程
BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, void *),//binder驱动执行完注销后返回此码通知Client进程
BR_FAILED_REPLY = _IO('r', 17), // 处理进程发送的BC_TRANSACTION异常时,返回此码
};
描述一个Binder实体对象或是引用
struct binder_ptr_cookie {
void *ptr; //Binder引用的句柄 或是
void *cookie;//接收死亡通知对象的地址
};
进程中线程的通信传输的数据 用户空间数据
struct binder_transaction_data {
union {
size_t handle; /* target descriptor of command transaction */ //引用对象的句柄值
void *ptr; /* target descriptor of return transaction */ //指向Service组件内部弱引用计数的地址
} target;
void *cookie; /* target object cookie */ //目标Service组件的地址
unsigned int code; /* transaction command */ //双方约定好的通信代码
/* General information about the transaction. */
unsigned int flags; //描述进程间通信行为的特征
pid_t sender_pid; //进程的pid
uid_t sender_euid; //
size_t data_size; /* 通信数据的大小 */
size_t offsets_size; /* 偏移数组的大小 */
union {
struct {
/* transaction data */
const void *buffer; //指向数据缓冲区,真正保存通信数据的 大小由data_size决定
/* offsets from buffer to flat_binder_object structs */
const void *offsets; //保存每一个binder对象的位置,分别指向偏平结构的首地址
} ptr;//数据量大的时候
uint8_t buf[8]; //数据量小的时候
} data; //数据缓冲区是 来传输数据
};
上面的flags取值如下
enum transaction_flags {
TF_ONE_WAY = 0x01, /* 1表示异步的进程间通信过程 */
TF_ROOT_OBJECT = 0x04, /* contents are the component's root object */
TF_STATUS_CODE = 0x08, /* 1表示data数据缓冲区的内容是一个4字节的状态码 */
TF_ACCEPT_FDS = 0x10, /* 0表示源进程不允许结果护具中含有文件描述符 */
};
数据缓冲区中每一个Binder对象都使用一个flat_binder_object来描述
struct flat_binder_object {
/* 8 bytes for large_flat_header. */
unsigned long type; //区分是Binder实体对象还是引用对象,亦或是文件描述符
unsigned long flags; //只有描述的是Binder实体,它才有意义
/* 8 bytes of data. */
union {
void *binder; /* local Binder实体对象 指向一个内部弱引用对象的地址 */
signed long handle; /* remote Binder引用对象的句柄值 */
};
/* extra data associated with local object */
void *cookie; //指向该Service组件的地址
};
扁平结构中的type的取值如下
#define B_PACK_CHARS(c1, c2, c3, c4) \
((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
#define B_TYPE_LARGE 0x85
enum {
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引用对象
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),//弱类型引用
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),//
};