赞
踩
对冗余挑拣重点,对重点深入补充,输出结构清晰的精简版
- 深入 binder 驱动内部
binder_ioctl
binder_get_thread
binder_ioctl_write_read
binder_thread_write
binder_transaction
binder_thread_read
小结- binder Q&A
如何找到目标进程 Binder 实体
如何实现 Binder 线程的睡眠与唤醒- 最后
前两篇文章都有提到 binder_ioctl 方法,在 Binder 机制 [上] 中介绍了 binder_ioctl 支持的命令;Binder 机制 [中] 中提到 IPCThreadState 会调用到 binder_ioctl 方法。
书中对 binder 驱动内部调用的讲解没有分为较清晰的步骤,一口气就是 20 页篇幅的源码详解,理解起来有些难度,容易迷失。在细读了三四遍后,终于感觉对整体有些掌握了,结合前面的学习与自己的理解,将一次 IPC 调用中 binder 驱动的工作分为以下 5 步:
1.准备数据,根据命令分发给具体的方法去处理
2.找到目标进程的相关信息
3.将数据一次拷贝到目标进程所映射的物理内存块
4.记录待处理的任务,唤醒目标线程
5.调用线程进入休眠
6.目标进程直接拿到数据进行处理,处理完后唤醒调用线程
7.调用线程返回处理结果
与上篇文章一样仍以 getService() 为例,按照上面的工作步骤为脉络,深入分析驱动层中的执行逻辑,彻底搞定 binder 驱动!
在 IPCThreadState 中这样调用了 binder_ioctl() 方法:
ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
binder_ioctl() 方法中会根据 BINDER_WRITE_READ、BINDER_SET_MAX_THREADS 等不同 cmd 转调到不同的方法去执行,这里我们只关注 BINDER_WRITE_READ,简化后代码如下:
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg){ int ret; //拿到调用进程在 binder_open() 中记录的 binder_proc struct binder_proc *proc = filp->private_data; struct binder_thread *thread; binder_lock(__func__); //获取调用线程 binder_thread thread = binder_get_thread(proc); switch (cmd) { case BINDER_WRITE_READ: //处理 binder 数据读写,binder IPC 通信的核心逻辑 ret = binder_ioctl_write_read(filp, cmd, arg, thread); if (ret) goto err; break; case BINDER_SET_MAX_THREADS:{ ...} //设置 binder 最大线程数 case BINDER_SET_CONTEXT_MGR:{ ...} //设置 service 大管家,即 ServiceManager case BINDER_THREAD_EXIT:{ ...} //binder 线程退出命令,释放相关资源 case BINDER_VERSION: { ...} //获取 binder 驱动版本号 ... }
在 Binder 机制 [上] 中详细介绍过 binder_open() 方法,它主要做了两个工作:1.创建及初始化每个进程独有一份的、用来存放 binder 相关数据的 binder_proc 结构体,2.将 binder_proc 记录起来,方便后续使用。正是通过 file 来记录的:
static int binder_open(struct inode *nodp, struct file *filp){
...
filp->private_data = proc;
...
}
拿到调用进程后,进一步通过 binder_get_thread() 方法拿到调用线程,然后就交给 binder_ioctl_write_read() 方法去执行具体的 binder 数据读写了,可见 binder_ioctl() 方法本身的逻辑非常简单,将数据 arg 透传了出去。下面分别来看 binder_get_thread()、binder_ioctl_write_read() 这两个方法。
static struct binder_thread *binder_get_thread(struct binder_proc *proc){ struct binder_thread *thread = NULL; struct rb_node *parent = NULL; struct rb_node **p = &proc->threads.rb_node; //从 proc 中获取红黑树根节点 //查找 pid 等于当前线程 id 的thread,该红黑树以 pid 大小为序存放 while (*p) { parent = *p; thread = rb_entry(parent, struct binder_thread, rb_node); if (current->pid < thread->pid) //current->pid 是当前调用线程的 id p = &(*p)->rb_left; else if (current->pid > thread->pid) p = &(*p)->rb_right; else break; } if (*p == NULL) { //如果没有找到,则新创建一个 thread = kzalloc(sizeof(*thread), GFP_KERNEL); if (thread == NULL) return NULL; binder_stats_created(BINDER_STAT_THREAD); thread->proc = proc; thread->pid = current->pid; init_waitqueue_head(&thread->wait); //初始化等待队列 INIT_LIST_HEAD(&thread->todo); //初始化待处理队列 rb_link_node(&thread->rb_node, parent, p); //加入到 proc 的 threads 红黑树中 rb_insert_color(&thread->rb_node, &proc->threads); thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN; thread->return_error = BR_OK; thread->return_error2 = BR_OK; } return thread; }
binder_thread 是用来描述线程的结构体,binder_get_thread() 方法中逻辑也很简单,首先从调用进程 proc 中查找当前线程是否已被记录,如果找到就直接返回,否则新建一个返回,并记录到 proc 中。也就是说所有调用 binder_ioctl() 的线程,都会被记录起来。
此方法分为两部分来看,首先是整体:
static int binder_ioctl_write_read
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。