再学安卓 - binder之驱动函数ioctl

本文分析基于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+行,主要完成以下工作:

  1. binder_node和binder_proc查找,即Server端进程和节点
  2. binder_buffer用户空间映射到物理页,即Server端进程用户空间与物理内存的映射
  3. 复制数据到物理页,即Client端进程用户空间数据通过内核空间复制到物理页
  4. 添加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_NOOPBR_TRANSACTION_COMPLETEBR_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的控制命令我们并没有覆盖,不过剩余的代码都不会太复杂,有需要再查阅。最后奉上一张流程图。

相关推荐
小墙程序员5 小时前
Android 性能优化(四)优化Apk体积
android
Rains04226 小时前
TEE可信执行环境的安全业务保护方案
android·安全
Mr.pyZhang8 小时前
安卓基础组件Looper - 03 java层面的剖析
android·java·数据结构·epoll
Yang-Never8 小时前
OpenGL ES -> GLSurfaceView纹理贴图
android·java·开发语言·kotlin·android studio·贴图
暴怒的代码9 小时前
基础篇——深入解析SQL多表操作与关联查询:构建复杂数据关系的桥梁
android·java·sql
Nathan202406169 小时前
数据结构 - LinkedHashMap(二)
android·数据结构·面试
抛砖者10 小时前
9. Flink的性能优化
android·性能优化·flink
小墙程序员11 小时前
Android Framework 面试系列(八)ContentProvider
android
令狐掌门11 小时前
android智能指针android::sp使用介绍
android·android智能指针