---
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 對應一個 通訊協定、元件資料
> 
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 中的正確位置
> 
:::
```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)
```
任務關係如下圖
> 
如此雙向串連結的關係,讓 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 命令(協議)
> 
### 控制協議
* **控制協議**:就是拿來控制 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_` 開頭
> 
## Appendix & FAQ
:::info
:::
###### tags: `Binder`