從MediaServer看Binder的使用方式(二)

語言: CN / TW / HK

theme: channing-cyan

android原始碼分析目錄

一 概述

```cpp

[main_mediaserver.cpp]

int main(int argc __unused, char **argv __unused) { signal(SIGPIPE, SIG_IGN); //1 獲得一個ProcessState的例項 sp proc(ProcessState::self()); //2 獲得一個IServiceManager物件 sp sm(defaultServiceManager()); ALOGI("ServiceManager: %p", sm.get()); AIcu_initializeIcuOrDie(); //3 註冊服務 MediaPlayerService::instantiate(); ResourceManagerService::instantiate(); //4 註冊其他,預設為空 registerExtensions(); //5 ProcessState有關執行緒池 ProcessState::self()->startThreadPool(); //6 IPCThreadState有關執行緒池 IPCThreadState::self()->joinThreadPool(); } ```

上一篇我們說完了步驟 1 到步驟 3,並且到了 IPCThreadState 在和 Binder 互動時,會呼叫 waitForResponse,然後在 waitForResponse 中呼叫 talkWithDriver 和 Binder 進行互動,讀出 mIn 中的資料,然後通過 executeCommand 執行,接下來,我們再看看具體的細節

原始碼目錄

frameworks/native/libs/binder/IPCThreadState.cpp frameworks/native/libs/binder/ProcessState.cpp frameworks/native/libs/binder/IPCThreadState.cpp

二 talkWithDriver

```cpp [IPCThreadState.cpp]

status_t IPCThreadState::talkWithDriver(bool doReceive) { //這個就是我們在open_device時拿到的fd if (mProcess->mDriverFD <= 0) { return -EBADF; }

//binder_write_read是用來和binder裝置交換資料的結構
binder_write_read bwr;

// 讀
const bool needRead = mIn.dataPosition() >= mIn.dataSize();

//如果還需要讀,就先讀,不寫
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;

bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();

//我們將讀到的資料
if (doReceive && needRead) {
    bwr.read_size = mIn.dataCapacity();
    bwr.read_buffer = (uintptr_t)mIn.data();
} else {
    bwr.read_size = 0;
    bwr.read_buffer = 0;
}

//日誌
...

// 讀和寫都結束了
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
    IF_LOG_COMMANDS() {
        alog << "About to read/write, write size = " << mOut.dataSize() << endl;
    }

if defined(ANDROID)

    //通過ioctl的方式互動
    if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
        err = NO_ERROR;
    else
        err = -errno;

else

    err = INVALID_OPERATION;

endif

    if (mProcess->mDriverFD <= 0) {
        err = -EBADF;
    }
    IF_LOG_COMMANDS() {
        alog << "Finished read/write, write size = " << mOut.dataSize() << endl;
    }
} while (err == -EINTR);

//日誌
...

if (err >= NO_ERROR) {
    if (bwr.write_consumed > 0) {
        if (bwr.write_consumed < mOut.dataSize())
            mOut.remove(0, bwr.write_consumed);
        else {
            mOut.setDataSize(0);
            processPostWriteDerefs();
        }
    }
    if (bwr.read_consumed > 0) {
        mIn.setDataSize(bwr.read_consumed);
        mIn.setDataPosition(0);
    }
    //日誌
    ...
    return NO_ERROR;
}

return err;

} ```

三 startThreadPool

因為 registerExtensions 預設為空,所以我們接下來看 startThreadPool 的相關邏輯,相比於之前的邏輯,這個簡單很多

```cpp [ProcessState.cpp]

void ProcessState::startThreadPool() { AutoMutex _l(mLock); if (!mThreadPoolStarted) { mThreadPoolStarted = true; spawnPooledThread(true); } }

void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { String8 name = makeBinderThreadName(); ALOGV("Spawning new pooled thread, name=%s\n", name.string()); //傳入的引數是true sp t = new PoolThread(isMain); t->run(name.string()); } }

class PoolThread : public Thread { public: explicit PoolThread(bool isMain) : mIsMain(isMain) { }

protected: virtual bool threadLoop() { IPCThreadState::self()->joinThreadPool(mIsMain); return false; }

const bool mIsMain;

}; ```

四 joinThreadPool

接下來就是最後一步 joinThreadPool 了,它是定義在 IPCThreadState.cpp 中的函式

```cpp [IPCThreadState.cpp]

void IPCThreadState::joinThreadPool(bool isMain) {

//如果isMain是true,則需要寫入一個BC_ENTER_LOOPER
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

status_t result;
do {
    //處理已經死亡的BBinder物件
    processPendingDerefs();
    // 獲取下一條命令
    result = getAndExecuteCommand();

    if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
        LOG_ALWAYS_FATAL("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
              mProcess->mDriverFD, result);
    }

    // 不是主執行緒就不需要一直迴圈
    if(result == TIMED_OUT && !isMain) {
        break;
    }
} while (result != -ECONNREFUSED && result != -EBADF);


//退出Looper
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);

} ```

4.1 getAndExecuteCommand

在 joinThreadPool 也有一個和之前很像的函式 getAndExecuteCommand,它也是 talkWithDriver 和 executeCommand,因為之前已經說過,所以這裡就不重複了

```cpp [IPCThreadState.cpp]

status_t IPCThreadState::getAndExecuteCommand() { status_t result; int32_t cmd; //和Binder驅動互動 result = talkWithDriver(); 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++;
    if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&
            mProcess->mStarvationStartTimeMs == 0) {
        mProcess->mStarvationStartTimeMs = uptimeMillis();
    }
    pthread_mutex_unlock(&mProcess->mThreadCountLock);

    //執行cmd
    result = executeCommand(cmd);

    pthread_mutex_lock(&mProcess->mThreadCountLock);
    mProcess->mExecutingThreadsCount--;
    if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads &&
            mProcess->mStarvationStartTimeMs != 0) {
        int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;
        if (starvationTimeMs > 100) {
            ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms",
                  mProcess->mMaxThreads, starvationTimeMs);
        }
        mProcess->mStarvationStartTimeMs = 0;
    }
    pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
    pthread_mutex_unlock(&mProcess->mThreadCountLock);
}

return result;

} ```

五 總結

終於,一個關於 MediaServer 系統 Server 使用 Binder 的過程,就讓我們整理完畢了

  1. 在使用 Binder 之前,需要獲得一個 ProcessState 例項,這個物件是一個單例
  2. 然後需要拿到一個 defaultServiceManager 物件,這是一個 IServiceManager,它是一個 IBinder 物件,其實就是 BpBinder
  3. 我們如果想要給其他程序提供 Binder 服務,那麼我們需要將自己的服務註冊到 ServiceManager 中,這個註冊流程就是一個 Binder 通訊的過程
  4. 註冊完畢後通過 ProcessState::self()->startThreadPool 啟動一個執行緒進行 Binder 事件的接收處理,這個執行緒不會退出
  5. 將 MediaServer 的主執行緒,通過 IPCThreadState::self()->joinThreadPool 也加入 loop 迴圈,接收 Binder 事件

當然,這兩篇關於 Binder 的介紹,也只是很簡單的說明了 Binder 通訊時,native 層參與的類以及這些通訊的流程,對於更細節的東西,這裡並沒有提到,接下來我們就看看 Java 層的 Binder 機制,以及這些操作中的細節