Binder 驅動詳解(下)

前言

經過 Binder 上一篇文章的分析, 咱們知道了 Binder 驅動在咱們應用開發過程當中的使用方式, 瞭解到了 BBinderBpBinder 兩個很是重要的 Native 對象, 本次咱們就着重分析一下 Binder 在運行時庫層知識
由於運行時庫是使用 C/C++ 編寫的, 對於 Android 開發者來講, 可能有些晦澀難懂, 筆者也下了很大的功夫, 對此感興趣的同窗能夠耐心往下讀, 若是存在讓你們興奮的點, 那麼筆者就十分知足了node

  • 應用層框架層的 Binder 庫
  • ServiceManager 的啓動
  • ServiceManager 進程間通訊

一. AndroidRuntime 層的 Binder 庫

封裝的意義

Android 系統將各類 Binder 驅動程序操做封裝成一個 Binder 庫, 進程就可使用 Binder 庫, 方便地調用 HAL 驅動提供的服務, 屏蔽了底層的細節, 更有利於開發者進行使用linux

Binder 庫關鍵類

在 Binder 庫中, Service 組件與 Client 組件分別使用模板類 BnInterfaceBpInterface 來描述android

  • Service: BnInterface, 即 Binder native interface.
  • Client: BpInterface, 即 Binder proxy interface.

1. BnInterface

template<typename INTERFACE>
class BnInterface : public INTERFACE, public BBinder 
{
public:
    virtual sp<IInterface> queryLocalInterface(const String16& _descriptor);
    virtual cosnt String16& getInterfaceDescriptor() const;
    
protected:
    virtual IBinder* onAsBinder();
}
複製代碼
  • 模板參數 INTERFACE 是一個由進程自定義的 Service 組件接口, 模板類 BnInterface 須要實現該接口
  • 該類由又繼承了 BBinder

接下來分析一下 BBinder 的實現bash

class BBinder : public IBinder
{
public:
    ......
    virtual status_t transact(
        unit32_t code,
        const Parcel& data,
        Parcel* reply,
        unit32_t flag = 0
    );
proctected:
    ......
    virtual status_t onTransact(
        unit32_t code,
        const Parcel& data,
        Parcel* reply,
        unit32_t flag = 0
    )
}
複製代碼

BBinder 類有兩個重要的成員函數 transact 和 onTransactcookie

  • transact: 當 Binder 代理對象經過 Binder 驅動程序向一個 Binder 本地對象發出一個進程間的請求時, Binder 驅動程序就會調用該 Binder 本地對象的成員函數 transact 來處理該請求
  • onTransact: 該方法由 Binder 本地對象來實現, 它負責分發與業務相關的進程間的請求
class IBinder : public RefBase {
    ......
}
複製代碼

可見 IBinder 類又繼承了 RefBase, 也就是說 Binder 本地對象是經過引用計數技術來維護生命週期的併發

2. BpInterface

template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase 
{
public:
    BpInterface(const sp<IBinder>& remote);
    
protected:
    virtual IBinder* onAsBinder();
}
複製代碼

模板類 BpInterface 繼承了 BpRefBase, 後者爲 Binder 代理對象提供了抽象的進程間通訊接口app

class BpRefBase : public virtual RefBase 
{
protected:
    BpRefBase(const sp<IBinder>& o);
    ......
    inline IBinder* remote() {
        return mRemote;
    }
    inline IBinder* remote() const {
        return mRemote;
    }
private:
    .......
    IBinder* const mRemote;
}
複製代碼

能夠看到 BpRefBase 中有一個成員變量 mRemote, 它的實現類爲 BpBinder, 看看這個實現類是作了哪些操做框架

class BpBinder : public IBinder 
{
public: 
    BpBinder(int32_t handle);
    inline int32_t handle() const {
        return mHandle;
    }
    ......
    virtual status_t transact(
        unit32_t code,
        const Parcel& data,
        Parcel* reply,
        unit32_t flags = 0
    );
    ......
private:
    const int32_t mHandle;
}
複製代碼

能夠看到 BpBinder 中有一個 mHandle 句柄, 它表示 Binder 引用對象的句柄值, 能夠經過 handle 來獲取異步

  • mHandle: Client 組件就是經過這個句柄值來和 Binder 驅動程序中的 Binder 引用對象 binder_ref 創建對應關係

3. IPCThreadState

  • 每個使用了 Binder 進程間通訊的進程, 都有一個 Binder 線程池, 用來處理進程間的通訊請求
  • 對於每個線程來講, 它的內部都有一個 IPCThreadState 對象, 咱們能夠經過 IPCThreadState 類的靜態方法 self 來獲取
class IPCThreadState 
{
public:
    static IPCThread* self();
    ......
    status_t transact(
        int32_t handle,
        unit32_t code, 
        const Parcel& data,
        Parcel* reply,
        unit32_t flags
    );
    ......
private:
    status_t talkWithDriver(bool doRecive = true);
    ......
    const sp<ProcessState> mProcess;
    ......
}
複製代碼

能夠看到 IPCThreadState 內部存在一個成員變量 mProcessasync

  • 對於每個使用了 Binder 進程間通訊機制的進程來講, 它的內部都存在一個 ProcessState 對象

接下來看看 ProcessState 的實現

4. ProcessState

class ProcessState: public virtual RefBase 
{
public:
    static sp<ProcessState> self();
    ......
private:
    int mDriverID;
    void* mVMStart;
}
複製代碼
  • ProcessState 這個對象與當前使用 Binder 通訊的進程一一對應, 它主要負責
    • 經過 Binder 驅動打開 binder 設備文件 dev/binder
    • 將設備文件 dev/binder 映射到進程的地址空間
    • 每個 Binder 線程池裏的線程均可以經過它來與 Binder 驅動 創建鏈接

應用層 Binder 庫的 UML 圖

好的至此, 咱們隊 Binder 庫中幾個很是重要的 C++ 對象有了必定的認識, 接下來看看這幾個類的相互依賴關係

運行時庫依賴圖.png

二. ServiceManager 的啓動

運行時庫中的 ServiceManager 與 Java 中的 ServiceManager 是對應的

  • Service Manager 是 Binder 進程間通訊的核心組件之一
  • 它扮演着 Binder 進程間通訊機制的上下文管理者的角色
  • 同時負責管理系統中的 Service 組件, 而且向 Client 組件提供獲取 Service 代理對象的服務

因爲篇幅緣由, 這裏就不介紹 Java 中的 ServiceManager 了, 感興趣的同窗能夠看看 Zygote 與系統服務進程的啓動, 沒準能夠找到你想要的答案

啓動入口

ServiceManager 該程序的入口函數 main 實如今 service_manager.c 中

// frameworks/base/cmds/servicemanager/service_manager.c
int main(int argc, char **argv) {
    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;
    // 打開設備文件
    bs = binder_open(128*1024);
    // 將本身註冊爲 Binder 驅動的上下文管理者
    if (binder_become_context_manager(bs)) {
        return -1;
    }
    svcmgr_handle = svcmgr;
    // 循環等待和處理 Client 進程的通訊請求
    binder_loop(bs, svcmgr_handler);
    return 0;
}
複製代碼

可見 service_manager 的主函數中主要作了三件事情

  1. 調用 binder_open 打開 binder 設備文件 /dev/binder, 而且將其映射到本進程的地址空間, 返回一個 binder_state 結構體
  2. 調用 binder_become_context_manager 將本身註冊成爲一個 Binder 進程間通訊的上下文管理者
  3. 調用函數 binder_loop 來循環等待和處理 Client 進程的通訊請求

打開映射 Binder 設備文件

// frameworks/base/cmds/servicemanager/binder.c

struct binder_state *binder_open(size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;
    // 在堆內存中建立了 binder_state 的實例
    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }
    // 調用 open 函數打開 Binder 設備文件
    bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC);
    if (bs->fd < 0) {
        goto fail_open;
    }

    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        goto fail_open;
    }
    // 將給進程分配的內核緩衝區大小記錄到 binder_state 結構體對象中
    bs->mapsize = mapsize;
    // 調用函數 mmap 將設備文件 /dev/binder 映射到地址空間, 而且將其地址空間的首地址記錄到 binder_state 結構體對象中
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }
    // 返回這個 binder_state 這個結構體對象
    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}
複製代碼

可見 ServiceManager 的打開設備文件的操做很是簡單

  • 調用 open 函數打開 Binder 設備文件
    • 會調用 binder 驅動的 binder_open 打開設備文件, 返回一個 file 設備文件結構體的句柄值
  • 將給進程分配的內核緩衝區大小記錄到 binder_state 結構體對象中
  • 調用函數 mmap 將設備文件 /dev/binder 映射到地址空間
    • 返回爲其分配的地址空間的首地址
    • 記錄到 binder_state 結構體對象中

註冊爲 Binder 的上下文管理者

// frameworks/base/cmds/servicemanager/binder.c
int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}
複製代碼

能夠看到註冊上下文管理者的函數中, 調用了 ioctl 這個函數(即 Binder IO controller, 用於用戶空間與 Binder 驅動交互)

  • BINDER_SET_CONTEXT_MGR 爲 IO 控制命令
    • 這個標記位表明將當前進程註冊爲 binder context manager 即 Binder 的上下文管理者

接下來簡單的看一下, Binder 內核驅動中對這個 IO 控制命令作了哪些處理

// Binder 通訊上下文管理者的在 Binder 內核驅動中的 Binder 實體對象
static struct binder_node *binder_context_mgr_node;
// 描述了註冊了 Binder 通訊上下文管理者的有效用戶 ID
static struct binder_context_mgr_uid = -1;

static long binder_ioctrl(struct file *filp, unsigned int cmd, unsigned long arg) {
    // 獲取當前進程的 binder 線程, 沒有則建立一個
    thread = binder_get_thread(proc);
    
    switch(cmd) {
        ......
        case BINDER_SET_CONTEXT_MGR:
            // 說明 Binder 上下文管理者已經註冊過了
            if (binder_context_mgr_node != NULL) {
                goto error;
            }
            // 說明 Binder 上下文管理者已經註冊過了
            if (binder_context_mgr_uid != -1) {
                goto error;
            } else {
                // 通過一系列驗證以後, 給當前進程建立其對應的 binder 實體對象保存在全局的 binder_context_mgr_node 變量中
                binder_context_mgr_node = binder_new_node(proc, NULL, NULL);
            }
            ......
            break;
    } 
}
複製代碼

Binder 內核驅動中針對 BINDER_SET_CONTEXT_MGR 這個控制碼, 主要作了如下操做

  • 將爲這個請求成爲 Binder 上下文管理者的進程建立其對應的 binder 實體對象
  • 保存在內核驅動的靜態變量 binder_context_mgr_node 中

循環等待處理 Client 進程間的請求

// frameworks/base/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;
    // BC_ENTER_LOOPER: 控制位的含義是, 將當前線程註冊成爲 Binder 線程
    // 以便 Binder 驅動程序能夠將進程間的通訊請求分發給它處理
    readbuf[0] = BC_ENTER_LOOPER;
    // 該函數經過 IO 控制命令將 readbuf 發送給 Binder 驅動程序, 通知其處理 readbuf 中的控制位
    binder_write(bs, readbuf, sizeof(uint32_t));
    // for 循環從 binder 驅動中獲取須要處理的進程間通訊請求
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        // 經過 BINDER_WRITE_READ 控制位, 從 Binder 驅動中獲取當前是否有新的進程間請求須要處理
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        if (res < 0) {
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
        // 處理進程間的請求
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}
複製代碼

binder_loop 主要作了如下幾件事情

  • 經過 readbuf 記錄 BC_ENTER_LOOPER 控制碼, 將當前線程註冊成爲 Binder 線程, 以便 Binder 驅動程序能夠將進程間的通訊請求分發給它處理
  • 經過 binder_write 將 readbuf 控制碼發送給 binder 驅動處理, 其內部一樣是使用 ioctrl 與 binder 驅動通訊
  • for 循環不斷的從 binder 驅動中獲取新的進程間通訊請求

binder 驅動註冊 looper 線程

接下來看看 binder_write 方法的實現

// frameworks/base/cmds/servicemanager/binder.c
int binder_write(struct binder_state *bs, void *data, size_t len)
{
    struct binder_write_read bwr;
    int res;

    bwr.write_size = len;
    bwr.write_consumed = 0;
    // 將數據存儲在 write_buffer 中, 即 BC_ENTER_LOOPER 這個控制碼
    bwr.write_buffer = (uintptr_t) data;
    bwr.read_size = 0;
    bwr.read_consumed = 0;
    bwr.read_buffer = 0;
    // 調用 ioctl 與 binder 驅動通訊, 請求碼爲 BINDER_WRITE_READ
    res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
    if (res < 0) {
        fprintf(stderr,"binder_write: ioctl failed (%s)\n",
                strerror(errno));
    }
    return res;
}
複製代碼

可見真正用於和 Binder 內核驅動交互的請求碼爲 BINDER_WRITE_READ, 接下來看看 binder 驅動作了哪些處理

static long binder_ioctrl(struct file *filp, unsigned int cmd, unsigned long arg) {
    // 獲取當前進程的 binder 線程, 沒有則建立一個
    thread = binder_get_thread(proc);
    
    switch(cmd) {
        ......
        case BINDER_WRITE_READ:
            ......
            if (bwr.write_size > 0) {
                // 可見這裏將 BC_ENTER_LOOPER 請求碼轉發給了 binder_thread_write 函數
                ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.read_consumed);
            }
            ......
            break;
    } 
}

int binder_thread_write(......) {
    while(...) {
        switch(cmd) {
            case BC_ENTER_LOOPER:
                // 這裏將這個線程註冊成爲了 looper 線程, 至此 Binder 進行間的通訊請求便會交由這個線程處理
                thread->looper |= BINDER_LOOPER_STATE_ENTERED;
            break;
        }
    }
}
複製代碼

至此, ServiceManager 的主線程即可以接收到 Binder 驅動發送的通訊請求了

瞭解了 ServiceManager 如何啓動了以後, 咱們就進入重頭戲, 看看一次 Binder 驅動通訊的流程是如何進行的

三. ServiceManger 進程間通訊

Client 端通訊的發起

defaultServiceManager()->addService(String16("SampleService"), new SampleService);
複製代碼

在應用框架層中, 獲取 ServiceManager 代理對象的方式爲 defaultServiceManager()

  • 咱們調用了 BpServiceManager 這個 BpBinder 對象的 addService() 方法
  • 至此一個跨進程的請求就成功發起了

接下來看看 addService 這個方法在 BpServiceManager 代理類中的實現

// frameworks/base/libs/binder/IServiceManager.cpp
    virtual status_t addService(const String16& name, const sp<IBinder>& service,
            bool allowIsolated)
    {
        Parcel data, reply;
        // 1. 將通訊參數封裝到 data 中
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        // 2. 調用 BpBinder 的 transact
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        // 3. 讀取請求結束後 Server 經過 Binder 驅動返回回來的數據
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }
複製代碼

代理實現方法主要作了如下幾步操做

  • 將通訊參數封裝到 Parcel data 中
    • 寫入接口對應的描述
    • 寫入傳入的形參
  • 經過 BpBinder 的 transact 向 Binder 驅動發起跨進程調用請求
  • 讀取請求結束後 Server 經過 Binder 驅動返回回來的數據

BpBinder 的 transact 操做

// frameworks/native/libs/binder/BpBinder.cpp
status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // mAlive 用於判斷 Binder 代理對象所引用的 Binder 本地對象是否存活
    if (mAlive) {
        // 調用了 IPCThreadState 的 transact 方法
        // mHandle 爲這個代理對象的句柄值
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
    return DEAD_OBJECT;
}

// 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 = data.errorCheck();
    if (err == NO_ERROR) {
        // 1. 將 data 封裝到一個 binder_transaction_data 結構體對象中
        // handle 爲當前 Binder 代理對象的句柄值
        err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    }
    // TF_ONE_WAY 若爲 0 則說明是同步的進程間請求
    if ((flags & TF_ONE_WAY) == 0) {
        if (reply) {
            // 2. 經過 waitForResponse 向 Binder 驅動發送上面封裝的 binder_transaction_data 結構體對象
            // 操做碼爲 BC_TRANSACTION
            err = waitForResponse(reply);
        } else {
            ......
        }
    } else {
        ......
    }
    return err;
}
複製代碼

可見 Client 調用遠程方法時, 其代理對象的 transact 方法主要作了如下的操做

  • 經過 writeTransactionData 函數將 data 等數據封裝成爲一個 binder_transaction_data 對象, 用來和 Binder 驅動程序交互
    • 與 Binder 驅動程序交互的操做碼爲 BC_TRANSACTION
  • 經過 waitForResponse 函數, 向 Binder 驅動發起請求

先看看如何將 data 等數據封裝成爲 binder_transaction_data

// 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 對象
    binder_transaction_data tr;
    // 賦初始值
    tr.target.ptr = 0; 
    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) {
        // 將 data 中的數據拷貝到 tr 中
        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) {
        ......
    } else {
        ......
    }
    // mOut 描述一個命令緩衝協議區
    mOut.writeInt32(cmd);// 將 cmd 這個命令寫入, 表示這個命令以後須要發送給 Binder 驅動
    mOut.write(&tr, sizeof(tr));// 將 tr  這個結構體寫入, 用於後續與 Binder 驅動交互

    return NO_ERROR;
}
複製代碼

writeTransactionData 這個函數作的事情與咱們上述一致

接下來看看 waitForResponse 如何經過 tr 和 BC_TRANSACTION 命令與 Binder 驅動程序進行交互

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

    while (1) {
        // 可見這個裏調用了 talkWithDriver() 與 Binder 驅動交互
        if ((err=talkWithDriver()) < NO_ERROR) break;
        if (err < NO_ERROR) break;
        // 緩衝區 mIn 這個與 mOut 相對應, 它用於保存從 Binder 驅動程序接收到的返回協議
        if (mIn.dataAvail() == 0) continue;
        cmd = (uint32_t)mIn.readInt32();
        ......
    }
}

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    ......
    // 1. 定義 binder_write_read 結構體, 指定輸入緩衝區和輸出緩衝區
    binder_write_read bwr;
    
    // 指定從當前進程輸出到 Binder 驅動的緩衝區
    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();// 將要輸出的數據保存到 bwr 的 write_buffer 變量中
    // doReceive 用來描述調用者是否能夠收到 Binder 的返回協議碼
    if (doReceive && needRead) {
        // 設置從 Binder 驅動輸入到當前進程緩衝區的相關參數
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    
    // 若是輸出緩衝區和輸入緩衝區大小都爲 0, 說明不須要與 Binder 驅動交互
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        ......
        // 2. 使用 IO 控制命令 BINDER_WRITE_READ 來與 Binder 驅動進行交互, 說明要進行讀寫操做
        // bwr 即爲讀寫操做的內容
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
        ......
    } while (err == -EINTR);

    if (err >= NO_ERROR) {
        // 將 Binder 驅動已處理的命令協議從 mOut 中移除
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        // 將 Binder 驅動返回的命令協議保存到 mIn 中
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        return NO_ERROR;
    }
    return err;
}
複製代碼

talkWithDriver 這個函數很是重要, 它是 Binder 停留在應用程序框架層的最後一個函數, 主要作了以下操做

  • 將 IPCThreadState 中的輸出緩衝區 mOut 和輸入緩衝區 mIn 封裝到 binder_write_read 這個結構體對 bwr 中
  • 將 bwr 傳入 ioctl 函數, 經過 ioctl 函數與 Binder 驅動展開通訊

Binder 驅動處理 BC_TRANSACTION 協議碼

// kernel/goldfish/drivers/staging/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
			struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,
			binder_size_t *consumed)
{
	uint32_t cmd;
	void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
	void __user *ptr = buffer + *consumed;
	void __user *end = buffer + size;

	while (ptr < end && thread->return_error == BR_OK) {
	    // 獲取從用戶空間傳遞過來的指令碼保存在 cmd 中, 由上面可知, cmd 爲 BC_TRANSACTION
		if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
		......
		switch(cmd) {
		......
		// 處理 BC_TRANSACTION/BC_REPLY
		case BC_TRANSACTION:
		case BC_REPLY: {
		    // 從用戶空間拷貝數據到 transaction_data 中
			struct binder_transaction_data tr;
			if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr)))
				return -EFAULT;
			ptr += sizeof(tr);
			// 進行指令碼的處理操做
			binder_transaction(proc, thread, &tr,
					   cmd == BC_REPLY, 0);
			break;
		}
		......
		}
}

static void binder_transaction(struct binder_proc *proc,
			       struct binder_thread *thread,
			       struct binder_transaction_data *tr, int reply,
			       binder_size_t extra_buffers_size)
{
	......
	if (reply) {// 處理 BC_REPLY 指令, 到後面分析
	   ....
	} else {
	   // 處理 BC_TRANSACTION 指令
	   if (tr->target.handle) {
	        // 1. 獲取 Client 調用的 binder 引用對象
			struct binder_ref *ref;
			// 從 client 進程中, 經過句柄值, 獲取其在 linux 內核驅動的引用對象
			ref = binder_get_ref(proc, tr->target.handle, true);
			// 2. 經過引用對象找到其對應的實體對象
			target_node = ref->node;
		} else {
			target_node = context->binder_context_mgr_node;
		}
		......
		// 3. 經過 binder 實體對象, 找對對應的 Server 進程
		target_proc = target_node->proc;
		......
		// 4. 嘗試在 Server 進程找到最合適的空閒線程去處理此次 Client 端的請求
		if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
			struct binder_transaction *tmp;
			tmp = thread->transaction_stack;
			......
			while (tmp) {
				if (tmp->from && tmp->from->proc == target_proc)
					target_thread = tmp->from;
				tmp = tmp->from_parent;
			}
		}
	}
	// 5. 將目標線程的 todo 隊列和 wait 隊列保存到成員變量中
	if (target_thread) {
	    // 更新成員變量指向目標線程中的相關屬性
		target_list = &target_thread->todo;
		target_wait = &target_thread->wait;
	} else {
	    // 更新成員變量指向目標進程中的相關屬性
		target_list = &target_proc->todo;
		target_wait = &target_proc->wait;
	};

	/* TODO: reuse incoming transaction for reply */
	// 6.1 binder_transaction 對象 t 會被封裝成爲 BINDER_WORK_TRANSACTION 工做項, 
	// 後續會添加到 Server 目標線程的 todo 中, 以便其可以接受到 Binder 驅動發送的 BR_TRANSACTION 協議
	t = kzalloc_preempt_disabled(sizeof(*t));
	// 6.2 binder_transaction 對象 tcomplete 會被封裝成 BINDER_WORK_TRANSACTION_COMPLETE 工做項
	// 後續會發送到 Client 發起線程的 todo 隊列中, 以便其可以接收到 Binder 驅動發送的 BR_TRANSACTION_COMPLETE 協議
	tcomplete = kzalloc_preempt_disabled(sizeof(*tcomplete));
	// 初始化 t
	if (!reply && !(tr->flags & TF_ONE_WAY))
		t->from = thread;
	else
		t->from = NULL;
	t->sender_euid = task_euid(proc->tsk);
	t->to_proc = target_proc;
	t->to_thread = target_thread;
	t->code = tr->code;
	t->flags = tr->flags;
	t->priority = task_nice(current);
	// 從 tr 中複製數據到目標進程的內核緩衝區
	t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size, extra_buffers_size, !reply && (t->flags & TF_ONE_WAY));
	t->buffer->allow_user_free = 0;
	t->buffer->debug_id = t->debug_id;
	t->buffer->transaction = t;
	t->buffer->target_node = target_node;
	......
}
複製代碼

好了, 總結一下, 這個 binder 內核中處理用戶空間指令碼交換的方法主要是 binder_transaction, 關於 BC_TRANSACTION 它主要作了以下操做

  • 獲取 Client 調用的 binder 引用對象
  • 經過 binder 引用對象找到對應的 binder_node 實體對象
  • 經過 binder 實體對象, 找對對應的目標 Server 進程
  • 在 Server 進程找到最合適的空閒線程去處理此次 Client 端的請求
  • 將目標線程的 todo 隊列和 wait 隊列保存到成員變量中
  • 初始化 t 和 tcomplete 對象
    • t 交由目標進程處理
    • tcomplete 交由源進程處理

接下來就要處理方法的調用了, 咱們在應用層調用了 data.writeStrongBinder(binder); 將 binder 註冊到目標進程中, 看看 binder_transaction 是怎樣處理的

for (; offp < off_end; offp++) {
		struct flat_binder_object *fp;
		fp = (struct flat_binder_object *) (t->buffer->data + *offp)
		switch (fp->type) {
		// 咱們開始的時候調用的是 put 方法
		case BINDER_TYPE_BINDER:
		case BINDER_TYPE_WEAK_BINDER: {
		      struct binder_ref *ref;
		      // 8. 嘗試從 Client 進程中獲取參數中 binder 對象在 linux 內核驅動中的實體對象
		      struct binder_node* node = binder_get_node(proc, fp->binder);
		      if (node == NULL) {
		          // 若源進程沒有其實體對象, 則調用 binder_new_node 建立一個
		          node = binder_new_node(proc, fp->binder, fp->cookie);
		      }
		      // 9. 獲取 Sever 進程須要使用的 binder 引用對象(沒有則建立一個)
		      ref = binder_get_ref_for_node(target_proc, node);
		} break;
	}
複製代碼

可見這些的操做很是的重要

  • 從源進程中獲取這個 Binder 實體對象
  • 沒有實體對象則調用 binder_new_node 在源進程建立一個
  • 在目標進程中獲取 Binder 引用對象, 沒有則建立一個

好的, 線程參數數據也已經獲取完了, 咱們接着往下看 binder_transaction

if (reply) {
        ......
	} else if (!(t->flags & TF_ONE_WAY)) {
	    // 同步操做
		t->need_reply = 1;
		t->from_parent = thread->transaction_stack;
		thread->transaction_stack = t;
	} else {
	    // 若爲異步操做, 則將任務添加到目標 binder 進程的異步隊列中
		if (target_node->has_async_transaction) {
			target_list = &target_node->async_todo;
			target_wait = NULL;
		} else
			target_node->has_async_transaction = 1;
	}
	// 將 t 的工做項設置爲 BINDER_WORK_TRANSACTION
	t->work.type = BINDER_WORK_TRANSACTION;
	// 添加到目標進程的工做隊列的尾部
	list_add_tail(&t->work.entry, target_list);
	// 將 tcomplete 的工做項設置爲 BINDER_WORK_TRANSACTION_COMPLETE 
	tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
	// 添加到源進程的工做項的尾部
	list_add_tail(&tcomplete->entry, &thread->todo);
	if (target_wait) {
     	// 喚醒目標線線程去執行 BINDER_WORK_TRANSACTION 任務
		wake_up_interruptible(target_wait);
	}
	return;

複製代碼

找到了目標進程以及須要執行的線程後, 源線程和目標進程就回去併發的處理本身的工做項了

  • 將 BINDER_WORK_TRANSACTION 工做項發送到目標線程的工做隊列中
  • 將 BINDER_WORK_TRANSACTION_COMPLETE 工做項發送到源線程的工做隊列中

Client 端處理 BINDER_WORK_TRANSACTION_COMPLETE 工做項

static int binder_thread_read(.......) {
	......
	while(1) {
	    switch(w->type) {
	    ......
	    case BINDER_WORK_TRANSACTION_COMPLETE: {
	        // 將 BR_TRANSACTION_COMPLETE 返回用戶空間
			cmd = BR_TRANSACTION_COMPLETE;
			if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
				return -EFAULT;
			ptr += sizeof(uint32_t);
			binder_stat_br(proc, thread, cmd);
			list_del(&w->entry);
			kfree(w);
			binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
		} break;
		......
	    }
	}
	
}
複製代碼

可見 binder_thread_read 對 BINDER_WORK_TRANSACTION_COMPLETE 處理也很是簡單

  • 將 BR_TRANSACTION_COMPLETE 從 Linux 內核中投遞到用戶空間
  • 因此在用戶空間的 waitForResponse 中能夠經過 mIn 輸入緩衝區讀取到 binder 內核驅動傳遞過來的 BR_TRANSACTION_COMPLETE 指令

Client 端用戶空間處理 BR_TRANSACTION_COMPLETE 協議碼

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();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        // 從輸入緩衝區中讀取, 是否有 Binder 驅動寫入的數據
        cmd = (uint32_t)mIn.readInt32();
        // 主要查看 BR_TRANSACTION_COMPLETE 指令碼
        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        ......
    }

finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }
    
    return err;
}
複製代碼

能夠看到 BR_TRANSACTION_COMPLETE 指令碼很簡單

  • 跳出了 waitForResponse 的方法
  • 該操做會回到其上一級方法 talkWithDriver 中, 繼續循環等待目標進程將上次發出的進程間通訊請求返回回來

因此, 接下來的重頭戲即是咱們須要查看目標線程對 Binder 驅動發出的 BINDER_WORK_TRANSACTION 指令的處理

Server 端處理 BINDER_WORK_TRANSACTION 工做項

由前面可知, BINDER_WORK_TRANSACTION 會將工做項添加到目標進程的 todo 隊列中, 那麼目標進程就會被喚醒, 進而執行器 binder_thread_read 處理 todo 隊列中的工做項

// kernel/goldfish/drivers/staging/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
			struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,
			binder_size_t *consumed)
{ 
    ......
    // 循環從其讀取器工做項數據
    while (1) {
		uint32_t cmd;
		struct binder_transaction_data tr;
		struct binder_work *w;
		struct binder_transaction *t = NULL;
        // 1. 從其線程/進程的 todo 隊列中獲取工做項, 而且將數據存入 binder_work 結構體對象中
		if (!list_empty(&thread->todo)) {
			w = list_first_entry(&thread->todo, struct binder_work,
					     entry);
		} else if (!list_empty(&proc->todo) && wait_for_proc_work) {
			w = list_first_entry(&proc->todo, struct binder_work,
					     entry);
		} else {
		    ......
			break;
		}
		// 2. 處理工做項中對應的指令碼
		switch (w->type) {
		// 咱們主要關注對 BINDER_WORK_TRANSACTION 的處理
		case BINDER_WORK_TRANSACTION: {
		    // 2.1 將 binder_work 轉爲一個 binder_transaction 結構體對象
			t = container_of(w, struct binder_transaction, work);
		} break;
		......
		// 3. 將 binder_transaction 中的數據從 binder_transaction_data 中, 以便後續能夠傳輸到用戶空間
		if (t->buffer->target_node) {// target_node 不爲 NULL, 則指定協議碼爲 BR_TRANSACTION
			struct binder_node *target_node = t->buffer->target_node;
			// 將目標線程 binder 本地對象的信息複製到 tr 中, 以便目標線程的 thread 接收到 binder 驅動發送的 BR_TRANSACTION 以後, 能夠將返回協議交給指定的 binder 本地對象處理
			tr.target.ptr = target_node->ptr;
			tr.cookie =  target_node->cookie;
			t->saved_priority = task_nice(current);
			// 保證目標線程的優先級 < 源線程的優先級
			if (t->priority < target_node->min_priority &&
			    !(t->flags & TF_ONE_WAY))
				binder_set_nice(t->priority);
			else if (!(t->flags & TF_ONE_WAY) ||
				 t->saved_priority > target_node->min_priority)
				binder_set_nice(target_node->min_priority);
			cmd = BR_TRANSACTION;
		} else {
			......
		}
	    .......
        // 4. 將 tr 數據拷貝到目標進程的用戶空間中
		if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
		// 將對應的協議碼拷貝到用戶空間中
		if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr)))
			return -EFAULT;
		ptr += sizeof(tr);
        .......
        // 5. 這個工做項已經被處理了, 從鏈表中刪除
		list_del(&t->work.entry);
		t->buffer->allow_user_free = 1;
		// 判斷是否爲同步請求
		if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
			t->to_parent = thread->transaction_stack;
			t->to_thread = thread;
			// 壓入目標線程的任務棧中
			thread->transaction_stack = t;
		} else {
			t->buffer->transaction = NULL;
			kfree(t);
		}
		break;
	}
	return 0;
}
複製代碼

可見目標線程被喚醒以後他在 binder 驅動中作了以下的事情

  • 從目標線程的 todo 隊列中獲取工做項, 而且將數據存入 binder_work 結構體對象中
  • 對與 BINDER_WORK_TRANSACTION 這個指令碼, 主要是將 binder_work 結構體轉爲了 binder_transaction 結構體對象 t
  • 將 t 中的數據拷貝到 tr 這個結構體對象中, 以便於後續能夠將其傳輸到用戶空間
  • 將 tr 中的數據拷貝用用戶空間
  • 工做項處理完畢, 將其從 todo 隊列中移除

好的, 接下來就進入了目標進程的用戶空間了

Server 端處理 BR_TRANSACTION 協議碼

ServiceManager 被 Binder 驅動喚醒後, 會調用 binder_parse 方法來處理從 Binder 驅動程序中接收到的返回協議

// frameworks/base/cmds/servicemanager/binder.c
int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    int r = 1;
    uintptr_t end = ptr + (uintptr_t) size;

    while (ptr < end) {
        // 1. 從用戶空間的緩衝區中讀取 Binder 驅動傳遞過來的協議碼
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
        switch(cmd) {
        ......
        // 這裏主要關注 BR_TRANSACTION 協議
        case BR_TRANSACTION: {
            // 2. 從緩衝區中獲取通訊數據的結構體
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            ......
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;       // 解析從 Binder 驅動程序讀取回來的進程間通訊數據
                struct binder_io reply;     // 將通訊結果寫入 reply 中以便於傳給 Binder 驅動, 進而返回源進程
                int res;
                // 3. 初始化 reply 和 rdata
                bio_init(&reply, rdata, sizeof(rdata), 4); 
                // 4. 解析 txn 中的數據到 msg 中
                bio_init_from_txn(&msg, txn);
                // 5. 調用 func 函數指針, 處理協議, 將結果寫入 reply
                res = func(bs, txn, &msg, &reply);
                ......
                // 將通訊結果返回給 binder 驅動
                binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
            }
            ptr += sizeof(*txn);
            break;
        }
        }
    }
}
複製代碼

binder_parse 中所作的事情很是清晰

  • 讀取協議碼
  • 關於 BR_TRANSACTION 協議碼
    • 先獲取進程間通訊的數據 txn
    • 將 txn 中的數據解析到 binder_io 對象 msg 中
    • 調用 func 這個函數指針, 真正的執行協議的處理
    • 調用 binder_send_reply 將通訊結果返回給 binder 驅動

func 函數指針對本次跨進程調用的處理

在 ServiceManager 中, func 這個函數指針, 指代 svcmgr_handler 這個函數

// frameworks/base/cmds/servicemanager/service_manager.c
int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    ......
    // 驗證接口名稱的描述
    strict_policy = bio_get_uint32(msg);
    s = bio_get_string16(msg, &len);
    if (s == NULL) {
        return -1;
    }
    // 執行對應的方法
    switch(txn->code) {
    case SVC_MGR_ADD_SERVICE:
        // 獲取一個要註冊服務的名稱 (如"ActivityManagerService")
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        // 從 msg 中取出要註冊的服務 binder 引用對象的句柄值
        handle = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        // 執行添加服務的操做
        if (do_add_service(bs, s, len, handle, txn->sender_euid,
            allow_isolated, txn->sender_pid))
            return -1;
        break;

    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }
    // 調用這個函數, 將成功代碼 0 寫入到 binder 結構體 reply 中
    bio_put_uint32(reply, 0);
    return 0;
}
複製代碼

能夠看到一個很是重要的函數 do_add_service 這個函數真正執行了服務的添加過程

// frameworks/base/cmds/servicemanager/service_manager.c
int do_add_service(struct binder_state *bs,
                   const uint16_t *s, size_t len,
                   uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid)
{
    struct svcinfo *si;
    
    if (!handle || (len == 0) || (len > 127))
        return -1;

    // 判斷 uid 所指代的源進程, 是否有資格進行註冊操做
    if (!svc_can_register(s, len, spid, uid)) {
        return -1;
    }
    // 判斷 si 服務是否已經註冊了
    si = find_svc(s, len);
    if (si) {
        ......
    } else {
        // 建立一個 svcinfo 而且鏈入 svclist 中
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        si->handle = handle;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '\0';
        si->death.func = (void*) svcinfo_death;
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->next = svclist;
        svclist = si;
    }
    binder_acquire(bs, handle);
    // 綁定死亡通知
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}
複製代碼

至此就成功的將一個 Service 組件註冊到 ServiceManager 中了

binder_send_reply 將通訊結果返回給 binder 驅動

// frameworks/base/cmds/servicemanager/service_manager.c
void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
    struct {
        uint32_t cmd_free;
        binder_uintptr_t buffer;
        uint32_t cmd_reply;
        struct binder_transaction_data txn;
    } __attribute__((packed)) data;
    // cmd_free 的協議碼爲 BC_FREE_BUFFER
    data.cmd_free = BC_FREE_BUFFER;
    data.buffer = buffer_to_free;
    // cmd_reply 的協議爲 BC_REPLY
    data.cmd_reply = BC_REPLY;
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    // 將一些數據寫入 data 的 txn 中
    if (status) {
        data.txn.flags = TF_STATUS_CODE;
        data.txn.data_size = sizeof(int);
        data.txn.offsets_size = 0;
        data.txn.data.ptr.buffer = (uintptr_t)&status;
        data.txn.data.ptr.offsets = 0;
    } else {
        data.txn.flags = 0;
        data.txn.data_size = reply->data - reply->data0;
        data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
        data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
        data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
    }
    // 其內部調用 ioctl() 函數和 IO 控制命令 BINDER_WRITE_READ 將 BC_FREE_BUFFER/BC_REPLY 發送給 binder 驅動程序
    binder_write(bs, &data, sizeof(data));
}
複製代碼

能夠看到 binder_send_reply 中作的操做也比較清晰

  • 寫入 BC_FREE_BUFFER 協議碼
  • 寫入 BC_REPLY 協議碼
  • 最終調用了 binder_write 函數
    • 其內部調用 ioctl() 函數和 IO 控制命令 BINDER_WRITE_READ 將 BC_FREE_BUFFER/BC_REPLY 發送給 binder 驅動程序

接下來看看 Binder 驅動如何處理這些協議碼的

Binder 驅動處理 BC_FREE_BUFFER/BC_REPLY 協議碼

BC_FREE_BUFFER 協議碼的處理

// kernel/goldfish/drivers/staging/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
			struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,
			binder_size_t *consumed)
{
    ......
	while (ptr < end && thread->return_error == BR_OK) {
	    // 獲取從用戶空間傳遞過來的指令碼保存在 cmd 中, 由上面可知, cmd 爲 BC_TRANSACTION
		if (get_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
		......
		switch(cmd) {
		......
		// 處理 BC_FREE_BUFFER 協議碼
		case BC_FREE_BUFFER: {
			binder_uintptr_t data_ptr;
			struct binder_buffer *buffer;
			// 從用戶空間中獲得要釋放的內核緩衝區的地址, 存放到 data_ptr 中
			if (get_user_preempt_disabled(data_ptr, (binder_uintptr_t __user *)ptr))
				return -EFAULT;
			ptr += sizeof(binder_uintptr_t);
            // 獲取緩衝區
			buffer = binder_buffer_lookup(proc, data_ptr);
			if (buffer == NULL) {
				break;
			}
			// 判斷是否容許釋放
			if (!buffer->allow_user_free) {
				break;
			}
			// 說明內核緩衝區分配給 transaction 使用的
			if (buffer->transaction) {
				buffer->transaction->buffer = NULL;
				buffer->transaction = NULL;
			}
			if (buffer->async_transaction && buffer->target_node) {
				BUG_ON(!buffer->target_node->has_async_transaction);
				if (list_empty(&buffer->target_node->async_todo))
					buffer->target_node->has_async_transaction = 0;
				else
					list_move_tail(buffer->target_node->async_todo.next, &thread->todo);
			}
			// 減小相關的引用計數
			binder_transaction_buffer_release(proc, buffer, NULL);\
			// 釋放內核緩衝區 buffer 
			binder_free_buf(proc, buffer);
			break;
		}
		......
}
複製代碼

能夠看到 binder_thread_write 中對於 BC_FREE_BUFFER 協議碼的處理, 主要是釋放通訊過程給目標進程分配的內核緩衝區, 減小相關的引用計數

接下來看看 binder_thread_write 對 BC_REPLY 的處理

BC_REPLY 協議碼的處理

......
        // 處理 BC_TRANSACTION/BC_REPLY
		case BC_TRANSACTION:
		case BC_REPLY: {
		    // 從用戶空間拷貝數據到 binder_transaction_data 中
			struct binder_transaction_data tr;
			if (copy_from_user_preempt_disabled(&tr, ptr, sizeof(tr)))
				return -EFAULT;
			ptr += sizeof(tr);
			// 進行指令碼的處理操做
			binder_transaction(proc, thread, &tr,
					   cmd == BC_REPLY, 0);
			break;
		}
		......
		}
}

static void binder_transaction(struct binder_proc *proc,
			       struct binder_thread *thread,
			       struct binder_transaction_data *tr, int reply,
			       binder_size_t extra_buffers_size)
{
	......
	if (reply) {// 處理 BC_REPLY 指令
	    // 找尋目標線程(即 Client 端的線程)
	    in_reply_to = thread->transaction_stack;
		if (in_reply_to == NULL) {
			binder_user_error("%d:%d got reply transaction with no transaction stack\n",
					  proc->pid, thread->pid);
			return_error = BR_FAILED_REPLY;
			goto err_empty_call_stack;
		}
		// 恢復目標線程的優先級
		binder_set_nice(in_reply_to->saved_priority);
		if (in_reply_to->to_thread != thread) {
		    ......
			return_error = BR_FAILED_REPLY;
			in_reply_to = NULL;
			goto err_bad_call_stack;
		}
		// 將要處理的事務, 添加到線程棧的頂端
		thread->transaction_stack = in_reply_to->to_parent;
		target_thread = in_reply_to->from;
		if (target_thread == NULL) {
			return_error = BR_DEAD_REPLY;
			goto err_dead_binder;
		}
		if (target_thread->transaction_stack != in_reply_to) {
			return_error = BR_FAILED_REPLY;
			in_reply_to = NULL;
			target_thread = NULL;
			goto err_dead_binder;
		}
		target_proc = target_thread->proc;
		
	} else {// 處理 BC_TRANSACTION 指令, 前面已經分析過了
	   ......
	}
	
	// ....... 與分析 BC_TRANSACTION 後續一致
}
複製代碼

能夠看到 Binder 驅動對於 BC_REPLY 比較簡單, 除了 BC_REPLY 中的操做與 BC_TRANSACTION 有所不一樣, 後續的操做是一致的, 畢竟調用的是同一個方法 , 最終會封裝成兩個工做項 BINDER_WORK_TRANSACTION 和 BINDER_WORK_TRANSACTION_COMPLETE 分別發送給目標進程和源進程 (這裏的目標進程爲Client 端, 源進程爲 Server 端了, 由於本次發起 Binder 驅動通訊的爲 Server 端)

源進程接收到 BINDER_WORK_TRANSACTION_COMPLETE 以後, 就完全的結束此次的 Binder 通訊了, 這裏再也不贅述

接下來看看目標進程如何處理 BINDER_WORK_TRANSACTION 工做項

Client 端處理 BINDER_WORK_TRANSACTION 工做項

// kernel/goldfish/drivers/staging/android/binder.c
static int binder_thread_write(struct binder_proc *proc,
			struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,
			binder_size_t *consumed)
{
    ......
    // 循環從其讀取器工做項數據
    while (1) {
		uint32_t cmd;
		struct binder_transaction_data tr;
		struct binder_work *w;
		struct binder_transaction *t = NULL;
        ......
        
		// 處理工做項中對應的指令碼
		switch (w->type) {
		// 咱們主要關注對 BINDER_WORK_TRANSACTION 的處理
		case BINDER_WORK_TRANSACTION: {
			t = container_of(w, struct binder_transaction, work);
		} break;
		......
		// 將 binder_transaction 中的數據從 binder_transaction_data 中, 以便後續能夠傳輸到用戶空間
		if (t->buffer->target_node) {
			......// 上面已經分析過了
		} else {
		    // target_node 爲 NULL, 則指定協議碼 BR_REPLY
			tr.target.ptr = 0;
			tr.cookie = 0;
			cmd = BR_REPLY;
		}
	    .......
        // 將協議碼和數據拷貝到用戶空間
		if (put_user_preempt_disabled(cmd, (uint32_t __user *)ptr))
			return -EFAULT;
		ptr += sizeof(uint32_t);
		if (copy_to_user_preempt_disabled(ptr, &tr, sizeof(tr)))
			return -EFAULT;
		ptr += sizeof(tr);
        .......
        // 這個工做項已經被處理了, 從鏈表中刪除
		list_del(&t->work.entry);
		t->buffer->allow_user_free = 1;
		// 判斷是否爲同步請求
		if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
			// ...... 在上面已經分析過了
		} else {
		    // 直接釋放內核緩衝區的內存
			t->buffer->transaction = NULL;
			kfree(t);
		}
		break;
	}
	return 0;
}
複製代碼

能夠看到 BINDER_WORK_TRANSACTION 工做項的 t->buffer->target_node 爲 NULL 時, 會將協議碼置爲 BR_REPLY, 而後將數據寫入用戶空間, 接下來咱們看看用戶空間對 BR_REPLY 的處理

Client 端對 BR_REPLY 的處理

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();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
        // 從輸入緩衝區中讀取, 是否有 Binder 驅動寫入的數據
        cmd = (uint32_t)mIn.readInt32();
        // 主要查看 BR_TRANSACTION_COMPLETE 指令碼
        switch (cmd) {
        case BR_REPLY:
            { 
                // 從用戶緩衝區獲取 Binder 驅動寫入的數據
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                if (err != NO_ERROR) goto finish;
                if (reply) {
                    // 表示該線程發送的進程間通訊請求已經被處理了
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        // 這個方法將 Binder 驅動傳遞過來的數據寫入 Parcel 的 reply 中
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t),
                            freeBuffer, this);
                    } else {
                        ......
                    }
                } else {
                   ......
                }
            }
            // 跳出循環, 即 waitForResponse 等待進程通訊的結果的操做已經結束了
            goto finish;
        ......
    }

finish:
    ......
    
    return err;
}
複製代碼

能夠看到 Client 端用戶空間對 BR_REPLY 的操做也很是清晰

  • 從用戶空間獲取數據, 寫入 tr 中
  • 將 tr 中的數據寫入 Parcel 的 reply 中
  • 跳出循環, 即 waitForResponse 函數執行結束, 本次 Binder 進程間通訊結束

Binder 進程通訊回顧

至此, 一次 Binder 進程間的通訊就分析完了, 這裏再次梳理一下, 其主要包括以下幾個步驟

  • Client 端向 Binder 驅動發起 BC_TRANSACTION 協議碼
  • Binder 驅動處理 BC_TRANSACTION 協議, 產生了兩個工做項
    • BINDER_WORK_TRANSACTION_COMPLETE
      • Binder 驅動將 BR_TRANSACTION_COMPLETE 協議碼, 發送給 Client 端
    • BINDER_WORK_TRANSACTION
      • Binder 驅動將 BR_TRANSACTION 協議碼, 發送給 Server 端
  • Client 端處理 BR_TRANSACTION_COMPLETE 協議, 繼續等待通訊結果
  • Server 端處理 BR_TRANSACTION 協議
    • 調用 Client 端請求的方法, 獲得返回值寫入 reply
    • 將 BC_REPLY 發送給 Binder 驅動
  • Binder 驅動處理 BC_REPLY 協議碼, 產生了兩個工做項
    • BINDER_WORK_TRANSACTION_COMPLETE
      • Binder 驅動將 BR_TRANSACTION_COMPLETE 協議碼, 發送給 Server 端
    • BINDER_WORK_TRANSACTION
      • Binder 驅動將 BR_REPLY 協議碼, 發送給 Client 端
  • Server 端處理 BR_TRANSACTION_COMPLETE 協議碼, 表示通訊結束
  • Client 端處理 BR_REPLY 協議碼, 獲得經過結果
  • 至此一次通訊結束

時序圖

Binder 驅動通訊時序圖.png

總結

至此, 咱們 Binder 驅動的講解就結束了, 這部分的內容較之音視頻的難度, 感受也不遑多讓, 筆者花費了很大的精力去剖析, 的確很是的晦澀難懂, 這也是筆者爲什麼遲遲難如下筆的緣由

如有人可以堅持讀到這裏, 那這可真是至關使人欣慰的事情啊, 若可以幫你理清 Binder 驅動相關知識, 那我將會感到很是的榮幸

這裏祝你們新年快樂了, 但願新的一年裏, 你們都能收穫本身想要的, 越努力, 越幸運 !

相關文章
相關標籤/搜索