Binder native層源碼分析(三):BpBinder通信類

在源碼分析(一)的結尾,addService函數中構造了數據包Parcel data併發送給了service manager,如下:

    //將服務名和MediaPlayerService寫入Parcel
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
    data.writeString16(name);
    data.writeStrongBinder(service);
    data.writeInt32(allowIsolated ? 1 : 0);
    //在interface_cast分析中我們知道BpServiceManager的remote成員是handle值
    //爲0的BpBinder,這裏相當於BpBinder->transact。該函數完成數據的發送
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);

其中remote()返回BpBinder指針。我們接下來就來分析BpBinder,看數據是怎麼被交給Binder驅動的。

BpBinder

先看BpBinder的構造函數:

//\frameworks\native\libs\binder\BpBinder.cpp
BpBinder::BpBinder(int32_t handle)
    : mHandle(handle)
    , mAlive(1)
    , mObitsSent(0)
    , mObituaries(NULL)
{
    //...
}

構造BpBinder時需要一個參數handle用於初始化成員變量mHandle。每個BpBinder都有唯一一個和他對應的BBinder對象,BpBinder通過這個mHandle來標識它的傳輸目標。可以看到在後面的transact()中,這個mHandle會作爲一個參數傳入,代表本次通信的目的端。而本次分析的BpBinder的mHandle值爲0,代表service manager。

接下來看完成通信的transact函數。

BpBinder::transact

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

注意該函數的幾個參數。code控制服務端進入不同的分支,具體來說,在BBinder的transact()或onTransact()函數中會根據接收到的code的值進入不同的分支。data即傳輸的數據(很多情況下是遠程函數的參數數據),reply用於接收回復,flags表示本次通信的類型,默認爲0。

本次傳輸中,code是ADD_SERVICE_TRANSACTION,data主要是由MediaPlayerService構造而得的flat_binder_object

BpBinder的transact()函數只是將參數原封不動的交給IPCThreadState的transact()處理,並且還額外傳入了mHandle表示通信的目的端。

IPCThreadState::transact

//\frameworks\native\libs\binder\IPCThreadState.cpp,刪去了一些debug代碼
status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err = data.errorCheck();
    flags |= TF_ACCEPT_FDS;
    if (err == NO_ERROR) {
        LOG_ONEWAY(">>>> SEND from pid %d uid %d %s", getpid(), getuid(),
            (flags & TF_ONE_WAY) == 0 ? "READ REPLY" : "ONE WAY");
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
    
    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }
    
    if ((flags & TF_ONE_WAY) == 0) {//flags爲0,會進入該if
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        
    } else {//不會進入else
        err = waitForResponse(NULL, NULL);
    }
    
    return err;
}

IPCThreadState的transact()將傳進來的所有參數又傳給了writeTransactionData(),額外傳入了BC_TRANSACTION表示通信協議,然後waitForResponse()。

這兩個函數的函數名已經把transact()做的事情說得很清楚了,先是發送數據,然後等待響應。繼續看writeTransacctionData,看數據是怎麼發送的。

writeTransacctionData

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;

    tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
    tr.target.handle = handle;
    tr.code = code;
    tr.flags = binderFlags;
    tr.cookie = 0;
    tr.sender_pid = 0;
    tr.sender_euid = 0;
    
    const status_t err = data.errorCheck();
    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);
    mOut.write(&tr, sizeof(tr));
    
    return NO_ERROR;
}

這個函數中首先構造了一個binder_transaction_data對象tr,它的內容來源於傳入的Parcel data和其它用於通信的參數例如pid,uid,handle。其中,tr.data.ptr.buffer等於data.mData,即爲傳輸的數據。

函數的最後把協議BC_TRANSACTION和tr寫進mOut。mIn和mOut是IPCThreadState的成員,類型是Parcel。從代碼中可以看出,傳進來的Parcel data並不直接用於傳輸,而是用於填充binder_transaction_data,之後將協議cmd和binder_transaction_data寫入mOut,所以mOut纔是Binder通信中傳輸的Parcel。

writeTransactData僅僅只是做了填充數據結構的工作,並沒有將數據真正發送出去。事實上數據的發送和接收都在waitForResponse的talkWithDriver中進行。

waitForResponse分段1

waitForResponse中,我們只需看數據是怎麼發送的即可,後面部分的代碼暫時用不到,等用到時再分析。

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        err = mIn.errorCheck();
        

在waitForResponse()中,首先是調用talkWithDriver()。

talkWithDriver

//刪去dubug代碼
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }
    
    binder_write_read bwr;
    
    // Is the read buffer empty?
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    
    // We don't want to write anything if we are still reading
    // from data left in the input buffer and the caller
    // has requested to read the next data.
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    
    bwr.write_size = outAvail;
    bwr.write_buffer = (uintptr_t)mOut.data();

    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    
    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

這裏構造了一個binder_write_read類型變量bwr,然後根據mIn和mOut的mDataPos,mDataSize,mDataCapacity來填寫bwr。

當mIn的mDataPos<mDataSize時 ,說明mIn中還有未讀的數據,所以此時不需要Binder讀取數據,所以needRead爲false。當mDataPos>=mDataSize時,說明mIn中的數據都被我們讀完了,所以needRead爲true,代表我們希望從Binder中讀取數據放到mIn中。也就是說,只有當mIn中的數據全部讀完時,才允許從Binder中讀取新數據。

對bwr的read_size,write_size的賦值很容易理解,這裏不再浪費篇幅贅述,主要注意bwr的buffer指針等於mIn和mOut的數據指針,所以Binder驅動通過bwr就可以從mIn和mOut中讀寫數據。繼續看talkWithDriver的後半部分:

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
    } 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);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        return NO_ERROR;
    }
    return err;
}

調用了ioctl,該函數屬於驅動層,驅動層我放在後面的文章分析,所以在這裏就到函數爲止,不再繼續往底層鑽。

調用完ioctl之後,把mOut中已經發出去的數據移除掉,更新mIn的數據大小和讀寫位置(置零)。

總結

在前面的分析中,我們傳入的數據data經過了層層包裝和轉化,最終被傳給了Binder驅動,這裏用一張圖來直觀地表現這一過程。
在這裏插入圖片描述
其中,binder_transaction_data中除了儲存Parcel data的數據外,還添加了發送者和接收者等額外通信信息。

mOut和mIn是進程專門用於收發Binder通信數據的區域。數據被髮送前,統一被放到mOut中,而接收的數據則統一放到mIn中。

bwr記錄了mOut和mIn的相關信息,這樣驅動層就可以通過這些信息直接對mOut和mIn進行讀寫操作。

本文到此結束,下篇文章將分析Binder驅動層。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章