binder驱动分析

相关源码文件:

/drivers/android/binder.c
/drivers/staging/android/binder.c

1.binder_init
用户态的程序调用 Kernel 层方法需要陷入内核态,进行系统调用(syscall),打开 Binder 驱动方法的调用链为: open-> __open() -> binder_open()。 open() 为用户空间的方法,__open() 便是系统调用中相应的处理方法,内部通过查找会找到对应的内核 binder 驱动的 binder_open() 方法。但在驱动启动时首先会调用驱动的 xxx_init 方法。

static int __init binder_init(void)
{
    int ret;
    //创建名为binder的工作队列
    binder_deferred_workqueue = create_singlethread_workqueue("binder");
    ...

     // 基于 misc_class 构造一个设备,将 miscdevice 结构挂载到 misc_list 列表上,并初始化与 linux 设备模型相关的结构  
    ret = misc_register(&binder_miscdev);
    return ret;
}

static struct miscdevice binder_miscdev = {
    // 次设备号 动态分配
    .minor = MISC_DYNAMIC_MINOR, 
    // 设备名
    .name = "binder",  
    // 设备的文件操作结构,这是 file_operations 结构   
    .fops = &binder_fops  
};

// 相对应的一些操作
const struct file_operations binder_fops = {
	.owner = THIS_MODULE,
	.poll = binder_poll,
	.unlocked_ioctl = binder_ioctl,
	.compat_ioctl = binder_ioctl,
	.mmap = binder_mmap,
	.open = binder_open,
	.flush = binder_flush,
	.release = binder_release,
};

2.binder_open

static int binder_open(struct inode *nodp, struct file *filp)
{
    // 当前 binder 进程结构体
    struct binder_proc *proc; 
    // 分配binder proc记录空间
    proc = kzalloc(sizeof(*proc), GFP_KERNEL); 
    if (proc == NULL)
        return -ENOMEM;
    //获得当前线程的task_struct
    get_task_struct(current);
    // 将当前线程的 task 保存到 binder 进程的 tsk
    proc->tsk = current;   
    // 初始化 todo 列表
    INIT_LIST_HEAD(&proc->todo); 
    // 初始化 wait 队列
    init_waitqueue_head(&proc->wait); 
    // 将当前进程的 nice 值转换为进程优先级
    proc->default_priority = task_nice(current);  
    // 同步锁,因为 binder 支持多线程访问
    binder_lock(__func__);  
    // BINDER_PROC 对象创建数加1 
    binder_stats_created(BINDER_STAT_PROC); 
    // 将 proc_node 节点添加到 binder_procs 为表头的队列
    hlist_add_head(&proc->proc_node, &binder_procs); 
    proc->pid = current->group_leader->pid;
    // 初始化已分发的死亡通知列表
    INIT_LIST_HEAD(&proc->delivered_death); 
    // file 文件指针的 private_data 变量指向 binder_proc 数据
    filp->private_data = proc;   
    // 释放同步锁    
    binder_unlock(__func__); 
    return 0;
}

struct binder_proc {
	struct hlist_node proc_node;
    // threads 树 保存 binder_proc 进程内用于处理用户请求的线程
	struct rb_root threads;
    // nodes树 保存 binder_proc 进程内的 Binder 实体;
	struct rb_root nodes;
    // 进程内的 Binder 引用,即引用的其它进程的 Binder 实体,以句柄作 key 值来组织
	struct rb_root refs_by_desc;
    // 进程内的 Binder 引用,即引用的其它进程的 Binder 实体,以地址作 key 值来组织
	struct rb_root refs_by_node;
    // 当前进程 id
	int pid;
	struct vm_area_struct *vma;
	struct mm_struct *vma_vm_mm;
    // 将当前线程的 task_struct
	struct task_struct *tsk;
	struct files_struct *files;
	struct hlist_node deferred_work_node;
	int deferred_work;
    // 指向内核虚拟空间的地址
	void *buffer;
    // 用户虚拟地址空间与内核虚拟地址空间的偏移量
	ptrdiff_t user_buffer_offset;

	struct list_head buffers;
	struct rb_root free_buffers;
	struct rb_root allocated_buffers;
	size_t free_async_space;
    // 物理页的指针数组
	struct page **pages;
    // 映射虚拟内存的大小
	size_t buffer_size;
	uint32_t buffer_free;
	struct list_head todo;
	wait_queue_head_t wait;
	struct binder_stats stats;
	struct list_head delivered_death;
	int max_threads;
	int requested_threads;
	int requested_threads_started;
	int ready_threads;
	long default_priority;
	struct dentry *debugfs_entry;
};

同时Binder驱动就会在内核空间为该进程创建一个binder_proc结构体,并将该结构体放到全局的hash队列binder_procs中。通过遍历该集合中的数量就知道有多少个进程正在使用Binder驱动进行通信
在这里插入图片描述
在当前进程在用户空间开辟了的确是1M-8k的内存空间,但是这些Binder线程会同时共享这部分空间,都通过这部分空间来和Binder驱动进行传递数据。那么所以对于单个Binder线程能够传递的数据不会超过1M-8k。
3.binder_mmap

static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
    int ret;
    // 内核虚拟空间,vm_area_struct表示用户空间结构体
    struct vm_struct *area;
    // 从 filp 中获取之前打开保存的
    struct binder_proc *proc = filp->private_data;
    const char *failure_string;
    struct binder_buffer *buffer; 

    if (proc->tsk != current)
        return -EINVAL;
    // 保证映射内存大小不超过 4M,普通进程分配的是1M-8k,servicemanager是128k
    if ((vma->vm_end - vma->vm_start) > SZ_4M)
        vma->vm_end = vma->vm_start + SZ_4M;  
    // 同步锁
    mutex_lock(&binder_mmap_lock);  
    // 采用 IOREMAP 方式,分配一个连续的内核虚拟空间,与进程虚拟空间大小一致,本函数返回内核空间首地址
    area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
    if (area == NULL) {
        ret = -ENOMEM;
        failure_string = "get_vm_area";
        goto err_get_vm_area_failed;
    }
    // 指向内核虚拟空间的地址
    proc->buffer = area->addr; 
    // 地址偏移量 = 用户虚拟地址空间 - 内核虚拟地址空间,由于虚拟用户空间和内核空间是线性关系,这样方便相互之间的转换运算
    proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
    // 释放锁
    mutex_unlock(&binder_mmap_lock); 

    ...
         /* 分配存放struct page结构体指针的指针数组内存空间,主要用来保存指向申请的物理页struct page的指针。*/
    proc->pages = kzalloc(sizeof(proc->pages[0]) * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
    if (proc->pages == NULL) {
        ret = -ENOMEM;
        failure_string = "alloc page array";
        goto err_alloc_pages_failed;
    }
    //计算大小,普通应用是1M-8k
    proc->buffer_size = vma->vm_end - vma->vm_start;

    vma->vm_ops = &binder_vm_ops;
    vma->vm_private_data = proc;

    //分配物理页面,同时映射到内核空间和进程空间,先分配1个物理页
    if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
        ret = -ENOMEM;
        failure_string = "alloc small buf";
        goto err_alloc_small_buf_failed;
    }
    // binder_buffer 对象 指向 proc 的 buffer 地址
    buffer = proc->buffer;
    // 创建进程的 buffers 链表头
    INIT_LIST_HEAD(&proc->buffers); 
    // 将 binder_buffer 地址 加入到所属进程的 buffers 队列
    list_add(&buffer->entry, &proc->buffers); 
    buffer->free = 1;
    // 将空闲 buffer 放入 proc->free_buffers 中
    binder_insert_free_buffer(proc, buffer);
    // 异步可用空间大小为 buffer 总大小的一半。
    proc->free_async_space = proc->buffer_size / 2;
    barrier();
    proc->files = get_files_struct(current);
    proc->vma = vma;
    proc->vma_vm_mm = vma->vm_mm;
    return 0;

    ...// 错误flags跳转处,free释放内存之类的操作
    return ret;
}

static int binder_update_page_range(struct binder_proc *proc, int allocate,
            void *start, void *end,
            struct vm_area_struct *vma)
{
  void *page_addr;
  unsigned long user_page_addr;
  struct page **page;
  // 内存结构体
  struct mm_struct *mm; 

  if (vma)
        // binder_mmap 过程 vma 不为空,其他情况都为空
  		mm = NULL;
  	else
        // 获取 mm 结构体,从 tsk 中获取
  		mm = get_task_mm(proc->tsk); 
      
  if (mm) {
    // 获取 mm_struct 的写信号量
    down_write(&mm->mmap_sem); 
    vma = proc->vma;
  }

  // 此处 allocate 为 1,代表分配过程。如果为 0 则代表释放过程
  if (allocate == 0)
    goto free_range;

  //start是内核空间的起始地址,这里是一页一页的来分配物理内存并映射到虚拟用户空间和内核空间
  for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
    int ret;
    page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
    // 分配一个 page 的物理内存
    *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
    // 物理空间映射到虚拟内核空间,把物理空间开辟好的这一个页映射到内核空间
    ret = map_kernel_range_noflush((unsigned long)page_addr,
          PAGE_SIZE, PAGE_KERNEL, page);
    //  用户空间地址 = 内核空间地址 + 偏移量,根据便宜量计算虚拟用户空间首地址
    user_page_addr = (uintptr_t)page_addr + proc->user_buffer_offset;
    //物理空间映射到虚拟进程空间
    ret = vm_insert_page(vma, user_page_addr, page[0]);
  }
  
  if (mm) {
    // 释放内存的写信号量
    up_write(&mm->mmap_sem); 
     // 减少 mm->mm_users 计数
    mmput(mm);
  }
  return 0;

  //释放内存的流程
  free_range:
  ...
  return -ENOMEM;
}

上面这些代码大部分都是 linux 内核方面的知识了,我们来简单解释下:task_struct 代表的是进程或者线程管理的进程控制结构体,mm_struct 是 task_struct 结构体中虚拟地址管理的结构体,vm_area_struct 代表的是虚拟用户空间映射管理的结构体,vm_struct 代表的是内核空间管理的结构体。alloc_page 方法的作用是分配一个物理内存,map_kernel_range_noflush 方法的作用是将物理空间映射到虚拟内核空间,vm_insert_page 方法的作用是将物理空间映射到虚拟用户空间。binder_mmap 的主要作用就是开辟一块连续的内核空间,并且开辟一个物理页的地址空间,同时映射到用户空间和内核空间。
struct page:表示一个物理页的映射
struct vm_struct * get_vm_area(unsigned long size, unsigned long flags):寻找一块空间内存区域,创建一个vm_struct结构
void *kmalloc(size_t size, gfp_t flags):申请内存,内核调用
struct page * alloc_pages(gfp_t gfp_mask, unsigned int order):分配页并且返回struct page实例
int map_vm_area(struct vm_struct *area, pgprot_t prot, struct page ***pages):完成物理页到虚拟内存的映射
int vm_insert_page(struct vm_area_struct *, unsigned long addr, struct page *):把分配的物理页插到用户vm,可以理解物理页和虚拟用户空间映射

在这里插入图片描述
4. binder_ioctl

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    struct binder_proc *proc = filp->private_data;
    struct binder_thread *thread;  // binder线程,结构体binder_thread用来描述Binder线程池中的一个线程
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    // 进入休眠状态,直到中断唤醒,ait_event_interruptible()将本进程置为可中断的挂起状态,反复检查condition是否成立,如果成立则退出,如果不成立则继续休眠;条件满足后,即把本进程运行状态置为运行态
    ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
    if (ret)
        goto err_unlocked;

    binder_lock(__func__);
    // 从binder_proc中获取 binder_thread 【见下面解析】
    thread = binder_get_thread(proc);
    if (thread == NULL) {
        ret = -ENOMEM;
        goto err;
    }

    switch (cmd) {
    // 进行 binder 的读写操作
    case BINDER_WRITE_READ: 
        ret = binder_ioctl_write_read(filp, cmd, arg, thread);//【见下解析】
        if (ret)
            goto err;
        break;
    // 设置 binder 最大支持的线程数
    case BINDER_SET_MAX_THREADS: 
        if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
            ret = -EINVAL;
            goto err;
        }
        break;
    // 成为 binder 的上下文管理者,也就是 ServiceManager 成为守护进程
    case BINDER_SET_CONTEXT_MGR: 
        ret = binder_ioctl_set_ctx_mgr(filp);
        if (ret)
            goto err;
        break;
    // 当 binder 线程退出,释放 binder 线程
    case BINDER_THREAD_EXIT:   
        binder_free_thread(proc, thread);
        thread = NULL;
        break;
    // 获取 binder 的版本号
    case BINDER_VERSION: {  
        struct binder_version __user *ver = ubuf;

        if (size != sizeof(struct binder_version)) {
            ret = -EINVAL;
            goto err;
        }
        //把版本号拷贝到ver->protocol_version中,即上层传下来的ubuf地址中
        if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
                 &ver->protocol_version)) {
            ret = -EINVAL;
            goto err;
        }
        break;
    }
    default:
        ret = -EINVAL;
        goto err;
    }
    ret = 0;
err:
    if (thread)
        thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
    binder_unlock(__func__);
    wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);

err_unlocked:
    trace_binder_ioctl_done(ret);
    return ret;
}

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;
    // 根据当前进程的 pid,从 binder_proc 中查找相应的 binder_thread
    while (*p) {  
        parent = *p;
        //通过binder_proc中的rb_node找到其binder_thread对象
        thread = rb_entry(parent, struct binder_thread, rb_node);
        if (current->pid < thread->pid)
            p = &(*p)->rb_left;
        else if (current->pid > thread->pid)
            p = &(*p)->rb_right;
        else
            break;
    }
    //如果没找到,就根据当前的pid创建一个binder_thread 
    if (*p == NULL) {
        // 新建 binder_thread 结构体
        thread = kzalloc(sizeof(*thread), GFP_KERNEL); 
        if (thread == NULL)
            return NULL;
        binder_stats_created(BINDER_STAT_THREAD);
        thread->proc = proc;
        // 保存当前进程(线程)的 pid
        thread->pid = current->pid;  
        init_waitqueue_head(&thread->wait);
        // 初始化线程的 等待队列和工作队列
        INIT_LIST_HEAD(&thread->todo);
        // 把线程加入 proc->threads,插入到当前进程线程列表的红黑树
        rb_link_node(&thread->rb_node, parent, p);
        //调整红黑树的颜色
        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;
}

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;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    struct binder_write_read bwr;

    if (size != sizeof(struct binder_write_read)) {
        ret = -EINVAL;
        goto out;
    }
    // 把用户空间数据 ubuf 拷贝到 bwr
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { 
        ret = -EFAULT;
        goto out;
    }

    // 当写缓存中有数据,则执行 binder 写操作
    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);
        // 当写失败,再将 bwr 数据写回用户空间,并返回
        if (ret < 0) { 
            bwr.read_consumed = 0;
            if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                ret = -EFAULT;
            goto out;
        }
    }
    // 当读缓存中有数据,则执行 binder 读操作
    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);
        trace_binder_read_done(ret);
        // 唤醒等待状态的线程
        if (!list_empty(&proc->todo))
            wake_up_interruptible(&proc->wait); 
        // 当读失败,再将bwr数据写回用户空间,并返回
        if (ret < 0) { 
            if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
                ret = -EFAULT;
            goto out;
        }
    }
    // 将内核数据 bwr 拷贝到用户空间 ubuf
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) { 
        ret = -EFAULT;
        goto out;
    }
out:
    return ret;
}


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;
	void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
	void __user *ptr = buffer + *consumed;//每一轮write都来取一下首地址,从中获得cmd
	void __user *end = buffer + size;

	while (ptr < end && thread->return_error == BR_OK) {
		if (get_user(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
		trace_binder_command(cmd);
		switch (cmd) {
		case BC_INCREFS:
		...
		case BC_TRANSACTION: //添加服务时,cmd = BC_TRANSACTION
		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);//调用binder_transaction()来处理事务。
			break;
		}

		case BC_REGISTER_LOOPER:
			binder_debug(BINDER_DEBUG_THREADS,
				     "%d:%d BC_REGISTER_LOOPER\n",
				     proc->pid, thread->pid);
			if (thread->looper & BINDER_LOOPER_STATE_ENTERED) {
				thread->looper |= BINDER_LOOPER_STATE_INVALID;
				binder_user_error("%d:%d ERROR: BC_REGISTER_LOOPER called after BC_ENTER_LOOPER\n",
					proc->pid, thread->pid);
			} else if (proc->requested_threads == 0) {
				thread->looper |= BINDER_LOOPER_STATE_INVALID;
				binder_user_error("%d:%d ERROR: BC_REGISTER_LOOPER called without request\n",
					proc->pid, thread->pid);
			} else {
				proc->requested_threads--;
				proc->requested_threads_started++;
			}
			thread->looper |= BINDER_LOOPER_STATE_REGISTERED;
			break;
		case BC_ENTER_LOOPER:
			binder_debug(BINDER_DEBUG_THREADS,
				     "%d:%d BC_ENTER_LOOPER\n",
				     proc->pid, thread->pid);
			if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
				thread->looper |= BINDER_LOOPER_STATE_INVALID;
				binder_user_error("%d:%d ERROR: BC_ENTER_LOOPER called after BC_REGISTER_LOOPER\n",
					proc->pid, thread->pid);
			}
			thread->looper |= BINDER_LOOPER_STATE_ENTERED;
			break;
		case BC_EXIT_LOOPER:
			binder_debug(BINDER_DEBUG_THREADS,
				     "%d:%d BC_EXIT_LOOPER\n",
				     proc->pid, thread->pid);
			thread->looper |= BINDER_LOOPER_STATE_EXITED;
			break;

		...

			list_del_init(&death->work.entry);
			if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) {
				death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;
				if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
					list_add_tail(&death->work.entry, &thread->todo);
				} else {
					list_add_tail(&death->work.entry, &proc->todo);
					wake_up_interruptible(&proc->wait);
				}
			}
		} break;

		default:
			pr_err("%d:%d unknown command %d\n",
			       proc->pid, thread->pid, cmd);
			return -EINVAL;
		}
		*consumed = ptr - buffer;
	}
	return 0;
}

static void binder_transaction(struct binder_proc *proc,
                               struct binder_thread *thread,
                               struct binder_transaction_data *tr, int reply){
    // reply=(cmd==BC_REPLY)即false
    ...
    if (reply) {
        ...
    } else {
        if (tr->target.handle) {
            //如果参数事物信息中的进程的句柄不为0,即不是系统ServiceManager进程
            
            //定义binder引用
            struct binder_ref *ref;
            //根据参数binder进程和句柄handle来查找对应的binder
            ref = binder_get_ref(proc, tr->target.handle);
            ...
            //设置目标binder实体为上面找到的参数进程的binder引用的binder实体
            target_node = ref->node;
        } else {
            //如果参数事物信息中的进程的句柄为0,即是系统ServiceManager进程
            
            //设置通讯目标进程的Binder实体为ServiceManager对应的Binder
            target_node = binder_context_mgr_node;
        }
        //设置通讯目标进程为target_node对应的进程
        target_proc = target_node->proc;
        ...
         //判断目标线程是否为空
         //首先看target_thread是否为空,由于我们没有走if(replay)的TRUE逻辑,所以这里target_thread是为空的。那么,从目标进程信息target_proc分别去除todo任务队列和wait对象,赋值给target_list和target_wait。同样需要记住!

    if (target_thread) {
        ...
        //目标线程的todo队列
        target_list = &target_thread->todo;
        target_wait = &target_thread->wait;
        ...
    } else {
        //获得通讯目标进程的任务队列
        target_list = &target_proc->todo;
        //获取通讯目标进程的等待对象
        target_wait = &target_proc->wait;
    }
    ...
    /*其实这么多代码,主要使用在给binder事务t的成员赋值了。我们简单看几个我认为重要的赋值。

首先为事务t和work tcomplete申请了内存。然后设置事务t的from线程(也就是发送方线程)的值,如果不是BC_REPLAY事务,并且通讯标记没有TF_ONE_WAY(即本次通讯需要有响应),那么把参数thread赋值给t->from。前面说过,我们本次通讯是BC_TRANSACTION事务,所以事务t就需要储存发送方的线程信息,以便后面给发送方响应使用。

参数thread是那来的呢?顺着往回找,在binder_ioctl()中,我们从用户空间调用ioctl()函数的进程(即发送方进程)的进程信息中获取到了thread。

接着设置事务t的目标进程t->to_proc和目标进程的线程t->to_thread为前面处理好的target_proc和target_thread(本次通讯,target_thread为空哦)
然后把通讯事务数据tr中的code和flags赋值给事务t的code和flags。code和flags是什么呢?我们回到用户空间,定义通讯事务数据,即IPCThreadState::writeTransaction()中可以看到,code和flags均是传进来的参数。而的发源地是通讯的起始点IPCThreadState::self()->transact(0, IBinder::PING_TRANSACTION, data, NULL, 0),即code = IBinder::PING_TRANSACTION, flags = 0。记住了哦!后面还会用。
然后开始设置事务t的buffer信息。首先通过binder_alloc_buf()函数,在目标进程target_proc中为t->buffer申请了内存,即t->buffer指向了目标进程空间中的一段内存。然后配置一下t->buffer的信息,这些信息后面也会用到。记住了哦!
接着通过copy_from_user()函数,把用户空间的需要发送的数据拷贝到t->buffer的data中。
再往下到了if(replay),本次通讯会走false逻辑。于是,事务t会把发送方的事务栈transaction_stack储存在from_parent中,而发送方把自己的事务栈设置以成t开始。这些都需要记住,不然再往后你就会越来越迷糊!
*/

    //为本次通讯事务t申请空间
    t = kzalloc(sizeof(*t), GFP_KERNEL);
    ...
    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
    ...
    if (!reply && !(tr->flags & TF_ONE_WAY))
        //采用非one way通讯方式,即需要等待服务端返回结果的通讯方式
        
        //设置本次通讯事务t的发送线程为用户空间的线程
        t->from = thread;
    else
        t->from = NULL;
    ...
    //设置本次通讯事务的接收进程为目标进程
    t->to_proc = target_proc;
    //设置本次通讯事务的接收线程为目标线程
    t->to_thread = target_thread;
    //设置本次通讯事务的命令为用户空间传来的命令
    t->code = tr->code;
    //设置本次通讯事务的命令为用户空间传来的flags
    t->flags = tr->flags;
    ...
    //开始配置本次通讯的buffer
    //在目标进程中分配进行本次通讯的buffer的空间
    t->buffer = binder_alloc_buf(target_proc, tr->data_size,
                                 tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
    
    t->buffer->allow_user_free = 0; //通讯buffer允许释放
    t->buffer->transaction = t;  //把本次通讯存入buffer中
    //设置本次通讯的buffer的目标Binder实体为target_node
    //如前面一样,通过这个buffer可以找到对应的进程
    t->buffer->target_node = target_node;
    ...
    offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
    //将用户空间发送来的数据拷贝到本次通讯的buffer的data中
    copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)tr->data.ptr.buffer, tr->data_size);
    ...
    //将用户空间发送来的偏移量offsets拷贝给起始offp
    copy_from_user(offp, (const void __user *)(uintptr_t)tr->data.ptr.offsets, tr->offsets_size);
    ...
    //计算结尾off_end
    off_end = (void *)offp + tr->offsets_size;
    ...
    //判断是否是BC_REPLY
    if (reply) {
        ...
        
        binder_pop_transaction(target_thread, in_reply_to);
    } else if (!(t->flags & TF_ONE_WAY)) {
        //如果没有ONE_WAY标记,即需要等待响应
        t->need_reply = 1;  //1标示这是一个同步事务,需要等待对方回复。0表示这是一个异步事务,不用等对方回复
        //设置本次通讯事务的from_parent为发送方进程的事务
        t->from_parent = thread->transaction_stack;
        //设置发送方进程的事务栈为本次通讯事务
        thread->transaction_stack = t;
    }
    ...
   
     /*先把事务t的work.type类型设置为BINDER_WORK_TRANSACTION类型,这决定了该事务后面走的流程,然后把事务t的任务添加到目标进程的任务栈target_list中。接着把work tcomplete的类型设置为BINDER_WORK_TRANSACTION_COMPLETE,用于告诉发送方,和Binder驱动的一次talk完成了,同样,需要把这个项任务添加到发送方的任务列表里。
最后,通过wake_up_interruptible(target_wait)函数唤醒休眠中的目标进程,让它开始处理任务栈中的任务,也就是刚刚我们添加到target_list中的任务。接着return结束该函数。*/
    //将本次通讯事务的work类型设置为BINDER_WORK_TRANSACTION
    t->work.type = BINDER_WORK_TRANSACTION;
    //将本次通讯事务的work添加到目标进程的事务列表中
    list_add_tail(&t->work.entry, target_list);
    
    //设置work类型为BINDER_WORK_TRANSACTION_COMPLETE
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    //将BINDER_WORK_TRANSACTION_COMPLETE类型的work添加到发送方的事务列表中
    list_add_tail(&tcomplete->entry, &thread->todo);
    
    if (target_wait)
        //唤醒目标进程,开始执行目标进程的事务栈
        wake_up_interruptible(target_wait);
    return;
}

/*上一个函数结束后回到binder_thread_write()函数中,retrun 0;,binder_thread_write()函数结束。然后回到第6步binder_ioctl_write_read()函数中继续执行。*/
static int binder_ioctl_write_read(struct file *filp,
                                   unsigned int cmd, unsigned long arg,
                                   struct binder_thread *thread)
{
    ...
    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);
        ...
}

//Binder驱动会调用binder_thread_read()函数,为发送进程读取数据。我们看看是怎么读取的。
/*
我们先看这个片段,前面一堆代码掠过了。首先,需要看看能不能从发送进程的线程thread的任务栈中取出任务来,回顾binder_transaction()中,我们在最后往发送进程的线程thread的任务栈中添加了一个BINDER_WORK_TRANSACTION_COMPLETE类型的work。所以这里是能取到任务的,就直接执行下一步了。
*/
```cpp
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)
{
    ...
    while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;
        if (!list_empty(&thread->todo)) {
            //获取线程的work队列
            w = list_first_entry(&thread->todo, struct binder_work, entry);
        } else if (!list_empty(&proc->todo) && wait_for_proc_work) {
            //获取从进程获取work队列
            w = list_first_entry(&proc->todo, struct binder_work, entry);
        } else {
            //没有数据,则返回retry
            if (ptr - buffer == 4 &&
                !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
                goto retry;
            break;
        }
    ...
    switch (w->type) {
            ...
            case BINDER_WORK_TRANSACTION_COMPLETE:
                //设置cmd为BR_TRANSACTION_COMPLETE
                cmd = BR_TRANSACTION_COMPLETE;
                //将BR_TRANSACTION_COMPLETE写入用户进程空间的mIn中
                put_user(cmd, (uint32_t __user *)ptr)//从事务队列中删除本次work
                list_del(&w->entry);
                //释放
                kfree(w);
                break;
            ...
        }
}

//接着又会回到binder_ioctl_write_read()函数。
static int binder_ioctl_write_read(struct file *filp,
                                   unsigned int cmd, unsigned long arg,
                                   struct binder_thread *thread)
{
    ...
    //将内核的信使bwr拷贝到用户空间
    if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
    ...
}

/*上面函数最后会把内核中的信使拷贝到用户空间。然后,我们直接的再次的回到第3步的函数IPCThreadState::waitForResponse()中。经过刚刚的读取,这次mIn中可是有数据了哦!我们从mIn中取出cmd命令。这是什么命令呢?就是刚刚写到用户空间的BR_TRANSACTION_COMPLETE。在这段逻辑中,由于之前我们传入了一个fakeReplay进来,所以程序走bredk,然后继续循环,执行下一次talkWithDriver()函数。到此,我们和Binder内核的一次通讯算是完成了。
*/
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    ...
    while (1) {
        //真正和Binder驱动交互的是talkWithDriver()函数
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        ...
        if (mIn.dataAvail() == 0) continue;
        //取出在内核中写进去的cmd命令
        cmd = mIn.readInt32();
        ...
        
        switch (cmd) {
            //表示和内核的一次通讯完成
            case BR_TRANSACTION_COMPLETE:
                if (!reply && !acquireResult) goto finish;
                break;
            ...
        }
        
    }
    ...


static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
	int ret = 0;
	struct binder_proc *proc = filp->private_data;
	kuid_t curr_euid = current_euid();
    // 管理者只有一个(只能被设置一次),只有servicemanager
	if (binder_context_mgr_node != NULL) {
		pr_err("BINDER_SET_CONTEXT_MGR already set\n");
		ret = -EBUSY;
		goto out;
	}
	if (uid_valid(binder_context_mgr_uid)) {
		...
	} else {
		binder_context_mgr_uid = curr_euid;
	}
    // 静态变量 binder_context_mgr_node = binder_new_node,创建一个单独的binder_node
	binder_context_mgr_node = binder_new_node(proc, 0, 0);
	if (binder_context_mgr_node == NULL) {
		ret = -ENOMEM;
		goto out;
	}
	binder_context_mgr_node->local_weak_refs++;
	binder_context_mgr_node->local_strong_refs++;
	binder_context_mgr_node->has_strong_ref = 1;
	binder_context_mgr_node->has_weak_ref = 1;
out:
	return ret;
}

static struct binder_node *binder_new_node(struct binder_proc *proc,
					   binder_uintptr_t ptr,
					   binder_uintptr_t cookie)
{
    // 从 proc->nodes 中获取根节点, proc->nodes.rb_node代表的是本进程的binder,通过
	struct rb_node **p = &proc->nodes.rb_node;
	struct rb_node *parent = NULL;
	struct binder_node *node;

	while (*p) {
		parent = *p;
        // 根据 rb_node 的偏移量获取 binder_node 
		node = rb_entry(parent, struct binder_node, rb_node);

		if (ptr < node->ptr)
			p = &(*p)->rb_left;
		else if (ptr > node->ptr)
			p = &(*p)->rb_right;
		else
			return NULL;
	}
    // 刚开始肯定是 null ,创建一个 binder_node 
	node = kzalloc(sizeof(*node), GFP_KERNEL);
	if (node == NULL)
		return NULL;
	binder_stats_created(BINDER_STAT_NODE);
    // 添加到 binder_proc 的 nodes 中
	rb_link_node(&node->rb_node, parent, p);
    // 调整红黑树的颜色
	rb_insert_color(&node->rb_node, &proc->nodes);
	node->debug_id = ++binder_last_id;
	node->proc = proc;
	node->ptr = ptr;
	node->cookie = cookie;
	node->work.type = BINDER_WORK_NODE;
	INIT_LIST_HEAD(&node->work.entry);
	INIT_LIST_HEAD(&node->async_todo);
	binder_debug(BINDER_DEBUG_INTERNAL_REFS,
		     "%d:%d node %d u%016llx c%016llx created\n",
		     proc->pid, current->pid, node->debug_id,
		     (u64)node->ptr, (u64)node->cookie);
	return node;
}

通过以上分析,ioctl 命令有 BINDER_WRITE_READ (binder 读写交互)、BINDER_SET_CONTEXT_MGR(servicemanager进程成为上下文管理者)、BINDER_SET_MAX_THREADS(设置最大线程数)、BINDER_VERSION(获取 binder 版本)。有两个核心复杂方法 binder_thread_write 和 binder_thread_read,由于这里面的代码逻辑较为复杂,因此到后面我们带着线索再去具体分析。
在这里插入图片描述

总结:

1、Binder线程池的枚举状态:

enum{

BINDER_LOOPER_STATE_REGISTERD=0x01

BINDER_LOOPER_STATE_ENTERED=0x02

BINDER_LOOPER_STATE_EXITED=0x04

BINDER_LOOPER_STATE_INVALID=0x08

BINDER_LOOPER_STATE_WATTING=0x10

BINDER_LOOPER_STATE_NEED_RETURN=0x20
}
根据字面意思可以理解为六种状态:注册、进入线程池、退出、非法、等待、需要返回。
所有线程的初始状态都是NEED_RETURN,并且不管有没有work(所谓work就是线程监听到了消息),第一次read后都会返回。几乎所有的线程第一次ioctl都是write命令BC_REGISTER_LOOPER,把自己加入到线程池后返回,进而让应用程序知道线程是否成功进入线程池。
有一个例外的线程,就是用于open驱动的那个线程,一般是进程的主线程,第一次调用ioctl会阻塞在ENTER_LOOPER状态。

2、进程
每个使用Binder的进程都会在使用前打开binder驱动,驱动在open的时候会创建结构体binder_proc,并将它的指针记录到文件结构体private_data中,以后的驱动操作就能通过文件结构体来获取该进程的信息。
驱动里都是以binder_proc为单元来分配和记录资源的,驱动会给每个使用binder的进程创建一个/proc/binder/proc/pid文件,用来展示进程相关的binder信息。
/proc/binder/stats和/proc/binder/proc/pid能查看到相关信息

3.线程
主要结构体binder_thread
驱动会为每一个与binder有一腿的线程创建该结构体,并记录到进程的threads中

4线程池
ioctl(BINDER_WRITE_READ)有两步,第一步是write,会传给驱动一些以BC_开头的命令,让驱动去执行,一次可以传多个命令。第二步是read,会从驱动获取一些以BR_开头的回复,也可能是多个,read这一步有可能阻塞当前线程,直到驱动给它回复并唤醒它。
任何一个线程在read阶段返回时,会检查进程的ready_threads,这个变量表示当前进程有多少个线程正在等待进程work,这类线程必然处于WATTING状态,如果当前没有线程等待进程work,驱动在read的返回BR队列最前面加上一个BR_SPAWN_LOOPER通知该线程,线程池已经没有可用于处理进程work的线程了,你得赶紧给我产生一个新的,线程在read返回之后便会根据该BR创建一个新的线程,并让它以BC_REGISTER_LOOPER加入线程池并待命,然后自己才去处理这些read到的真正的work。
通过read返回这种方式创建的线程数不能超过进程的max_threads,此数通过ioctl(BINDER_SET_MAX_THREADS)设置,binder库中默认为15。但是,这个MAX并不约束进程的线程数,也不约束线程池中的线程数,仅仅约束在驱动要求下生成的线程数,即通过BC_REGISTER_LOOPER加入线程池中的线程数。
work分为两种,进程work和线程work,分别记录在binder_proc和binder_thread的to-do队列中。线程池中可能有多个线程在同时等待,但一个work只需唤醒一个线程来做即可。

在这里插入图片描述
对于底层Binder驱动,通过 binder_procs 链表记录所有创建的 binder_proc 结构体,binder 驱动层的每一个 binder_proc 结构体都与用户空间的一个用于 binder 通信的进程一一对应,且每个进程有且只有一个 ProcessState 对象,这是通过单例模式来保证的。在每个进程中可以有很多个线程,每个线程对应一个 IPCThreadState 对象,IPCThreadState 对象也是单例模式,即一个线程对应一个 IPCThreadState 对象,在 Binder 驱动层也有与之相对应的结构,那就是 Binder_thread 结构体。在 binder_proc 结构体中通过成员变量 rb_root threads,来记录当前进程内所有的 binder_thread。
Binder 线程池:每个 Server 进程在启动时创建一个 binder 线程池,并向其中注册一个 Binder 线程;之后 Server 进程也可以向 binder 线程池注册新的线程,或者 Binder 驱动在探测到没有空闲 binder 线程时主动向 Server 进程注册新的的 binder 线程。对于一个 Server 进程有一个最大 Binder 线程数限制,默认为16个 binder 线程,例如 Android 的 system_server 进程就存在16个线程。对于所有 Client 端进程的 binder 请求都是交由 Server 端进程的 binder 线程来处理的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值