美文网首页
android binder 进程系列 进程的Binder线程池

android binder 进程系列 进程的Binder线程池

作者: 蜗牛是不是牛 | 来源:发表于2022-12-14 16:26 被阅读0次

    基于Android 6.0源码剖析,分析Binder线程池以及binder线程启动过程。

    frameworks/base/cmds/app_process/app_main.cpp
    frameworks/native/libs/binder/ProcessState.cpp
    framework/native/libs/binder/IPCThreadState.cpp
    kernel/drivers/staging/android/binder.c
    
    

    一. 概述

    Android系统启动完成后,ActivityManager, PackageManager等各大服务都运行在system_server进程,app应用需要使用系统服务都是通过binder来完成进程之间的通信,上篇文章彻底理解Android Binder通信架构,从整体架构以及通信协议的角度来阐述了Binder架构。那对于binder线程是如何管理的呢,又是如何创建的呢?其实无论是system_server进程,还是app进程,都是在进程fork完成后,便会在新进程中执行onZygoteInit()的过程中,启动binder线程池。接下来,就以此为起点展开从线程的视角来看看binder的世界。

    二. Binder线程创建

    Binder线程创建与其所在进程的创建中产生,Java层进程的创建都是通过Process.start()方法,向Zygote进程发出创建进程的socket消息,Zygote收到消息后会调用Zygote.forkAndSpecialize()来fork出新进程,在新进程中会调用到RuntimeInit.nativeZygoteInit方法,该方法经过jni映射,最终会调用到app_main.cpp中的onZygoteInit,那么接下来从这个方法说起。

    2.1 onZygoteInit

    [-> app_main.cpp]

    virtual void onZygoteInit()
    {
        //获取ProcessState对象
        sp<ProcessState> proc = ProcessState::self();
        //启动新binder线程 【见小节2.2】
        proc->startThreadPool();
    }
    
    

    ProcessState::self()是单例模式,主要工作是调用open()打开/dev/binder驱动设备,再利用mmap()映射内核的地址空间,将Binder驱动的fd赋值ProcessState对象中的变量mDriverFD,用于交互操作。startThreadPool()是创建一个新的binder线程,不断进行talkWithDriver()。 详细过程,见注册服务的[小节二].

    2.2 PS.startThreadPool

    [-> ProcessState.cpp]

    void ProcessState::startThreadPool()
    {
        AutoMutex _l(mLock);    //多线程同步
        if (!mThreadPoolStarted) {
            mThreadPoolStarted = true;
            spawnPooledThread(true);  【见小节2.3】
        }
    }
    
    

    启动Binder线程池后, 则设置mThreadPoolStarted=true. 通过变量mThreadPoolStarted来保证每个应用进程只允许启动一个binder线程池, 且本次创建的是binder主线程(isMain=true). 其余binder线程池中的线程都是由Binder驱动来控制创建的。

    2.3 PS.spawnPooledThread

    [-> ProcessState.cpp]

    void ProcessState::spawnPooledThread(bool isMain)
    {
        if (mThreadPoolStarted) {
            //获取Binder线程名【见小节2.3.1】
            String8 name = makeBinderThreadName();
            //此处isMain=true【见小节2.3.2】
            sp<Thread> t = new PoolThread(isMain);
            t->run(name.string());
        }
    }
    
    

    2.3.1 makeBinderThreadName

    [-> ProcessState.cpp]

    String8 ProcessState::makeBinderThreadName() {
        int32_t s = android_atomic_add(1, &mThreadPoolSeq);
        String8 name;
        name.appendFormat("Binder_%X", s);
        return name;
    }
    
    

    获取Binder线程名,格式为Binder_x, 其中x为整数。每个进程中的binder编码是从1开始,依次递增; 只有通过spawnPooledThread方法来创建的线程才符合这个格式,对于直接将当前线程通过joinThreadPool加入线程池的线程名则不符合这个命名规则。 另外,目前Android N中Binder命令已改为Binder:<pid>_x格式, 则对于分析问题很有帮忙,通过binder名称的pid字段可以快速定位该binder线程所属的进程p.

    2.3.2 PoolThread.run

    [-> ProcessState.cpp]

    class PoolThread : public Thread
    {
    public:
        PoolThread(bool isMain)
            : mIsMain(isMain)
        {
        }
    
    protected:
        virtual bool threadLoop()
        {
            IPCThreadState::self()->joinThreadPool(mIsMain); //【见小节2.4】
            return false;
        }
        const bool mIsMain;
    };
    
    

    从函数名看起来是创建线程池,其实就只是创建一个线程,该PoolThread继承Thread类。t->run()方法最终调用 PoolThread的threadLoop()方法。

    2.4 IPC.joinThreadPool

    [-> IPCThreadState.cpp]

    void IPCThreadState::joinThreadPool(bool isMain)
    {
        //创建Binder线程
        mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
        set_sched_policy(mMyThreadId, SP_FOREGROUND); //设置前台调度策略
    
        status_t result;
        do {
            processPendingDerefs(); //清除队列的引用[见小节2.5]
            result = getAndExecuteCommand(); //处理下一条指令[见小节2.6]
    
            if (result < NO_ERROR && result != TIMED_OUT
                    && result != -ECONNREFUSED && result != -EBADF) {
                abort();
            }
    
            if(result == TIMED_OUT && !isMain) {
                break; ////非主线程出现timeout则线程退出
            }
        } while (result != -ECONNREFUSED && result != -EBADF);
    
        mOut.writeInt32(BC_EXIT_LOOPER);  // 线程退出循环
        talkWithDriver(false); //false代表bwr数据的read_buffer为空
    }
    
    
    • 对于isMain=true的情况下, command为BC_ENTER_LOOPER,代表的是Binder主线程,不会退出的线程;
    • 对于isMain=false的情况下,command为BC_REGISTER_LOOPER,表示是由binder驱动创建的线程。

    2.5 processPendingDerefs

    [-> IPCThreadState.cpp]

    void IPCThreadState::processPendingDerefs()
    {
        if (mIn.dataPosition() >= mIn.dataSize()) {
            size_t numPending = mPendingWeakDerefs.size();
            if (numPending > 0) {
                for (size_t i = 0; i < numPending; i++) {
                    RefBase::weakref_type* refs = mPendingWeakDerefs[i];
                    refs->decWeak(mProcess.get()); //弱引用减一
                }
                mPendingWeakDerefs.clear();
            }
    
            numPending = mPendingStrongDerefs.size();
            if (numPending > 0) {
                for (size_t i = 0; i < numPending; i++) {
                    BBinder* obj = mPendingStrongDerefs[i];
                    obj->decStrong(mProcess.get()); //强引用减一
                }
                mPendingStrongDerefs.clear();
            }
        }
    }
    
    

    2.6 getAndExecuteCommand

    [-> IPCThreadState.cpp]

    status_t IPCThreadState::getAndExecuteCommand()
    {
        status_t result;
        int32_t cmd;
    
        result = talkWithDriver(); //与binder进行交互[见小节2.7]
        if (result >= NO_ERROR) {
            size_t IN = mIn.dataAvail();
            if (IN < sizeof(int32_t)) return result;
            cmd = mIn.readInt32();
    
            pthread_mutex_lock(&mProcess->mThreadCountLock);
            mProcess->mExecutingThreadsCount++;
            pthread_mutex_unlock(&mProcess->mThreadCountLock);
    
            result = executeCommand(cmd); //执行Binder响应码 [见小节2.8]
    
            pthread_mutex_lock(&mProcess->mThreadCountLock);
            mProcess->mExecutingThreadsCount--;
            pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
            pthread_mutex_unlock(&mProcess->mThreadCountLock);
    
            set_sched_policy(mMyThreadId, SP_FOREGROUND);
        }
        return result;
    }
    
    

    2.7 talkWithDriver

    //mOut有数据,mIn还没有数据。doReceive默认值为true
    status_t IPCThreadState::talkWithDriver(bool doReceive)
    {
        binder_write_read bwr;
        ...
        // 当同时没有输入和输出数据则直接返回
        if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
        ...
    
        do {
            //ioctl执行binder读写操作,经过syscall,进入Binder驱动。调用Binder_ioctl
            if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
                err = NO_ERROR;
            ...
        } while (err == -EINTR);
        ...
        return err;
    }
    
    

    在这里调用的isMain=true,也就是向mOut例如写入的便是BC_ENTER_LOOPER. 经过talkWithDriver(), 接下来程序往哪进行呢?在文章彻底理解Android Binder通信架构详细讲解了Binder通信过程,那么从binder_thread_write()往下说BC_ENTER_LOOPER的处理过程。

    2.7.1 binder_thread_write

    [-> binder.c]

    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;
        void __user *end = buffer + size;
        while (ptr < end && thread->return_error == BR_OK) {
            //拷贝用户空间的cmd命令,此时为BC_ENTER_LOOPER
            if (get_user(cmd, (uint32_t __user *)ptr)) -EFAULT;
            ptr += sizeof(uint32_t);
            switch (cmd) {
              case BC_REGISTER_LOOPER:
                  if (thread->looper & BINDER_LOOPER_STATE_ENTERED) {
                    //出错原因:线程调用完BC_ENTER_LOOPER,不能执行该分支
                    thread->looper |= BINDER_LOOPER_STATE_INVALID;
    
                  } else if (proc->requested_threads == 0) {
                    //出错原因:没有请求就创建线程
                    thread->looper |= BINDER_LOOPER_STATE_INVALID;
    
                  } else {
                    proc->requested_threads--;
                    proc->requested_threads_started++;
                  }
                  thread->looper |= BINDER_LOOPER_STATE_REGISTERED;
                  break;
    
              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;
    
              case BC_EXIT_LOOPER:
                  thread->looper |= BINDER_LOOPER_STATE_EXITED;
                  break;
            }
            ...
        }
        *consumed = ptr - buffer;
      }
      return 0;
    }
    
    

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

    2.7.2 binder_thread_read

    binder_thread_read(){
      ...
    retry:
        //当前线程todo队列为空且transaction栈为空,则代表该线程是空闲的
        wait_for_proc_work = thread->transaction_stack == NULL &&
            list_empty(&thread->todo);
    
        if (thread->return_error != BR_OK && ptr < end) {
            ...
            put_user(thread->return_error, (uint32_t __user *)ptr);
            ptr += sizeof(uint32_t);
            goto done; //发生error,则直接进入done
        }
    
        thread->looper |= BINDER_LOOPER_STATE_WAITING;
        if (wait_for_proc_work)
            proc->ready_threads++; //可用线程个数+1
        binder_unlock(__func__);
    
        if (wait_for_proc_work) {
            if (non_block) {
                ...
            } else
                //当进程todo队列没有数据,则进入休眠等待状态
                ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
        } else {
            if (non_block) {
                ...
            } else
                //当线程todo队列没有数据,则进入休眠等待状态
                ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
        }
    
        binder_lock(__func__);
        if (wait_for_proc_work)
            proc->ready_threads--; //可用线程个数-1
        thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
    
        if (ret)
            return ret; //对于非阻塞的调用,直接返回
    
        while (1) {
            uint32_t cmd;
            struct binder_transaction_data tr;
            struct binder_work *w;
            struct binder_transaction *t = NULL;
    
            //先考虑从线程todo队列获取事务数据
            if (!list_empty(&thread->todo)) {
                w = list_first_entry(&thread->todo, struct binder_work, entry);
            //线程todo队列没有数据, 则从进程todo对获取事务数据
            } else if (!list_empty(&proc->todo) && wait_for_proc_work) {
                w = list_first_entry(&proc->todo, struct binder_work, entry);
            } else {
                ... //没有数据,则返回retry
            }
    
            switch (w->type) {
                case BINDER_WORK_TRANSACTION: ...  break;
                case BINDER_WORK_TRANSACTION_COMPLETE:...  break;
                case BINDER_WORK_NODE: ...    break;
                case BINDER_WORK_DEAD_BINDER:
                case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
                case BINDER_WORK_CLEAR_DEATH_NOTIFICATION:
                    struct binder_ref_death *death;
                    uint32_t cmd;
    
                    death = container_of(w, struct binder_ref_death, work);
                    if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)
                      cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
                    else
                      cmd = BR_DEAD_BINDER;
                    put_user(cmd, (uint32_t __user *)ptr;
                    ptr += sizeof(uint32_t);
                    put_user(death->cookie, (void * __user *)ptr);
                    ptr += sizeof(void *);
                    ...
                    if (cmd == BR_DEAD_BINDER)
                      goto done; //Binder驱动向client端发送死亡通知,则进入done
                    break;
            }
    
            if (!t)
                continue; //只有BINDER_WORK_TRANSACTION命令才能继续往下执行
            ...
            break;
        }
    
    done:
        *consumed = ptr - buffer;
        //创建线程的条件
        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);
        }
        return 0;
    }
    
    

    当发生以下3种情况之一,便会进入done

    • 当前线程的return_error发生error的情况;
    • 当Binder驱动向client端发送死亡通知的情况;
    • 当类型为BINDER_WORK_TRANSACTION(即收到命令是BC_TRANSACTION或BC_REPLY)的情况;

    任何一个Binder线程当同时满足以下条件,则会生成用于创建新线程的BR_SPAWN_LOOPER命令:

    1. 当前进程中没有请求创建binder线程,即requested_threads = 0;
    2. 当前进程没有空闲可用的binder线程,即ready_threads = 0;(线程进入休眠状态的个数就是空闲线程数)
    3. 当前进程已启动线程个数小于最大上限(默认15);
    4. 当前线程已接收到BC_ENTER_LOOPER或者BC_REGISTER_LOOPER命令,即当前处于BINDER_LOOPER_STATE_REGISTERED或者BINDER_LOOPER_STATE_ENTERED状态。【小节2.6】已设置状态为BINDER_LOOPER_STATE_ENTERED,显然这条件是满足的。

    从system_server的binder线程一直的执行流: IPC.joinThreadPool –> IPC.getAndExecuteCommand() -> IPC.talkWithDriver() ,但talkWithDriver收到事务之后, 便进入IPC.executeCommand(), 接下来,从executeCommand说起.

    2.8 IPC.executeCommand

    status_t IPCThreadState::executeCommand(int32_t cmd)
    {
        status_t result = NO_ERROR;
        switch ((uint32_t)cmd) {
          ...
          case BR_SPAWN_LOOPER:
              //创建新的binder线程 【见小节2.3】
              mProcess->spawnPooledThread(false);
              break;
          ...
        }
        return result;
    }
    
    

    Binder主线程的创建是在其所在进程创建的过程一起创建的,后面再创建的普通binder线程是由spawnPooledThread(false)方法所创建的。

    2.9 思考

    默认地,每个进程的binder线程池的线程个数上限为15,该上限不统计通过BC_ENTER_LOOPER命令创建的binder主线程, 只计算BC_REGISTER_LOOPER命令创建的线程。 对此,或者很多人不理解,例个栗子:某个进程的主线程执行如下方法,那么该进程可创建的binder线程个数上限是多少呢?

    ProcessState::self()->setThreadPoolMaxThreadCount(6);  // 6个线程
    ProcessState::self()->startThreadPool();   // 1个线程
    IPCThread::self()->joinThreadPool();   // 1个线程
    
    

    首先线程池的binder线程个数上限为6个,通过startThreadPool()创建的主线程不算在最大线程上限,最后一句是将当前线程成为binder线程,所以说可创建的binder线程个数上限为8,如果还不理解,建议再多看看这几个方案的源码,多思考整个binder架构。

    三. 总结

    Binder设计架构中,只有第一个Binder主线程(也就是Binder_1线程)是由应用程序主动创建,Binder线程池的普通线程都是由Binder驱动根据IPC通信需求创建,Binder线程的创建流程图:

    binder_thread_create

    每次由Zygote fork出新进程的过程中,伴随着创建binder线程池,调用spawnPooledThread来创建binder主线程。当线程执行binder_thread_read的过程中,发现当前没有空闲线程,没有请求创建线程,且没有达到上限,则创建新的binder线程。

    Binder的transaction有3种类型:

    1. call: 发起进程的线程不一定是在Binder线程, 大多數情況下,接收者只指向进程,并不确定会有哪个线程来处理,所以不指定线程;
    2. reply: 发起者一定是binder线程,并且接收者线程便是上次call时的发起线程(该线程不一定是binder线程,可以是任意线程)。
    3. async: 与call类型差不多,唯一不同的是async是oneway方式不需要回复,发起进程的线程不一定是在Binder线程, 接收者只指向进程,并不确定会有哪个线程来处理,所以不指定线程。

    Binder系统中可分为3类binder线程:

    • Binder主线程:进程创建过程会调用startThreadPool()过程中再进入spawnPooledThread(true),来创建Binder主线程。编号从1开始,也就是意味着binder主线程名为binder_1,并且主线程是不会退出的。
    • Binder普通线程:是由Binder Driver来根据是否有空闲的binder线程来决定是否创建binder线程,回调spawnPooledThread(false) ,isMain=false,该线程名格式为binder_x
    • Binder其他线程:其他线程是指并没有调用spawnPooledThread方法,而是直接调用IPC.joinThreadPool(),将当前线程直接加入binder线程队列。例如: mediaserver和servicemanager的主线程都是binder线程,但system_server的主线程并非binder线程。

    本文转自 http://gityuan.com/2016/10/29/binder-thread-pool/,如有侵权,请联系删除。

    相关文章

      网友评论

          本文标题:android binder 进程系列 进程的Binder线程池

          本文链接:https://www.haomeiwen.com/subject/qcklqdtx.html