騷年菠劝!用Binder原理徹底征服大廠面試官吧

前言
在上一篇文章中赶诊,我們學習了ServiceManager中的Binder機制舔痪,有一個問題由于篇幅問題沒有講完,那就是MediaPlayerService是如何注冊的夺英。通過了解MediaPlayerService是如何注冊的滋捶,可以得知系統(tǒng)服務的注冊過程重窟。

1.從調用鏈角度說明MediaPlayerService是如何注冊的
我們先來看MediaServer的入口函數(shù),代碼如下所示扭仁。
frameworks/av/media/mediaserver/main_mediaserver.cpp

int main(int argc __unused, char **argv __unused)
{
    signal(SIGPIPE, SIG_IGN);
    //獲取ProcessState實例
    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm(defaultServiceManager());
    ALOGI("ServiceManager: %p", sm.get());
    InitializeIcuOrDie();
    //注冊MediaPlayerService
    MediaPlayerService::instantiate();//1
    ResourceManagerService::instantiate();
    registerExtensions();
    //啟動Binder線程池
    ProcessState::self()->startThreadPool();
    //當前線程加入到線程池
    IPCThreadState::self()->joinThreadPool();
}

這段代碼中的很多內容都在上一篇文章介紹過了斋枢,接著分析注釋1處的代碼知给。

frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp

void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService涩赢,());
}

defaultServiceManager返回的是BpServiceManager轩勘,不清楚的看Binder這么弱還跑來面騰訊绊寻?這篇文章。參數(shù)是一個字符串和MediaPlayerService冰蘑,看起來像是Key/Value的形式來完成注冊祠肥,接著看addService函數(shù)梯皿。

frameworks/native/libs/binder/IServiceManager.cpp

 virtual status_t addService(const String16& name, const sp<IBinder>& service,
                                bool allowIsolated, int dumpsysPriority) {
        Parcel data, reply;//數(shù)據包
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name); //name值為"media.player"
        data.writeStrongBinder(service); //service值為MediaPlayerService
        data.writeInt32(allowIsolated ? 1 : 0);
        data.writeInt32(dumpsysPriority);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);//1
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }

data是一個數(shù)據包,后面會不斷的將數(shù)據寫入到data中忠烛, 注釋1處的remote()指的是mRemote权逗,也就是BpBinder旬迹。addService函數(shù)的作用就是將請求數(shù)據打包成data,然后傳給BpBinder的transact函數(shù)屹耐,代碼如下所示椿猎。
frameworks/native/libs/binder/BpBinder.cpp

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

BpBinder將邏輯處理交給IPCThreadState犯眠,先來看IPCThreadState::self()干了什么?
frameworks/native/libs/binder/IPCThreadState.cpp

IPCThreadState* IPCThreadState::self()
{   
    //首次進來gHaveTLS的值為false
    if (gHaveTLS) {
restart:
        const pthread_key_t k = gTLS;//1
        IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);//2
        if (st) return st;
        return new IPCThreadState;//3
    }
    ...
    pthread_mutex_unlock(&gTLSMutex);
    goto restart;
}

注釋1處的TLS的全稱為Thread local storage,指的是線程本地存儲空間量蕊,在每個線程中都有TLS残炮,并且線程間不共享。注釋2處用于獲取TLS中的內容并賦值給IPCThreadState*指針泉瞻。注釋3處會新建一個IPCThreadState苞冯,這里可以得知IPCThreadState::self()實際上是為了創(chuàng)建IPCThreadState抱完,它的構造函數(shù)如下所示。
frameworks/native/libs/binder/IPCThreadState.cpp

IPCThreadState::IPCThreadState()
    : mProcess(ProcessState::self()),
      mStrictModePolicy(0),
      mLastTransactionBinderFlags(0)
{
    pthread_setspecific(gTLS, this);//1
    clearCaller();
    mIn.setDataCapacity(256);
    mOut.setDataCapacity(256);
}

注釋1處的pthread_setspecific函數(shù)用于設置TLS碉怔,將IPCThreadState::self()獲得的TLS和自身傳進去撮胧。IPCThreadState中還包含mIn、一個mOut锻离,其中mIn用來接收來自Binder驅動的數(shù)據墓怀,mOut用來存儲發(fā)往Binder驅動的數(shù)據傀履,它們默認大小都為256字節(jié)。
知道了IPCThreadState的構造函數(shù)碴犬,再回來查看IPCThreadState的transact函數(shù)梆暮。
frameworks/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err;

    flags |= TF_ACCEPT_FDS;
    ...
    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);//1

    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }

    if ((flags & TF_ONE_WAY) == 0) {
       ...
        if (reply) {
            err = waitForResponse(reply);//2
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
       ...
    } else {
       //不需要等待reply的分支
        err = waitForResponse(NULL, NULL);
    }

    return err;
}

調用BpBinder的transact函數(shù)實際上就是調用IPCThreadState的transact函數(shù)服协。注釋1處的writeTransactionData函數(shù)用于傳輸數(shù)據,其中第一個參數(shù)BC_TRANSACTION代表向Binder驅動發(fā)送命令協(xié)議啦粹,向Binder設備發(fā)送的命令協(xié)議都以BC_開頭偿荷,而Binder驅動返回的命令協(xié)議以BR_開頭。這個命令協(xié)議我們先記住卖陵,后面會再次提到他遭顶。

現(xiàn)在分別來分析注釋1的writeTransactionData函數(shù)和注釋2處的waitForResponse函數(shù)。

1.1 writeTransactionData函數(shù)分析
frameworks/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;//1

    tr.target.ptr = 0; 
    tr.target.handle = handle;//2 
    tr.code = code;  //code=ADD_SERVICE_TRANSACTION
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;

    const status_t err = data.errorCheck();//3
    if (err == NO_ERROR) {
        tr.data_size = data.ipcDataSize();
        tr.data.ptr.buffer = data.ipcData();
        tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } else if (statusBuffer) {
        tr.flags |= TF_STATUS_CODE;
        *statusBuffer = err;
        tr.data_size = sizeof(status_t);
        tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
        tr.offsets_size = 0;
        tr.data.ptr.offsets = 0;
    } else {
        return (mLastError = err);
    }

    mOut.writeInt32(cmd);  //cmd=BC_TRANSACTION
    mOut.write(&tr, sizeof(tr));

    return NO_ERROR;
}

注釋1處的binder_transaction_data結構體(tr結構體)是向Binder驅動通信的數(shù)據結構泪蔫,注釋2處將handle傳遞給target的handle,用于標識目標撩荣,這里的handle的值為0,代表了ServiceManager饶深。
注釋3處對數(shù)據data進行錯誤檢查餐曹,如果沒有錯誤就將數(shù)據賦值給對應的tr結構體。最后會將BC_TRANSACTION和tr結構體寫入到mOut中敌厘。
上面代碼調用鏈的時序圖如下所示台猴。


1.2 waitForResponse函數(shù)分析
接著回過頭來查看waitForResponse函數(shù)做了什么,waitForResponse函數(shù)中的case語句很多,這里截取部分代碼饱狂。
frameworks/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;
    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;//1
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        cmd = (uint32_t)mIn.readInt32();
        IF_LOG_COMMANDS() {
            alog << "Processing waitForResponse Command: "
                << getReturnString(cmd) << endl;
        }
        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;

        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
            goto finish;
       ...
        default:
            //處理各種命令協(xié)議
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
}
finish:
    ...
    return err;
}

注釋1處的talkWithDriver函數(shù)的內部通過ioctl與Binder驅動進行通信曹步,代碼如下所示。
frameworks/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }
    //和Binder驅動通信的結構體
    binder_write_read bwr; //1
    //mIn是否有可讀的數(shù)據休讳,接收的數(shù)據存儲在mIn
    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();//2
    //這時doReceive的值為true
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();//3
    } 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__)
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)//4
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
     ...
    } while (err == -EINTR);
    ...
    return err;
}

注釋1處的 binder_write_read是和Binder驅動通信的結構體讲婚,在注釋2和3處將mOut、mIn賦值給binder_write_read的相應字段俊柔,最終通過注釋4處的ioctl函數(shù)和Binder驅動進行通信筹麸,這一部分涉及到Driver Binder的內容
了,就不再詳細介紹了雏婶。

1.3 小節(jié)
從調用鏈的角度來看物赶,MediaPlayerService是如何注冊的貌似并不復雜,因為這里只是簡單的介紹了一個調用鏈分支酵紫,可以簡單的總結為以下幾個步驟:

addService函數(shù)將數(shù)據打包發(fā)送給BpBinder來進行處理。

BpBinder新建一個IPCThreadState對象,并將通信的任務交給IPCThreadState泽示。

IPCThreadState的writeTransactionData函數(shù)用于將命令協(xié)議和數(shù)據寫入到mOut中。

IPCThreadState的waitForResponse函數(shù)主要做了兩件事,一件事是通過ioctl函數(shù)操作mOut和mIn來與Binder驅動進行數(shù)據交互赤赊,另一件事是處理各種命令協(xié)議抛计。

2.從進程角度說明MediaPlayerService是如何注冊的
實際上MediaPlayerService的注冊還涉及到了進程,如下圖所示晨逝。

從圖中看出是以C/S架構為基礎阀趴,addService是在MediaPlayerService進行的棚菊,它是Client端,用于請求添加系統(tǒng)服務。而Server端則是指的是ServiceManager像屋,用于完成系統(tǒng)服務的添加戈轿。
Client端和Server端分別運行在兩個進程中思杯,通過向Binder來進行通信色乾。更詳細點描述攘须,就是兩端通過向Binder驅動發(fā)送命令協(xié)議來完成系統(tǒng)服務的添加浮驳。這其中命令協(xié)議非常多,過程也比較復雜,這里對命令協(xié)議進行了簡化术陶,只涉及到了四個命令協(xié)議煤痕,其中
BC_TRANSACTION和BR_TRANSACTION過程是一個完整的事務梧宫,BC_REPLY和BR_REPLY是一個完整的事務。
Client端和Server端向Binder驅動發(fā)送命令協(xié)議以BC開頭摆碉,而Binder驅動向Client端和Server端返回的命令協(xié)議以BR_開頭塘匣。

步驟如下所示:
1.Client端向Binder驅動發(fā)送BC_TRANSACTION命令。
2.Binder驅動接收到請求后生成BR_TRANSACTION命令巷帝,喚醒Server端后將BR_TRANSACTION命令發(fā)送給ServiceManager忌卤。
3.Server端中的服務注冊完成后,生成BC_REPLY命令發(fā)送給Binder驅動锅睛。
4.Binder驅動生成BR_REPLY命令埠巨,喚醒Client端后將BR_REPLY命令發(fā)送個Client端。

通過這些協(xié)議命令來驅動并完成系統(tǒng)服務的注冊现拒。

3.總結
本文分別從調用鏈角度和進程角度來講解MediaPlayerService是如何注冊的辣垒,間接的得出了服務是如何注冊的
。這兩個角度都比較復雜印蔬,因此這里分別對這兩個角度做了簡化勋桶,作為應用開發(fā),我們不需要注重太多的過程和細節(jié)侥猬,只需要了解大概的步驟即可例驹。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市退唠,隨后出現(xiàn)的幾起案子鹃锈,更是在濱河造成了極大的恐慌,老刑警劉巖瞧预,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屎债,死亡現(xiàn)場離奇詭異仅政,居然都是意外死亡,警方通過查閱死者的電腦和手機盆驹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門圆丹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人躯喇,你說我怎么就攤上這事辫封。” “怎么了廉丽?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵倦微,是天一觀的道長。 經常有香客問我雅倒,道長璃诀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任蔑匣,我火速辦了婚禮劣欢,結果婚禮上,老公的妹妹穿的比我還像新娘裁良。我一直安慰自己凿将,他們只是感情好,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布价脾。 她就那樣靜靜地躺著牧抵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪侨把。 梳的紋絲不亂的頭發(fā)上犀变,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音秋柄,去河邊找鬼获枝。 笑死,一個胖子當著我的面吹牛骇笔,可吹牛的內容都是我干的省店。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼笨触,長吁一口氣:“原來是場噩夢啊……” “哼懦傍!你這毒婦竟也來了?” 一聲冷哼從身側響起芦劣,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤粗俱,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后虚吟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體源梭,經...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡娱俺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了废麻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡模庐,死狀恐怖烛愧,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情掂碱,我是刑警寧澤怜姿,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站疼燥,受9級特大地震影響沧卢,放射性物質發(fā)生泄漏。R本人自食惡果不足惜醉者,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一但狭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧撬即,春花似錦立磁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至粒竖,卻和暖如春颅崩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蕊苗。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工沿后, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人岁歉。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓得运,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锅移。 傳聞我的和親對象是個殘疾皇子熔掺,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354