Android binder学习笔记 – binder线程创建

  • Post author:
  • Post category:其他




1. 前言

本文主要是

binder系列

文章的总结笔记,主要是理清binder的总体流程和总体架构,期间会对照Android R进行代码重读,也会按照自己的理解对内容进行调整,以加深对binder总体的理解。本文主要讲述 获取binder线程创建,主要以vold进程的binder线程创建为例。



2. vold/main

vold/main
    |--mkdir("/dev/block/vold", 0755);
    |--vm = VolumeManager::Instance()
    |--nm = NetlinkManager::Instance()
    |--vm->start()
    |--process_config(vm, &has_adoptable, &has_quota, &has_reserved)
    |--android::vold::VoldNativeService::start()
    |      |--IPCThreadState::self()->disableBackgroundScheduling(true);
    |      |--BinderService<VoldNativeService>::publish();
    |      |--sp<ProcessState> ps(ProcessState::self())
    |      |--ps->startThreadPool()
    |      |--ps->giveThreadPoolName()
    |--nm->start()
    |--coldboot("/sys/block");



|- -ProcessState::self()

ProcessState::self()是单例模式,主要工作是调用open()打开/dev/binder驱动设备,再利用mmap()映射内核的地址空间,将Binder驱动的fd赋值ProcessState对象中的变量mDriverFD,用于交互操作。

ProcessState::self()
   |--if (gProcess != nullptr) return gProcess;
   |--gProcess = new ProcessState(kDefaultDriver)
           |--open_driver(driver)
           |      |--status_t result = ioctl(fd, BINDER_VERSION, &vers)
           |      |--size_t maxThreads = DEFAULT_MAX_BINDER_THREADS
           |      |--result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
           |--mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | 
                     MAP_NORESERVE, mDriverFD, 0);

ProcessState的单例模式的惟一性,因此一个进程只打开binder设备一次,其中ProcessState的成员变量mDriverFD记录binder驱动的fd,用于访问binder设备。ProcessState是负责打开Binder节点并做mmap映射,IPCThreadState是负责与Binder驱动进行具体的命令交互。open_driver会创建binder_proc,binder_proc的pid为当前进程组的领头进程,它会被保存到filp->private_data中



|- -ps->startThreadPool()

ps->startThreadPool();
   |--mThreadPoolStarted = true
   |--spawnPooledThread(true)        
        |  //获取Binder线程名Binder:<pid>_x
        |--String8 name = makeBinderThreadName();
        |--sp<Thread> t = new PoolThread(isMain);
        |--t->run(name.string())
               |--threadLoop()
threadLoop()
	|--IPCThreadState::self()->joinThreadPool(mIsMain)
	     |  //对于isMain=true的情况下, command为BC_ENTER_LOOPER,代表的是Binder主线程,不会退出的线程;
         |  //对于isMain=false的情况下,command为BC_REGISTER_LOOPER,表示是由binder驱动创建的线程。
	     |--mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER)
	     |--do {
	     |       //清除队列的引用
	     |       processPendingDerefs(); 
	     |       result = getAndExecuteCommand()
	     |  } while (result != -ECONNREFUSED && result != -EBADF);
	     |--mOut.writeInt32(BC_EXIT_LOOPER);
         |--talkWithDriver(false)

startThreadPool主要是通过创建线程池,实际只创建了一个线程,最终执行joinThreadPool,通过循环调用getAndExecuteCommand->talkWithDriver来与binder驱动进行交互。

IPCThreadState::getAndExecuteCommand
    |--result = talkWithDriver();
    |      |-- binder_write_read bwr
    |      |--bwr.write_size = outAvail;
    |      |  bwr.write_buffer = (uintptr_t)mOut.data()
    |      |--do {
    |      |      if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
    |      |          err = NO_ERROR;
    |      |  } while (err == -EINTR);
    |--size_t IN = mIn.dataAvail()
    |--cmd = mIn.readInt32()
    |--mProcess->mExecutingThreadsCount++;
    |--result = executeCommand(cmd)



|- – -ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)

ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr)
    |--struct binder_proc *proc = filp->private_data
    |--wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 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;       
       ...
       }    
binder_ioctl_write_read(filp, cmd, arg, thread)
    |--struct binder_proc *proc = filp->private_data
    |  void __user *ubuf = (void __user *)arg
    |--copy_from_user(&bwr, ubuf, sizeof(bwr))
    |--if (bwr.write_size > 0)
    |      binder_thread_write(proc, thread,
    |                       bwr.write_buffer,
    |                       bwr.write_size,
    |                       &bwr.write_consumed);
    |--if (bwr.read_size > 0)
    |      binder_thread_read(proc, thread, bwr.read_buffer,
    |                     bwr.read_size,
    |                     &bwr.read_consumed,
    |                     filp->f_flags & O_NONBLOCK);
    |--copy_to_user(ubuf, &bwr, sizeof(bwr)        

首先进入binder_thread_write过程

binder_thread_write(proc, thread,
                           bwr.write_buffer,
                           bwr.write_size,
                           &bwr.write_consumed);
    |--struct binder_context *context = proc->context
    |  void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    |  void __user *ptr = buffer + *consumed;
    |  void __user *end = buffer + size;   
    |--while (ptr < end && thread->return_error.cmd == BR_OK)
           //拷贝用户空间的cmd命令,此时为BC_ENTER_LOOPER
           if (get_user(cmd, (uint32_t __user *)ptr)) -EFAULT;
           switch (cmd) {
           ......
           case BC_ENTER_LOOPER:
               if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
                   //出错原因:线程调用完BC_REGISTER_LOOPER,不能立刻执行该分支
                   thread->looper |= BINDER_LOOPER_STATE_INVALID;
               }
               //创建Binder主线程                               
               thread->looper |= BINDER_LOOPER_STATE_ENTERED;
               break;            

处理完BC_ENTER_LOOPER命令后,一般情况下成功设置thread->looper |= BINDER_LOOPER_STATE_ENTERED。那么binder线程的创建是在什么时候呢? 那就当该线程有事务需要处理的时候,进入binder_thread_read()过程

binder_thread_read(proc, thread, bwr.read_buffer,
                     bwr.read_size,
                     &bwr.read_consumed,
                     filp->f_flags & O_NONBLOCK)
    |--void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    |  void __user *ptr = buffer + *consumed;
    |  void __user *end = buffer + size; 
    |  //当前线程todo队列为空且transaction栈为空,则代表该线程是空闲的
    |--wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
    |  if (non_block) {
    |      if (!binder_has_work(thread, wait_for_proc_work))                                                                                                                                              
    |         ret = -EAGAIN;                                                                                                                                                                         
    |  } else //阻塞的则等待处理完毕
    |      ret = binder_wait_for_work(thread, wait_for_proc_work);
    |  thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
    |--while (1) {
    |      uint32_t cmd;
    |      struct binder_transaction_data_secctx tr;
    |      struct binder_transaction_data *trd = &tr.transaction_data;
    |      struct binder_work *w = NULL;
    |      struct list_head *list = NULL;
    |      struct binder_transaction *t = NULL;
    |      struct binder_thread *t_from;
    |      size_t trsize = sizeof(*trd);
    |
    |      binder_inner_proc_lock(proc);
    |      if (!binder_worklist_empty_ilocked(&thread->todo))
    |              list = &thread->todo;
    |      else if (!binder_worklist_empty_ilocked(&proc->todo) &&
    |                 wait_for_proc_work)
    |              list = &proc->todo;
    |      else {
    |              binder_inner_proc_unlock(proc);
    |
    |              /* no data added */
    |              if (ptr - buffer == 4 && !thread->looper_need_return)
    |                      goto retry;
    |              break;
    |      }

    |      if (end - ptr < sizeof(tr) + 4) {
    |              binder_inner_proc_unlock(proc);
    |              break;
    |      }
    |      w = binder_dequeue_work_head_ilocked(list);
    |      if (binder_worklist_empty_ilocked(&thread->todo))
    |              thread->process_todo = false;
    |
    |      switch (w->type) {
    |      case BINDER_WORK_TRANSACTION: {
    |                    binder_inner_proc_unlock(proc);
    |                    t = container_of(w, struct binder_transaction, work);
    |      } break;
    |      ....
    |      if (!t)
    |           continue;
    |      ...
    |--if (proc->requested_threads + proc->ready_threads == 0 &&
	        proc->requested_threads_started < proc->max_threads &&
	        (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
	         BINDER_LOOPER_STATE_ENTERED)))
	        proc->requested_threads++;
	        // 生成BR_SPAWN_LOOPER命令,用于创建新的线程
	        put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer)



|- – -executeCommand(cmd)

IPC.joinThreadPool –> IPC.getAndExecuteCommand() -> IPC.talkWithDriver() ,但talkWithDriver收到事务之后, 便进入IPC.executeCommand(), 接下来,从executeCommand说起.

IPCThreadState::executeCommand(int32_t cmd)
    |--BBinder* obj;
    |  RefBase::weakref_type* refs;
    |  status_t result = NO_ERROR;
    |--switch ((uint32_t)cmd)
    |  ...
    |  case BR_SPAWN_LOOPER:
    |    mProcess->spawnPooledThread(false);
    |    break;
    | ...



参考文档


进程的Binder线程池工作过程