本文分析基于Android14(aosp分支android-14.0.0_r28)(内核分支common-android14-6.1)
前言
binder_ioctl是Binder驱动中最核心的函数,没有之一,它负责通信两端IPC数据的收发以及Binder参数的设置。在IPC通信过程中,进程调用此函数执行相关命令,达成传输的目的。因此,我们单独用一篇文章的篇幅来分析它。本文内容较多,大致分为基础结构体 、C端流程 和S端流程三部分,建议分段阅读。
函数签名如下: static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
- filp:文件描述符指针,即binder设备文件/dev/binder
- cmd:ioctl命令
- arg:数据指针,指向命令对应的参数,该指针是用户空间地址
以下是我们之前遇到过的一些命令(未列出的用到时再查)
ioctl命令 | 描述 |
---|---|
BINDER_WRITE_READ | 读写IPC数据 |
BINDER_SET_MAX_THREADS | 设置Binder线程池线程最大个数 |
BINDER_SET_CONTEXT_MGR_EXT | 设置Binder管理者 |
BINDER_VERSION | 获取Binder版本信息 |
BINDER_ENABLE_ONEWAY_SPAM_DETECTION | 开关oneway滥用检测 |
基础结构体
在binder_ioctl调用过程中,会涉及到一些关键的结构体,内容较多,先大致过一下,在分析代码时对照着看效果更好,同时可以对照后面的关系图,理清这些结构体之间的关系。
binder_proc(Binder进程)
c
// common/drivers/android/binder_internal.h
struct binder_proc {
// 哈希链表节点,该节点链接到全局进程哈希表中(binder_procs)
struct hlist_node proc_node;
// 红黑树根节点,管理该进程的所有Binder线程(binder_thread),按线程PID排序
struct rb_root threads;
// 红黑树根节点,管理该进程发布的所有Binder节点(binder_node),按地址排序
struct rb_root nodes;
// 红黑树根节点,按句柄desc(uint32_t)管理Binder引用(binder_ref),以desc排序
struct rb_root refs_by_desc;
// 红黑树根节点,按Binder节点指针(binder_node *)管理Binder引用,以Binder节点地址排序
struct rb_root refs_by_node;
// 双向链表头,存储等待事务完成的线程,当线程因事务未完成而阻塞时,会被加入此列表
struct list_head waiting_threads;
// 进程PID,唯一标识该进程
int pid;
// 指向进程的任务结构体,提供内核级任务信息的查询
struct task_struct *tsk;
// 进程的安全凭证,记录进程的UID、GID等信息,用于Binder的权限检查
const struct cred *cred;
// 哈希链表节点,用于将该进程链接到延迟工作队列,管理延迟任务,比如:清理死亡进程的资源
struct hlist_node deferred_work_node;
// 延迟工作标志,指示是否有延迟工作(如资源清理)需要处理
int deferred_work;
// 记录该进程中未完成的事务数量,用于事务状态跟踪
int outstanding_txns;
// 标志位,指示该进程是否已经死亡,死亡进程需要触发资源清理和死亡通知
bool is_dead;
// 标志位,指示该进程是否被冻结,在系统挂起等场景下,Binder需要暂停相关事务处理
bool is_frozen;
// 标志位,指示是否有同步接收的事务,管理同步事务状态
bool sync_recv;
// 标志位,指示是否有异步接收的事务,管理异步事务状态
bool async_recv;
// 等待队列,线程可以通过此队列等待进程冻结或解冻完成
wait_queue_head_t freeze_wait;
// 双向链表头,存储该进程中待处理的事务或命令
struct list_head todo;
// 统计信息结构体,记录 Binder 活动数据,用于调试和性能分析
struct binder_stats stats;
// 双向链表头,存储已传递的死亡通知,管理服务端死亡通知
struct list_head delivered_death;
// 该进程Binder线程池中线程的最大数目
int max_threads;
// 该进程请求驱动创建线程的数目,当该线程启动成功,此数值-1
int requested_threads;
// 该进程请求驱动已创建成功的线程数目,当该线程启动成功,此数值+1
int requested_threads_started;
// 临时引用计数,用于调试或内部资源管理
int tmp_ref;
// 该进程的默认优先级,设置Binder线程调度优先级
struct binder_priority default_priority;
// debugfs条目,用于调试时查看Binder状态
struct dentry *debugfs_entry;
// Binder数据缓冲区描述符,管理mmap申请到的整个缓冲区
struct binder_alloc alloc;
// Binder上下文,包含全局信息如设备文件描述符
struct binder_context *context;
// 内部自旋锁,保护Binder内部数据结构的并发访问
spinlock_t inner_lock;
// 外部自旋锁,保护Binder外部数据结构
spinlock_t outer_lock;
// binderfs 条目,支持用户空间挂载和管理Binder设备
struct dentry *binderfs_entry;
// 标志位,指示是否启用单向调用(Oneway)滥用检测
bool oneway_spam_detection_enabled;
};
binder_thread(Binder线程)
c
// common/drivers/android/binder_internal.h
struct binder_thread {
// 指向该线程所属的binder_proc结构体
// 提供了访问进程级资源(如文件描述符、线程集合等)的途径
struct binder_proc *proc;
// 红黑树节点,用于将该线程插入到proc->threads红黑树中,按线程ID排序
struct rb_node rb_node;
// 双向链表节点,当线程完成自身todo队列里的工作项时,通过此节点加入到proc->waiting_threads列表中
struct list_head waiting_thread_node;
// 本线程ID(PID),也是用户空间中的TID
int pid;
// 标识线程的looper状态,比如:是否在循环等待事务,仅由本线程修改
int looper;
// 标志位,用于通知该线程退出等待状态并返回用户空间处理,可由其他线程写入
bool looper_need_return;
// 指向该线程当前处理的事务栈顶
// Binder支持事务嵌套,此字段管理事务的调用栈,确保正确追踪事务层次
struct binder_transaction *transaction_stack;
// 双向链表头,存储该线程待处理的事务或命令(binder_work节点)
// 线程从此列表中获取任务并依次处理,是线程工作队列的核心
struct list_head todo;
// 标志位,指示todo队列中是否有需要马上处理的工作项
bool process_todo;
// 在事务处理失败时记录的错误码,用于反馈给调用方
struct binder_error return_error;
// 在处理事务回复时记录的错误码,用于通知服务端
struct binder_error reply_error;
// 扩展错误信息,提供更详细的错误状态描述,便于调试和错误分析
struct binder_extended_error ee;
// 等待队列,线程在等待事务或资源时,通过此队列进入睡眠状态,等待被唤醒
// 队列中的节点为struct wait_queue_entry
wait_queue_head_t wait;
// 统计信息结构体,记录该线程的Binder活动数据(如事务次数、错误数)
struct binder_stats stats;
// 临时引用计数
atomic_t tmp_ref;
// 标志位,标记该线程是否死亡,以便触发资源清理操作
bool is_dead;
// 指向本线程的内核描述符,提供对本线程内核级信息(如调度、状态)的访问
struct task_struct *task;
// 自旋锁,用于保护线程优先级相关的并发访问
spinlock_t prio_lock;
// 下一个事务的优先级,影响线程的调度优先级,确保高优先级事务优先执行
struct binder_priority prio_next;
// 指示线程优先级是否被调整,用于管理线程的调度策略。
enum binder_prio_state prio_state;
};
binder_node(Binder接口)
c
// common/drivers/android/binder_internal.h
// 代表一个Binder接口(Server端)
struct binder_node {
// 调试ID,用于在日志和调试中唯一标识该binder_node
int debug_id;
// 自旋锁,用于保护该binder_node的并发访问
spinlock_t lock;
// 工作项,表示该节点需要处理的工作任务
struct binder_work work;
union {
// 红黑树节点,用于将该binder_node插入到proc->nodes红黑树中,按地址排序
struct rb_node rb_node;
// 哈希链表节点,用于在节点失效或死亡时加入到全局死节点列表,以便清理
struct hlist_node dead_node;
};
// 指向该binder_node所属的binder_proc结构体,标识该节点所在的进程
struct binder_proc *proc;
// 哈希链表头,存储所有引用该binder_node的binder_ref结构体
struct hlist_head refs;
// 内部强引用计数,由Binder驱动内部维护,用于确保驱动自身对节点的引用,防止资源被意外释放
int internal_strong_refs;
// 记录当前进程中对该binder_node的弱引用数量,弱引用不会阻止对象被销毁
int local_weak_refs;
// 记录当前进程中对该binder_node的强引用数量,强引用会影响对象的生命周期
int local_strong_refs;
// 临时引用计数,用于调试或暂存引用计数,通常在资源管理过程中临时使用
int tmp_refs;
// 弱引用指针,指向用户空间中Binder对象的地址
binder_uintptr_t ptr;
// 指向用户空间中Binder对象的地址,与上一条指向相同
binder_uintptr_t cookie;
struct {
/*
* 以下位字段均受proc->inner_lock锁保护
*/
// 指示当前是否存在强引用
u8 has_strong_ref:1;
// 指示是否有待处理的强引用请求
u8 pending_strong_ref:1;
// 指示当前是否存在弱引用
u8 has_weak_ref:1;
// 指示是否有待处理的弱引用请求
u8 pending_weak_ref:1;
};
struct {
/*
* 事务和调度配置结构体,控制事务处理时的调度和行为
* 初始化后保持不变
*/
// 调度策略(如 SCHED_NORMAL 或 SCHED_FIFO)
u8 sched_policy:2;
// 是否继承实时调度优先级
u8 inherit_rt:1;
// 是否允许传递文件描述符
u8 accept_fds:1;
// 事务是否使用安全上下文
u8 txn_security_ctx:1;
// 最小优先级,设置事务的最低优先级
u8 min_priority;
};
// 当前是否有待处理的异步事务
bool has_async_transaction;
// 双向链表头,存储待处理的异步事务,线程会从中提取任务执行
struct list_head async_todo;
};
binder_ref(Binder引用)
c
// common/drivers/android/binder_internal.h
// 代表一个Binder代理(Client端)
struct binder_ref {
/* Lookups needed: */
/* node + proc => ref (transaction) */
/* desc + proc => ref (transaction, inc/dec ref) */
/* node => refs + procs (proc exit) */
// 包含句柄 (desc)、强弱引用计数等核心信息
// data.desc与用户空间中BpBinder.mHandle相等
struct binder_ref_data data;
// 红黑树节点,用于将本引用插入到binder_proc->refs_by_desc红黑树中,便于通过句柄data.desc快速查找
struct rb_node rb_node_desc;
// 红黑树节点,用于将本引用插入到binder_proc->refs_by_node红黑树中,便于通过binder_node快速查找
struct rb_node rb_node_node;
// 哈希链表节点,用于将本引用加入到binder_node->refs哈希链表中
struct hlist_node node_entry;
// 指向所属进程的binder_proc,标识本引用所在的进程
struct binder_proc *proc;
// 指向本引用对应的远程服务对象binder_node
struct binder_node *node;
// 当引用的binder_node死亡时,Binder驱动通过此字段通知引用方
struct binder_ref_death *death;
// 当引用的binder_node被冻结时 (例如系统挂起或进程冻结),Binder驱动通过此字段通知引用方
struct binder_ref_freeze *freeze;
};
c
// common/drivers/android/binder_internal.h
struct binder_ref_data {
// 调试ID
int debug_id;
// 远程Binder接口句柄,与用户空间中BpBinder.mHandle相等
uint32_t desc;
// 强引用计数
int strong;
// 弱引用计数
int weak;
};
binder_transaction(Binder事务)
c
// common/drivers/android/binder_internal.h
struct binder_transaction {
// 调试ID,用于在日志和调试中唯一标识该binder_transaction
int debug_id;
// 用于将事务链接到处理线程的todo链表中(binder_thread.todo)
// 与binder_transaction关联的binder_work的type为BINDER_WORK_TRANSACTION或BINDER_WORK_TRANSACTION_COMPLETE
struct binder_work work;
// 指向发起此事务的Binder线程,用于关联上下文和可能的回复路径
struct binder_thread *from;
// 发起事务的进程PID
pid_t from_pid;
// 发起事务的线程ID
pid_t from_tid;
// 发起方父事务指针(针对嵌套事务)
// 若当前事务是嵌套的(如一个事务处理中触发另一个事务),指向父事务
struct binder_transaction *from_parent;
// 事务的目标进程,用于关联到正确的接收方进程
struct binder_proc *to_proc;
// 事务的目标线程,若指定则直接投递到该线程的待处理队列
struct binder_thread *to_thread;
// 目标端父事务指针(针对嵌套事务)
// 在接收方处理嵌套事务时,关联其父事务上下文
struct binder_transaction *to_parent;
// 标记事务是否为同步调用(1表示需要回复,0表示单向调用)
unsigned need_reply:1;
// 指向存储实际传输数据的缓冲区,包含Parcel序列化数据及Binder对象偏移
struct binder_buffer *buffer;
// 用户定义的操作指令(比如:AIDL生成文件中的方法编号)
unsigned int code;
// 控制事务行为的标志(比如:TF_ONE_WAY表示单向调用)
unsigned int flags;
// 记录发送线程的调度优先级,用于接收方线程优先级继承
struct binder_priority priority;
// 接收方线程处理事务前保存的原优先级,处理完成后恢复
struct binder_priority saved_priority;
// 标记是否已设置优先级,防止多次修改接收方线程优先级
bool set_priority_called;
// 标记是否为嵌套事务,表示此事务是否在另一个事务的处理过程中被触发
bool is_nested;
// 发送方的有效用户ID,用于安全审核,验证发送方权限
kuid_t sender_euid;
// 事务开始时间戳,记录事务创建时间,用于性能分析和超时监控
ktime_t start_time;
// 文件描述符修复链表,存储需跨进程调整的文件描述符映射信息(如binder_fd_array_object)
struct list_head fd_fixups;
// 安全上下文指针,SELinux或其他安全模块的上下文信息,用于权限检查
binder_uintptr_t security_ctx;
// 自旋锁,保护from,to_proc,to_thread字段的并发访问
spinlock_t lock;
// 预留字段,供Android厂商扩展自定义数据(如芯片特性优化)
ANDROID_VENDOR_DATA(1);
};
binder_work(Binder工作项)
c
// common/drivers/android/binder_internal.h
struct binder_work {
// 链表节点,添加到binder_thread.todo链表中等待处理
struct list_head entry;
// 工作项类型枚举
enum binder_work_type {
// 表示与一个Binder事务关联(如跨进程调用请求或回复)
BINDER_WORK_TRANSACTION = 1,
// 事务完成通知,用于异步事务的ACK确认
BINDER_WORK_TRANSACTION_COMPLETE,
// 单向事务(oneway)滥用通知
BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT,
// 错误返回通知,当事务处理失败时,通过此类型传递错误码(如 -EFAULT)
BINDER_WORK_RETURN_ERROR,
// 节点相关工作项,用于处理与binder_node相关的任务(如节点引用计数变更、节点销毁等)
BINDER_WORK_NODE,
// 节点死亡通知,当服务端进程终止时,通知注册了死亡回调的客户端
BINDER_WORK_DEAD_BINDER,
// 死亡通知并清理资源,在发送死亡通知后,同时清除相关资源(如死亡回调记录)
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
// 清除死亡通知,当客户端主动取消死亡回调注册时使用
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
} type;
};
flat_binder_object(传输中的Binder)
c
// common/include/uapi/linux/android/binder.h
struct flat_binder_object {
// hdr.type存放对象类型,便于Binder驱动识别对象类型
// hdr.type常见取值:
// BINDER_TYPE_BINDER表示本地Binder对象,BINDER_TYPE_HANDLE表示远程Binder句柄
struct binder_object_header hdr;
// 标志位,控制对象的传输行为和特性,如优先级或是否接受文件描述符
// FLAT_BINDER_FLAG_PRIORITY:指定事务的调度优先级
// FLAT_BINDER_FLAG_ACCEPTS_FDS:指示是否允许传递文件描述符
__u32 flags;
/* 8 bytes of data. */
union {
// 指向本地Binder对象的指针,在本地进程中有效
binder_uintptr_t binder;
// 远程进程中的Binder对象句柄,用于跨进程引用
// 与binder_ref.data.desc值相等
__u32 handle;
};
// 指向用户空间中Binder对象的指针,仅在本地进程中有效
binder_uintptr_t cookie;
};
binder_transaction_data(Binder事务数据)
c
// common/include/uapi/linux/android/binder.h
// bionic/libc/kernel/uapi/linux/android/binder.h
// 内核和AOSP中都有一份binder_transaction_data的定义,二者相同
struct binder_transaction_data {
union {
__u32 handle; // 远程对象的句柄(无符号32位整型),即Server端Binder接口的编号
binder_uintptr_t ptr; // 本地对象指针,指向Binder接口对象
} target;
binder_uintptr_t cookie; // 附加数据,由调用者自己定义
// 接口方法编码(32位无符号整型),分为应用方法编码和特殊编码
// 应用方法编码:介于FIRST_CALL_TRANSACTION(0x00000001)和LAST_CALL_TRANSACTION(0x00ffffff)
// 特殊编码:大于LAST_CALL_TRANSACTION,由系统定义,具体参见IBinder.java
__u32 code;
__u32 flags; // 定义事务行为,最常见的值是FLAG_ONEWAY(异步单向请求)
pid_t sender_pid; // 发起本次事务的进程ID(PID)
uid_t sender_euid; // 发起本次事务进程的有效用户ID(EUID)
binder_size_t data_size; // 本次事务发送的数据缓冲区大小(64位无符号整型),单位字节
// binder_object偏移量数组总大小,单位字节
binder_size_t offsets_size;
union {
struct {
// 指向事务数据缓冲区的指针
binder_uintptr_t buffer;
// 指向存放binder_object偏移量数组的指针
binder_uintptr_t offsets;
} ptr;
// 若是小数据(8字节以内),则使用buf传输,不额外分配内存
__u8 buf[8];
} data;
};
binder_object是一个联合体,它可以代表flat_binder_object、binder_fd_object、binder_buffer_object、binder_fd_array_object。其中最常见的就是flat_binder_object,代表一个Binder接口在通信传输过程中的形态。
ptr.offsets和offsets_size是专门为了从数据缓冲区提取binder_object结构数据而设立的。ptr.offsets指向一个数组,这个数组的每一个元素都是基于数据缓冲区起始地址(ptr.buffer)的一个偏移量。通过偏移量,就能算出每一个binder_object结构体的起始地址,然后按照binder_object结构体的长度,完整读取出binder_object存储的数据。而offsets_size记录了数组总大小,驱动通过offsets + offsets_size就能将整个数组拷贝到内核空间。
binder_transaction_data组装好之后,会和通信码拼接,一起写入到IPCThreadState.mOut中,最终结构如下图所示:
binder_write_read(Binder数据读写信息)
c
// common/include/uapi/linux/android/binder.h
// bionic/libc/kernel/uapi/linux/android/binder.h
struct binder_write_read {
// 告诉驱动需要从write_buffer中读取多少字节的数据
binder_size_t write_size;
// 由驱动填充的值,表示实际处理了多少字节来自write_buffer的数据
binder_size_t write_consumed;
// 指向需要驱动读取的用户空间地址指针
binder_uintptr_t write_buffer;
// 提供给驱动写入的缓冲区大小(单位:字节)
binder_size_t read_size;
// 由驱动填充的值,表示实际写入了多少字节到read_buffer
binder_size_t read_consumed;
// 提供给驱动写入的缓冲区用户空间地址指针
binder_uintptr_t read_buffer;
};
binder_write_read的字段非常简单,它只关心数据在哪里,有多少,消费了多少,至于数据是什么,它并不关心。
结构体关系图
主要结构体之间的关系如下:
结构体本身是没有Server和Client之分的,图中之所以将它们用虚线框圈出来且看起来字段不一样,是因为当它们作为Server或Client角色时,所用到的字段不一样。实际上在通信过程中,同一个进程往往既要扮演Server角色,也要扮演Client角色。
通信模型
异步通信:
同步通信:
代表编号②的工作项(binder_work)是在发起方数据全部写入缓冲区后,唤醒Server端Binder线程之前,就放入了发起方的Binder线程todo队列中,但此工作项不会立即执行,它的执行时机是BR_REPLY来临时即发起方线程被再次唤醒时。由于②号工作项先入队,因此它先于⑥号工作项执行。
我们以同步通信为例,讲解整个通信过程。异步通信的过程差别不太大,若遇到有差异的地方会顺带提一下。
分析链路分为两条:
- Client发起请求和接收回复:① BC_TRANSACTION、② BR_TRANSACTION_COMPLETE、⑥ BR_REPLY
- Server接收请求和发起回复:③ BR_TRANSACTION、④ BC_REPLY、⑤ BR_TRANSACTION_COMPLETE
为什么按照这两条链路分析?因为这样是按照代码执行的逻辑一步步推进,更符合思维直觉,不需要读者在两端的线程之间跳转,阅读时的上下文切换相对少一些。
Client发起请求和接收回复
进入读写命令流程
1. 查找或创建Client本地线程
经过系统调用后,来到驱动函数binder_ioctl。
c
// common/drivers/android/binder.c
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) {
... ...
// 在当前进程的binder_proc.threads红黑树中,按照当前线程的pid查找
// 若没有,则新建binder_thread用来代表本线程
thread = binder_get_thread(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
// 根据命令做相应操作
switch (cmd) {
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
... ...
}
... ...
}
2. 解析binder_write_read
c
// common/drivers/android/binder.c
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread) {
int ret = 0;
struct binder_proc *proc = filp->private_data;
... ...
// __user宏用于标记一个指针指向的是用户空间地址
// 也就是说,当内核代码需要访问该数据时,不能直接读写,需要使用copy_from_user/copy_to_user等函数
// 实际上arg就指向binder_write_read
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
... ...
// 从用户空间拷贝binder_write_read到内核中
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
... ...
// 驱动根据用户进程写入的数据进行处理
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
// 处理异常,驱动Reply数据量清零,回传更新后的bwr到用户空间
if (ret < 0) {
bwr.read_consumed = 0;
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
... ...
out:
return ret;
}
3. 进入binder_thread_write
c
// common/drivers/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed) {
uint32_t cmd;
struct binder_context *context = proc->context;
// 用户缓冲区起始地址
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
// 当前处理位置
void __user *ptr = buffer + *consumed;
// 用户缓冲区结束地址
void __user *end = buffer + size;
// 逐条处理命令,直到缓冲区结束或线程发生错误
while (ptr < end && thread->return_error.cmd == BR_OK) {
int ret;
// 从用户空间安全读取4字节的命令码(比如BC_TRANSACTION)
// get_user是一个宏,用于从用户空间复制一个基础类型的数据到内核空间,与copy_from_user类似
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
// 指针后移
ptr += sizeof(uint32_t);
trace_binder_command(cmd);
// 更新命令统计数据
if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]);
atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]);
atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]);
}
// 根据命令,执行操作,当前我们讨论的是发送请求,因此看BC_TRANSACTION分支
switch (cmd) {
... ...
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
// 从用户空间拷贝binder_transaction_data数据到内核
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
// 指针后移
ptr += sizeof(tr);
// 调用核心事务处理函数
binder_transaction(proc, thread, &tr,
cmd == BC_REPLY, 0);
break;
}
... ...
}
// 设置已读取数据量
*consumed = ptr - buffer;
}
return 0;
}
开启事务
binder_transaction函数用于发起Binder事务,一共有800+行,主要完成以下工作:
- binder_node和binder_proc查找,即Server端进程和节点
- binder_buffer用户空间映射到物理页,即Server端进程用户空间与物理内存的映射
- 复制数据到物理页,即Client端进程用户空间数据通过内核空间复制到物理页
- 添加binder_work工作项并唤醒工作线程
代码分支和嵌套非常多,极容易迷路,因此我写了一份"路书"(Code Map),对照着看代码就比较清晰了。
1. binder_node和binder_proc查找
c
// common/drivers/android/binder.c
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size) {
//初始化一大堆变量
... ...
struct binder_proc *target_proc = NULL;
struct binder_node *target_node = NULL;
... ...
// 若是BC_REPLY命令
if (reply) {
... ...
} else { // 若不是BC_REPLY命令,则执行此分支
// target.handle等于binder_ref_data.desc,即远程Binder接口句柄
// 若target.handle不等于0,则表示当前需要通信的对端Binder接口是普通Binder
if (tr->target.handle) {
struct binder_ref *ref;
binder_proc_lock(proc);
// 通过proc->refs_by_desc红黑树,查找desc与target.handle相等的节点(binder_ref)
// 注意:proc为client进程的binder_proc
ref = binder_get_ref_olocked(proc, tr->target.handle,
true);
if (ref) {
// 增加引用计数,同时给target_node和target_proc赋值,它们就是通信的目标端
target_node = binder_get_node_refs_for_txn(
ref->node, &target_proc,
&return_error);
} else {
binder_user_error("%d:%d got transaction to invalid handle, %u\n",
proc->pid, thread->pid, tr->target.handle);
return_error = BR_FAILED_REPLY;
}
binder_proc_unlock(proc);
} else { // 若target.handle等于0,则表示当前需要跟ServiceManager通信
... ...
// 这个分支的操作和上面基本一致,只是binder_node直接从conext中得到
// 因为ServiceManager启动的时候已经将自己注册好了
target_node = context->binder_context_mgr_node;
... ...
}
// 接下来做一些安全检查,然后尝试从binder_transaction栈中找一个合适的Binder线程作为target_thread
// 属于支线流程,我们先跳过
... ...
}
... ...
}
2. binder_buffer用户空间映射到物理页
c
// common/drivers/android/binder.c
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size) {
struct binder_transaction *t;
struct binder_work *tcomplete;
... ...
// 申请一个binder_transaction和bidner_work的内存空间
t = kzalloc(sizeof(*t), GFP_KERNEL);
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
// 后续是一连串初始化binder_transaction各个字段的操作
// 其中在t->from的字段里放了一个binder_thread,这个线程将用来接收Server端的回复
... ...
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
else
t->from = NULL;
... ...
// 创建binder_buffer,然后申请一个物理页,通过vm_insert_page函数将二者关联映射
// 完成了Server端用户虚拟空间和实际物理页的映射
t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
tr->offsets_size, extra_buffers_size,
!reply && (t->flags & TF_ONE_WAY));
// 初始化t->buffer字段
... ...
}
c
// common/drivers/android/binder_alloc.c
struct binder_buffer *binder_alloc_new_buf(struct binder_alloc *alloc,
size_t data_size,
size_t offsets_size,
size_t extra_buffers_size,
int is_async) {
struct binder_buffer *buffer, *next;
size_t size;
int ret;
... ...
// 对data_size、offsets_size和extra_buffers_size进行对齐计算,并检查溢出,确保缓冲区大小合法
size = sanitized_size(data_size, offsets_size, extra_buffers_size);
... ...
// 为binder_buffer结构体分配内存
next = kzalloc(sizeof(*next), GFP_KERNEL);
... ...
binder_alloc_lock(alloc);
// 从alloc->free_buffers红黑树中找到比size稍大一点的buffer,并且将剩余大小分割出来
// 将buffer从free_buffers移除,添加到allocated_buffers中
buffer = binder_alloc_new_buf_locked(alloc, next, size, is_async);
// 初始化buffer的其他字段
... ...
binder_alloc_unlock(alloc);
// binder_install_buffer_pages函数主要完成以下2件事情
// 1. 调用alloc_page函数: 分配一个物理页面
// 2. 调用vm_insert_page函数:将分配的物理页面映射到用户空间的指定地址
ret = binder_install_buffer_pages(alloc, buffer, size);
... ...
return buffer;
}
3. 复制数据到物理页
c
// common/drivers/android/binder.c
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size) {
... ...
// 将Client用户空间的binder_object偏移量数组复制到内核空间
if (binder_alloc_copy_user_to_buffer(
&target_proc->alloc,
t->buffer,
ALIGN(tr->data_size, sizeof(void *)),
(const void __user *)
(uintptr_t)tr->data.ptr.offsets,
tr->offsets_size)) {
// 异常处理
... ...
}
... ...
// 依次取出偏移量,然后获取binder_object并做相应处理
off_start_offset = ALIGN(tr->data_size, sizeof(void *));
buffer_offset = off_start_offset;
off_end_offset = off_start_offset + tr->offsets_size;
... ...
for (buffer_offset = off_start_offset; buffer_offset < off_end_offset;
buffer_offset += sizeof(binder_size_t)) {
struct binder_object_header *hdr;
size_t object_size;
struct binder_object object;
binder_size_t object_offset;
binder_size_t copy_size;
// 将binder_object偏移量数组中的偏移量从物理页中拷贝到内核虚拟地址&object_offset中
if (binder_alloc_copy_from_buffer(&target_proc->alloc,
&object_offset,
t->buffer,
buffer_offset,
sizeof(object_offset))) {
// 异常处理
... ...
}
// 计算得出binder_object在client用户空间的地址
// 调用binder_alloc_copy_user_to_buffer拷贝到物理页中
copy_size = object_offset - user_offset;
if (copy_size && (user_offset > object_offset ||
object_offset > tr->data_size ||
binder_alloc_copy_user_to_buffer(
&target_proc->alloc,
t->buffer, user_offset,
user_buffer + user_offset,
copy_size))) {
// 异常处理
... ...
}
// 从Client用户空间拷贝binder_object数据到object中
object_size = binder_get_object(target_proc, user_buffer,
t->buffer, object_offset, &object);
... ...
// 增加偏移量,为下次循环做准备
user_offset = object_offset + object_size;
// 判断binder_object类型
hdr = &object.hdr;
switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct flat_binder_object *fp;
// 根据hdr指针,获得flat_binder_object指针(通过container_of宏)
fp = to_flat_binder_object(hdr);
// 查找或新建binder_node和binder_ref,并建立二者的关联
// 并修改flat_binder_object.hdr.type
ret = binder_translate_binder(fp, t, thread);
if (ret < 0 ||
// 将flat_binder_object存入buffer对应的物理页中
binder_alloc_copy_to_buffer(&target_proc->alloc,
t->buffer,
object_offset,
fp, sizeof(*fp))) {
// 若出错则进行异常处理
... ...
}
} break;
... ...
}
// 将除了binder_object之外的数据,从用户空间拷贝到t->buffer对应的物理页中
if (binder_alloc_copy_user_to_buffer(
&target_proc->alloc,
t->buffer, user_offset,
user_buffer + user_offset,
tr->data_size - user_offset)) {
// 若出错则进行异常处理
... ...
}
}
}
以上从用户空间复制数据到物理页的操作全部完成,其中很多指针操作以及偏移量的计算,过于细节,就不复述了。
不过其中binder_translate_binder函数值得看看,当binder_object的type为BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER时,表明binder_object是一个Binder实体,将它传输到对端使用的话,需要转化为Binder引用(引用句柄Handle)。而这个转化的步骤就是在binder_translate_binder函数中进行的。还记得吗?第9篇时我们在Parcel::unflattenBinder函数中的疑问,这里得到了完美解答。当时我们只是直接抛出了答案,阅读源码后,我们知道了整件事的来龙去脉。阅读源码的一大乐趣就是这样,不断提出疑问,不断解答疑问。从中获得的正向反馈不亚于完成某个项目功能。
c
// common/drivers/android/binder.c
static int binder_translate_binder(struct flat_binder_object *fp,
struct binder_transaction *t,
struct binder_thread *thread) {
struct binder_node *node;
struct binder_proc *proc = thread->proc;
struct binder_proc *target_proc = t->to_proc;
struct binder_ref_data rdata;
int ret = 0;
// 从当前进程的binder_proc->nodes中查找与fp->binder对应的binder_node
// 若没有则新建
node = binder_get_node(proc, fp->binder);
if (!node) {
node = binder_new_node(proc, fp);
if (!node)
return -ENOMEM;
}
... ...
// 在目标进程的target_proc.refs_by_node中查找binder_node对应的binder_ref
// 若没有则新建
ret = binder_inc_ref_for_node(target_proc, node,
fp->hdr.type == BINDER_TYPE_BINDER,
&thread->todo, &rdata);
if (ret)
goto done;
// 修改falt_binder_object的值,将其变成Binder句柄(Handle)
if (fp->hdr.type == BINDER_TYPE_BINDER)
fp->hdr.type = BINDER_TYPE_HANDLE;
else
fp->hdr.type = BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0;
fp->handle = rdata.desc;
fp->cookie = 0;
... ...
return ret;
}
4. 添加binder_work工作项并唤醒工作线程
c
// common/drivers/android/binder.c
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size) {
struct binder_transaction *t;
struct binder_work *tcomplete;
... ...
if (t->buffer->oneway_spam_suspect)
tcomplete->type = BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT;
else
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
// 将t->work的类型设置为BINDER_WORK_TRANSACTION
t->work.type = BINDER_WORK_TRANSACTION;
if (reply) { // 若是BC_REPLY
... ...
} else if (!(t->flags & TF_ONE_WAY)) { // 若是BC_TRANSACTION且不是oneway异步请求,需要BR_REPLY
binder_inner_proc_lock(proc);
// tcomplete代表BR_TRANSACTION_COMPLETE类型的工作项
// 这里将它加入到client(事务发起方)的工作线程的todo队列中,但会延迟执行
binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete);
t->need_reply = 1;
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t;
binder_inner_proc_unlock(proc);
// 将t->work工作项插入目标进程的某个todo队列中并唤醒线程
return_error = binder_proc_transaction(t,
target_proc, target_thread);
... ...
} else { // 若是BC_TRANSACTION且是oneway异步请求,不需要BR_REPLY
... ...
//将tcomplete工作项插入到client(事务发起方)工作线程的todo队列中
binder_enqueue_thread_work(thread, tcomplete);
//将t->work工作项插入目标进程的某个todo队列中并唤醒线程
return_error = binder_proc_transaction(t, target_proc, NULL);
... ...
}
... ...
return;
}
上面提到最终t->work工作项会被插入到某个todo队列中,那么到底是什么队列呢?binder_proc_transaction函数给出了答案。
c
// common/drivers/android/binder.c
static int binder_proc_transaction(struct binder_transaction *t,
struct binder_proc *proc,
struct binder_thread *thread) {
struct binder_node *node = t->buffer->target_node;
bool oneway = !!(t->flags & TF_ONE_WAY);
bool pending_async = false;
struct binder_transaction *t_outdated = NULL;
bool skip = false;
bool enqueue_task = true;
... ...
// 若是异步请求
if (oneway) {
if (node->has_async_transaction)
// 若binder_node正在处理异步事务,将当下标记为异步等待
pending_async = true;
else
// 若binder_node没在处理异步事务,将当前node标记为处理异步任务中
node->has_async_transaction = true;
}
... ...
//若没有传入指定的目标线程,且目标binder_node没在处理异步事务,且没有设置跳过标记
if (!thread && !pending_async && !skip)
// 从目标进程的proc->waiting_threads链表中取第一个线程
// 若链表为空,则返回NULL
thread = binder_select_thread_ilocked(proc);
... ..
if (thread) { // 在proc->waiting_threads链表中找到了目标线程
// 设置事务优先级
binder_transaction_priority(thread, t, node);
// 将t->work插入到目标线程的todo队列中
binder_enqueue_thread_work_ilocked(thread, &t->work);
} else if (!pending_async) { // 没有找到合适的目标线程,且binder_node没在处理异步事务
... ...
// 将t->work插入到目标进程的todo队列中
binder_enqueue_work_ilocked(&t->work, &proc->todo);
} else { // 没有找到合适的目标线程,且binder_node正在处理异步事务
... ...
// 将t->work加入到binder_node的async_todo队列中
binder_enqueue_work_ilocked(&t->work, &node->async_todo);
}
... ...
// binder_node没在处理异步事务
if (!pending_async)
// 唤醒目标线程
binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */);
... ...
return 0;
}
最后看看线程是怎么唤醒的。
c
// common/drivers/android/binder.c
static void binder_wakeup_thread_ilocked(struct binder_proc *proc,
struct binder_thread *thread,
bool sync) {
assert_spin_locked(&proc->inner_lock);
// 若thread不为空,那么通过内核的wait_queue_head_t等待队列机制唤醒工作线程
if (thread) {
trace_android_vh_binder_wakeup_ilocked(thread->task, sync, proc);
if (sync)
wake_up_interruptible_sync(&thread->wait);
else
wake_up_interruptible(&thread->wait);
return;
}
// 走到这一步,依然有可能没有找到合适的工作线程,一般为以下两种情况:
// 1. 所有工作线程都在忙着处理Binder事务,针对这种情况,只能等待线程空闲
// 2. 有些线程在epoll轮询时被阻塞在等待队列中,但他们没有被添加到waiting_threads中
// 针对这种情况,只能唤醒所有不是在处理Binder事务的阻塞线程
binder_wakeup_poll_threads_ilocked(proc, sync);
}
static void binder_wakeup_poll_threads_ilocked(struct binder_proc *proc,
bool sync) {
struct rb_node *n;
struct binder_thread *thread;
for (n = rb_first(&proc->threads); n != NULL; n = rb_next(n)) {
thread = rb_entry(n, struct binder_thread, rb_node);
if (thread->looper & BINDER_LOOPER_STATE_POLL &&
binder_available_for_proc_work_ilocked(thread)) {
... ...
if (sync)
wake_up_interruptible_sync(&thread->wait);
else
wake_up_interruptible(&thread->wait);
}
}
}
到这里,请求发起方的数据和指定已经经过驱动写入对端的内存空间中,并且驱动已唤醒对端的Binder线程,请求即将被处理。但我们先不跳到对端去,我们继续发起方的流程。
进入休眠
binder_transaction函数结束后,层层返回,又回到binder_ioctl_write_read函数中,继续执行binder_thread_read函数。
c
// common/drivers/android/binder.c
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block) {
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
int ret = 0;
bool nothing_to_do = false;
bool force_spawn = false;
int wait_for_proc_work;
if (*consumed == 0) {
// 先往bwr.read_buffer中写入通信码BR_NOOP
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
}
retry:
binder_inner_proc_lock(proc);
// 若binder_thread的事务栈为空且todo队列也为空,那么wait_for_proc_work为true
// 说明binder_thread的工作项全部完成了
wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
binder_inner_proc_unlock(proc);
// 标记thread的looper状态为waiting
thread->looper |= BINDER_LOOPER_STATE_WAITING;
... ...
// 是否以非阻塞的方式打开驱动文件
// ProcessState::open_driver函数中,打开时标记设置为O_RDWR|O_CLOEXEC,因此这里non_block为false
if (non_block) {
if (!binder_has_work(thread, wait_for_proc_work))
ret = -EAGAIN;
} else {
// 当前线程或将进入休眠,等待唤醒
ret = binder_wait_for_work(thread, wait_for_proc_work);
}
... ...
}
我们先停一下,进入binder_wait_for_work看看。这个函数决定了当前线程是否主动让出CPU,进入休眠。
c
// common/drivers/android/binder.c
static int binder_wait_for_work(struct binder_thread *thread,
bool do_proc_work) {
//DEFINE_WAIT宏创建一个名为wait的wait_queue_entry(内核等待队列机制中的项)
DEFINE_WAIT(wait);
struct binder_proc *proc = thread->proc;
int ret = 0;
binder_inner_proc_lock(proc);
for (;;) {
// 将wait(wait_queue_entry)加入binder_thread的等待队列中
prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE|TASK_FREEZABLE);
// 确认binder_thread是否有可以做的工作项,若有,则立即跳出循环,wait出队
if (binder_has_work_ilocked(thread, do_proc_work))
break;
// 加入到proc->waiting_threads等待队列中
if (do_proc_work)
list_add(&thread->waiting_thread_node,
&proc->waiting_threads);
trace_android_vh_binder_wait_for_work(do_proc_work, thread, proc);
binder_inner_proc_unlock(proc);
// 让出CPU,当前线程进入休眠。当再次被唤醒时,程序将从下一行开始执行
schedule();
// 恢复执行,再次加锁
binder_inner_proc_lock(proc);
// 从proc->waiting_threads中将binder_thread删除
list_del_init(&thread->waiting_thread_node);
// 唤醒之后检查一下当前是否有信号需要处理,若有,则将系统调用返回值设置为-EINTR
// 正如第9篇中我们讲到的IPCThreadState::talkWithDriver中调用ioctl的while循环重复的条件是err == -EINTR
// 也就是说之前系统调用被打断了,重新调用ioctl
if (signal_pending(current)) {
ret = -EINTR;
break;
}
}
finish_wait(&thread->wait, &wait);
binder_inner_proc_unlock(proc);
return ret;
}
通常情况下,Client端发出请求后,就会在schedule()那行停下来,等Server端完成请求后,驱动再唤醒Client侧。这与我们日常开发APP时在上层的体验一致,调用同步Binder接口(非oneway)若Server端迟迟不回复,那么Client侧会卡死。因此,我们通常不会在主线程调用同步Binder接口。
接收回复
当Client侧线程被唤醒后,此时binder_thread的todo队列中,已经有了2个binder_work,依次为BINDER_WORK_TRANSACTION_COMPLETE(BR_TRANSACTION_COMPLETE) 和 BINDER_WORK_TRANSACTION(BR_REPLY)。那么后续流程如下:
处理BINDER_WORK_TRANSACTION_COMPLETE。
c
// common/drivers/android/binder.c
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block) {
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
... ...
// 解除线程等待标志位
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
if (ret)
return ret;
while (1) {
uint32_t cmd;
struct binder_transaction_data_secctx tr;
struct binder_transaction_data *trd = &tr.transaction_data;
struct binder_work *w = NULL;
struct list_head *list = NULL;
struct binder_transaction *t = NULL;
struct binder_thread *t_from;
size_t trsize = sizeof(*trd);
binder_inner_proc_lock(proc);
... ...
// 将todo队列赋给list
if (!binder_worklist_empty_ilocked(&thread->todo))
// binder_thread的todo队列中有新的工作项
list = &thread->todo;
else if (!binder_worklist_empty_ilocked(&proc->todo) &&
wait_for_proc_work)
// binder_proc的todo队列中有新的工作项
list = &proc->todo;
else {
no_work:
binder_inner_proc_unlock(proc);
// 缓冲区中只有4字节,即函数开头写入的通信码BR_NOOP,那么没有需要处理的,跳到retry继续执行
if (ptr - buffer == 4 && !thread->looper_need_return)
goto retry;
break;
}
skip:
// 传入的参数大小不正确
if (end - ptr < sizeof(tr) + 4) {
binder_inner_proc_unlock(proc);
break;
}
trace_android_vh_binder_thread_read(&list, proc, thread);
// 取出一个工作项
w = binder_dequeue_work_head_ilocked(list);
if (binder_worklist_empty_ilocked(&thread->todo))
// 如果binder_thread的todo队列空了,标记一下
thread->process_todo = false;
switch (w->type) {
case BINDER_WORK_TRANSACTION_COMPLETE:
case BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT: {
// 将binder_work工作项的类型,转换为通信码
if (proc->oneway_spam_detection_enabled &&
w->type == BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT)
cmd = BR_ONEWAY_SPAM_SUSPECT;
else
cmd = BR_TRANSACTION_COMPLETE;
binder_inner_proc_unlock(proc);
// 释放binder_work占用的内核空间
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
// 通信码写入用户空间
if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
// 更新统计数据,打印调试日志
... ...
} break;
... ...
}
// 以上流程中t未被赋值,因此为NULL,那么返回循环开始,继续执行
if (!t)
continue;
... ...
}
}
再次回到while开头,这次取出的是 BINDER_WORK_TRANSACTION :
c
// common/drivers/android/binder.c
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block) {
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
... ...
while (1) {
struct binder_work *w = NULL;
... ...
w = binder_dequeue_work_head_ilocked(list);
... ...
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
binder_inner_proc_unlock(proc);
// container_of宏根据结构体(binder_transaction)中的字段(work)和字段值(w)反向找到结构体指针
t = container_of(w, struct binder_transaction, work);
} break;
... ...
}
// 上面的代码通过w的值,找到binder_transaction并赋值给了t
// 因此,这次不会返回循环开始,而是继续向下执行
if (!t)
continue;
// 此时的t->buffer是Server端发起BC_REPLY时创建的,target_node不会被赋值,所以为NULL
// 因此,我们只看else分支
if (t->buffer->target_node) {
... ...
} else {
// 将本地指针置为0,通信码设置为BR_REPLY
trd->target.ptr = 0;
trd->cookie = 0;
cmd = BR_REPLY;
}
// 设置binder_transaction_data中剩余的字段
trd->code = t->code;
trd->flags = t->flags;
trd->sender_euid = from_kuid(current_user_ns(), t->sender_euid);
// 从binder_transaction中取出用来接收Server端回复的线程
// 可以看看binder_transaction函数的分析,我们之前提到过
t_from = binder_get_txn_from(t);
if (t_from) {
struct task_struct *sender = t_from->proc->tsk;
// 找到发送方的PID
trd->sender_pid =
task_tgid_nr_ns(sender,
task_active_pid_ns(current));
trace_android_vh_sync_txn_recvd(thread->task, t_from->task);
} else {
trd->sender_pid = 0;
}
... ...
// 将需要Client端读取的数据区指针赋给trd变量
trd->data_size = t->buffer->data_size;
trd->offsets_size = t->buffer->offsets_size;
trd->data.ptr.buffer = (uintptr_t)t->buffer->user_data;
trd->data.ptr.offsets = trd->data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
... ...
// 写入通信码,当前语境下是BR_REPLY
if (put_user(cmd, (uint32_t __user *)ptr)) {
// 写入失败的异常处理
... ...
}
ptr += sizeof(uint32_t);
// 将binder_transaction_data(需要client读取的)拷贝至用户空间
if (copy_to_user(ptr, &tr, trsize)) {
// 写入失败的异常处理
... ...
}
ptr += trsize;
... ...
// 一次完整的binder_transaction结束,后续代码都是扫尾工作
// 减少binder_thread引用计数
if (t_from)
binder_thread_dec_tmpref(t_from);
//标记binder_buffer为允许释放
t->buffer->allow_user_free = 1;
if (cmd != BR_REPLY && !(t->flags & TF_ONE_WAY)) {
... ...
} else {
// 释放binder_transaction占用空间及相关资源
binder_free_transaction(t);
}
break;
}
done:
// 更新驱动向数据区写入了多少数据
*consumed = ptr - buffer;
... ...
return 0;
}
至此,Client端的binder_thread_read 全部结束,回到binder_ioctl_write_read 函数中,将更新后的binder_write_read拷贝到用户空间,层层向上返回,最终系统调用ioctl将调用结果状态返回到用户空间,整个驱动的操作就结束了。回到IPCThreadState::waitForResponse后,依次读出 BR_NOOP 、 BR_TRANSACTION_COMPLETE 和 BR_REPLY 进行上层处理,这些操作跟驱动无关,也比较简单,就不再赘述了。
Server接收请求和发起回复
Binde线程启动即休眠
通过前面的探索我们已经知道Binder线程在启动后,会按照以下调用链进入内核态: IPCThreadState::getAndExecuteCommand | ---> IPCThreadState::talkWithDriver | ---> ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
此时bwr中write_size为0,read_size为mIn.dataCapacity()(即256byte)。因此在binder_ioctl_write_read函数中,不会调用binder_thread_write,直接进入binder_thread_read。
c
// common/drivers/android/binder.c
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread) {
... ...
// write_size为0,因此跳过此分支
if (bwr.write_size > 0) {
... ...
}
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
... ...
}
... ...
out:
return ret;
}
Binder线程启动后并无需要处理的工作项,那么它会在binder_wait_for_work函数中进入休眠。若有疑问,请回看上一小节关于binder_wait_for_work函数的分析。
Binder线程唤醒
由前面的分析得知,binder_proc_transaction函数中会将一个binder_work(BINDER_WORK_TRANSACTION类型)投入到目标Binder线程的todo队列中,那么当它被唤醒后,将跳出binder_wait_for_work函数,继续后面的代码。
c
// common/drivers/android/binder.c
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block) {
... ...
if (non_block) {
if (!binder_has_work(thread, wait_for_proc_work))
ret = -EAGAIN;
} else {
ret = binder_wait_for_work(thread, wait_for_proc_work);
}
// Binder线程被唤醒后,从这里继续执行
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
if (ret)
return ret;
while (1) {
uint32_t cmd;
struct binder_transaction_data_secctx tr;
struct binder_transaction_data *trd = &tr.transaction_data;
struct binder_work *w = NULL;
struct list_head *list = NULL;
struct binder_transaction *t = NULL;
struct binder_thread *t_from;
size_t trsize = sizeof(*trd);
... ...
// 取出binder_work(BINDER_WORK_TRANSACTION类型)
w = binder_dequeue_work_head_ilocked(list);
if (binder_worklist_empty_ilocked(&thread->todo))
thread->process_todo = false;
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
// 通过binder_work,找到binder_transaction
t = container_of(w, struct binder_transaction, work);
} break;
}
... ...
// target_node为通过handle值找到的目标节点
if (t->buffer->target_node) {
// 给binder_transaction_data赋值
struct binder_node *target_node = t->buffer->target_node;
trd->target.ptr = target_node->ptr;
trd->cookie = target_node->cookie;
binder_transaction_priority(thread, t, target_node);
// 设置通信码
cmd = BR_TRANSACTION;
} else {
... ...
}
// 后续就是给binder_transaction_data剩余字段赋值,和上一小节的内容重复,不再赘述
... ...
t_from = binder_get_txn_from(t);
... ...
// 写入通信码,当前语境下是BR_TRANSACTION
if (put_user(cmd, (uint32_t __user *)ptr)) {
// 写入失败的异常处理
... ...
}
ptr += sizeof(uint32_t);
// 拷贝binder_transaction_data到用户空间
if (copy_to_user(ptr, &tr, trsize)) {
// 拷贝失败的异常处理
... ...
}
ptr += trsize;
... ...
// 当前语境下cmd为BR_TRANSACTION且为同步通信(非Oneway)
if (cmd != BR_REPLY && !(t->flags & TF_ONE_WAY)) {
binder_inner_proc_lock(thread->proc);
// 将Binder线程的事务栈顶元素赋给t->to_parent
t->to_parent = thread->transaction_stack;
// 将当前Binder线程保存到t->to_thread
t->to_thread = thread;
// 将当前事务保存到当前Binder线程的事务栈顶
thread->transaction_stack = t;
binder_inner_proc_unlock(thread->proc);
} else {
... ...
}
break;
}
... ...
}
至此,Server端的binder_thread_read 结束,回到binder_ioctl_write_read函数中,将更新后的binder_write_read拷贝到用户空间,层层向上返回,最终系统调用ioctl将调用结果状态返回到用户空间。
用户空间将调用Server端具体的实现函数,调用链如下: IPCThreadState::executeCommand | ---> BBinder::transact | ---> JavaBBinder::onTransact | ---> env->CallBooleanMethod(... gBinderOffsets.mExecTransact ...) | ---> Binder.java::execTransact | ---> Binder.java::execTransactInternal | ---> Binder.java::onTransact
Binder线程发起回复
Java层的Binder::onTransact实现函数完成操作后,IPCThreadState将调用sendReply进入回复阶段。
c
// frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags) {
status_t err;
status_t statusBuffer;
// 写入通信码BC_REPLY
err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
if (err < NO_ERROR) return err;
return waitForResponse(nullptr, nullptr);
}
此后调用流程和Client端发起请求时一样,ioctl -> binder_ioctl -> binder_ioctl_write_read -> binder_thread_write -> binder_transaction。
c
// common/drivers/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed) {
... ...
while (ptr < end && thread->return_error.cmd == BR_OK) {
int ret;
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
... ...
switch (cmd) {
... ...
case BC_REPLY: {
struct binder_transaction_data tr;
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr,
cmd == BC_REPLY, 0);
break;
}
... ...
}
*consumed = ptr - buffer;
}
return 0;
}
流程再次来到binder_transaction函数。
c
// common/drivers/android/binder.c
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size) {
... ...
// 分析client发起请求时,我们看的是else分支,但此时reply为true,所以我们看if分支
if (reply) {
binder_inner_proc_lock(proc);
// 取出在Binder线程被唤醒时保存的事务栈顶元素
in_reply_to = thread->transaction_stack;
// 检查对应的事务是否为空,若空,则异常处理
if (in_reply_to == NULL) {
... ...
}
// 检查取出事务栈顶的事务对应的Binder线程是否为当前线程,若否,则异常处理
if (in_reply_to->to_thread != thread) {
... ...
}
thread->transaction_stack = in_reply_to->to_parent;
binder_inner_proc_unlock(proc);
// 取出client端线程,并赋值给target_thread
target_thread = binder_get_txn_from_and_acq_inner(in_reply_to);
... ...
// 找到client的进程
target_proc = target_thread->proc;
target_proc->tmp_ref++;
binder_inner_proc_unlock(target_thread->proc);
trace_android_vh_binder_reply(target_proc, proc, thread, tr);
} else {
... ...
}
... ...
// 映射内存并拷贝数据,这里和Client发起请求时一样,只是这次是Server端将数据拷贝到Client端的用户空间中
... ...
if (t->buffer->oneway_spam_suspect)
tcomplete->type = BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT;
else
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
t->work.type = BINDER_WORK_TRANSACTION;
if (reply) {
// 将tcomplete工作项放入Binder线程的todo队列中
binder_enqueue_thread_work(thread, tcomplete);
... ...
// 检查一下client端线程是否还活着
if (target_thread->is_dead) {
// 异常处理
... ...
}
... ...
// client端线程的事务栈元素出栈
binder_pop_transaction_ilocked(target_thread, in_reply_to);
// 将t->work工作项放入client端线程的todo队列中
binder_enqueue_thread_work_ilocked(target_thread, &t->work);
... ...
// 唤醒client端线程
wake_up_interruptible_sync(&target_thread->wait);
... ...
// 释放binder_transaction占用空间及相关资源
binder_free_transaction(in_reply_to);
} else if (!(t->flags & TF_ONE_WAY)) {
... ...
} else {
... ...
}
... ...
return;
}
至此,唤醒client端线程后,Binder线程基本完成了本次任务,当然,最后还有一个tcomplete工作项在todo队列中,完成它之后,Binder线程再次进入binder_thread_read函数中并进入休眠,等待有缘人再次唤醒。
总结
本篇我们梳理了一次同步通信在内核中的整个流程。还有很多ioctl的控制命令我们并没有覆盖,不过剩余的代码都不会太复杂,有需要再查阅。最后奉上一张流程图。