Binder机制 - ServiceManager(1)启动

ServiceManager启动:

ServiceManager进程由init进程解析相应的.rc启动并进入执行入口main函数。

不同的版本具体实现有差异,但都包含这三个关键动作:

  1. 打开binder设备驱动,mmap映射共享内存

  2. 向驱动注册为服务管理者

  3. 进入循环不断处理请求

android 8.0:

  1. binder_open 打开binder设备驱动

  2. binder_become_context_manager 注册为服务管理者

  3. binder_loop 进入循环等待处理请求

本文以8.0代码为例讲解:

1、打开binder设备驱动以及映射共享内存

../frameworks/native/cmd/binder.c

C++ 复制代码
struct binder_state *binder_open(size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    ...
    // 1
    bs->fd = open("/dev/binder", O_RDWR);
    ...

    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        ...
        // ERR
    }

    bs->mapsize = mapsize;
    // 2
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    ...
    return bs;
...
}

Binder驱动设备注册,设置了驱动主体函数入口,包括本系列常用到的 open对应驱动binder_open,mmap对应驱动binder_mmap,ioctl对应binder_ioctl

C++ 复制代码
device_initcall(binder_init);

static int __init binder_init(void)
{
    ...
    // 设备注册
    ret = misc_register(&binder_miscdev);
    ...
}

static struct miscdevice binder_miscdev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "binder",
	.fops = &binder_fops // 驱动主体函数入口指针
};

static 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,
};

1.1 open -> 驱动 binder_open

C++ 复制代码
static HLIST_HEAD(binder_procs);
static int binder_open(struct inode *nodp, struct file *filp)
{
	struct binder_proc *proc;

	...
	//binder_proc分配内存,这个是ServiceManager进程上下文的结构体
	proc = kzalloc(sizeof(*proc), GFP_KERNEL);
	if (proc == NULL)
		return -ENOMEM;
	get_task_struct(current);
	//将proc->tsk指向当前线程current
	proc->tsk = current;
	//proc的待处理事务列表,创建头结点
	INIT_LIST_HEAD(&proc->todo);
	//proc的等待队列,创建头结点
	init_waitqueue_head(&proc->wait);
	//设置proc的进程优先级为当前进程优先级
	proc->default_priority = task_nice(current);

	binder_lock(__func__);

	binder_stats_created(BINDER_STAT_PROC);
	//将ServiceManager的proc保存到"全局哈希表binder_procs"中
	hlist_add_head(&proc->proc_node, &binder_procs);
	//设置进程id
	proc->pid = current->group_leader->pid;
	INIT_LIST_HEAD(&proc->delivered_death);
	//将proc赋给filp私有数据private_data
	//这样,mmap(),ioctl()等函数都可以通过私有数据获取到proc,即该进程的上下文信息
	filp->private_data = proc;

	binder_unlock(__func__);
	...
	return 0;
}
  1. 创建并初始化ServiceManager的binder_proc。binder_proc是描述Binder进程的上下文信息结构体。也就是将ServiceManager的进程的信息存储到proc中。

  2. 将ServiceManager的binder_proc添加到全局哈希表binder_procs中。

  3. 将proc设为filp的private_data,使得在mmap(),ioctl()等函数中,我们都可以通过filp的private_data来获取proc信息。

1.2 mmap ->驱动 binder_mmap

C++ 复制代码
static int binder_mmap(struct file *filp, struct vm_area_struct *vma)
{
  int ret;
  struct vm_struct *area;
  // 1
  struct binder_proc *proc = filp->private_data;
  const char *failure_string;
  struct binder_buffer *buffer;

  // 有效性检查:映射的内存不能大于4M
  if ((vma->vm_end - vma->vm_start) > SZ_4M)
      vma->vm_end = vma->vm_start + SZ_4M;

  ...

  vma->vm_flags = (vma->vm_flags | VM_DONTCOPY) & ~VM_MAYWRITE;

  mutex_lock(&binder_mmap_lock);

  // 2 获取空闲的内核空间地址
  area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP);
  ...

  // 3.1 将内核空间地址赋值到proc->buffer
  proc->buffer = area->addr;
  // 3.2 计算 "内核空间地址" 和 "进程虚拟地址" 的偏移
  proc->user_buffer_offset = vma->vm_start - (uintptr_t)proc->buffer;
  mutex_unlock(&binder_mmap_lock);

  // 3.3 分配内存,proc->pages为内存对象起始地址
  proc->pages = kzalloc(sizeof(proc->pages[0]) 
                        * ((vma->vm_end - vma->vm_start) / PAGE_SIZE), GFP_KERNEL);
  ...

  // 3.4 内核空间的内存大小 = 进程虚拟地址区域(用户空间)的内存大小
  proc->buffer_size = vma->vm_end - vma->vm_start;

  vma->vm_ops = &binder_vm_ops;
  // 将 proc(进程上下文信息) 赋值给vma私有数据
  vma->vm_private_data = proc;

  // 4 通过调用binder_update_page_range()来分配物理页面,
  // 以及将物理内存映射到内核空间以及用户空间
  if (binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)) {
      goto err_alloc_small_buf_failed;
  }
  buffer = proc->buffer;
  INIT_LIST_HEAD(&proc->buffers);
  // 将物理内存添加到proc->buffers链表中进行管理。
  list_add(&buffer->entry, &proc->buffers);
  buffer->free = 1;
  binder_insert_free_buffer(proc, buffer);
  proc->free_async_space = proc->buffer_size / 2;
  barrier();
  proc->files = get_files_struct(proc->tsk);
  // 将用户空间地址信息保存到proc中
  proc->vma = vma;
  proc->vma_vm_mm = vma->vm_mm;

  return 0;
  ...
}
  1. proc = filp->private_data。该flip是在binder_open()初始化的,通过 flip获取到 binder_proc。

  2. area = get_vm_area(vma->vm_end - vma->vm_start, VM_IOREMAP)。

    • get_vm_area:从内核虚拟地址中,获取指定大小的空闲地址,起始地址赋值给area。
    • area:vm_struct类型。vm_struct 描述内核虚拟地址信息(vm_area_struct 是描述进程虚拟地址信息)
  3. 赋值:

    proc->buffer(内核空间分配的虚拟内存的起始地址)

    proc->user_buffer_offset(内核空间地址和进程虚拟地址的偏移值)

    • 用于内核虚拟地址和进程虚拟地址相互获取

      内核 + offset = 用户进程地址,已知其中一个就能获得另一个

    proc->pages(内核空间所占物理页面)

    • struct page 结构体
    • 映射内存的page页的数组,page是描述物理内存的结构体

    proc->buffer_size(内核地址空间的大小)

  4. 调用binder_update_page_range(proc, 1, proc->buffer, proc->buffer + PAGE_SIZE, vma)。

    • 分配物理内存

    binder_update_page_range 的分析

    C++ 复制代码
    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;
        ...
        // 分配物理页面,
        // 并将"内核空间"和"用户空间(进程的内存区域)"指向同一块物理内存。
        for (page_addr = start; page_addr < end; page_addr += PAGE_SIZE) {
            int ret;
            page = &proc->pages[(page_addr - proc->buffer) / PAGE_SIZE];
    
            // 分配物理页面
            *page = alloc_page(GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO);
            ...
            // 将物理页面映射到内核空间中
            ret = map_kernel_range_noflush((unsigned long)page_addr,
                        PAGE_SIZE, PAGE_KERNEL, page);
            flush_cache_vmap((unsigned long)page_addr,
                    (unsigned long)page_addr + PAGE_SIZE);
            ...
            user_page_addr =
                (uintptr_t)page_addr + proc->user_buffer_offset;
            // 将物理页面映射插入到进程的虚拟内存中
            ret = vm_insert_page(vma, user_page_addr, page[0]);
            ...
        }
        ...
        return 0;
        ...
    }

小结

打开/dev/binder设备驱动,Binder驱动新建并初始化描述该进程的binder_proc结构体,

获取内核虚拟地址,将其和该进程用户空间虚拟地址映射到同一物理内存。

2、注册为管理者 binder_become_context_manager

C++ 复制代码
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

向驱动发起注册请求

2.1 binder_ioctl

C 复制代码
// 全局binder实体,准确点说是ServiceManager的binder实体
static struct binder_node *binder_context_mgr_node;
// ServiceManager守护进程的uid
static uid_t binder_context_mgr_uid = -1;
static int binder_stop_on_user_error;
...
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    int ret;
    // 取SM的binder_proc
    struct binder_proc *proc = filp->private_data;
    struct binder_thread *thread;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    trace_binder_ioctl(cmd, arg);

    ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
    ...

    binder_lock(__func__);
    // 取SM的binder_thread
    //在proc进程中查找该线程对应的 binder_thread;
    //若查找失败,则新建一个binder_thread,并添加到proc->threads中
    thread = binder_get_thread(proc);
    ...

    switch (cmd) {
        ...
        // *** 注册服务管理者
        case BINDER_SET_CONTEXT_MGR:
            ret = binder_ioctl_set_ctx_mgr(filp);
            ...
    }
    ret = 0;
err:
    // 去掉thread的BINDER_LOOPER_STATE_NEED_RETURN标记
    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);
    ...

    return ret;
}
  1. proc = flip->private_data,获取binder_proc。flip的私有数据是在binder_open()时设置的

  2. cmd的值是我们在service_manager.c调用ioctl()传入的参数 BINDER_SET_CONTEXT_MGR,调用binder_ioctl_set_ctx_mgr

  3. 代码最后调用 thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN

    • 表示清除thread->looper的 BINDER_LOOPER_STATE_NEED_RETURN标记
    • BINDER_LOOPER_STATE_NEED_RETURN标记:是在调用binder_get_thread()中创建binder_thread对象时添加的。

2.2 binder_ioctl_set_ctx_mgr

C 复制代码
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();

    ...
    if (uid_valid(binder_context_mgr_uid)) {
        if (!uid_eq(binder_context_mgr_uid, curr_euid)) {
            pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
                   from_kuid(&init_user_ns, curr_euid),
                   from_kuid(&init_user_ns,
                    binder_context_mgr_uid));
            ret = -EPERM;
            goto out;
        }
    } else {
        //设置ServiceManager对应的uid
        binder_context_mgr_uid = curr_euid;
    }
    //新建binder实体,并将proc保存到binder实体中;
    //然后,将该binder实体赋值给binder_context_mgr_node,是一个全局变量。
    //这个binder实体是ServiceManager对应的binder实体。
    binder_context_mgr_node = binder_new_node(proc, 0, 0);
    ...
    //设置binder实体的引用计数相关参数
    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;
}
  1. 设置binder_context_mgr_uid,代表ServiceManager所对应的uid;

  2. 创建binder实体,并将该新建的Binder实体赋值给binder_context_mgr_node,binder_context_mgr_node是一个全局变量;

  3. 最后,设置binder实体的引用计数等参数。

关于binder_node结构体:

  • Binder驱动为每一个 service创建一个 binder_node 代表service在驱动中的binder实体。
  • 而对于ServiceManager这个服务管理者也是作为服务存在,Binder驱动创建了全局变量binder_context_mgr_node 作为其binder实体。

2.3 创建binder实体

binder_new_node(proc, 0, 0)

C++ 复制代码
static struct binder_node *binder_new_node(struct binder_proc *proc,
                       binder_uintptr_t ptr,
                       binder_uintptr_t cookie)
{
    struct rb_node **p = &proc->nodes.rb_node;
    struct rb_node *parent = NULL;
    struct binder_node *node;

    //在binder_proc的nodes红黑树中,查找是否有匹配的binder实体(通过ptr来判断)
    while (*p) {
        parent = *p;
        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;
    }

    //如果没有找到要找的binder实体,则新建该binder实体
    node = kzalloc(sizeof(*node), GFP_KERNEL);
    if (node == NULL)
        return NULL;
    binder_stats_created(BINDER_STAT_NODE);
    // 将node链接到红黑树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中
    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);
    ...
    return node;
}
  1. 首先在 proc->nodes 红黑树中查找是否有binder实体(binder_node)存在。有的话,返回NULL,即不需要新建binder实体;
  2. 没有查找到,新建并初始化binder_node,然后将其添加到proc->nodes红黑树中。

小结

是从用户空间向驱动发起的请求,在binder驱动中主要做了如下事情:

  • 新建当前线程对应的binder_thread对象,并将其添加到进程上下文信息binder_proc的threads红黑树中;

  • 新建ServiceManager对应的binder实体并将其保存为全局变量binder_context_mgr_node中。

3、进入循环处理 binder_loop

C++ 复制代码
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    
    //告诉Kernel binder驱动,我(ServiceManager进程)要进入消息循环状态了,请做好相关准备
    readbuf[0] = BC_ENTER_LOOPER;
    // 1 发起BC_ENTER_LOOPER
    binder_write(bs, readbuf, sizeof(uint32_t));

    // 2 循环
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        //向Kernel binder驱动中发送消息(消息顺序是先写后读)
        //先将消息传递给Kernel binder驱动,然后再从kernel读取消息反馈
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

        ...
        
        // 3 解析读取的消息反馈
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        ...
    }
}
  1. binder_write(,BC_ENTER_LOOPER,) 准备进入消息循环状态,通知 Kernel Binder驱动做好相关准备。

  2. 进入消息循环后,调用ioctl(,BINDER_WRITE_READ,)

  3. binder_parse 解析处理ioctl返回了的数据

3.1 发起BC_ENTER_LOOPER命令

3.1.1 binder_write

C++ 复制代码
int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;

    bwr.write_size = len;			//数据长度
    bwr.write_consumed = 0;
    bwr.write_buffer = (uintptr_t) data;	//BC_ENTER_LOOPER
    bwr.read_size = 0;			//read_size为0,不会读取驱动数据			
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}

这里涉及到了Binder通信中常用的数据结构体binder_write_read。

bwr.write_size>0:表示通过ServiceManager有数据(即BC_ENTER_LOOPER指令)发送给Binder驱动,

bwr.write_buffer:发送的数据(BC_ENTER_LOOPER)

bwr.write_consumed:已经被读取并处理的数据的大小。

bwr.read_XXX:保存Binder驱动即将反馈给ServiceManager的各种信息,这里都是0。

这里可以看出,binder_write 函数就是通过ioctl向binder读写数据,只写不读。

3.1.2 binder_ioctl - BINDER_WRITE_READ

binder_loop -> binder_write -> binder_ioctl

C++ 复制代码
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;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    ...
    // 中断等待函数。
    // 1. 当binder_stop_on_user_error < 2为true时;不会进入等待状态;直接跳过。
    // 2. 当binder_stop_on_user_error < 2为false时,进入等待状态。
    //    当有其他进程通过wake_up_interruptible来唤醒binder_user_error_wait队列,
    //    并且binder_stop_on_user_error < 2为true时;
    //    则继续执行;否则,再进入等待状态。
    // 1 跳过
    ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
    if (ret)
        goto err_unlocked;

    binder_lock(__func__);
    // 在proc进程中查找该线程对应的binder_thread;
    // 若查找失败,则新建一个binder_thread,并添加到proc->threads中。
    // 2 上一次已经创建过了,可以直接获取到
    thread = binder_get_thread(proc);
    ...
    switch (cmd) {
    case BINDER_WRITE_READ:
        ret = binder_ioctl_write_read(filp, cmd, arg, thread);
        if (ret)
            goto err;
        break;
    }
    ...
    ret = 0;
    ...
    return ret;
}

3.1.3 binder_ioctl_write_read

binder_loop -> binder_write -> binder_ioctl -> binder_ioctl_write_read

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;
    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;
    }
    // 3 将binder_write_read从"用户空间" 拷贝到 "内核空间"
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
    ...
    // 4 write_size>0,进行写操作
    if (bwr.write_size > 0) {
        ret = binder_thread_write(proc, thread,
                      bwr.write_buffer,
                      bwr.write_size,
                      &bwr.write_consumed);
        ...
    }
    // 5 read_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);
        if (!list_empty(&proc->todo))
            wake_up_interruptible(&proc->wait);
        ...
    }
    ...
    // 6
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
out:
    return ret;
}
  1. wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2)

    binder_stop_on_user_error < 2为true。因此,不进入中断等待状态而是直接跳过该函数。

  2. thread = binder_get_thread(proc)。

    上次调用ioctl时,已经创建了该线程对应的binder_thread对象,因此这次能在proc->threads红黑树中找到

  3. copy_from_user(&bwr, ubuf, sizeof(bwr)) 作用是将用户空间的数据拷贝到内核空间。

    将ServiceManager调用ioctl(bs->fd, BINDER_WRITE_READ, &bwr)时的bwr对象拷贝到Binder驱动中。

  4. 在binder_write()中设置的 bwr.write_size>0;所以,调用binder_thread_write()进行写操作。

  5. 在binder_write()中设置的bwr.read_size==0;所以,不调用binder_thread_read()进行读操作。

  6. 读写操作完毕之后,将bwr从内核空间再拷贝到用户空间。

3.1.4 binder_thread_write

binder_loop -> binder_write -> binder_ioctl -> binder_ioctl_write_read -> binder_thread_write

C++ 复制代码
int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
          void __user *buffer, int size, signed long *consumed)
{
  uint32_t cmd;
  void __user *ptr = buffer + *consumed;
  void __user *end = buffer + size;

  // 读取binder_write_read.write_buffer中的内容。
  // 每次读取32bit(即4个字节)的数据
  while (ptr < end && thread->return_error == BR_OK) {
      // 从用户空间读取32bit到内核中,并赋值给cmd。
      if (get_user(cmd, (uint32_t __user *)ptr))
          return -EFAULT;

      ptr += sizeof(uint32_t);
      ...
      switch (cmd) {
      ...
      case BC_ENTER_LOOPER:
          ...
          // 设置线程的状态为BINDER_LOOPER_STATE_ENTERED;
          // 即,进入了循环状态
          thread->looper |= BINDER_LOOPER_STATE_ENTERED;
          break;
      ...
      }
      // 更新bwr.write_consumed的值
      *consumed = ptr - buffer;
  }
  return 0;
}

说明:

  1. binder_thread_write()从brw.write_buffer中每次读取前4个字节作为cmd。

    这里这4个字节就是ServiceManager传递的指令BC_ENTER_LOOPER。

  2. case BC_ENTER_LOOPER:将BINDER_LOOPER_STATE_ENTERED加入到thread->looper中。告诉Binder驱动,ServiceManager进程进入了消息循环状态。

3.2 正式进入循环体

回到 binder_loop

C++ 复制代码
void binder_loop(struct binder_state *bs, binder_handler func)
{
    ...
    // 2 循环
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;

        //向Kernel binder驱动中发送消息(消息顺序是先写后读)
        //先将消息传递给Kernel binder驱动,然后再从kernel读取消息反馈
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

        ...
        
        // 3 解析读取的消息并反馈
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        ...
    }
}

此时的 bwr 内容:

C++ 复制代码
    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
for (;;) {
    bwr.read_size = sizeof(readbuf);
    bwr.read_consumed = 0;
    bwr.read_buffer = (unsigned) readbuf;

bwr.write_size=0,而bwr.read_size>0;表示只从Binder驱动读取数据,并不向Binder驱动写入数据。

可以看出循环体中的ioctl实际就是负责只读不写。(写动作在后面的binder_parse里会根据不同场景俺需调用binder_write 写)

接着调用ioctl()再次进入到Binder驱动binder_ioctl()执行 binder_ioctl_write_read:

3.2.1 binder_ioctl_write_read

binder_loop -> binder_ioctl -> binder_ioctl_write_read

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) {
        ...
    }
    // read_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);
        if (!list_empty(&proc->todo))
            wake_up_interruptible(&proc->wait);
        ...
    }
    ...
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }
out:
    return ret;
}

binder_thread_read

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;
      int wait_for_proc_work;

      // 1 如果*consumed=0,则写入BR_NOOP到用户传进来的bwr.read_buffer缓存区
    if (*consumed == 0) {
        if (put_user(BR_NOOP, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
    }

retry:
      // 2 等待proc进程的事务标记。
      // 当线程的事务栈为空 并且 待处理事务列表为空时,该标记位true。
    wait_for_proc_work = thread->transaction_stack == NULL &&
                list_empty(&thread->todo);

    ...

      // 设置线程为"等待状态"
    thread->looper |= BINDER_LOOPER_STATE_WAITING;
    if (wait_for_proc_work)
        proc->ready_threads++;

    ...
    if (wait_for_proc_work) {
        ...
        // 设置当前线程的优先级=proc->default_priority。
        //即,当前线程要处理proc的事务,所以设置优先级和proc一样
        binder_set_nice(proc->default_priority);
        // 4
        if (non_block) {
             // 非阻塞式的读取,则通过binder_has_proc_work()读取proc的事务;
            // 若没有,则直接返回
            if (!binder_has_proc_work(proc, thread))
                ret = -EAGAIN;
        } else
            // 4 阻塞式的读取,则阻塞等待事务的发生。
            ret = wait_event_freezable_exclusive(proc->wait,
                                                 binder_has_proc_work(proc, thread));
    } else {
        ...
    }
  ...
}
  1. 刚进来还没有消费,bwr.read_consumed=0,将BR_NOOP拷贝到用户空间的bwr.read_buffer缓存区中。

  2. 目前为止,并没有进程将事务添加到当前线程中;因此,线程的事务栈和待处理事务队列都是为空。于是得到wait_for_proc_work的值是true。

  3. non_block表示是否阻塞式读取,这里是false,调用 wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread))。

    目前ServiceManager进程中没有待处理事务,因此binder_has_proc_work(proc, thread)为false。

    从而当前线程进入中断等待状态,等待其它进程将ServiceManager唤醒。

至此,ServiceManager进入了等待状态,binder_loop()就分析就告一段落了。

3.2.2 binder_parse

解析读取的消息并反馈。假如3.2.1读取到了数据,这里负责解析和处理。后续的addService getService讲解的文章会涉及到。

小结

循环体内实际分两部分,ioctl和binder_parse,ioctl负责从驱动读数据,binder_parse负责解析和处理读到的数据(binder_parse也有涉及向驱动发起请求的场景,如BR_TRANSACTION的处理,此时会调用binder_write进入驱动后仅做写处理,读取还是通过下一次循环ioctl读)。处理完毕后进入下一次循环重复该动作。

android 10.0:

安卓10 对binder通信相关内容做了重构:

  1. 把对binder通信的相关操做了解耦,给了ProcessState/IPCThreadState处理,与普通进程的方式一致了;
  2. 把自己注册为binder服务也是通过调用自己的addService把自己注册为服务;
  3. 把自己设置为服务管理者,也抽取到ProcessState调用处理。与普通进程的binder管理方式归一。

而最终实际要做的事情都是一致的:打开驱动,mmap映射内存,注册为管理者,进入循环等待请求。本文没有写安卓10的实现方式,如果有需要后续可以另外补充。

SM启动总结

对于ServiceManager进程而言

  1. 打开Binder设备文件,映射内存。
  2. 向Binder驱动发起将自己注册为Binder上下文管理者。
  3. 进入循环,等待Client请求。

对于Binder驱动而言

  1. 初始化ServiceManager对应的进程上下文环境(binder_proc),并将内核虚拟地址和进程虚拟地址映射到同一物理内存。
  2. 接到ServiceManager注册为Binder上下文管理者的请求后,建立ServiceManager对应的Binder实体binder_node,并将该Binder实体保存为全局变量binder_context_mgr_node。
  3. 最后,ServiceManager进入消息循环后发起binder_thread_read,如果没有事务会进入等待状态,等待其他进程发起请求将其唤醒。
相关推荐
雨白9 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹10 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空12 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭12 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日13 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安13 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑14 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟18 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡19 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0019 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体