本文主要对Binder驱动下的数据结构和设备的初始化过程进行简要的分析,以理清数据结构对象之间的关系。基于Linux-4.15node
Binider进程的通讯机制以下图所示:算法
Client、Service以及ServiceManager都运行在用户空间中,而Binder Driver运行在内核空间中,其中ServiceManager和Binder驱动由系统负责提供,Clien和Services由应用程序实现。而Binder驱动与Service、Client、Servicemanager的交互是经过open、mmap、ioctl等接口实现通讯的。为了更好地理解上层的Binder通讯,所以有必要对下层驱动进行简单的分析。segmentfault
binder_work
处理的工做项struct binder_work{ //用于嵌入宿主,包括进程,线程 struct list_head entry; //描述工做项类型 enum { BINDER_WORK_TRANSACTION = 1, BINDER_WORK_TRANSACTION_COMPLETE, BINDER_WORK_RETURN_ERROR, BINDER_WORK_NODE, //下面三项为死亡通知类型 BINDER_WORK_DEAD_BINDER, BINDER_WORK_DEAD_BINDER_AND_CLEAR, BINDER_WORK_CLEAR_DEATH_NOTIFICATION, } type; }
binder_work
主要是为了描述待处理的工做项,因为工做项有可能属于进程或线程,利用entry能够将该结构体嵌入到宿主的结构当中,而list_head
的数据结构是双向链表的节点,而list_head
自己是不带有数据阈的,其经常使用的用法即嵌入到其余数据结构如binder_work
造成链表,双向链表的结构如图所示:有关kernel双向链表的实现,可参考Kernel list数据结构学习数组
type描述的是工做项的类型,能够分为cookie
BINDER_WORK_TRANSACTION
BINDER_WORK_TRANSACTION_COMPLETE
BINDER_WORK_TRANSACTION_NODE
BINDER_WORK_DEAD_BINDER
BINDER_WORK_DEAD_BINDER_AND_CLEAR
BINDER_WORK_CLEAR_DEATH_NOTIFICATION
binder_node
用于描述Binder实体对象,每一个Service组件都在Binder驱动中对应一个binder_node
实体,以描述其在内核的状态。其数据结构以下:数据结构
struct binder_node { int debug_id; spinlock_t lock; struct binder_work work;//Binder实体须要处理的工做项 union { struct rb_node rb_node; //红黑树节点,宿主进程有一棵红黑树维护全部Binder实体 struct hlist_node dead_node;//hash节点,用于保存在全局hash列表中 }; struct binder_proc *proc; //用于指向Binder实体对象宿主进程 struct hlist_head refs; //全部Client引用了该Binder实体都会保存在该hash变量中 int internal_strong_refs;//强引用计数 int local_weak_refs; //弱引用计数 int local_strong_refs; //强引用计数 int tmp_refs; binder_uintptr_t ptr;//指向Service组件地址 binder_uintptr_t cookie;//指向Service组件内部的引用计数 struct { /* * bitfield elements protected by * proc inner_lock */ u8 has_strong_ref:1; u8 pending_strong_ref:1; u8 has_weak_ref:1; u8 pending_weak_ref:1; }; struct { /* * invariant after initialization */ u8 accept_fds:1; u8 min_priority; }; bool has_async_transaction;//Binder实体是否正在处理一个异步事务 struct list_head async_todo;//用于保存异步事务队列 };
debug_id
用于在debug中标志Binder实体的ID身份binder_node
包含一个binder工做项Binder_node
实体,而binder_node
实体中将会有一个rb_node
(红黑树)节点结构,做为宿主红黑树中的一员。在这里能够稍微补充下关于红黑树的基本特性:异步
红黑树的优点在于可以以O(log2 n)的时间复杂度进行搜索、插入、删除操做。此外,因为它的设计,任何不平衡都会在三次旋转以内解决。固然,还有一些更好的,但实现起来更复杂的数据结构可以作到一步旋转以内达到平衡,但红黑树可以给咱们一个比较“便宜”的解决方案。红黑树的算法时间复杂度和AVL相同,但统计性能比AVL树更高。
binder_ref
来进行描述(后面会介绍),所以将全部引用了该binder实体对象的全部引用都放在了哈希表当中,即hlist_head refs
。internal_strong_refs,local_strong_refs
均描述binder实体对象的强引用数,local_weak_refs
描述了其弱引用数。has_strong_ref,has_weak_ref,pending_strong_ref,pending_weak_ref
则是相关的标志位。其中又有以下规则:async
has_strong_ref,has_weak_ref
均置1。has_async_transaction
描述是否正在处理异步事务。通常而言Binder驱动程序都将一个事务保存在线程的todo队列中,代表线程将要处理该事务。而每一个事务和一个binder实体相关联,表示该事务的处理目标对象。而经过binder_node
是能够找到对应的Service组件,那么与该Binder对应的Service组件将会处理该事务。注:异步的概念就是单向的进程间通讯请求,即不须要等待应答的进程间通讯请求。同步则须要等待应答才会作下一步的动做。因为不须要应答,Binder驱动认为异步事务优先级比同步优先级要低。
min_priority
表示Binder实体在处理来自Client的请求时,处理线程(Server进程中的线程)所应该具有的最小线程优先级accept_fds
描述Binder实体对象是否能够接收包含有文件描述符的进程通讯数据。为1时能够接收。binder_ref_death
死亡接收通知struct binder_ref_death { /** * @work: worklist element for death notifications * (protected by inner_lock of the proc that * this ref belongs to) */ struct binder_work work; binder_uintptr_t cookie; };
binder_ref_death
用来描述Binder的死亡接收通知。正常而言,当Service组件被其余Client组件所引用时,是不能被销毁的,可是存在Service之外崩溃的情景,此时,Client可以经过Binder驱动得知Service Dead的消息。所以client会将一个可以接收死亡通知的地址注册在Binder驱动程序中。ide
Client接收Binder驱动死亡通知情景:函数
binder_ref_death
结构体,并将这些结构体添加到Client进程的todo队列中等待处理,死亡通知类型设置为BINDER_WORK_DEAD_BINDER
BINDER_WORK_DEAD_BINDER
。Client注销Binder驱动死亡通知情景
binder_ref_death
结构体,并将类型work修改为为BINDER_WORK_CLEAR_DEATH_NOTIFICATION
,而后将该结构体封装成一个工做项添加到client进程的todo队列等待处理。BINDER_WORK_DEAD_BINDER_AND_CLEAR
。与Service组件在Binder驱动中存在一个Binder实体相似,Client在Binder驱动中也对应一个Binder引用对象以描述其在内核的状态。一样也是使用强弱计数来控制生命周期。
binder_ref_data
将debug_id,desc,strong,weak
等属性封装起来。
struct binder_ref_data { int debug_id; uint32_t desc; int strong; int weak; };
debug_id
标志Binder引用对象,帮助Binder驱动程序进行调试。struct binder_ref { /* Lookups needed: */ /* node + proc => ref (transaction) */ /* desc + proc => ref (transaction, inc/dec ref) */ /* node => refs + procs (proc exit) */ struct binder_ref_data data; struct rb_node rb_node_desc;//以desc为红黑树节点 struct rb_node rb_node_node;//以binder实体为红黑树节点 struct hlist_node node_entry;//哈希节点,放置在Binder实体哈希列表中 struct binder_proc *proc;//binder引用对象的宿主 struct binder_node *node;//指向binder实体 struct binder_ref_death *death; };
binder_proc
为结构的宿主中(进程或者线程)会有两棵红黑树(事实上宿主有多棵红黑树进行管理数据结构)保存Binder引用对象,而rb_node_desc
和rb_node_node
分别是红黑树中的节点,分别是以句柄值(desc)和对应的Binder实体对象的地址来做为保存的关键字。binder_node *node
是引用指向的Binder实体对象。binder_ref_death
结构体保存在death中。binder_buffer
内核缓冲区binder_buffer
用来描述内核缓冲区,它是用于进程通讯中传输数据的。每一个参与Binder进程间通讯的进程在Binder驱动进程中都有一个内核缓冲区列表,用于保存为进程分配的内核缓冲区,其中binder_buffer
中的entry就是链表的节点。
struct binder_buffer { struct list_head entry; /* free and allocated entries by address */ struct rb_node rb_node; /* free entry by size or allocated entry */ /* by address */ unsigned free:1; //判断该内核缓冲区是由空闲内核缓冲区红黑树仍是正在使用的空闲内核缓冲区红黑树 unsigned allow_user_free:1; //置1时,Service组件会请求Binder驱动程序释放内核缓冲区 unsigned async_transaction:1; //关联的事务是否为异步 unsigned free_in_progress:1;//判断是否正在释放内核缓冲区 unsigned debug_id:28; struct binder_transaction *transaction; struct binder_node *target_node;//内核缓冲区正在使用的binde实体对象 size_t data_size; size_t offsets_size;//数据缓冲区的偏移量,偏移数组保存Binde对象 size_t extra_buffers_size; void *data;//指向大小可变的数据缓冲区 };
rb_node
就是在指红黑树的节点,而区别是那棵树是经过变量free来区别,假如free为1,那么是空闲内核缓冲区。
注:Binder驱动中常常看到unsigned 变量:n这样的写法,那是由于C中并无bool类型,C Programmer会利用unsigned(这里的unsigned实际是unsigned int)并指定须要用的bits数目来实现,但令我疑惑的是虽然只指定使用其中的n bits,可是实际上仍是以unsigned int的大小来存储数据的,但当struct程序是以
__attribute((packed))
来规定数据规格来编译时,那么其数据结构所占的大小就为1
binder_transaction
为事务结构体,用以描述该内核缓冲区正在处理哪个事务,值得留意的是binder_transaction
中也存在者指向内核缓冲区的指针,即它们的关系是双向的。前面说到过事务与对应的Binder对象是相关联的,这是由于binder_buffer
中存在着指向binder实体的指针,那么transaction经过找到内核缓冲区便可找到相关联的binder实体。从而将内核缓冲区内容交给Service处理。allow_user_free
为1时,则Service处理完事务后将会请求Binder驱动程序释放内核缓冲区。data_size
、offset_size
分别用来描述数据缓冲区,其中data指向的是实际的数据内容。数据缓冲区保存的数据分为两种类型,一种是普通数据,另外的是Binder对象,Binder驱动不关心数据的内容,可是要知道其中的Binder对象,从而根据它们来维护内核中实体对象以及引用对象的生命周期。offset_size
中。struct binder_proc { struct hlist_node proc_node; struct rb_root threads;//Binder线程池根节点,以线程ID做为关键字 struct rb_root nodes;//Binder实体对象红黑树根节点 struct rb_root refs_by_desc;//binder引用对象红黑树,以desc为关键字 struct rb_root refs_by_node;//binder引用对象红黑树,以binder_node地址为关键字 struct list_head waiting_threads; int pid; struct task_struct *tsk; struct files_struct *files; struct mutex files_lock; struct hlist_node deferred_work_node;//保存进程能够延迟执行的工做项,为哈希列表 int deferred_work; bool is_dead; struct list_head todo;//待处理工做项,当进程接收到进程间通讯请求时,Binder驱动封装工做项加入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 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; };
binder_proc
用来描绘使用Binder通讯机制进行通讯的进程,当进程调用函数open时打开/dev/binder时,Binder驱动将会为其建立一个binder_proc
结构体,并将其保存在一个全局的哈希表当中,其中proc_node
即为该进程在哈希表的节点。struct binder_alloc { struct mutex mutex; struct vm_area_struct *vma;//内核缓冲区的用户空间地址 struct mm_struct *vma_vm_mm; 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 binder_lru_page *pages; size_t buffer_size;//binde驱动为进程分配的内核缓冲区大小 uint32_t buffer_free; int pid; };
refs_by_desc
,refs_by_node
,管理内核缓冲区的free_buffers
,allocated_buffers
,管理Binder实体对象的nodes。user_buffer_offset
保存,所以只要知道其中一个地址即可以经过该相差值得出另一个空间的地址。另外因为用户空间的地址高于内核空间地址,因此计算user_buffer_offset
的差值时,都是使用用户空间地址减去内核空间地址。进程收到进程间通讯的请求时,将会把请求封装成工做项加入到进程的待处理工做项todo队列中。线程池中的wait的等待队列维护着空闲的Binder线程,当线程的宿主进程增长了新工做项后,Binder驱动就会唤醒这些线程以便它们可以处理新工做项。
deffered_work_node
正是哈希表的节点,并使用deffered_work
维护延迟工做项的类型。struct binder_thread { struct binder_proc *proc; struct rb_node rb_node; struct list_head waiting_thread_node; int pid; int looper; /* only modified by this thread */ bool looper_need_return; /* can be written by other thread */ struct binder_transaction *transaction_stack; struct list_head todo; struct binder_error return_error; struct binder_error reply_error; wait_queue_head_t wait; struct binder_stats stats; atomic_t tmp_ref; bool is_dead; };
enum { BINDER_LOOPER_STATE_REGISTERED = 0x01, BINDER_LOOPER_STATE_ENTERED = 0x02, BINDER_LOOPER_STATE_EXITED = 0x04, BINDER_LOOPER_STATE_INVALID = 0x08, BINDER_LOOPER_STATE_WAITING = 0x10, BINDER_LOOPER_STATE_POLL = 0x20, };
参考文献: 《Android系统源代码情景分析》