--- title: 'Binder - 物件參考' disqus: kyleAlien --- Binder - 物件參考 === ## OverView of Content [TOC] ## Binder service 計數 * 當 BinderService 計數從 `0->1`、`1->0` 時,BinderDriver 會對目標 BinderService 發送 **BINDER_WORK_NODE 命令**,該命令會增減 `binder_node` 的計數,之後再由 BinderService 去增減自身 `RefBase` 的計數 ```c= // binder.c static int binder_thread_read( struct binder_proc *proc, // Service 進程 struct binder_thread *thread, // Service 線程 binder_uintptr_t binder_buffer, // IPCThreadState 的 mIn 數據空間 size_t size, binder_size_t *consumed, int non_block) { // 使用者空間的指針 void __user *buffer = (void __user *)(uintptr_t)binder_buffer; // 取得開頭結尾指針 void __user *ptr = buffer + *consumed; // 數據的起點 void __user *end = buffer + size; // 數據的終點 int ret = 0; int wait_for_proc_work; ... while (1) { uint32_t cmd; // 創建 binder_transaction_data_secctx struct binder_transaction_data_secctx tr; // 取 tr#transaction_data struct binder_transaction_data *trd = &tr.transaction_data; // 當前需要執行的 binder_work struct binder_work *w = NULL; // 任務 header struct list_head *list = NULL; // 要對外傳輸的訊息 struct binder_transaction *t = NULL; struct binder_thread *t_from; size_t trsize = sizeof(*trd); ... // 判斷 binder_work#type switch (w->type) { // Server 透過 SM 註冊 Server 服務時會調用 case BINDER_WORK_NODE: { ... int has_weak_ref; int has_strong_ref; void __user *orig_ptr = ptr; // Server 的計數 // 強計數 strong = node->internal_strong_refs || node->local_strong_refs; // 弱計數 weak = !hlist_empty(&node->refs) || node->local_weak_refs || node->tmp_refs || strong; // 是否有強引用 has_strong_ref = node->has_strong_ref; // 是否有弱引用 has_weak_ref = node->has_weak_ref; // 有弱引用,但沒有 ref if (weak && !has_weak_ref) { node->has_weak_ref = 1; // 設為 1 node->pending_weak_ref = 1; node->local_weak_refs++; } // 有強引用,但沒有 ref if (strong && !has_strong_ref) { node->has_strong_ref = 1; // 設為 1 node->pending_strong_ref = 1; node->local_strong_refs++; } if (!strong && has_strong_ref) node->has_strong_ref = 0; // 設為 0 (清除) if (!weak && has_weak_ref) node->has_weak_ref = 0; // 設為 0 (清除) if (!weak && !strong) { ... 釋放 server node } else { ... } if (weak && !has_weak_ref) // @ 查看 binder_put_node_cmd ret = binder_put_node_cmd( proc, thread, &ptr, node_ptr, node_cookie, node_debug_id, BR_INCREFS, "BR_INCREFS"); ... 省略部分 if (ret) return ret; } break; } } } ``` :::info 這段邏輯在 Binder Kernel 中處理,接下來準備進入 BinderServer 的處理 ::: ### Binder service - Native BBinder 增、減計數 * 從 Binder 驅動命令 `BINDER_WORK_NODE` 可以看到: 1. **一個 BinderServer 代表的是一個 BinderNode**,並且 **一個 BinderNode 也對應多個強、弱 ref 引用** > ![](https://i.imgur.com/my6dH3p.png) 2. `binder_put_node_cmd`:將數據寫數 Server 提供的一個使用者空間緩衝區 ```c= // binder.c static int binder_put_node_cmd(struct binder_proc *proc, struct binder_thread *thread, void __user **ptrp, binder_uintptr_t node_ptr, binder_uintptr_t node_cookie, int node_debug_id, uint32_t cmd, const char *cmd_name) { void __user *ptr = *ptrp; // 寫入 Server 使用者空間 if (put_user(cmd, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); if (put_user(node_ptr, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); if (put_user(node_cookie, (binder_uintptr_t __user *)ptr)) return -EFAULT; ptr += sizeof(binder_uintptr_t); ... *ptrp = ptr; return 0; } ``` 3. 對應處理加、減計數的則是 IPCThreadState#[**executeCommand**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/IPCThreadState.cpp) 函數: :::info * BBinder 的最終父類就是 RefBase * 增加參考計數是緊急的事情必須先做,相反減少計數並非僅及,可以等到 `BINDER_WRITE_READ` 再去執行 ::: ```cpp= // IPCThreadState.cpp status_t IPCThreadState::executeCommand(int32_t cmd) { BBinder* obj; RefBase::weakref_type* refs; status_t result = NO_ERROR; switch ((uint32_t)cmd) { ... case BR_ACQUIRE: refs = (RefBase::weakref_type*)mIn.readPointer(); obj = (BBinder*)mIn.readPointer(); ... log // 增加強記數 obj->incStrong(mProcess.get()); ... log mOut.writeInt32(BC_ACQUIRE_DONE); mOut.writePointer((uintptr_t)refs); mOut.writePointer((uintptr_t)obj); break; case BR_RELEASE: refs = (RefBase::weakref_type*)mIn.readPointer(); obj = (BBinder*)mIn.readPointer(); ... log // 不急著處理 mPendingStrongDerefs.push(obj); break; case BR_INCREFS: refs = (RefBase::weakref_type*)mIn.readPointer(); obj = (BBinder*)mIn.readPointer(); // 增加弱記數 refs->incWeak(mProcess.get()); mOut.writeInt32(BC_INCREFS_DONE); mOut.writePointer((uintptr_t)refs); mOut.writePointer((uintptr_t)obj); break; case BR_DECREFS: refs = (RefBase::weakref_type*)mIn.readPointer(); obj = (BBinder*)mIn.readPointer(); // consume // 不急著處理 mPendingWeakDerefs.push(refs); break; ... } } ``` ## Binder node 計數 在 BinderKernel 中,**BinderServer 是以一個 `binder_node` 結構表示** ### Binder node 增加計數 ```c= // binder.c static int binder_inc_node(struct binder_node *node, int strong, int internal, struct list_head *target_list) { int ret; // 鎖住目標 Service node binder_node_inner_lock(node); // @ 分析 binder_inc_node_nilocked ret = binder_inc_node_nilocked(node, strong, internal, target_list); binder_node_inner_unlock(node); return ret; } ``` * **`binder_inc_node_nilocked` 函數**:首先要知道 1. **BinderDriver** 在請求一個 BinderServer 處理事務時就會 **增加 `binder_node`#`local_weak_refs`、`binder_node`#`local_strong_refs` 計數 (內部)** 2. **BinderClient** 在請求一個 BinderServer 處理事務時就會 **增加 `binder_node`#`internal_strong_refs` 計數 (外部)** :::success [**Binder Kernel 結構介紹**](https://hackmd.io/TtT34BAwRP2sZ3vALt4Drg) ::: 接下來請看程式註解 ```c= // binder.c static int binder_inc_node_nilocked(struct binder_node *node, // Binder 實體物件 int strong, // 是否曾加強計數 int internal, // 是否是內部 (是否需要 BinderServer 操作計數 struct list_head *target_list) // 目標處理程序的 TODO List { struct binder_proc *proc = node->proc; // 取得 Server 運行的 proc ... if (strong) { // 增加強記數 if (internal) { // 內部 (Client 請求 // 對於 ServiceManager 來說不用增加,因為 ServiceManager 不會被回收 if (target_list == NULL && node->internal_strong_refs == 0 && !(node->proc && node == node->proc->context->binder_context_mgr_node && node->has_strong_ref)) { pr_err("invalid inc strong node for %d\n", node->debug_id); return -EINVAL; } // 增加內部引用計數 node->internal_strong_refs++; } else // 外部 (BinderDriver 引用 // 增加外部引用計數 node->local_strong_refs++; // 增加強引用計數,避免 Server 被銷毀 if (!node->has_strong_ref && target_list) { struct binder_thread *thread = container_of(target_list, struct binder_thread, todo); binder_dequeue_work_ilocked(&node->work); BUG_ON(&thread->todo != target_list); // 要求 BinderServer 添加強引用計數 // 對 thread#todo 列表新增 binder_enqueue_deferred_thread_work_ilocked(thread, &node->work); } } else { if (!internal) node->local_weak_refs++; if (!node->has_weak_ref && list_empty(&node->work.entry)) { // 目標 Proc 無添加列表則失敗 if (target_list == NULL) { pr_err("invalid inc weak node for %d\n", node->debug_id); return -EINVAL; } /* * See comment above */ // 要求 BinderServer 添加一個弱引用計數 binder_enqueue_work_ilocked(&node->work, target_list); } } return 0; } ``` ### Binder node 減少計數 ```c= // binder.c static void binder_dec_node(struct binder_node *node, int strong, int internal) { bool free_node; // 鎖住目標 Service node binder_node_inner_lock(node); // @ 分析 binder_dec_node_nilocked free_node = binder_dec_node_nilocked(node, strong, internal); binder_node_inner_unlock(node); // 是否需要釋放 Node 資源 if (free_node) binder_free_node(node); } ``` * **`binder_dec_node_nilocked` 函數**:首先要知道 1. **BinderDriver** 在請求一個 BinderServer 釋放時就會 **減少 `binder_node`#`local_weak_refs`、`binder_node`#`local_strong_refs` 計數 (內部)** 2. **BinderClient** 在請求一個 BinderServer 釋放時就會 **減少 `binder_node`#`internal_strong_refs` 計數 (外部)** :::success [**Binder Kernel 結構介紹**](https://hackmd.io/TtT34BAwRP2sZ3vALt4Drg) ::: ```c= // binder.c static bool binder_dec_node_nilocked(struct binder_node *node, // BinderServer int strong, // 減少強還是弱指針 int internal) // 是否是內部 (是否需要 BinderServer 操作計數 { struct binder_proc *proc = node->proc; ... if (strong) { // 強計數操作 if (internal) node->internal_strong_refs--; // 目標 Service 減少計數 else node->local_strong_refs--; // BinderDriver 內部減少強計數 // 仍有值,直接返回,結束操作 if (node->local_strong_refs || node->internal_strong_refs) return false; } else { if (!internal) node->local_weak_refs--; // BinderDriver 內部減少弱計數 // 仍有值,直接返回,結束操作 if (node->local_weak_refs || node->tmp_refs || !hlist_empty(&node->refs)) return false; } // 接下來代表 強計數 or 弱計數其中一個為 0 // proc 存在 // 並且判斷是否有強 or 弱引用 if (proc && (node->has_strong_ref || node->has_weak_ref)) { // 判斷是否增加一個 `BINDER_WORK_NODE` 到目標進程 if (list_empty(&node->work.entry)) { // 添加 todo 任務給 BinderService binder_enqueue_work_ilocked(&node->work, &proc->todo); // 喚醒目標 proc 進行處理任務 ! binder_wakeup_proc_ilocked(proc); } } else { // 準備 release 物件 if (hlist_empty(&node->refs) && !node->local_strong_refs && !node->local_weak_refs && !node->tmp_refs) { // 判斷 node 的 proc 對象 if (proc) { // 還在 BinderService 的紅黑樹中 binder_dequeue_work_ilocked(&node->work); // 從紅黑樹中移除 rb_erase(&node->rb_node, &proc->nodes); ... deug } else { // 存在死亡 Binder 清單內 ... deug spin_lock(&binder_dead_nodes_lock); /* * tmp_refs could have changed so * check it again */ if (node->tmp_refs) { spin_unlock(&binder_dead_nodes_lock); return false; } // 刪除 node hlist_del(&node->dead_node); spin_unlock(&binder_dead_nodes_lock); ... deug } return true; } } return false; } ``` ### Binder node & BBinder 關係 * **設計為 1 個 `binder_node` 對應多個 `binder_ref`;這樣好的好處是 BinderDriver 不會多次與 BinderServer 通訊** > 也就是不會 BinderDriver 不會多次發出 `BR_INCREFS`、`BR_ACQUIRE`... 等等命令要求 BinderServer 修改計數 > ![](https://i.imgur.com/ZFxQ7EK.png) ## Binder ref 計數 * Binder ref (參考物件) 就是 **binder_ref**: 1. `binder_ref` 在 Kernel space 被創建 2. 被 User space 空間中的` Binder 代理物件` (BpBinder) 所參考 * BinderClient & BinderDriver 通訊順序圖如下 (注意 Space) > ![](https://i.imgur.com/0GKTLw1.png) ### Binder ref - 計數入口 :::info 目前以 Client 的角度來對 BinderDriver 傳送命令 ::: * 主要是透過 `BC_INCREFS`、`BC_ACQUIRE`、`BC_RELEASE`、`BC_DECREFS` 這四個指令來控制 Binder 參考物件的生命週期 1. 使用者 Client 使用一個 **控制碼** 傳送到 BinderDriver 空間,找到對應的 binder_ref :::info * **控制碼** (句柄) 這個控制碼是 BinderDriver 為了 BinderClient 創建的,可以對應到一個 `binder_ref`;**其中 ==最特別的是控制碼 0,它是 ServierManager 的控制碼==** ::: ```c= // binder.c static int binder_thread_read( struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, // IPCThreadState 的 mIn 數據空間 size_t size, binder_size_t *consumed, int non_block) { // 使用者空間的指針 void __user *buffer = (void __user *)(uintptr_t)binder_buffer; // 取得開頭結尾指針 void __user *ptr = buffer + *consumed; // 數據的起點 void __user *end = buffer + size; // 數據的終點 int ret = 0; int wait_for_proc_work; struct binder_context *context = proc->context; ... while (1) { uint32_t cmd; // 創建 binder_transaction_data_secctx struct binder_transaction_data_secctx tr; // 取 tr#transaction_data struct binder_transaction_data *trd = &tr.transaction_data; // 當前需要執行的 binder_work struct binder_work *w = NULL; // 任務 header struct list_head *list = NULL; // 要對外傳輸的訊息 struct binder_transaction *t = NULL; struct binder_thread *t_from; size_t trsize = sizeof(*trd); ... // 判斷 binder_work#type switch (cmd) { case BC_INCREFS: case BC_ACQUIRE: case BC_RELEASE: case BC_DECREFS: { uint32_t target; const char *debug_string; bool strong = cmd == BC_ACQUIRE || cmd == BC_RELEASE; bool increment = cmd == BC_INCREFS || cmd == BC_ACQUIRE; struct binder_ref_data rdata; // 將控制碼儲存到 target 變數中 if (get_user(target, (uint32_t __user *)ptr)) return -EFAULT; ptr += sizeof(uint32_t); ret = -1; // 判斷控制碼是否是 0 (是否是 ServiceManager) if (increment && !target) { struct binder_node *ctx_mgr_node; // 鎖住當前 proc 中的 context mutex_lock(&context->context_mgr_node_lock); ctx_mgr_node = context->binder_context_mgr_node; if (ctx_mgr_node) { // 不可以有控制碼為 0 的 proc (0 只能設定一次) if (ctx_mgr_node->proc == proc) { binder_user_error("%d:%d context manager tried to acquire desc 0\n", proc->pid, thread->pid); mutex_unlock(&context->context_mgr_node_lock); return -EINVAL; } // 增加引用 // @ 分析 binder_inc_ref_for_node ret = binder_inc_ref_for_node( proc, ctx_mgr_node, strong, NULL, &rdata); } mutex_unlock(&context->context_mgr_node_lock); } if (ret) ret = binder_update_ref_for_handle( proc, target, increment, strong, &rdata); // 無法取得控制碼對應的 binder node if (!ret && rdata.desc != target) { binder_user_error("%d:%d tried to acquire reference to desc %d, got %d instead\n", proc->pid, thread->pid, target, rdata.desc); } switch (cmd) { case BC_INCREFS: debug_string = "IncRefs"; break; case BC_ACQUIRE: debug_string = "Acquire"; break; case BC_RELEASE: debug_string = "Release"; break; case BC_DECREFS: default: debug_string = "DecRefs"; break; } if (ret) { binder_user_error("%d:%d %s %d refcount change on invalid ref %d ret %d\n", proc->pid, thread->pid, debug_string, strong, target, ret); break; } ...debug 訊息 break; } ... } } } ``` * **`binder_inc_ref_for_node` 函數**: 1. 透過 `binder_get_ref_for_node_olocked` 取得 (創建) 相對應的 `binder_ref` > 在這裡就是取得 ServiceManger 的 `binder_node` 2. 透過 `binder_inc_ref_olocked` 增加計數 ```c= // binder.c static int binder_inc_ref_for_node(struct binder_proc *proc, struct binder_node *node, bool strong, struct list_head *target_list, struct binder_ref_data *rdata) { struct binder_ref *ref; struct binder_ref *new_ref = NULL; int ret = 0; // 鎖住 proc 對象 binder_proc_lock(proc); // 取得 node 對應的引用 // @ 查看 binder_get_ref_for_node_olocked ref = binder_get_ref_for_node_olocked(proc, node, NULL); if (!ref) { binder_proc_unlock(proc); // 創建新 node (`binder_ref`) 空間 new_ref = kzalloc(sizeof(*ref), GFP_KERNEL); if (!new_ref) return -ENOMEM; binder_proc_lock(proc); // 再次取得 binder_ref ref = binder_get_ref_for_node_olocked(proc, node, new_ref); } // @ 查看 binder_inc_ref_olocked // return 0 代表成功 ret = binder_inc_ref_olocked(ref, strong, target_list); *rdata = ref->data; if (ret && ref == new_ref) { // 清除失敗的引用 binder_cleanup_ref_olocked(new_ref); ref = NULL; } binder_proc_unlock(proc); if (new_ref && ref != new_ref) // 其他 thread 已創建,那就是放已分配的 new_ref kfree(new_ref); return ret; } ``` * **`binder_get_ref_for_node_olocked` 函數**:**取得 BinderServer 對應的 binder_ref 引用** ```c= // binder.c static struct binder_ref *binder_get_ref_for_node_olocked( struct binder_proc *proc, struct binder_node *node, struct binder_ref *new_ref) { struct binder_context *context = proc->context; struct rb_node **p = &proc->refs_by_node.rb_node; struct rb_node *parent = NULL; struct binder_ref *ref; struct rb_node *n; // 從 proc 中取得 `refs_by_node` (BinderServer 的參考) while (*p) { parent = *p; ref = rb_entry(parent, struct binder_ref, rb_node_node); if (node < ref->node) p = &(*p)->rb_left; else if (node > ref->node) p = &(*p)->rb_right; else return ref; } if (!new_ref) return NULL; // 以下創建新的 `binder_ref` binder_stats_created(BINDER_STAT_REF); new_ref->data.debug_id = atomic_inc_return(&binder_last_id); new_ref->proc = proc; new_ref->node = node; rb_link_node(&new_ref->rb_node_node, parent, p); rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node); new_ref->data.desc = (node == context->binder_context_mgr_node) ? 0 : 1; for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) { ref = rb_entry(n, struct binder_ref, rb_node_desc); if (ref->data.desc > new_ref->data.desc) break; new_ref->data.desc = ref->data.desc + 1; } p = &proc->refs_by_desc.rb_node; while (*p) { parent = *p; ref = rb_entry(parent, struct binder_ref, rb_node_desc); if (new_ref->data.desc < ref->data.desc) p = &(*p)->rb_left; else if (new_ref->data.desc > ref->data.desc) p = &(*p)->rb_right; else BUG(); } rb_link_node(&new_ref->rb_node_desc, parent, p); rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc); binder_node_lock(node); hlist_add_head(&new_ref->node_entry, &node->refs); ... debug binder_node_unlock(node); return new_ref; } ``` ### Binder ref - 增加計數 * `binder_inc_ref_olocked` 函數:請看註解,主要是增加 `binder_ref` or BinderServer 強、弱指標計數 ```c= // binder.c static int binder_inc_ref_olocked(struct binder_ref *ref, // Binder 參考物件 int strong, // 增加強 還是 弱 計數 struct list_head *target_list) // 目標 BinderServer 的 TODO List { int ret; if (strong) { // 增加強計數 if (ref->data.strong == 0) { // 增加強計數,並要求 BinderServer 也增加計數 (internal_strong_refs 設為 1) ret = binder_inc_node(ref->node, 1, 1, target_list); if (ret) return ret; } ref->data.strong++; // 增加引用強計數 } else { // 增加弱計數 if (ref->data.weak == 0) { // 增加外部弱計數 ret = binder_inc_node(ref->node, 0, 1, target_list); if (ret) return ret; } ref->data.weak++; // 增加引用弱計數 } return 0; } ``` ### Binder ref & node 關係 多個 binder_ref 對一個 binder_noed > ![](https://i.imgur.com/O3C7WvD.png) ## Binder proxy - 計數 * **Binder proxy 就是 Client 端的 ++BpBinder 實現++**: 1. Proxy 在使用者空間建立,並執行在使用者空間 2. **跟 Kernel 不同行程,所以不能直接使用指標操控 Kernel 的 Ref 物件** 3. Proxy 必須透過核心取得 ref 的控制碼,**透過控制碼訪問核心的 Ref 物件** 4. **Ref 物件再找到對應的 Binder node** 概念流程圖如下 > ![](https://i.imgur.com/kNEcEdC.png) ### Binder proxy - [BpBinder](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/include/binder/BpBinder.h) 儲存控制碼 * BpBinder (也就是 Client) 會透過 Kernel 提供的控制碼與 binder_ref 連結; * 並且 Client 物件會透過 [**ProcessState**](https://cs.android.com/android/platform/superproject/+/master:system/libhwbinder/include/hwbinder/ProcessState.h) 內的一個 `handle_entry` 型態的 Binder 代理清單來取得 BpBinder 物件; * `handle_entry` 使用 Key/Value 來儲存控制碼、BpBinder > Key:Kernel 提供的控制碼 > Value:使用者空間的 BpBinder 物件 ```cpp= // ProcessState.h class ProcessState : public virtual RefBase { public: ... private: ... // 串列 struct handle_entry { IBinder* binder; RefBase::weakref_type* refs; }; // 透過 lookupHandleLocked 來找到對應的 BpBinder handle_entry* lookupHandleLocked(int32_t handle); } ``` :::info * 快取的好處是可以減少 BpBinder 的創建 ::: ### Binder proxy - 增加 ref 弱計數 :::success 增加計數的時機在 **BpBinder 建構函數** ::: * 接著看 [**BpBinder**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/BpBinder.cpp) 的實現:從建構函數中可以看到,**++BpBinder (也就是 Proxy) 的生命週期改為使用弱計數控制++** ```cpp= // BpBinder.cpp BpBinder::BpBinder(Handle&& handle) : mStability(0), mHandle(handle), mAlive(true), mObitsSent(false), mObituaries(nullptr), mTrackedUid(-1) { // 由弱計數控制生命週期 extendObjectLifetime(OBJECT_LIFETIME_WEAK); } BpBinder::BpBinder(BinderHandle&& handle, int32_t trackedUid) : // 呼叫自身的建構函數 BpBinder(Handle(handle)) { ... mTrackedUid = trackedUid; ALOGV("Creating BpBinder %p handle %d\n", this, this->binderHandle()); // 準備添加弱計數 // @ 追蹤 incWeakHandle 函數 IPCThreadState::self()->incWeakHandle(this->binderHandle(), this); } ``` * [**IPCThreadState**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/IPCThreadState.cpp)#`incWeakHandle` 函數:不會立刻新增弱引用次數,會 **等待 ++下次 `BINDER_WRITE_READ` 命令時++,再請求 Binder Kernel 對對應的 binder_ref 添加弱參考計數** ```cpp= // IPCThreadState.cpp void IPCThreadState::incWeakHandle(int32_t handle, BpBinder *proxy) { ... // 增加弱引用計數 mOut.writeInt32(BC_INCREFS); // 指定 handle 找到對應的 ref mOut.writeInt32(handle); .... } ``` ### Binder proxy - 減少 ref 弱計數 :::success 增加計數的時機在 **BpBinder 解構函數** ::: * 接著看 [**BpBinder**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/BpBinder.cpp) 的解構: ```cpp= // BpBinder.cpp BpBinder::~BpBinder() { ... IPCThreadState* ipc = IPCThreadState::self(); ... if (ipc) { ... // 減少弱計數 ipc->decWeakHandle(binderHandle()); } } ``` * [**IPCThreadState**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/IPCThreadState.cpp)#`decWeakHandle` 函數:不會立刻減少弱引用次數,會 **等待 ++下次 `BINDER_WRITE_READ` 命令時++,再請求 Binder Kernel 對對應的 binder_ref 減少弱參考計數** ```cpp= // IPCThreadState.cpp void IPCThreadState::decWeakHandle(int32_t handle) { ... // 減少弱引用計數 mOut.writeInt32(BC_DECREFS); // 指定 handle 找到對應的 ref mOut.writeInt32(handle); ... } ``` ### Binder proxy - 增加強記數 * 由於 RefBase 的設計,^1.^ Binder proxy 被其他強引用參考時,就會 ^2.^ 增加 proxy 強引用計數,並 ^3.^ 呼叫 `onFirstRef` 函數;現在就來看看 BpBinder#`onFirstRef` 函數 ```cpp= // BpBinder.cpp void BpBinder::onFirstRef() { ... IPCThreadState* ipc = IPCThreadState::self(); // 準備增加 ref 的強引用計數 if (ipc) ipc->incStrongHandle(binderHandle(), this); } ``` * [**IPCThreadState**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/IPCThreadState.cpp)#`incStrongHandle` 函數:不會立刻新增強引用次數,會 **等待 ++下次 `BINDER_WRITE_READ` 命令時++,再請求 Binder Kernel 對對應的 binder_ref 新增強參考計數** ```cpp= // IPCThreadState.cpp void IPCThreadState::incStrongHandle(int32_t handle, BpBinder *proxy) { ... // 增加強引用計數 mOut.writeInt32(BC_ACQUIRE); // 指定 handle 找到對應的 ref mOut.writeInt32(handle); ... } ``` ### Binder proxy - 減少強記數 * 由於 RefBase 的設計,^1.^ Binder proxy 不再被強引用參考時,就會 ^2.^ 減少 proxy 強引用計數,並 ^3.^ 呼叫 `onLastStrongRef` 函數;現在就來看看 BpBinder#`onLastStrongRef` 函數 ```cpp= // BpBinder.cpp void BpBinder::onLastStrongRef(const void* /*id*/) { ... IPCThreadState* ipc = IPCThreadState::self(); // 準備減少對應的 ref 強記數 if (ipc) ipc->decStrongHandle(binderHandle()); ... } ``` * [**IPCThreadState**](https://cs.android.com/android/platform/superproject/+/master:frameworks/native/libs/binder/IPCThreadState.cpp)#`decStrongHandle` 函數:不會立刻減少強引用次數,會 **等待 ++下次 `BINDER_WRITE_READ` 命令時++,再請求 Binder Kernel 對對應的 binder_ref 減少強參考計數** ```cpp= // IPCThreadState.cpp void IPCThreadState::decStrongHandle(int32_t handle) { ... // 減少強引用計數 mOut.writeInt32(BC_RELEASE); // 指定 handle 找到對應的 ref mOut.writeInt32(handle); ... } ``` ### Binder proxy & ref 關係 一個 Binder proxy 只有在創建時才會增加 Binder ref 的弱計數,相對減少也是在銷毀時才會減少計數 > ![](https://i.imgur.com/5LARWxo.png) ## Appendix & FAQ :::info ::: ###### tags: `Binder`