Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 從源碼角度分析Android中的Binder機制的前因後果

從源碼角度分析Android中的Binder機制的前因後果

編輯:關於Android編程

前面我也講述過一篇文章《帶你從零學習linux下的socket編程》,主要是從進程通信的角度開篇然後延伸到linux中的socket的開發。本篇文章依然是從進程通信的角度去分析下Android中的進程通信機制。

為什麼在Android中使用binder通信機制?

眾所周知linux中的進程通信有很多種方式,比如說管道、消息隊列、socket機制等。socket我們再熟悉不過了,然而其作為一款通用的接口,通信開銷大,數據傳輸效率低,主要用在跨網絡間的進程間通信以及在本地的低速通信。消息隊列和管道都是采用存儲-轉發模式,效率上面也有點低,因為這種模式的數據傳輸要經過兩次的內存拷貝,先從發送方的緩存區拷貝到內核開辟的緩存區中,然後再從內核拷貝到接受方的緩存區。傳統的ipc沒有任何的安全措施,兩個進程之間沒有辦法鑒別對方的身份,而在Android中,每個應用在安裝後都會被分配一個uid,所以這個身份也有了保障,也更安全。為了保障安全和高效率,Android提供了一套全新的ipc通信機制也就是binder。

binder通信模型

一個進程間通信可以簡單理解成為Client-server模式,binder機制在Android系統中的一個模型如下:

Client獲得到server端的proxy對象。 Client通過調用proxy對象的方法向server發送請求。 proxy對象通過binder設備節點,把Client請求信息發送到linux內核空間,由binder驅動獲取並發送到服務進程。 服務進程處理Client請求,通過linux內核的binder驅動把結果返回給proxy對象。 客戶端收到proxy的返回結果。

當client的線程進入到binder驅動後,內核會把client的線程掛起,然後進入服務進程,一直到結果返回,Client線程才會被喚醒,所以這個過程又類似於“線程遷移”,就是一個線程進入到另外一個進程中帶著結果返回。

binder組成結構

Binder驅動

binder是內核中的一個字符驅動設備位於/dev/binder。這個設備是Android系統IPC的核心部分,客戶端的服務代理用來通過它向server發送請求,服務器也是通過它把處理結果返回給客戶端的服務代理對象。這部分內容,在Android中通過一個IPCThreadState對象封裝了對Binder驅動的操作。 Service Manager

這個東西主要用來負責管理服務。Android中提供的系統服務都要通過Service Manager注冊自己,將自己添加進服務管理鏈表中,為客戶端提供服務。而客戶端如果要和特定的系統服務端通訊,就需要向Service Manager來查詢和獲得所需要服務。可以看出Service Manager是系統服務對象的管理中心。 服務(Server)

需要強調的是這裡服務是指的是System Server,而不是SDK server,向客戶端提供服務。 客戶端

一般是指Android系統上面的應用程序。它可以請求Server中的服務。 代理對象
是指在客戶端應用程序中獲取生成的Server代理(proxy)類對象。從應用程序角度看代理對象和本地對象沒有差別,都可以調用其方法,方法都是同步的,並且返回相應的結果。

從源碼分析MediaPlayerService

和其他介紹binder機制的博客文章一樣,接下來我也會從MediaPlayerService的角度去介紹bidner(建議大家看下深入理解Android這本書),源碼在frameworkaseMediaMediaServerMain_mediaserver.cpp中。之所以選擇MPS,是因為這個service牽涉了許多重要的服務,比如說音頻、攝像等。

int main(int argc, char** argv)
{
    // 打開驅動設備,將設備文件描述符映射到內存,這快內存作為數據交互的地方
    sp proc(ProcessState::self());

    sp sm = defaultServiceManager();

    LOGI(ServiceManager: %p, sm.get());

    waitBeforeAdding( String16(media.audio_flinger) );

    AudioFlinger::instantiate();

    waitBeforeAdding( String16(media.player) );

    // 注冊MediaPlayerService,
    MediaPlayerService::instantiate();

    waitBeforeAdding( String16(media.camera) );

    CameraService::instantiate();

    waitBeforeAdding( String16(media.audio_policy) );

    AudioPolicyService::instantiate();

    ProcessState::self()->startThreadPool();

    IPCThreadState::self()->joinThreadPool();

}

首先看下ProcessState的構造函數

ProcessState::ProcessState()
: mDriverFD(open_driver())// 開啟binder
, mVMStart(MAP_FAILED) // 內存映射的起始地址
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{

    ......
    mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
    .....
}

上述ProcessState構造函數代碼首先調用open_driver()方法,進入看看先:

static int open_driver()
{
    if (gSingleProcess) {
        return -1;
    }

    // 打開binder設備,設備成功打開返回一個設備的文件描述符
    int fd = open(/dev/binder, O_RDWR);   
    if (fd >= 0) {
        // 改變已經打開的文件的性質。如果FD_CLOEXEC位是0,執行execve的過程中,文件保持打開。反之則關閉。
        fcntl(fd, F_SETFD, FD_CLOEXEC);
        int vers;
        #if defined(HAVE_ANDROID_OS)
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        #else
        status_t result = -1;
        errno = EPERM;
        #endif
        if (result == -1) {
            LOGE(Binder ioctl to obtain version failed: %s, strerror(errno));
            close(fd);
            fd = -1;
        }
        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
            LOGE(Binder driver protocol does not match user space protocol!);
            close(fd);
            fd = -1;
        }
    #if defined(HAVE_ANDROID_OS)
    size_t maxThreads = 15;
    result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
    if (result == -1) {
        LOGE(Binder ioctl to set max threads failed: %s, strerror(errno));
    }
    #endif

    } else {
        LOGW(Opening '/dev/binder' failed: %s
, strerror(errno));
    }
    return fd;
}

open_driver是打開binder設備 /dev/binder文件,打開成功後會返回一個文件描述符,接著調用fcntl函數來改變剛才打開的設備文件的性質。接著在調用ioctl函數,ioctl是設備驅動程序中對設備的I/O通道進行管理的函數。所謂對I/O通道進行管理,就是對設備的一些特性進行控制,例如串口的傳輸波特率、馬達的轉速等等,BINDER_SET_MAX_THREADS 就是用戶程序對設備的控制命令,maxThreads則是補充參數,這句話的意思就是告訴binder驅動,這個文件描述符支持的最大線程數。在構造函數中使用mmap函數將該文件描述符映射到內存中,以便使用這塊內存來接受數據。構造函數中做了兩件比較重要的事情:

打開了/dev/binder設備文件,獲取了一個與設備文件相關聯的文件描述符,這也就相當於獲得了一個與內核的binder驅動有了交互的通道。 使用mmap函數將該文件描述符映射到內存中,以便使用這塊內存來接受數據。

接著再回到ProcessState::self()中來看看:

sp ProcessState::self()
{
    if (gProcess != NULL) return gProcess;
    AutoMutex _l(gProcessMutex);
    if (gProcess == NULL) gProcess = new ProcessState;
    return gProcess;
}

這個self僅僅是返回了ProcessState對象,ProcessState是以單例模式設計的,主要用來維護當前進程和binder設備通信時的一個狀態。

有了和binder驅動的交互通道後,接下來做了什麼呢?我們看下以下這段代碼:

sp sm = defaultServiceManager();

defaultServiceManager的業務實現在IServiceManager.cpp中,原型如下:

sp defaultServiceManager(){
    if (gDefaultServiceManager != NULL)return gDefaultServiceManager;
    AutoMutex _l(gDefaultServiceManagerLock);
    if (gDefaultServiceManager == NULL) {
        gDefaultServiceManager = interface_cast(ProcessState::self()->getContextObject(NULL));
    }
    return gDefaultServiceManager;
}

最重要的就是這句代碼了:

gDefaultServiceManager = interface_cast(ProcessState::self()->getContextObject(NULL))

再回到ProcessState中去看看getContextObject的實現

sp ProcessState::getContextObject(const sp& caller)
{
    if (supportsProcesses()) {
        return getStrongProxyForHandle(0);
    } else {
        return getContextObject(String16(default), caller);
    }
}

supportsProcesses()就是說當前的設備是否支持進程,這毫無疑問,真實的設備肯定是支持的,所以接下來肯定是要走getStrongProxyForHandle(0)方法的。

sp ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            b = new BpBinder(handle); // 創建一個BpBinder
            e->binder = b;// 給資源項填充這個binder
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }
    // 返回了new BpBinder(0); /
    return result;
}

lookupHandleLocked函數用來查找對應的資源,一開始肯定是沒有的,所以走b==null的分支,在這個分支裡面使用handler==0作為參數新建了一個BpBinder,然後把這個binder賦值給了handle_entry的binder屬性,最後把這個BpBinder對象返回,也就是說返回的是new BpBinder(0)。我覺得這裡面應該是把handle_entry資源項和當前進程掛鉤的,要不然這裡為啥要把BpBinder和這個資源項綁定在一起呢。

在這裡有兩個比較重要的東西,就是BpBinder和BBinder,他們兩個是成雙成對的出現,BpBinder是客戶端和服務端的代理類,而BBinder也就是BpBinder對立的一端,代表客戶端要交互的目的端。換種說法講就是BpBinder如果是客戶端,那麼BBinder則是服務端。

接下來gDefaultServiceManager = interface_cast(ProcessState::self()->getContextObject(NULL))就應該轉變為如下的代碼了:

gDefaultServiceManager = interface_cast(BpBinder)

這個interface_cast方法描述如下:

template
inline sp interface_cast(const sp& obj)
{
    return INTERFACE::asInterface(obj);
}

interface_cast是一個模板方法,INTERFACE代表的就是IserviceManager,裡面調用的是IserviceManager.asInterface方法,而在這個方法裡面,則是利用了BpBinder對象構建了一個BpManagerService對象,我們知道BpBinder和BBinder兩者是一個相互通信的關系,那麼為什麼這裡又來了個BpManagerService,BpManagerService實現了IServiceManager中的業務函數,並且BpManagerService中有個mRemote對象指向了BpBinder對象。就這樣,BpManagerService實現了業務函數並且也有了和服務端進行通信的代表。接下來就分析一下服務的注冊了。

在MediaPlayerService.cpp中:

void MediaPlayerService::instantiate() {
    // defaultServiceManager實際上返回的是BpManagerService,是IServiceManager的後代
    defaultServiceManager()->addService(String16(media.player), new   MediaPlayerService());
}

defaultServiceManager()返回的就是BpManagerService,其實現了IServiceManager的業務方法,所以我們要去BpManagerService中看看addService的實現:

virtual status_t addService(const String16& name, const sp& service)
{
    Parcel data, reply; // 數據包
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
    data.writeString16(name);
    data.writeStrongBinder(service);
    // 將數據打包後,把通信的工作交給了BpServiceManager
    // 這個remote其實就也就是mRemote,也就是BpBinder對象
    // 數據打包後,傳給了BpBinder對象的transact函數,也就意味著把通信的工作交給了BpBinder對象來完成
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);

    // 所以addService函數其實就是一個業務層的函數,工作很簡單,就是把數據打包後,再交給通信層去處理
    return err == NO_ERROR ? reply.readExceptionCode() : err;
}

Parcel我們可以理解成一個在進程間傳送的數據包,data是我們要發送的數據包,而reply則是服務返回過來的數據包,數據打包後,交由remote()函數的transact方法執行,remote()方法返回的就是BpManagerService中的mRemote對象,這個對象指向的是之前的BpBinder對象,所以與服務通信的工作就交給了BpBinder對象來完成,下面前往BpBinder中去看看這個方法的實現:

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

BpBinder其實沒有什麼可以做的實際工作,也就是個表象,這個真實工作又交給了IPCThreadState的transact,IPCThreadState才是在進程中真正干活的伙計,前往IPCThreadState.cpp中看看:

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

if (err == NO_ERROR) {
    LOG_ONEWAY(>>>> SEND from pid %d uid %d %s, getpid(), getuid(),
        (flags & TF_ONE_WAY) == 0 ? READ REPLY : ONE WAY);
    // handle值為0,代表通信的目的端
    // BC_TRANSACTION參數: 應用程序向binder設備發送消息的消息碼。
    // 而binder設備向應用程序回復消息碼以BR_開頭。

    // 先發數據,然後等待結果返回
    // 其實在這個方法裡面只是把數據寫入到mOut中,並沒有發出去
    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}

........
if ((flags & TF_ONE_WAY) == 0) {
   ............
    #endif
    if (reply) {
        err = waitForResponse(reply);
    } else {
        Parcel fakeReply;
        err = waitForResponse(&fakeReply);
    }
    #if 0
    .........
} else {
    err = waitForResponse(NULL, NULL);
}

return err;
}

writeTransactionData看樣子就只是把數據寫入到一個地方了,原型如下:

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;

    //handle是0,表示目的端 
    tr.target.handle = handle;
    // 消息碼
    tr.code = code;
    tr.flags = binderFlags;

    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(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 = statusBuffer;
        tr.offsets_size = 0;
        tr.data.ptr.offsets = NULL;
    } else {
        return (mLastError = err);
    }

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

    return NO_ERROR;
}

這個方法只是直接把命令直接寫到out中去,並沒有把這個消息發出去,所以繼續往下看waitForResponse方法,承載了發送和接受數據的工作肯定就在這裡面了:

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

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

    cmd = 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;

    case BR_FAILED_REPLY:
        err = FAILED_TRANSACTION;
        goto finish;

    case BR_ACQUIRE_RESULT:
        {
            LOG_ASSERT(acquireResult != NULL, Unexpected brACQUIRE_RESULT);
            const int32_t result = mIn.readInt32();
            if (!acquireResult) continue;
            *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
        }
        goto finish;

    case BR_REPLY:
        {
            binder_transaction_data tr;
            err = mIn.read(&tr, sizeof(tr));
            LOG_ASSERT(err == NO_ERROR, Not enough command data for brREPLY);
            if (err != NO_ERROR) goto finish;

            if (reply) {
                if ((tr.flags & TF_STATUS_CODE) == 0) {
                    reply->ipcSetDataReference(
                        reinterpret_cast(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(size_t),
                        freeBuffer, this);
                } else {
                    err = *static_cast(tr.data.ptr.buffer);
                    freeBuffer(NULL,
                        reinterpret_cast(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(size_t), this);
                }
            } else {
                freeBuffer(NULL,
                    reinterpret_cast(tr.data.ptr.buffer),
                    tr.data_size,
                    reinterpret_cast(tr.data.ptr.offsets),
                    tr.offsets_size/sizeof(size_t), this);
                continue;
            }
        }
        goto finish;

    default:
        err = executeCommand(cmd);
        if (err != NO_ERROR) goto finish;
        break;
    }
}

finish:
if (err != NO_ERROR) {
    if (acquireResult) *acquireResult = err;
    if (reply) reply->setError(err);
    mLastError = err;
}

return err;
}

waitForResponse承載了發送和接受數據,那麼發送給binder驅動,這個過程是怎麼通信的呢?看下talkWithDriver函數:

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
LOG_ASSERT(mProcess->mDriverFD >= 0, Binder driver is not opened);

// binder_write_read是與binder驅動交換數據的結構
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 = (long unsigned int)mOut.data();

// This is what we'll read.
if (doReceive && needRead) {
    //  接受數據緩沖區信息的填充,如果以後收到數據了,就直接填在mIn中
    bwr.read_size = mIn.dataCapacity();
    bwr.read_buffer = (long unsigned int)mIn.data();
} else {
    bwr.read_size = 0;
}

IF_LOG_COMMANDS() {
    TextOutput::Bundle _b(alog);
    if (outAvail != 0) {
        alog << Sending commands to driver:  << indent;
        const void* cmds = (const void*)bwr.write_buffer;
        const void* end = ((const uint8_t*)cmds)+bwr.write_size;
        alog << HexDump(cmds, bwr.write_size) << endl;
        while (cmds < end) cmds = printCommand(alog, cmds);
        alog << dedent;
    }
    alog << Size of receive buffer:  << bwr.read_size
        << , needRead:  << needRead << , doReceive:  << doReceive << endl;
}

// Return immediately if there is nothing to do.
// 如果沒有任何的數據交換,則立馬返回
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(HAVE_ANDROID_OS)
    if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
        err = NO_ERROR;
    else
        err = -errno;
#else
    err = INVALID_OPERATION;
#endif
    IF_LOG_COMMANDS() {
        alog << Finished read/write, write size =  << mOut.dataSize() << endl;
    }
} while (err == -EINTR);

IF_LOG_COMMANDS() {
    alog << Our err:  << (void*)err << , write consumed: 
        << bwr.write_consumed <<  (of  << mOut.dataSize()
        << ), read consumed:  << bwr.read_consumed << endl;
}

if (err >= NO_ERROR) {
    if (bwr.write_consumed > 0) {
        if (bwr.write_consumed < (ssize_t)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);
    }
    IF_LOG_COMMANDS() {
        TextOutput::Bundle _b(alog);
        alog << Remaining data size:  << mOut.dataSize() << endl;
        alog << Received commands from driver:  << indent;
        const void* cmds = mIn.data();
        const void* end = mIn.data() + mIn.dataSize();
        alog << HexDump(cmds, mIn.dataSize()) << endl;
        while (cmds < end) cmds = printReturnCommand(alog, cmds);
        alog << dedent;
    }
    return NO_ERROR;
}

return err;
}

看來這裡和binder驅動設備進行通信並不是使用read/write方式,而是ioctl方式。這樣就完成了binder通信了。

就分析到這裡吧。有空再深入研究

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved