---
# System prepended metadata

title: Binder 驅動 - 結構
tags: [Binder]

---

---
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`
