相关内容

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
    • 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
  • enum binder_driver_return_protocol
    • 定义了一堆形如BR_XXX的命令,是binder驱动给用户空间发送的命令定义
  • enum binder_driver_command_protocol
    • 定义了一堆形如BC_XXX的命令,是用户空间给binder驱动发送的命令定义

binder驱动 - 方法调用链路

  1. IPCTransactionState::transact
  2. IPCTransactionState::waitForResponse
  3. IPCTransactionState::talkWithDriver

——进入kernel——

  1. binder_ioctl
  2. binder_ioctl_write_read
  3. binder_thread_write
  4. 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代码并没有使用该能力。