從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 機制,以及這些操作中的細節