--- title: 'Binder 驅動 - 結構' disqus: kyleAlien --- Binder 驅動 - 結構 === ## OverView of Content [TOC] ## Binder 重點結構 Binder 驅動 ([**binder.c**](https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/drivers/android/binder.c)) 中有用到許多不同的 struct,大致上可以分為兩類:^1.^ **Kernal Space (Binder 驅動) 與 User Space ++共用結構++**、^2.^ **只在 Kernel Binder 驅動中使用的數據結構** ## Native & Binder 驅動 - 共用結構 * Binder 再透過 `open` 開啟 `/dev/binder` 驅動後,需要透過 IO 控制函數 `ioctl` 來進一步與 Binder 驅動進行互動; ioctl 之下有許多命令,其中最重要的就是 **BINDER_WRITE_READ 指令** ```c= // binder.h // @ 查看 binder_write_read 結構 #define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read) #define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, __s64) #define BINDER_SET_MAX_THREADS _IOW('b', 5, __u32) #define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, __s32) #define BINDER_SET_CONTEXT_MGR _IOW('b', 7, __s32) ... 省略部分命令 ``` * **Kernal Space (Binder 驅動) 與 User Space ++共用結構++**:這些 struct 大多會定義在 [**binder.h**](https://cs.android.com/android/kernel/superproject/+/common-android-mainline:prebuilts/build-tools/sysroots/arm-linux-musleabihf/include/linux/android/binder.h) 中 (標黃色是較為重要的) | struct 名稱 | 說明 | | -------- | -------- | | **==binder_write_read==** | 儲存一次的 read、write 操作 | | **==binder_transaction_data==** | 儲存將要傳輸的資訊 | | binder_version | Binder 版本 | | flat_binder_object | 描述 BinderIPC 中傳遞的對象 (Service 對象) | | binder_ptr_cookie | 包含一個指針、Cookie | | binder_handle_cookie | 包含一個句柄、Cookie | ### binder_write_read - 傳遞訊息 * **binder_write_read**:**User Space 傳遞的資料 & Binder 驅動回覆的訊息**;這個結構會由 User 傳入 Binder 驅動 :::success 1. **`write_buffer`、`read_buffer` 成員**:最終會指向 `binder_transaction_data` 結構 2. **`write_buffer`、`read_buffer` 成員**:是一個 Array,內部儲存的順序是 Code 對應一個 通訊協定、元件資料 > ![](https://i.imgur.com/y4gFNHD.png) 3. **`write_buffer` 成員**:它內部所儲存的 Command Protocol,定義在 `binder_driver_command_protocol` 中,**BC_ 開頭的命令** (註釋說明) 4. **`read_buffer` 成員**:它內部所儲存的 Return Protocol,定義在 `binder_driver_return_protocol` 中,**BR_ 開頭的命令** (註釋說明) ::: ```c= // binder.h struct binder_write_read { // wirte 開頭,是 User 寫入數據 binder_size_t write_size; // 指定 `write_buffer` 大小 binder_size_t write_consumed; // 描述 Binder 驅動 已消耗 的資料 binder_uintptr_t write_buffer; // 使用者空間的 Buffer (將要傳輸的資料) // Binder 驅動回覆數據 binder_size_t read_size; // 指定 `read_buffer` 大小 binder_size_t read_consumed; // 描述使用者空間 已消耗 的資料 binder_uintptr_t read_buffer; // 指向使用者空間的 Buffer,內容是 Binder 回覆給使用者的資料 }; ``` [**binder.h**](https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/include/uapi/linux/android/binder.h)#`binder_driver_command_protocol` ```c= // linux/android/binder.h enum binder_driver_command_protocol { /** * 最後一個參數是使用 `struct binder_transaction_data` 結構 * * BC_TRANSACTION: * User 請 BinderDriver 傳遞訊息給 BinderService * * BC_REPLY: * BinderService 處理完後,User 請 BinderDriver 傳給 User */ BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data), BC_REPLY = _IOW('c', 1, struct binder_transaction_data), // 目前不支援 BC_ACQUIRE_RESULT = _IOW('c', 2, __s32), /* * void *: ptr to transaction data received on a read * * 它指向在 BinderDriver 內分配的 "核心 Buffer" * BinderDriver 就是透過核心 Buffer 將 User 請求傳遞給 BinderService */ BC_FREE_BUFFER = _IOW('c', 3, binder_uintptr_t), /** * 描述 BinderService 的句柄 (控制碼) */ BC_INCREFS = _IOW('c', 4, __u32), // +弱計數 BC_ACQUIRE = _IOW('c', 5, __u32), // +強計數 BC_RELEASE = _IOW('c', 6, __u32), // -強計數 BC_DECREFS = _IOW('c', 7, __u32), // -弱計數 /** * void *: ptr to binder * void *: cookie for binder * * BinderDriver 第一次添加強、弱計數 * BinderDriver 會使用 `BR_ACQUIRE`、`BR_INCREFS` 要求 BinderService 增加強弱引用 * BinderService 添加完後會傳給 BinderDriver `BC_INCREFS_DONE`、`BC_ACQUIRE_DONE` */ BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie), // BinderService 添加 `弱引用` 結束 BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie), // BinderService 添加 `強引用` 結束 // 當前不支援 BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc), /** * Binder Thread pool 相關操作 */ BC_REGISTER_LOOPER = _IO('c', 11), // BinderDriver "主動" 請求一個 Thread 註冊進 BinderDriver BC_ENTER_LOOPER = _IO('c', 12), // Thread 進入 BinderDriver BC_EXIT_LOOPER = _IO('c', 13), // Thread 離開 BinderDriver /** * 希望接收、取消 BinderService 的死亡通知 */ BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, // 接收 struct binder_handle_cookie), BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, // 取消 struct binder_handle_cookie), /** * 指向一個死亡接收通知結構 binder_uintptr_t * * User 接收到死亡通知後,就會發送 `BC_DEAD_BINDER_DONE` 給 BinderDriver */ BC_DEAD_BINDER_DONE = _IOW('c', 16, binder_uintptr_t), /* * void *: cookie */ BC_TRANSACTION_SG = _IOW('c', 17, struct binder_transaction_data_sg), BC_REPLY_SG = _IOW('c', 18, struct binder_transaction_data_sg), /* * binder_transaction_data_sg: the sent command. */ }; ``` [**binder.h**](https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/include/uapi/linux/android/binder.h)#`binder_driver_return_protocol` ```c= // linux/android/binder.h enum binder_driver_return_protocol { /** * 從 BinderDriver 處理 User 請求時發出的錯誤代號 */ BR_ERROR = _IOR('r', 0, __s32), /** * BinderDriver 處理成功 */ BR_OK = _IO('r', 1), /* * binder_transaction_data_secctx: the received command. */ BR_TRANSACTION_SEC_CTX = _IOR('r', 2, struct binder_transaction_data_secctx), /** * BR_TRANSACTION: * BinderDriver 發送給 BinderService 訊息,要求 BinderService 處理完 User 請求後回覆給 BinderDriver * * BR_REPLY: * BinderDriver 接收到 BinderService 回覆後,透過 `BR_REPLY` 通知 User */ BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data), BR_REPLY = _IOR('r', 3, struct binder_transaction_data),] // 木前不支援 BR_ACQUIRE_RESULT = _IOR('r', 4, __s32), /** * 最後通訊目標 BinderService 已經不存在 */ BR_DEAD_REPLY = _IO('r', 5), /** * 通知 User,BinderDriver 已收到 User 命令 */ BR_TRANSACTION_COMPLETE = _IO('r', 6), /** * 要求 BinderService 增加或減少強、弱引用 */ BR_INCREFS = _IOR('r', 7, struct binder_ptr_cookie), BR_ACQUIRE = _IOR('r', 8, struct binder_ptr_cookie), BR_RELEASE = _IOR('r', 9, struct binder_ptr_cookie), BR_DECREFS = _IOR('r', 10, struct binder_ptr_cookie), // 尚未支援 BR_ATTEMPT_ACQUIRE = _IOR('r', 11, struct binder_pri_ptr_cookie), /** * 空指令,讓 User 執行一個空指令,為了以後可以取代 `BR_SPAWN_LOOPER` */ BR_NOOP = _IO('r', 12), /** * BinderDriver 設定要求使用者創建一個 Thread 並註冊到 BinderDriver 中 */ BR_SPAWN_LOOPER = _IO('r', 13), // 尚未支援 BR_FINISHED = _IO('r', 14), /** * binder_uintptr_t 代表一個 void*,用來接收 BinderService 的死亡通知 * * 當 BinderDriver 收到 BinderService 死亡通知,就會透過 `BR_DEAD_BINDER` 通知使用者 */ BR_DEAD_BINDER = _IOR('r', 15, binder_uintptr_t), BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, binder_uintptr_t), /** * 接收到使用者 `BC_TRANSACTION` 處理失敗時,就會透過 `BR_FAILED_REPLY` 回覆使用者 */ BR_FAILED_REPLY = _IO('r', 17), /** * 接收到使用者 `BC_TRANSACTION` 處理被暫停時,就會透過 `BR_FROZEN_REPLY` 回覆使用者 */ BR_FROZEN_REPLY = _IO('r', 18), /** * User 發送過多 oneway 命令給 BinderService,異步 buffer 超出閥值 */ BR_ONEWAY_SPAM_SUSPECT = _IO('r', 19), }; ``` ### binder_ptr_cookie - Binder 服務 * **binder_ptr_cookie**:BinderDriver 用來描述一個 BinderService 服務 or BinderService 的死亡通知;在不同狀況下 `ptr`、`cookie` 成員是代表不同的意思 | binder_ptr_cookie 狀態/成員 | ptr | cookie | | - | - | - | | BinderService | **服務本身** | 服務本身 | | BinderService 的死亡通知 | **BinderDriver 參考物件 (Proxy) 的句柄** | 指向一個 User 接收死亡通知的地址 | ```c= // binder.h struct binder_ptr_cookie { binder_uintptr_t ptr; binder_uintptr_t cookie; }; ``` * **binder_handle_cookie**: ```c= // binder.h struct binder_handle_cookie { __u32 handle; binder_uintptr_t cookie; } __packed; ``` ### binder_transaction_data - 描述資料 * **binder_transaction_data**:**User 將資料傳給 BinderDriver 的數據**; **如果使用者向 BinderDriver 下達 `BC_TRANSACTION` 命令,就需要在進到 BinderDriver 之前把 Parcel 數據寫入到 `binder_transaction_data` 結構中** :::info 1. **`target` 成員**:可能用來描述 `本地` or `遠端` BinderServicen,所以會有兩種狀況 | 描述對象\ | union 成員 | 指向 | | - | - | - | | 本地 Service | ptr | 本地 Service 地址 | | 遠端 Service | handle | 遠端 Service 的句柄 | 2. **`cookie` 成員**:只有當 BinderDriver 在發出 `BR_TRANSACTION` 像一個 BinderService 發出請求時,cookie 才有意義,它指向 BinderService 的地址 3. **`sender_pid`、`sender_euid` 成員**:兩個是 Binder 驅動程式填寫,Service 可以用來做安全檢查 4. **`data` 成員**:在資料不大時,可以直接使用 `buf`;如果資料過大就必須使用 `ptr`,其規劃如下圖,透過 offsets_size 將指定每個 binder 在 buffer 中的位子 下圖是 2 個 Binder 物件 (`flat_binder_object`),透過 offset 就可以找到在 buffer 中的正確位置 > ![](https://i.imgur.com/k2xn12c.png) ::: ```c= // binder.h struct binder_transaction_data { // 不同狀態下代表了不同含意 union { __u32 handle; // 控制碼 binder_uintptr_t ptr; } target; // User 另外指定的參數 binder_uintptr_t cookie; // 傳遞給 BinderService 的命令,與 BinderDriver 無關 __u32 code; // flags 定義如 transaction_flags,代表這次通訊的特徵 __u32 flags; // 以下兩個是 Binder 驅動程式填寫 __kernel_pid_t sender_pid; // 發起請求的 進程 ID uid_t sender_euid; // 發起請求的 UID binder_size_t data_size; // 描述 data 大小 binder_size_t offsets_size; // 描述 data 偏移 union { // 資料較大時須使用 ptr struct { binder_uintptr_t buffer; // 指向緩存空間 binder_uintptr_t offsets; } ptr; __u8 buf[8]; // 資料較少時,可以直接使用 } data; }; enum transaction_flags { TF_ONE_WAY = 0x01, // 異步 TF_ROOT_OBJECT = 0x04, /* contents are the component's root object */ TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */ TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */ TF_CLEAR_BUF = 0x20, /* clear buffer on txn complete */ TF_UPDATE_TXN = 0x40, /* update the outdated pending async txn */ }; ``` ### binder_version - 版本 * **binder_version**:Binder 版本號 ```c= // binder.h struct binder_version { __s32 protocol_version; }; ``` ### flat_binder_object - 傳遞的 Service * **flat_binder_object**:**描述 Binder IPC 中傳遞的對象,並且可以使用 type 來區分當前的 `flat_binder_object` 的區別,可以包括如下** 1. **BinderService 實體物件** 2. **Binder 代理物件** 3. **FD 檔案描述符號** :::info 1. Binder 驅動傳遞對象並不是用 ~~序列化~~,而是 **透過 ==特定標示== 進行傳遞** 2. 其中的 `type` 有 5 種,使用 enum 標示 ```c= // binder.h enum { // Binder 實體物件 (Local) BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE), BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE), // Binder 代理句柄 (Remote) BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE), BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE), // 檔案描述符 FD BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE), BINDER_TYPE_FDA = B_PACK_CHARS('f', 'd', 'a', B_TYPE_LARGE), BINDER_TYPE_PTR = B_PACK_CHARS('p', 't', '*', B_TYPE_LARGE), }; ``` ::: ```c= // binder.h struct flat_binder_object { struct binder_object_header hdr; __u32 flags; // 只有是 LocalBinder 時才有意義 // 對象 共同體 union { binder_uintptr_t binder; // Local Service 同 process 對象 __u32 handle; // 遠端跨 process 對象 (控制碼) }; binder_uintptr_t cookie; // Local Service 時指向 Binder 的實體位置 }; struct binder_object_header { __u32 type; }; ``` ## Binder 驅動 - 獨用結構 * **只在 Kernel Binder 驅動中使用的數據結構**:這些結構主要是定義在 [**binder_internal.h**](https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/drivers/android/binder_internal.h)、[**binder_alloc.h**](https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/drivers/android/binder_alloc.h) 中 | struct 名稱 | 說明 | 位置 | | -------- | -------- | - | | **==binder_proc==** | Binder 所在的進程 | binder_internal | | **==binder_thread==** | Binder 所在的 thread | binder_internal | | **==binder_node==** | 描述了一個實際的 Binder 節點,**對應一個 Service** | binder_internal | | **==binder_ref==** | 對於 Binder 虛擬設備的引用 | binder_internal | | **==binder_alloc==** | 這個結構存在 binder_proc 中 | binder_alloc | **==binder_buffer==** | Binder 通訊過程中儲存資料的 buffer | binder_alloc | | binder_work | 通訊過程的一項任務 | binder_internal | | binder_transaction | 一次要傳輸的任務 | binder_internal | ### binder_alloc - 儲存 Page、Bufffer * **binder_alloc**:會在 `binder_init`、`binder_mmap` 的時候初始化,用來儲存 Page、Buffer 資料 :::info 1. 每個 binder_proc 都會有 binder_alloc,它管理與該進程通訊的 `binder_buffer` 結構 2. `binder_alloc` 會儲存 **應用端 (vma)** & **核心地址 (buffers)** :::warning * 兩個都是虛擬地址,最終都會指向 page 來儲存資料 ::: 3. **binder_alloc 會管理一大塊內存區塊 (page),並將其透過 `binder_buffer` 結構,來接其切小方便管理** ::: ```c= // binder_alloc.h struct binder_lru_page { struct list_head lru; // 最終 page 結構 struct page *page_ptr; struct binder_alloc *alloc; }; struct binder_alloc { struct mutex mutex; // 互斥鎖 struct vm_area_struct *vma; // 使用者空間的地址 struct mm_struct *vma_vm_mm; void __user *buffer; // 指向 `binder_buffer` 結構 header struct list_head buffers; struct rb_root free_buffers; // 可用 buf struct rb_root allocated_buffers; // 已經使用 buf size_t free_async_space; // 異步時的可用空間 // 使用 LRU 結構儲存 page struct binder_lru_page *pages; size_t buffer_size; // 緩存上限 uint32_t buffer_free; // 儲存閒置空間的大小 int pid; size_t pages_high; bool oneway_spam_detected; }; ``` ### binder_proc - Proc 資訊 * **binder_proc**:當一個 Process 使用 open 開啟 `/dev/binder` 後,**Binder 驅動就會為開啟者創建一個 `binder_proc` 結構** > binder_proc 會管理 Binder IPC 過程中的緩存,管理結構 `binder_alloc` :::info 1. **`threads` 成員**:集合 Binder Thread Pool 並用紅黑樹管理 > 如果應用 Binder Thread 通訊太少,Binder 驅動也會要求應用建立更多 BinderThread 到 Pool 中 2. **`deferred_work_node` 成員**:代表了延遲交易列表,其狀態有如下 ```c= // binder.c enum binder_deferred_state { // 喚醒 Binder Thead 休眠狀態,準備開始做事 BINDER_DEFERRED_FLUSH = 0x01, // 關閉 `/dev/binder` 分配的資源 BINDER_DEFERRED_RELEASE = 0x02, }; ``` ::: ```c= // binder_internal.h struct binder_proc { struct hlist_node proc_node; // 指向全域列表的某個 Node // 集合 Binder Thread Pool struct rb_root threads; // 集合 Binder 實體物件 struct rb_root nodes; // 集合 Binder 代理(參考) 物件 struct rb_root refs_by_desc; struct rb_root refs_by_node; struct list_head waiting_threads; int pid; struct task_struct *tsk; const struct cred *cred; // 延遲交易 struct hlist_node deferred_work_node; int deferred_work; // 延遲交易的狀態 int outstanding_txns; bool is_dead; bool is_frozen; bool sync_recv; bool async_recv; wait_queue_head_t freeze_wait; // 待該進程處理的 Task struct list_head todo; struct binder_stats stats; // 統計程式資料 // 接收 Binder Service 的死亡通知 struct list_head delivered_death; // 該 Proc 可支援的最大 Binder Thread 上限 int max_threads; // Binder "主動請求" 處理一個 Thread 時,requested_threads + 1 // Proc#Thread 處理請求後,requested_threads - 1,並將 requested_threads_started + 1 int requested_threads; int requested_threads_started; int tmp_ref; struct binder_priority default_priority; struct dentry *debugfs_entry; // 分配、儲存空間 struct binder_alloc alloc; struct binder_context *context; spinlock_t inner_lock; spinlock_t outer_lock; struct dentry *binderfs_entry; bool oneway_spam_detection_enabled; }; ``` ### binder_thread - Binder Thread * **binder_thread**:**用來描述 Binder Thread Pool 中的 Thread**,代表 `binder_proc#threads` 中的紅黑樹上的一個節點 :::info 1. **`looper` 成員**:用來描述一個 Thread 的狀態,只有該 Thread 可該便自身的狀態 ```c= // binder.c enum { // 通知該 Thread 可以處理 Binder 請求 // Binder 驅動主動設定 Thread 為 BinderThread,BC_REGISTER_LOOPER BINDER_LOOPER_STATE_REGISTERED = 0x01, // App 應用主動設定 Thread 為 BinderThread,BC_ENTER_LOOPER BINDER_LOOPER_STATE_ENTERED = 0x02, BINDER_LOOPER_STATE_EXITED = 0x04, // BC_EXITED_LOOPER // 異常狀態 BINDER_LOOPER_STATE_INVALID = 0x08, // BinderThread 處於空閒狀態 BINDER_LOOPER_STATE_WAITING = 0x10, BINDER_LOOPER_STATE_POLL = 0x20, }; ``` ::: ```c= // binder_internal.h struct binder_thread { // 指向所屬的 Proc struct binder_proc *proc; // 指向紅黑樹的一個 Node struct rb_node rb_node; struct list_head waiting_thread_node; // Thread 的 ID int pid; // Thread 的狀態 int looper; /* only modified by this thread */ bool looper_need_return; /* can be written by other thread */ // 要處理的任務集合,內部都是 binder_transaction struct binder_transaction *transaction_stack; // 該 Thread 要處理的任務 struct list_head todo; bool process_todo; struct binder_error return_error; struct binder_error reply_error; // 等待其他 Thread 喚醒 wait_queue_head_t wait; // 統計該 Thread 處理的請求數量 struct binder_stats stats; atomic_t tmp_ref; bool is_dead; struct task_struct *task; spinlock_t prio_lock; struct binder_priority prio_next; enum binder_prio_state prio_state; }; ``` ### binder_work - Binder 待處理任務 * **binder_work**:**該結構會串起 Binder 尚未處理的工作列表** :::info 這些尚未處理的工作可能屬於 Proc、Thread、Node... 等等,**Binder 驅動可以根據 type 設定值判斷 `binder_work` 在哪裡運作** ::: ```c= // binder_internal.h struct binder_work { struct list_head entry; // 代辦工作事項的入口 enum binder_work_type { BINDER_WORK_TRANSACTION = 1, BINDER_WORK_TRANSACTION_COMPLETE, BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT, BINDER_WORK_RETURN_ERROR, // 加、減強弱引用時 BINDER_WORK_NODE, BINDER_WORK_DEAD_BINDER, BINDER_WORK_DEAD_BINDER_AND_CLEAR, BINDER_WORK_CLEAR_DEATH_NOTIFICATION, } type; }; ``` ### binder_ref - BinderService 引用 * **binder_ref**:描述 Client (應用端) 對於 Binder 的引用 :::info 1. `binder_ref` 紀錄強、弱計數 2. **`desc` 成員:代表了對於 BinderClient 的操控句柄 (控制碼),透過句柄 Binder Kernel 就可以找到對應的 Binder 參考物件,再透過 Binder Client 參考物件的變數 node 找到實體的 BinderClient 物件** :::warning * BinderClient 參考物件的 `desc` 在虛擬地址 (同個 Process) 中必須是唯一 ! 因此,可能存在兩個相同的 `desc`,但其實是代表了兩個不同的物件!! ::: 3. 一個 BinderService 會使用 **兩個紅黑樹** 來儲存指向它的物件;而 `binder_ref` 結構中的 `rb_node_desc` (句柄)、`rb_node_node` (實際位址) 則各代表紅黑樹上的一個點 4. `proc` 成員代表個該 ref 引用的目標的進程 > 假設一個 `binder_ref` 指向 ServiceManager,那 binder_ref#proc 就指向了 ServiceManager 進程 ::: ```c= // binder_internal.h struct binder_ref_data { int debug_id; uint32_t desc; // Client 句柄 (控制碼) int strong; int weak; }; struct binder_ref { /* Lookups needed: */ /* node + proc => ref (transaction) */ /* desc + proc => ref (transaction, inc/dec ref) */ /* node => refs + procs (proc exit) */ struct binder_ref_data data; struct rb_node rb_node_desc; // Service 句柄 (控制碼) struct rb_node rb_node_node; // Service 實際位址 struct hlist_node node_entry; // 使用該 ref 的 proc 進程 struct binder_proc *proc; // 目標 binder node struct binder_node *node; // BinderClient 監聽 Binder 死亡 struct binder_ref_death *death; }; ``` ### binder_node - BinderService 節點 * **binder_node**:描述了一個實際的 Binder 節點,**對應一個 ==Service==**;透過強弱參考計數來維護 BinderService 的生命週期 :::info 1. 一個 Proc 中可以存在多個 BinderService,所以 Proc 使用 **紅黑樹結構** 來儲存 Binder 到紅黑樹節點 2. 一個 Binder 同步同訊,會將該請求存在 Thread 的 todo 列表,而異步則會儲存在 `async_todo` 中 > **優先序**:**同步請求** > **異步請求** 3. 計數強弱指針數量,當數量從 `0->1`、`1->0` 時,`work` 轉為 `BINDER_WORK_NODE`,並去調整 `has_strong_ref`、`has_weak_ref` 的設定 ::: ```c= // binder_internal.h struct binder_node { int debug_id; spinlock_t lock; struct binder_work work; union { struct rb_node rb_node; // 指向紅黑樹的某個 Node struct hlist_node dead_node; }; struct binder_proc *proc; // 該 Service 所在的 proc struct hlist_head refs; // 指向該 Service 的 Client 列表 ! // 強、弱參考計數 int internal_strong_refs; // 外部 (Client 增加 int local_weak_refs; // 內部 (BinderDriver 增加 int local_strong_refs; // 內部 (BinderDriver 增加 int tmp_refs; // 指向 Service 的只用者空間地址 ! binder_uintptr_t ptr; // 指向 Service 計數器 `weakref_impl` binder_uintptr_t cookie; // 指向 Service 地址 // 增加計數的 Flag struct { u8 has_strong_ref:1; u8 pending_strong_ref:1; u8 has_weak_ref:1; u8 pending_weak_ref:1; }; struct { u8 sched_policy:2; u8 inherit_rt:1; u8 accept_fds:1; // 是否接收檔案描述符 u8 txn_security_ctx:1; u8 min_priority; // Binder Service 處理請求的 Thread 的優先序 }; // 該 Binder Service 是否正在處理一個異步請求 bool has_async_transaction; // Binder Service 待處理的 "異步" 事項 struct list_head async_todo; }; ``` * 每個 Binder 實體物件都會有一個 `refs` 列表,還有三個 `internal_strong_refs`、`local_weak_refs`、`local_strong_refs` 成員用來表示各種計數 1. **`local_weak_refs`、`local_strong_refs`**:用來表示 **內部強、弱參考計數**,也是用來 **==描述 Server 引用的計數==** > 這邊的 `local` 其實就是站在 BinderDriver 自身的角度 2. **`internal_strong_refs`**:用來表示 **外部強參考計數**,也是用來 **==描述 Client 引用的計數==** :::success * 弱引用可以透過 `refs` 成員計算機大小 ::: ### binder_ref_death - 死亡通知引用 * **binder_ref_death**:BinderService 關閉時通知使用者 :::info 死亡通知可以由 Binder 驅動自動通知,也可以由 Client (應用) 註冊通知 ::: ```c= // binder_internal.h struct binder_ref_death { /* work 值可能如下 - BINDER_WORK_DEAD_BINDER, // Binder 死亡 - BINDER_WORK_DEAD_BINDER_AND_CLEAR, - BINDER_WORK_CLEAR_DEATH_NOTIFICATION, // Binder 尚未死亡 */ struct binder_work work; // 儲存使用 Binder Service 使用者的地址 binder_uintptr_t cookie; }; ``` ### binder_buffer - 核心緩衝區 * **binder_buffer**:核心緩衝區,Binder 通訊過程中儲存資料的 buffer,每個 Proc 都會有自己的 `binder_buffer` :::info 1. **`entry` 成員**: 指向核心緩衝區的列表 2. 核心使用兩個 **紅黑樹** 來描述核心緩衝區、空閒核心緩衝區;而成員 `rb_node` 則指向正在使用 or 空閒其中一個紅黑樹節點 3. **`async_transaction` 成員**: 異步交易 Flag,可用來限制異步交易的大小 (異步只有一半),確保同步交易可以先被處理 4. **`data` 成員**:該成員代表了 **真正的交易資料,並且這筆資料分為 2 種型態** * 一般資料 * **Binder 資料**:代表該筆交易還需要再訪問另外一個 BinderService (需要 Binder 指針,強弱引用... 等等),需要透過 `offsets_size` 來到 Binder 物件的位置 ::: ```c= // binder_alloc.h struct binder_buffer { struct list_head entry; /* free and allocated entries by address */ // 指向使用中、空閒的紅黑樹節點 struct rb_node rb_node; /* free entry by size or allocated entry */ /* by address */ unsigned free:1; // 是否是可用空閒 (1 代表可用,0 則是不可用) unsigned clear_on_free:1; unsigned allow_user_free:1; // 在 BinderService 處理完任務後,釋放 `binder_transaction` unsigned async_transaction:1; // 異步交易 unsigned oneway_spam_suspect:1; unsigned debug_id:27; // binder 交易(通訊) `資訊` struct binder_transaction *transaction; // 使用該緩衝區的 node struct binder_node *target_node; // 交易資料描述 size_t data_size; // 大小 size_t offsets_size; // Binder 物件的偏移量 size_t extra_buffers_size; // 儲存真正的交易資料 void __user *user_data; int pid; }; ``` ### binder_transaction - Proc 通訊過程 * **binder_transaction 結構**:用來描述 Proc 之間通訊的過程; :::info 1. **`work` 成員**:該交易被目標 Proc、Thread 接受後,`work` 狀態改為 `BINDER_WORK_TRANSACTION` 2. 目標 BinderServer 有可能會修改 Thread 的優先度(`priority`),所以會將原來 Thread 的優先度保存到 `saved_priority`,**方便之後恢復優先度** :::warning * 目標 Thread 的優先度不能低於 BinderServer 的優先度,否則會處理不到 ::: 3. `to_parent`、`from_parent` 成員:主要是為了 **有效利用 Thread 工作**;以下假設任務的依賴 ```c= ## 任務表達 -> TaskN(ThreadM) -> Tn(m) Thread A 發現交易 T1,但需要等 T1(B) 先處理 Thread B 在處理 T1(B) 時,又需要先等 T2(C) 處理 Thread C 在處理 T2(C) 時,又需要等等 T3(A) 處理 ## ------------------------------------------------------ 最終結果 A 要先處理 T3(A),再處理 T1(B) ``` 任務關係如下圖 > ![](https://i.imgur.com/iFTPLIM.png) 如此雙向串連結的關係,讓 Binder 驅動可以找到 Task 所屬的處理 Thread,並且當下正在處理的 Task 會放到 Stack 頂端,處理完後又會按照 `to_parent` 去處理下個 Task ::: ```c= // binder_internal.h struct binder_transaction { int debug_id; struct binder_work work; // 該交易的狀態 // 來源 struct binder_thread *from; // 目標 struct binder_proc *to_proc; struct binder_thread *to_thread; // 一個交易所依賴的交易,目標要處理的下一個交易 struct binder_transaction *to_parent; struct binder_transaction *from_parent; // 判斷是否是同步 unsigned need_reply:1; /* unsigned is_dead:1; */ /* not used at the moment */ // 為這次交易分配的一個核心緩衝區 (處理程式通訊的資料) struct binder_buffer *buffer; unsigned int code; unsigned int flags; // 描述來源 Thread 的優先序 struct binder_priority priority; struct binder_priority saved_priority; // 保存來源 Thread 優先度 bool set_priority_called; bool is_nested; kuid_t sender_euid; struct list_head fd_fixups; binder_uintptr_t security_ctx; /** * @lock: protects @from, @to_proc, and @to_thread * * @from, @to_proc, and @to_thread can be set to NULL * during thread teardown */ spinlock_t lock; ANDROID_VENDOR_DATA(1); }; ``` ## [Binder](https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/drivers/android/binder.c) 命令 - 分類 * 在使用 Binder 時會接觸到很多命令,這些命令猶如網路 7 層模型的結構,是一層層解析,最終完成使用者的設定 Binder 驅動命令寫在 `linux/android/binder.h` 中 (上面有說明,這邊就不再提) * Binder 命令(協議) > ![](https://i.imgur.com/EK88yx1.png) ### 控制協議 * **控制協議**:就是拿來控制 BinderDriver 的命令;控制協議由 `binder_ioctl` 處理,列出幾個較為重要的控制命令 | 命令 | 說明 | 參數類型 | | -------- | -------- | -------- | | BINDER_WRITE_READ | 對 binder 讀寫,IPC 通過這個命令來進行數據傳遞 | binder_write_read | | BINDER_SET_MAX_THREADS | 設定進程支援的最大 Binder thread 數量 | size_t | | BINDER_SET_CONTEXT_MGR | 將呼叫進程設定為 `/dev/binder` 文件的管理者(ServiceManager) | - | | BINDER_THREAD_EXIT | 通知 binder thread 退出 | - | | BINDER_VERSION | 取得 binder 版本號 | - | :::info 會先處理 控制協議,才會處理 驅動協議 ::: ### 驅動協議 * **驅動協議**:驅動又可分為兩類指令,不同指令代表了不同方向 * **binder_driver_command_protocol**:進程發送給 Binder 的命令,**這些命令會以 ==BC_== 開頭**,這些驅動命令會由 `binder_thread_write` 函數處理 | 命令 | 說明 | 參數類型 | | -------- | -------- | -------- | | **BC_TRANSACTION** | 傳輸資料給 binder,也就是 client 對 service 的請求 | binder_transaction_data | | **BC_REPLY** | service 對於 client 端的回覆 | binder_transaction_data | | BC_REGISTER_LOOPER | 通知 **main** thread 已進入 ready | - | | BC_ENTER_LOOPER | 通知 **child** thread 已進入 ready | - | | BC_EXIT_LOOPER | 通知驅動 thread 已退出 | - | | BC_INCREFS_DONE | BR_INCREFS 的回覆 | binder_ptr_cookie | | BC_ACQUIRE_DONE | BR_ACQUIRE 的回覆 | binder_ptr_cookie | * **binder_driver_return_protocol**:Binder 發送給進程的命令,**這些命令會以 ==BR_== 開頭**,這些驅動命令會由 `binder_thread_read` 函數處理 | 命令 | 說明 | 參數類型 | | -------- | -------- | -------- | | BR_ERROR | 發生錯誤 | - | | BR_OK | 操作完成 | - | | BR_TRANSACTION | 通知進程,收到 Binder 請求(Servier 端) | binder_transaction_data | | BR_REPLY | 通知進程,收到 Binder 回覆(Client) | binder_transaction_data | | BR_INCREFS | 弱引用 + 1 | binder_ptr_cookie | | BR_ACQUIRE | 強引用 + 1 | binder_ptr_cookie | * 下圖我們來看一個完整的 Binder 請求命令 (不包括引用計數、Service 死亡通知),可以發現往 Binder 的命令都是 `BC_` 開頭,離開 Binder (或是說 Binder 主動要求) 的命令都是 `BR_` 開頭 > ![](https://i.imgur.com/2Jm7Mx6.png) ## Appendix & FAQ :::info ::: ###### tags: `Binder`