Binder 驅動詳解(下)
前言
通過 Binder 上一篇文章的分析, 我們知道了 Binder 驅動在我們應用開發過程中的使用方式, 瞭解到了 BBinder 和 BpBinder 兩個非常重要的 Native 物件, 本次我們就著重分析一下 Binder 在執行時庫層知識
因為執行時庫是使用 C/C++ 編寫的, 對於 Android 開發者來說, 可能有些晦澀難懂, 筆者也下了很大的功夫, 對此感興趣的同學可以耐心往下讀, 如果存在讓大家興奮的點, 那麼筆者就十分滿足了
- 應用層框架層的 Binder 庫
- ServiceManager 的啟動
- ServiceManager 程序間通訊
一. AndroidRuntime 層的 Binder 庫
封裝的意義
Android 系統將各種 Binder 驅動程式操作封裝成一個 Binder 庫, 程序就可以使用 Binder 庫, 方便地呼叫 HAL 驅動提供的服務, 遮蔽了底層的細節, 更有利於開發者進行使用
Binder 庫關鍵類
在 Binder 庫中, Service 元件與 Client 元件分別使用模板類 BnInterface 和 BpInterface 來描述
- 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 的實現
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 和 onTransact
- 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 代理物件提供了抽象的程序間通訊介面
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 內部存在一個成員變數 mProcess
- 對於每一個使用了 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 的主函式中主要做了三件事情
- 呼叫 binder_open 開啟 binder 裝置檔案 /dev/binder, 並且將其對映到本程序的地址空間, 返回一個 binder_state 結構體
- 呼叫 binder_become_context_manager 將自己註冊成為一個 Binder 程序間通訊的上下文管理者
- 呼叫函式 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 = ⌖_thread->todo; target_wait = ⌖_thread->wait; } else { // 更新成員變數指向目標程序中的相關屬性 target_list = ⌖_proc->todo; target_wait = ⌖_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 = ⌖_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 端
- BINDER_WORK_TRANSACTION_COMPLETE
- 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 端
- BINDER_WORK_TRANSACTION_COMPLETE
- Server 端處理 BR_TRANSACTION_COMPLETE 協議碼, 表示通訊結束
- Client 端處理 BR_REPLY 協議碼, 得到通過結果
- 至此一次通訊結束
時序圖

Binder 驅動通訊時序圖.png
總結
至此, 我們 Binder 驅動的講解就結束了, 這部分的內容較之音視訊的難度, 感覺也不遑多讓, 筆者花費了很大的精力去剖析, 的確非常的晦澀難懂, 這也是筆者為何遲遲難以下筆的原因
若有人能夠堅持讀到這裡, 那這可真是相當令人欣慰的事情啊, 若能夠幫你理清 Binder 驅動相關知識, 那我將會感到非常的榮幸
這裡祝大家新年快樂了, 希望新的一年裡, 大家都能收穫自己想要的, 越努力, 越幸運 !