天天看點

binder學習筆記(十)—— 穿越到驅動層 http://www.cnblogs.com/palance/p/5724433.html 從應用層登陸,順流直下進入驅動層

http://www.cnblogs.com/palance/p/5724433.html

Binder驅動層的代碼在

kernel/goldfish/drivers/staging/android

下的

binder.c

binder.h

。Android源碼是不帶Linux核心的,驅動正是在這個核心裡,需要單獨下載下傳,出門左轉參見《Anrdoid源碼、核心編譯》。驅動的相關知識先不在這裡展開了,那又是一個龐大的體系,以後再啃。直奔我們的主題——用戶端為

test()

組織的請求資料是:

binder學習筆記(十)—— 穿越到驅動層 http://www.cnblogs.com/palance/p/5724433.html 從應用層登陸,順流直下進入驅動層

驅動程式是如何處理這個資料包的呢?

從應用層登陸,順流直下

為此,還需要先從應用層往下看,frameworks/native/libs/binder/IPCThreadState.cpp:548,就從這裡登陸吧。用戶端組織test()請求資料時,調用到IPCThreadState::transact(...)

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{   // code=TEST, flag=0

    flags |= TF_ACCEPT_FDS;
    ......
    
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    
        ......
        if (reply) {
            err = waitForResponse(reply);  // 這次重點看這裡
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        ......
    
    return err;
}
           

函數使用writeTransactionData(…)打包好資料後,接下來調用waitForResponse(…)把資料發出去。

frameworks/native/libs/binder/IPCThreadState.cpp:712

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

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;
        ......
        }
        ......
    
    return err;
}
           

繼續調用talkWithDriver()和驅動對話,frameworks/native/libs/binder/IPCThreadState.cpp:803

status_t IPCThreadState::talkWithDriver(bool doReceive)
{   // doReceive=true
    ......
    binder_write_read bwr;
    ......
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();// mIn有上一輪IO讀出尚未解析的資料,是以needRead=true
    ......
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0; // outAvail=mOut.dataSize()
    
    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();
    } 
    ......
    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        ......
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0) // 重點在這
            err = NO_ERROR;
        ......
    } while (err == -EINTR);
    ......    
    return err;
}
           

doReceive取預設值為true,在通過

test()

調用到

talkWithDriver(...)

之前,和驅動的對話已經做了好幾輪了,比如

defaultServiceManager()

和ServiceManager的對話,

getService(...)

和Service的對話,此時mIn中應該是有之前讀出尚未解析的資料,是以needRead=true,outAvail=mOut.dataSize()。可以組織一個gdb确認mIn此時的内容。

組織一個gdb确認此時mIn的内容

需要開啟三個終端完成調試:

  1. Target1 在模拟器上啟動server
    $ adb shell /data/local/tmp/testservice/TestServer
               
  2. Target2 在模拟器上通過gdbserver啟動用戶端
    $ adb shell gdbserver :1234 /data/local/tmp/testservice/TestClient
    Process /data/local/tmp/testservice/TestClient created; pid = 1254
    Listening on port 1234
    Remote debugging from host 127.0.0.1
               
  3. Host1 在宿主端啟動gdb

    ``` bash

    $ ./prebuilts/gcc/darwin-x86/arm/arm-linux-androideabi-4.9/bin/arm-linux-androideabi-gdb out/debug/target/product/generic/obj/EXECUTABLES/TestClient_intermediates/LINKED/TestClient

    ......

    (gdb) b main

    Breakpoint 1 at 0xb6f571fc: file external/testservice/TestClient.cpp, line 14.

    (gdb) c

    Continuing.

    ......

    (gdb) set solib-absolute-prefix out/debug/target/product/generic/symbols/

    Reading symbols from ...... linker...done.

    ......

    Loaded symbols for ......

    ......

    (gdb) b IPCThreadState.cpp:846 # 在talkWithDriver(...)内下斷點

    Breakpoint 2 at 0xb6eaf884: file frameworks/native/libs/binder/IPCThreadState.cpp, line 846.

    (gdb) c

    ......

    然後就是若幹輪的continue和backtrace,直到停在由test()調用觸發的talkWithDriver(...)

    ......

Breakpoint 2, android::IPCThreadState::talkWithDriver ([email protected]=0xb6c24000,[email protected]=true) at frameworks/native/libs/binder/IPCThreadState.cpp:846

846 if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

(gdb) bt

0 android::IPCThreadState::talkWithDriver ([email protected]=0xb6c24000,[email protected]=true) at frameworks/native/libs/binder/IPCThreadState.cpp:846

1 0xb6eafed2 in android::IPCThreadState::waitForResponse (this=0xb6c24000, reply=0xbeaa1ad4, acquireResult=0x0) at frameworks/native/libs/binder/IPCThreadState.cpp:718

2 0xb6eb0088 in android::IPCThreadState::transact (this=0xb6c24000, handle=1,[email protected]=1, data=..., [email protected]=0xbeaa1ad4, flags=16, [email protected]=0) at frameworks/native/libs/binder/IPCThreadState.cpp:604

3 0xb6eab08e in android::BpBinder::transact (this=0xb6c090c0, code=1, data=..., reply=0xbeaa1ad4, flags=0) at frameworks/native/libs/binder/BpBinder.cpp:165

4 0xb6f3e42e in android::BpTestService::test (this=) at external/testservice/TestClient.cpp:10

5 0xb6f3e23c in main () at external/testservice/TestClient.cpp:18

(gdb) p mIn

$1 = {mError = 0, mData = 0xb6c27000 "\fr", mDataSize = 48, mDataCapacity = 256, mDataPos = 48, mObjects = 0x0, mObjectsSize = 0, mObjectsCapacity = 0, mNextObjectHint = 0, mFdsKnown = true, mHasFds = false, mAllowFds = true, mOwner = 0x0, mOwnerCookie = 0x0,

mOpenAshmemSize = 0}

(gdb) p needRead

$2 = true

```

結果和我猜測的一緻。

綜上所述,在

IPCThreadState::talkWithDriver(...)

中調用

ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &brw);
           

傳入的資料bwr即為:

binder學習筆記(十)—— 穿越到驅動層 http://www.cnblogs.com/palance/p/5724433.html 從應用層登陸,順流直下進入驅動層

進入驅動層

終于可以有此穿越到驅動層了!binder驅動層對接ioctl的函數是binder_ioctl(...)。kernel/goldfish/drivers/staging/android/binder.c:2716

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{   // cmd=BINDER_WRITE_READ
    int ret;
    struct binder_proc *proc = filp->private_data;
    struct binder_thread *thread;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;

    ......
    thread = binder_get_thread(proc);
    ......

    switch (cmd) {
    case BINDER_WRITE_READ: {
        struct binder_write_read bwr;
        ......
        if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {// 把上圖總使用者空間的bwr複制到核心
            ......
        }
        ......
        if (bwr.write_size > 0) {
            ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
            trace_binder_write_done(ret);
            ......
        }
        // bwr.read_size來自IPCThreadState::talkWithDriver,當讀緩沖區為空,會将
        // mIn緩沖區交給它,bwr.read_size=mIn.dataCapacity()
        if (bwr.read_size > 0) {
            ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
            trace_binder_read_done(ret);
            if (!list_empty(&proc->todo))
                wake_up_interruptible(&proc->wait);
            ... ...
        }
        ......
    }
    ......
    }
    ret = 0;
......
    return ret;
}
           

先盡可能地剪掉細枝末節,來看重點調用

binder_thread_write(proc, thread, (void __user *)bwr.write_buffer,
                            bwr.write_size, &bwr.write_consumed);
           

kernel/goldfish/drivers/staging/android/binder.c:1837

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
            void __user *buffer, int size, signed long *consumed)
{
    uint32_t cmd;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    while (ptr < end && thread->return_error == BR_OK) {
        if (get_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        trace_binder_command(cmd);
        if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
            binder_stats.bc[_IOC_NR(cmd)]++;
            proc->stats.bc[_IOC_NR(cmd)]++;
            thread->stats.bc[_IOC_NR(cmd)]++;
        }
        switch (cmd) {
        ......
        case BC_TRANSACTION:
        case BC_REPLY: {
            struct binder_transaction_data tr;

            if (copy_from_user(&tr, ptr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr); // 對照前面的圖逆向拆解
            binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
            break;
        }
        ......
        }
        *consumed = ptr - buffer;
    }
    return 0;
}
           

這個cmd的值是BC_TRANSACTION,是以應該繼續

binder_transaction(proc, thread, &tr, false)

。這個函數實在太長了,後面再花一節的篇幅深入該函數。

不過很清晰的一點:該函數僅在出錯的時候才傳回小于零的整數,如果一切正常就傳回0。函數

binder_ioctl(...)在case BINDER_WRITE_READ

這一枝上,如果沒有發生錯誤,則傳回

binder_thread_write(...)

。也就是說:如果一切正常,

binder_ioctl(...)

會傳回0,不管io的資料有多大。

分類:  Android