進程的Binder線程池工做過程

基於Android 6.0源碼剖析,分析Binder線程池以及binder線程啓動過程。android

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

一. 概述git

Android系統啓動完成後,ActivityManager, PackageManager等各大服務都運行在system_server進程,app應用須要使用系統服務都是經過binder來完成進程之間的通訊,那對於binder線程是如何管理的呢,又是如何建立的呢?其實不管是system_server進程,仍是app進程,都是在進程fork完成後,便會在新進程中執行onZygoteInit()的過程當中,啓動binder線程池。接下來,就以此爲起點展開從線程的視角來看看binder的世界。cookie

二. Binder線程建立多線程

Binder線程建立與其所在進程的建立中產生,Java層進程的建立都是經過Process.start()方法,向Zygote進程發出建立進程的socket消息,Zygote收到消息後會調用Zygote.forkAndSpecialize()來fork出新進程,在新進程中會調用到RuntimeInit.nativeZygoteInit方法,該方法通過jni映射,最終會調用到app_main.cpp中的onZygoteInit,那麼接下來從這個方法提及。架構

2.1 onZygoteInitapp

[-> app_main.cpp]socket

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()。async

2.2 PS.startThreadPool函數

[-> ProcessState.cpp]oop

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

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

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(), 接下來程序往哪進行呢?從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;
}

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命令:

當前進程中沒有請求建立binder線程,即requested_threads = 0;
當前進程沒有空閒可用的binder線程,即ready_threads = 0;(線程進入休眠狀態的個數就是空閒線程數)
當前進程已啓動線程個數小於最大上限(默認15);
當前線程已接收到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線程的建立流程圖:

每次由Zygote fork出新進程的過程當中,伴隨着建立binder線程池,調用spawnPooledThread來建立binder主線程。當線程執行binder_thread_read的過程當中,發現當前沒有空閒線程,沒有請求建立線程,且沒有達到上限,則建立新的binder線程。

Binder的transaction有3種類型:

call: 發起進程的線程不必定是在Binder線程, 大多數情況下,接收者只指向進程,並不肯定會有哪一個線程來處理,因此不指定線程;
reply: 發起者必定是binder線程,而且接收者線程即是上次call時的發起線程(該線程不必定是binder線程,能夠是任意線程)。
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線程。

做者:MIUI資深工程師-袁輝輝我的博客:gityuan.com

更多內容能夠關注小米開放平臺訂閱號:
官方QQ交流羣:398616987
想要了解更多?
那就關注咱們吧!
圖片描述

相關文章
相關標籤/搜索