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,如果没有事务会进入等待状态,等待其他进程发起请求将其唤醒。
相关推荐
试行11 分钟前
Android实现自定义下拉列表绑定数据
android·java
Dingdangr5 小时前
Android中的Intent的作用
android
技术无疆5 小时前
快速开发与维护:探索 AndroidAnnotations
android·java·android studio·android-studio·androidx·代码注入
GEEKVIP5 小时前
Android 恢复挑战和解决方案:如何从 Android 设备恢复删除的文件
android·笔记·安全·macos·智能手机·电脑·笔记本电脑
Jouzzy12 小时前
【Android安全】Ubuntu 16.04安装GDB和GEF
android·ubuntu·gdb
极客先躯12 小时前
java和kotlin 可以同时运行吗
android·java·开发语言·kotlin·同时运行
Good_tea_h15 小时前
Android中的单例模式
android·单例模式
计算机源码社20 小时前
分享一个基于微信小程序的居家养老服务小程序 养老服务预约安卓app uniapp(源码、调试、LW、开题、PPT)
android·微信小程序·uni-app·毕业设计项目·毕业设计源码·计算机课程设计·计算机毕业设计开题
丶白泽20 小时前
重修设计模式-结构型-门面模式
android
晨春计1 天前
【git】
android·linux·git