相关内容
Binder是个非常大的主题,和Binder相关的内容包括:
- Binder是一个C-S架构实现,BnInterface、BBinder代表本地Binder对象,BpInterface、BpBinder代表远端Binder对象代理
- Binder之间的通信传递的数据是经过Parcel包装的
- Binder之间的通信是由binder driver支持的
- Binder对象不是简单的纯数据对象,需要对Binder对象进行跨进程的引用技术
- Binder驱动向Linux Kernel注册了一个新的misc设备驱动
搜集到的资料
android-binder-ipc.pdf
Linux Kernel Mailing List的讨论:https://lkml.org/lkml/2009/6/25/3
https://lwn.net/Articles/466304/
https://source.android.com/devices/architecture/hidl/binder-ipc
Binder,作为一个横跨用户层和内核层的ipc机制,被批评做了太多的事情,和DBus的目标雷同且缺少文档。它的实现里使用了很多一般来说不应该在文件驱动力使用的能力,如进程管理和内存管理。但是另一方面,它也的确实现了非常强大的ipc能力,填补了Linux ipc机制中的能力空缺。
系统Service和应用Service
系统服务是注册到ServiceManager中的,任何一个进程都可以根据系统服务的名称向ServiceManager查询到系统服务的Binder,从而能够通过Binder调用系统服务。
App通过Manifest导出的服务则没有被注册到ServiceManager,当一个Client App需要使用某个App注册的服务时,需要请求ActivityManagerService(以下简称AMS),AMS负责记录该请求,然后启动Service所属进程并拿到Service返回的Binder,用Binder回调请求服务启动时传过来的callback对象(这个callback对象本身也是一个Binder对象,因此才能够在进程间传递),将Service创建的Binder提供给Client。
Binder驱动和Android Framework
阅读C代码是一件比较困难的事情,不同于C++或者Java代码的面向对象风格写法,按照对象划分属性和方法定义,C代码往往是以模块来划分的,一个模块由一个或多个源文件组成,里面定义的所有结构体、常量、宏、函数、变量共同组成了一个大的内聚的整体。
要想理解C代码,要按照程序=数据结构+算法的思路对代码进行梳理,首先要梳理清除数据结构的含义及数据结构间的关系,中间如果有任何疑问,再带着问题去梳理具体修改和维护这些数据结构的算法代码。最后的目的,是梳理清楚数据结构,及修改这些数据结构的算法。
驱动实现文件
暴露给用户空间的头文件:https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/android
内核内部驱动实现:https://elixir.bootlin.com/linux/latest/source/drivers/android
用户空间的头文件定义了用户空间和binder驱动沟通时所用到的命令和数据结构,内核内部的实现文件里则定义了驱动内维护的数据结构。
下面主要看下驱动内部定义的数据结构:
结构体定义:
- struct binder_work - 代表worklist队列中的一项任务
- struct list_head entry;
- enum type
- BIDNER_WORK_TRANSACTION
- BINDER_WORK_TRANSACTION_COMPLETE
- BINDER_WORK_RETURN_ERROR
- BINDER_WORK_NODE - binder node最近发生过变化,且binder_work是一个嵌入在struct binder_node中的结构,可以通过container_of取出binder_node检查node引用计数;
- BINDER_WORK_DEAD_BINDER
- BINDER_WORK_DEAD_BINDER_AND_CLEAR
- BINDER_WORK_CLEAR_DEATH_NOTIFICATION
- struct binder_error
- struct binder_node - 代表一个binder对象
- int debug_id
- spinlock_t lock
- struct binder_work work - type为BINDER_WORK_NODE的binder_work对象,当binder_node引用计数变化时需要将work添加到任务队列中;
- union
- struct rb_node rb_node - 存活的binder索引红黑树索引节点,红黑树根为struct binder_proc
- struct hlist_node dead_node - 死亡的binder列表索引,表头为()
- struct binder_proc *proc - 拥有Binder对象的进程
- struct hlist_head refs - 所有引用了这个binder的哈希表头,索引为struct binder_ref->node_entry
- int internal_strong_refs - 反映binder被其他进程持有情况的引用计数
- int local_weak_refs - binder在本地进程的弱引用计数
- int local_strong_refs - binder在本地进程的强引用计数
- int tmp_refs - 内核临时引用计数
- binder_uintptr_t ptr - node的用户空间数据指针
- binder_uintptr_t cookie - node的用户空间cookie
- u8 has_strong_ref - 是否通知过用户空间为node增加强引用计数
- u8 pending_strong_ref - 用户空间是否已经完成增加强引用计数
- u8 has_weak_ref - 是否通知过用户空间为node增加弱引用计数
- u8 pending_weak_ref - 用户空间是否已经完成增加弱引用计数
- u8 accept_fds - 是否允许传输fd
- u8 txn_security_ctx - 需要sender的security context
- u8 min_priority - 最小调度优先级
- bool has_async_transaction - 是否有正在进行的async transaction
- struct list_head async_todo - async work列表表头
- struct binder_ref_death - 被嵌入到struct bidner_ref中,存储binder死亡通知必要的信息
- struct binder_ref_data - 被嵌入到struct binder_ref中,存储binder_ref引用句柄和引用计数
- struct binder_ref - 用于追踪其他进程对binder对象的引用情况
- struct binder_ref_data data - 内嵌的struct binder_ref_data
- struct rb_node rb_node_desc - 以struct binder_proc中refs_by_desc为根的红黑树索引,以binder_ref_data->desc值排序
- struct rb_node rb_node_node - 以struct binder_proc中refs_by_node为根的红黑树索引,以binder_node地址排序
- struct hlist_node node_entry - 以struct binder_node->refs为表头的表索引
- struct binder_proc *proc - 该binder_ref所属进程
- struct binder_node *node - 该binder_ref引用的binder对象
- struct binder_ref_death *death
- struct binder_proc - binder进程信息
- struct hlist_node proc_node - 全局的binder_procs列表索引
- struct rb_root threads - 进程内所有binder_thread组成一棵红黑树
- struct rb_root nodes - 进程拥有的binder对象组成一棵红黑树,以struct binder_node->ptr排序
- struct rb_root refs_by_desc - 本进程持有的其他进程binder对象引用
- struct rb_root refs_by_node - 本进程持有的其他进程binder对象引用
- struct list_head waiting_threads - 正在进程内等待工作的binder_thread
- int pid - 进程的group_leader进程
- struct task_struct tsk - 进程的group_leader进程的task_struct(task_struct是内核的进程描述符)
- struct hlist_node deferred_work_node - 全局列表binder_deferred_list中的索引
- int deferred_work - 要执行的延迟任务的bitmap,具体bit位定义在enum binder_deferred_state
- bool is_dead - 进程已经死亡,但还有正在进行的transaction,等transaction结束后清理
- struct list_head todo - 进程work列表
- struct bidner_stats stats
- struct list_head delivered_death
- int max_threads
- int requested_threads
- int requested_threads_started
- int max_threads
- 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
- struct dentry binderfs_entry
- struct binder_thread - binder线程信息
- struct binder_proc *proc
- struct rb_node rb_node
- struct list_head waiting_thread_node
- int pid
- int looper
- bool looper_need_return
- struct binder_transaction *transaction
- struct list_head todo
- bool process_todo - 是否处理binder_thread上的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
- struct binder_txn_fd_fixup - 存储一次binder transaction过程中需要修复的fd信息,由struct binder_transaction->fd_fixups索引
- struct binder_transaction - 存储一次binder transaction过程中需要的信息
- int debug_id
- struct binder_work work
- 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
- struct binder_buffer *buffer
- unsigned int code
- unsigned int flags
- long priority
- long saved_priority
- kuid_t sender_euid
- struct list_head fd_fixups
- binder_uintptr_t security_ctx
- spinlock_t lock
- struct binder_object
全局变量定义:
- binder_deferred_list: HList
- binder_procs: HList
- binder_devices: HList
- binder_dead_nodes: HList
用户空间头文件
用户空间中定义了和binder驱动交互时用到的数据结构以及命令常量,这些定义在binder驱动中同样被用到,binder驱动实现文件也会include用户空间头文件。
结构体定义:
- struct binderfs_device
- binder通信传输单元数据结构定义:
- struct binder_object_header
- struct flat_binder_object
- struct binder_fd_object
- struct binder_buffer_object
- struct binder_fd_array_object
- struct binder_write_read
- struct binder_transaction_data
常量定义:
- BINDERTYPE:
- BINDER_TYPE_BINDER
- BINDER_TYPE_WEAK_BINDER
- BINDER_TYPE_HANDLE
- BINDER_TYPE_WEAK_HANDLE
- BINDER_TYPE_FD
- BINDER_TYPE_FDA
- BINDER_TYPE_PTR
- ioctl命令定义:
- IOWR
- BINDER_WRITE_READ
- 这是user space和kernel通信的核心命令,后面跟着一系列user space传给kernel的BC_XXX命令
- BINDER_VERSION
- BINDER_GET_NODE_DEBUG_INFO
- BINDER_GET_NODE_INFO_FOR_REF
- BINDER_WRITE_READ
- IOW
- BINDER_SET_IDLE_TIMEOUT
- BINDER_SET_MAX_THREADS
- BINDER_SET_IDLE_PRIORITY
- BINDER_SET_CONTEXT_MGR
- BINDER_THREAD_EXIT
- BINDER_SET_CONTEXT_MGR_EXT
- IOWR
- enum binder_driver_return_protocol
- 定义了一堆形如BR_XXX的命令,是binder驱动给用户空间发送的命令定义
- enum binder_driver_command_protocol
- 定义了一堆形如BC_XXX的命令,是用户空间给binder驱动发送的命令定义
binder驱动 - 方法调用链路
- IPCTransactionState::transact
- IPCTransactionState::waitForResponse
- IPCTransactionState::talkWithDriver
——进入kernel——
- binder_ioctl
- binder_ioctl_write_read
- binder_thread_write
- binder_thread_read
上面的链路中:
binder_thread_write负责处理BC_XXX命令,即处理写入binder驱动的命令,这一阶段发生的事情包括:
- 若命令是BC_TRANSACTION且需要reply,则创建binder_transaction,加入到binder_thread.transaction_stack栈中,并将binder_transaction.work加入到target process的work队列,随后target process中的某个线程处理这个work时,会将binder_transaction加入到自己的transaction_stack中;
- 读取用户空间传入的参数,将其中的binder对象进行特殊处理,在方法调用完成之前,为binder对象的引用计数增加1,在方法调用完成后,再将binder对象的引用计数减少1;
- 根据BC命令,为当前进程或者其他进程的binder_proc以及binder_thread安排任务;
- 例如如果命令是BC_TRANSACTION,则会为当前进程安排一个deferred BINDER_WORK_TRANSACTION_COMPLETE任务,给target process安排一个BINDER_WORK_TRANSACTION任务;
- 如果传入的parcel包含binder对象,会为当前进程安排一个deferred BINDER_WORK_NODE任务;
binder_thread_read负责处理当前线程binder_thread以及当前进程binder_proc上挂载的任务,但并不是所有线程都会关注进程上挂载的任务,binder_available_for_proc_work_ilocked方法用于确定当前线程是否应该仅处理本线程上挂载的任务,忽略进程上挂载的任务,只有当前线程transaction_stack为空,且没有待处理的线程任务时,才会尝试处理进程任务。该机制确保正在发生方法调用,等待结果的线程只会在方法调用完成后唤醒,。
在处理任务时,会生成要返回给用户空间的BR_XXX命令,当所有任务处理完毕后,或者当遇到错误时,会返回到用户空间,此时用户空间处理BR命令,部分BR命令的处理逻辑还会再给binder发送后续的BC命令。
binder驱动 - 任务
binder驱动中,表示进程的binder_proc和表示线程的binder_thread,它们都定义了一个用于保存任务的队列。
其中,binder_proc上面的任务是不区分具体线程的,是任何一个加入到binder中的空闲线程都可以处理的,需要处理此类任务时,只需要随意唤醒一个线程即可,如果当前没有可用线程,则要求user space创建一个新线程加入进来;
binder_thread上面的任务则是只有这个binder_thread线程可以处理的任务;
任务是通过链表存储的,按添加的顺序被挨个取出进行处理。
在为binder_thread添加任务时,可以添加”deferred”任务,这种任务添加时不会设置binder_thread.process_todo标记,导致相关方法检查binder_thread是否有任务时,会认为没有任务,在设置了该标记以后才能检查到任务并进行处理。
相关方法:
- static bool binder_has_work_ilocked(struct binder_thread *thread, bool do_proc_work)
- 判断是否有binder_thread可以处理的任务,若do_proc_work=true,则除了检查bidner_thread.todo,还会检查binder_proc.todo,此外这里面也会检查上面提到的process_todo。
Android framework
相关源码:
Parcel内部封装了binder间通讯时传递的数据:
parcel.h: http://aosp.opersys.com/xref/android-10.0.0_r46/xref/frameworks/native/libs/binder/include/binder/Parcel.h
Parcel.cpp: http://aosp.opersys.com/xref/android-10.0.0_r46/xref/frameworks/native/libs/binder/Parcel.cpp
IPCThreadState内部封装了具体的和binder驱动通信的逻辑:
http://aosp.opersys.com/xref/android-10.0.0_r46/xref/frameworks/native/include/binder/IPCThreadState.h
http://aosp.opersys.com/xref/android-10.0.0_r46/xref/frameworks/native/libs/binder/IPCThreadState.cpp
支持local binder和remote binder
在向Parcel写入好数据后,需要使用Binder#transact方法将cmd和Parcel发送给Binder,进行ipc调用。这里如果Binder实际上是个Local Binder对象,也就是BBinder对象,是不会发生Parcel对象通过binder驱动进行序列化传输的;只有当Binder是remote Binder proxy对象时,才会调用IPCThreadState#transact,将Parcel对象通过binder驱动序列化传输给remove binder对象。
binder的跨进程引用计数
BpBinder代表其他进程的BBinder对象代理,一个BBinder对象,即使在它所处的进程内,已经没有任何其他C++或者Java对象引用它了,如果其他进程还有它的BpBinder代理对象,它也不可以被销毁。只有当所有其他进程内它的BpBinder对象都已经被销毁后,这个BBinder对象才能在它所处的进程内被销毁。这是一种跨进程的引用计数技术。
BpBinder内部保存着一个binder驱动为当前进程生成的另一个进程中的BBinder对象的句柄,binder驱动内部记录着BBinder对象所在的进程以及BBInder对象在进程内的内存位置。
binder驱动其实支持弱引用的概念,但是,根据Hackborn在mailing list中提到的说法,由于Java领域不存在和binder驱动支持的弱引用相似的概念,因此user space代码并没有使用该能力。