当前位置:   article > 正文

细读《深入理解 Android 内核设计思想》(五)Binder 机制 [下]_binder_get_thread

binder_get_thread

对冗余挑拣重点,对重点深入补充,输出结构清晰的精简版

  1. 深入 binder 驱动内部
    binder_ioctl
    binder_get_thread
    binder_ioctl_write_read
    binder_thread_write
    binder_transaction
    binder_thread_read
    小结
  2. binder Q&A
    如何找到目标进程 Binder 实体
    如何实现 Binder 线程的睡眠与唤醒
  3. 最后

深入 binder 驱动内部

前两篇文章都有提到 binder_ioctl 方法,在 Binder 机制 [上] 中介绍了 binder_ioctl 支持的命令;Binder 机制 [中] 中提到 IPCThreadState 会调用到 binder_ioctl 方法。

书中对 binder 驱动内部调用的讲解没有分为较清晰的步骤,一口气就是 20 页篇幅的源码详解,理解起来有些难度,容易迷失。在细读了三四遍后,终于感觉对整体有些掌握了,结合前面的学习与自己的理解,将一次 IPC 调用中 binder 驱动的工作分为以下 5 步:

1.准备数据,根据命令分发给具体的方法去处理
2.找到目标进程的相关信息
3.将数据一次拷贝到目标进程所映射的物理内存块
4.记录待处理的任务,唤醒目标线程
5.调用线程进入休眠
6.目标进程直接拿到数据进行处理,处理完后唤醒调用线程
7.调用线程返回处理结果

与上篇文章一样仍以 getService() 为例,按照上面的工作步骤为脉络,深入分析驱动层中的执行逻辑,彻底搞定 binder 驱动!

binder_ioctl

在 IPCThreadState 中这样调用了 binder_ioctl() 方法:

    ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
  • 1

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 驱动版本号
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

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;
    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

拿到调用进程后,进一步通过 binder_get_thread() 方法拿到调用线程,然后就交给 binder_ioctl_write_read() 方法去执行具体的 binder 数据读写了,可见 binder_ioctl() 方法本身的逻辑非常简单,将数据 arg 透传了出去。下面分别来看 binder_get_thread()、binder_ioctl_write_read() 这两个方法。

binder_get_thread

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

binder_thread 是用来描述线程的结构体,binder_get_thread() 方法中逻辑也很简单,首先从调用进程 proc 中查找当前线程是否已被记录,如果找到就直接返回,否则新建一个返回,并记录到 proc 中。也就是说所有调用 binder_ioctl() 的线程,都会被记录起来。

binder_ioctl_write_read

此方法分为两部分来看,首先是整体:

static int binder_ioctl_write_read
    声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/319140?site
    推荐阅读
    相关标签
      

    闽ICP备14008679号