Android跨進程通信IPC之10——Binder相關結構體簡介

移步系列Android跨進程通信IPC系列

1、結構體binder_work
2、結構體binder_thread
3、構體binder_stats
4、結構體binder_proc
5、結構體binder_node
6、結構體binder_ref
7、結構體binder_ref_death
8、結構體binder_state
9、結構體binder_buffer
10、結構體binder_transaction
11、結構體binder_transaction_data
12、結構體transaction_flags
13、結構體flat_binder_object
14、結構體binder_write_read
15、結構體binder_ptr_cookie
16、總結

1 結構體binder_work

1.1 位置

位置在
Linux的binder.c 240行

1.2 代碼注釋

binder_work代表binder驅動中進程要處理的工作項

struct binder_work {
    struct list_head entry;  //用于實現一個雙向鏈表,存儲的所有binder_work隊列
    enum {
        BINDER_WORK_TRANSACTION = 1,
        BINDER_WORK_TRANSACTION_COMPLETE,
        BINDER_WORK_NODE,
        BINDER_WORK_DEAD_BINDER,
        BINDER_WORK_DEAD_BINDER_AND_CLEAR,
        BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
    } type;  //描述工作項的類型
};

2 結構體binder_thread

2.1 代碼位置

位置在
Linux的binder.c 368行

2.2 代碼注釋

binder_thread 代表Binder線程池中的每個線程信息

struct binder_thread {
    //宿主進程,即線程屬于那么Binder進程
    struct binder_proc *proc;  
    //紅黑樹的一個節點,binder_proc使用紅黑樹來組織Binder線程池中的線程
    struct rb_node rb_node;  
    // 當前線程的PID
    int pid;
    // 當前線程的狀態信息
    int looper;
    //事務堆棧,將事務封裝成binder_transaction,添加到事務堆棧中,
    //定義了要接收和發送的進程和線程消息
    struct binder_transaction *transaction_stack;
    //隊列,當有來自Client請求時,將會添加到to_do隊列中
    struct list_head todo;
    // 記錄閱讀buf事務時出現異常錯誤情況信息
    uint32_t return_error; /* Write failed, return error code in read buf */
    // 記錄閱讀事務時出現異常錯誤情況信息
    uint32_t return_error2; /* Write failed, return error code in read */
        /* buffer. Used when sending a reply to a dead process that */
        /* we are also waiting on */
     // 等待隊列,當Binder處理事務A依賴于其他Binder線程處理事務B的情況
     // 則會在sleep在wait所描述的等待隊列中,知道B事物處理完畢再喚醒
    wait_queue_head_t wait;
     // Binder線程相關統計數據
    struct binder_stats stats;
};

這里面說下上面looper對應的值

enum {
    // Binder驅動 請求創建該線程,通過BC_REGISTER_LOOPER協議通知
    //Binder驅動,注冊成功設置此狀態
    BINDER_LOOPER_STATE_REGISTERED  = 0x01,
    //該線程是應用程序主動注冊的  通過 BC_ENTER_LOOPER 協議
    BINDER_LOOPER_STATE_ENTERED     = 0x02,
    // Binder線程退出
    BINDER_LOOPER_STATE_EXITED      = 0x04,
    // Binder線程處于無效
    BINDER_LOOPER_STATE_INVALID     = 0x08,
    // Binder線程處于空閑狀態
    BINDER_LOOPER_STATE_WAITING     = 0x10,
     // Binder線程處于需要返回用戶控件
     // 使用場景是:1、線程注冊為Binder線程后,還沒有準備好去處理進程間通信
     //,需要返回用戶空間做其他初始化準備;2、調用flush來刷新Binder線程池                                                                                                                 
    BINDER_LOOPER_STATE_NEED_RETURN = 0x20
};

3 結構體binder_stats

binder_stats 代表d的是Binder線程相關統計數據

3.1 代碼位置

位置在
Linux的binder.c 173行

3.2 代碼注釋

struct binder_stats {
        // 統計各個binder響應碼的個數
    int br[_IOC_NR(BR_FAILED_REPLY) + 1];
        // 統計各個binder請求碼的個數
    int bc[_IOC_NR(BC_REPLY_SG) + 1];
        // 統計各種obj創建的個數
    int obj_created[BINDER_STAT_COUNT];
        // 統計各種obj刪除個數
    int obj_deleted[BINDER_STAT_COUNT];
};

4 結構體binder_proc

binder_proc 代表的是一個正在使用Binder進程通信的進程,binder_proc為管理其信息的記錄體,當一個進程open /dev/binder 時,Binder驅動程序會為其創建一個binder_proc結構體,用以記錄進程的所有相關信息,并把該結構體保存到一個全局的hash表中

4.1 代碼位置

位置在
Linux的binder.c 322行

4.2 代碼注釋

struct binder_proc {
        /** 進程相關參數 */

        //上述全局hash表中一個節點,用以標記該進程
    struct hlist_node proc_node;
        // 進程組ID
    int pid;
         // 任務控制模塊 
    struct task_struct *tsk; 
        // 文件結構體數組
    struct files_struct *files;

       /**  Binder線程池每一個Binder進程都有一個線程池,由Binder驅動來維護,Binder線程池中所有線程由一個紅黑樹來組織,RB樹以線程ID為關鍵字  */
        //上述紅黑樹的根節點
    struct rb_root threads;
        
       /** 一系列Binder實體對象(binder_node)和Binder引用對象(binder_ref) */
       /** 在用戶控件:運行在Server端稱為Binder本地對象,運行在Client端稱為Binder代理對象*/
       /**  在內核空間:Binder實體對象用來描述Binder本地對象,Binder引用對象來描述Binder代理對象 */
         // Binder實體對象列表(RB樹),關鍵字 ptr
    struct rb_root nodes;
         // Binder引用對象,關鍵字  desc
    struct rb_root refs_by_desc;
         // Binder引用對象,關鍵字  node
    struct rb_root refs_by_node;
         // 這里有兩個引用對象,是為了方便快速查找 


     /**  進程可以調用ioctl注冊線程到Binder驅動程序中,當線程池中沒有足夠空閑線程來處理事務時,Binder驅動可以主動要求進程注冊更多的線程到Binder線程池中 */
        // Binder驅動程序最多可以請求進程注冊線程的最大數量
    int max_threads;
        // Binder驅動每主動請求進程添加注冊一個線程的時候,requested_threads+1
    int requested_threads;
        // 進程響應Binder要求后,requested_thread_started+1,request_threads-1,表示Binder已經主動請求注冊的線程數目
    int requested_threads_started;

        // 進程當前空閑線程的數目
    int ready_threads;
        // 線程優先級,初始化為進程優先級
    long default_priority;
        //進程的整個虛擬地址空間
    struct mm_struct *vma_vm_mm;

        /** mmap 內核緩沖區*/
        // mmap——分配的內核緩沖區  用戶控件地址(相較于buffer)
    struct vm_area_struct *vma; 
        // mmap——分配內核緩沖區,內核空間地址(相交于vma)  兩者都是虛擬地址
    void *buffer;
        // mmap——buffer與vma之間的差值
    ptrdiff_t user_buffer_offset;

        /** buffer指向的內核緩沖區,被劃分為很多小塊進行性管理;這些小塊保存在列表中,buffer就是列表的頭部 */
         // 內核緩沖列表
    struct list_head buffers;
         // 空閑的內存緩沖區(紅黑樹)
    struct rb_root free_buffers;
         // 正在使用的內存緩沖區(紅黑樹)
    struct rb_root allocated_buffers;
        // 當前可用來保存異步事物數據的內核緩沖區大小
    size_t free_async_space;
        //  對應用于vma 、buffer虛擬機地址,這里是他們對應的物理頁面
    struct page **pages;
        //  內核緩沖區大小
    size_t buffer_size;
        // 空閑內核緩沖區大小
    uint32_t buffer_free;

         /** 進程每接收到一個通信請求,Binder將其封裝成一個工作項,保存在待處理隊列to_do中  */
        //待處理隊列
    struct list_head todo;
        // 等待隊列,存放一些睡眠的空閑Binder線程
    wait_queue_head_t wait;
        // hash表,保存進程中可以延遲執行的工作項
    struct hlist_node deferred_work_node;
        // 延遲工作項的具體類型
    int deferred_work;
    
        //統計進程相關數據,具體參考binder_stats結構體
    struct binder_stats stats;
        // 表示 Binder驅動程序正在向進程發出死亡通知
    struct list_head delivered_death;
        // 用于debug
    struct dentry *debugfs_entry;
        // 連接 存儲binder_node和binder_context_mgr_uid以及name
    struct binder_context *context;
};

5 結構體binder_node

binder_node 代表的是Binder實體對象,每一個service組件或者ServiceManager在Binder驅動程序中的描述,Binder驅動通過強引用和弱引用來維護其生命周期,通過node找到空間的Service對象

5.1 代碼位置

位置在
Linux的binder.c 252行

5.2 代碼注釋

struct binder_node {
        // debug調試用的
    int debug_id;
    struct binder_work work;  //binder驅動中進程要處理的工作項 

        /** 每一個binder進程都由一個binder_proc來描述,binder進程內部所有Binder實體對象,
        由一個紅黑樹來進行組織(struct rb_root nodes)  ; rb_node 則對應nodes的一個節點 */
    union {
        //用于本節點連接紅黑樹
        struct rb_node rb_node;
        // 如果Binder 實體對象對應的進程死亡,銷毀節點時需要將rb_node從紅黑樹中刪除,
        //如果本節點還沒有引用切斷,則用dead_node將其隔離到另一個鏈表中,
        //直到通知所有進程切斷與該節點的引用后,該節點才能銷毀
        struct hlist_node dead_node;
    };

    // 指向該Binder實體對象 對應的進程,進程由binder_proc描述
    struct binder_proc *proc;
    // 該 Binder實體對象可能同時被多個Client組件引用,所有指向本實體對象的引用都
    //保存在這個hash隊列中refs表示隊列頭部;這些引用可能隸屬于不同進程,遍歷該
    //hash表能夠得到這些Client組件引用了這些對象
    struct hlist_head refs;

    /** 引用計數 
     * 1、當一個Binder實體對象請求一個Service組件來執行Binder操作時。會增加該Service
     * 組件的強/弱引用計數同時,Binder實體對象將會has_strong_ref與has_weak_ref置為1 
     *2、當一個Service組件完成一個Binder實體對象請求的操作后,Binder對象會請求減少該
     * Service組件的強/弱引用計數
     * 3、Binder實體對象在請求一個Service組件增加或減少強/弱引用計數的過程中,
     * 會將pending_strong_ref和pending_weak_ref置為1,當Service組件完成增加
     * 或減少計數時,Binder實體對象會將這兩個變量置為0
     */

    //遠程強引用 計數
    int internal_strong_refs;  //實際上代表了一個binder_node與多少個binder_ref相關聯
    //本地弱引用技數
    int local_weak_refs;
     //本地強引用計數
    int local_strong_refs;

    unsigned has_strong_ref:1;
    unsigned pending_strong_ref:1;
    unsigned has_weak_ref:1;
    unsigned pending_weak_ref:1;

     /** 用來描述用戶控件中的一個Service組件 */
    // 描述用戶控件的Service組件,對應Binder實體對應的Service在用戶控件的(BBinder)的引用
    binder_uintptr_t ptr;
    // 描述用戶空間的Service組件,Binder實體對應的Service在用戶控件的本地Binder(BBinder)地址
    binder_uintptr_t cookie;

     // 異步事務處理,單獨講解
    unsigned has_async_transaction:1;
    struct list_head async_todo;
    // 表示該Binder實體對象能否接收含有該文件描述符的進程間通信數據。當一個進程向
    //另一個進程發送數據中包含文件描述符時,Binder會在目標進程中打開一個相同的文件
    //故設為accept_fds為0 可以防止源進程在目標進程中打開文件
    unsigned accept_fds:1;
     // 處理Binder請求的線程最低優先級
    unsigned min_priority:8;

};

這里說下binder_proc和binder_node關系:

可以將binder_proc理解為一個進程,而將binder_noder理解為一個Service。binder_proc里面有一個紅黑樹,用來保存所有在它所描述的進程里面創建Service。而每一個Service在Binder驅動里面都有一個binder_node來描述。

5.3 異步事物處理

  • 異步事務處理,目的在于為同步交互讓路,避免長時間阻塞發送送端
  • 異步事務定義:(相對于同步事務)單向進程間通信要求,即不需要等待應答的進程間通信請求
  • Binder驅動程序認為異步事務的優先級低于同步事務,則在同一時刻,一個Binder實體對象至多只有一個異步事物會得到處理。而同步事務則無此限制。
  • Binder將事務保存在一個線程binder_thread的todo隊列中,表示由該線程來處理該事務。每一個事務都關聯Binder實體對象(union target),表示該事務的目標處理對象,表示要求該Binder實體對象對應的Service組件在制定線程中處理該事務,而如果Binder發現一個事務時異步事務,則會將其保存在目標Binder對象的async_todo的異步事務中等待處理

6 結構體binder_ref

binder_ref 代表的是Binder的引用對象,每一個Clinet組件在Binder驅動中都有一個Binder引用對象,用來描述它在內核中的狀態。

6.1 代碼位置

位置在
Linux的binder.c 281行

6.2 代碼注釋

struct binder_ref {
    /* Lookups needed: */
    /*   node + proc => ref (transaction) */
    /*   desc + proc => ref (transaction, inc/dec ref) */
    /*   node => refs + procs (proc exit) */
        //debug 調試用的
        int debug_id;
     
        /** binder_proc中使用紅黑樹(對應兩個rb_root變量) 來存儲器內部所有引用對象,
         *下面的rb_node則是紅黑樹中的節點
         */
        //Binder引用的宿主進程
        struct binder_proc *proc;
        //對應 refs_by_desc,以句柄desc索引  關聯到binder_proc->refs_by_desc紅黑樹 
        struct rb_node rb_node_desc;
         //對應refs_by_node,以Binder實體對象地址作為關鍵字關聯到binder_proc->refs_by_node紅黑樹
        struct rb_node rb_node_node;

        /** Client通過Binder訪問Service時,僅需指定一個句柄,Binder通過該desc找到對應的binder_ref,
         *  再根據該binder_ref中的node變量得到binder_node(實體對象),進而找到對應的Service組件
         */
        // 對應Binder實體對象中(hlist_head) refs引用對象隊列中的一個節點
        struct hlist_node node_entry;
        // 引用對象所指向的Binder實體對象
        struct binder_node *node;
        // Binder引用的句柄值,Binder驅動為binder驅動引用分配一個唯一的int型整數(進程范圍內唯一)
        // ,通過該值可以在binder_proc->refs_by_desc中找到Binder引用,進而可以找到Binder引用對應的Binder實體
        uint32_t desc;

        // 強引用 計數
        int strong;
        // 弱引用 計數
        int weak;
      
        //  表示Service組件接受到死亡通知
        struct binder_ref_death *death;
};

7 結構體binder_ref_death

  • binder_ref_death 一個死亡通知的結構體
  • Client組件無法控制它所引用的Service組件的生命周期,由于Service組件所在的進程可能意外崩潰。
  • Client進程需要能夠在它所引用的Service組件死亡時獲的通知,進而進行響應。則Client進程就需要向Binder驅動注冊一個用來接收死亡通知的對象地址(這里的cookie)

7.1 代碼位置

位置在
Linux的binder.c 276行

7.2 代碼注釋

struct binder_ref_death {
         //標志該通知具體的死亡類型
        struct binder_work work;   
         // 保存負責接收死亡通知的對象地址
        binder_uintptr_t cookie;
};

這里隨帶說一下Binder驅動向Client進程發送死亡通知的情況:

    1. Binder驅動檢測到Service組件死亡時,會找到對應Serivce實體對象(binder_node),再通過refs變量找到引用它的所有Client進程(binder_ref),再通過death變量找到Client進程向Binder注冊的死亡通知接收地址;Binder將死亡通知binder_ref_death封裝成工作項,添加到Client進程to_do隊列中等待處理。這種情況binder_work類型為BINDER_WORK_DEAD_BINDER
    1. Client進程向Binder驅動注冊一個死亡接收通知時,如果它所引用的Service組件已經死亡,Binder會立即發送通知給Client進程。這種情況binder_work類型為BINDER_WORK_DEAD_BINDER
    1. 當Client進程向Binder驅動注銷一個死亡通知時,也會發送通知,來響應注銷結果
  • ①當Client注銷時,Service組件還未死亡:Binder會找到之前Client注冊的binder_ref_death,當binder_work修改為BINDER_CLEAR_NOTIFICATION,并將通知按上述步驟添加到Client的to_do隊列中
  • ②當Client注銷時,Service已經死亡,Binder同理將binder_work修改為WORK_DEAD_BINDER_AND_CLEAR,然后添加到todo中

8 結構體binder_state

binder_state 代表著binder設備文件的狀態

8.1 代碼位置

位置在
/frameworks/native/cmds/servicemanager/binder.c 89行

8.2 代碼注釋

struct binder_state
{
    //打開 /dev/binder之后得到的文件描述符
    int fd;
    //mmap將設備文件映射到本地進程的地址空間,映射后的到地址空間中,映射后得到的地址空間地址,及大小。
    void *mapped;
    // 分配內存的大小,默認是128K
    size_t mapsize;
};

9 結構體binder_buffer

  • binder_buffer 內核緩沖區,用以在進程間傳遞數據。
  • binder驅動程序管理這個內存映射地址空間方法,即管理buffer~(buffer+buffer_size)這段地址空間的,這個地址空間被劃分為一段一段來管理,每一段是結構體struct binder_buffer來描述。
  • 每一個binder_buffer通過其成員entry從低到高地址連入到struct binder_proc中的buffers表示鏈表中去

9.1 代碼位置

位置在
Linux的binder.c 298行

9.2 代碼注釋

struct binder_buffer {
        //entry對應內核緩沖區列表的buffers(內核緩沖區列表)
        struct list_head entry; /* free and allocated entries by address */
        //結合free,如果free=1,則rb_node對應free_buffers中一個節點(內核緩沖區)
        //如果free!=1,則對應allocated_buffers中的一個節點
        struct rb_node rb_node; /* free entry by size or allocated entry */
                /* by address */
        unsigned free:1;

         /**  Binder將事務數據保存到一個內核緩沖區(binder_transaction.buffer),然后交由Binder
          * 實體對象(target_node) 處理,而target_node會將緩沖區的內容交給對應的Service組件
          * (proc) 來處理,Service組件處理完事務后,若allow_user_free=1,則請求Binder釋放該
          * 內核緩沖區
          */
        unsigned allow_user_free:1;
         // 描述一個內核緩沖區正在交給那個事務transaction,用以中轉請求和返回結果
        struct binder_transaction *transaction;
         // 描述該緩沖區正在被那個Binder實體對象使用
        struct binder_node *target_node;

        //表示事務時異步的;異步事務的內核緩沖區大小是受限的,這樣可以保證事務可以優先放到緩沖區
        unsigned async_transaction:1;
         //調試專用
        unsigned debug_id:29;

        /** 
         *  存儲通信數據,通信數據中有兩種類型數據:普通數據與Binder對象
         *  在數據緩沖區最后,有一個偏移數組,記錄數據緩沖區中每一個Binder
         *  對象在緩沖區的偏移地址
         */
        //  數據緩沖區大小
        size_t data_size;
        // 偏移數組的大小(其實也是偏移位置)
        size_t offsets_size;
        // 用以保存通信數據,數據緩沖區,大小可變
        uint8_t data[0];
        // 額外緩沖區大小
        size_t extra_buffers_size;
};

10 結構體binder_transaction

binder_transaction 描述Binder進程中通信過程,這個過程稱為一個transaction(事務),用以中轉請求和返回結果,并保存接受和要發送的進程信息

10.1 代碼位置

位置在
Linux的binder.c 383行

10.2 代碼注釋

struct binder_transaction {
        //調試調用
        int debug_id;
        // 用來描述的處理的工作事項,這里會將type設置為BINDER_WORK_TRANSACTION,具體結構看binder_work
        struct binder_work work;

        /**   源線程 */
        // 源線程,即發起事務的線程
        struct binder_thread *from;
        // 源線程的優先級
        long    priority;
        //源 線程的用戶 ID
        kuid_t  sender_euid;

        /**  目標線程*/
        //  目標進程:處理該事務的進程
        struct binder_proc *to_proc;
        // 目標線程:處理該事務的線程
        struct binder_thread *to_thread;
   
         // 表示另一個事務要依賴事務(不一定要在同一個線程中)
        struct binder_transaction *from_parent;
         // 目標線程下一個需要處理的事務
        struct binder_transaction *to_parent;

        // 標志事務是同步/異步;設為1表示同步事務,需要等待對方回復;設為0異步
        unsigned need_reply:1;
        /* unsigned is_dead:1; */   /* not used at the moment */
     
        /* 參考binder_buffer中解釋,指向Binder為該事務分配內核緩沖區
         *  code與flag參見binder_transaction_data
         */
        struct binder_buffer *buffer;
        unsigned int    code;
        unsigned int    flags;

        /**  目標線程設置事務錢,Binder需要修改priority;修改前需要將線程原來的priority保存到
         *    saved_priority中,用以處理完事務回復到原來優先級
         *   優先級設置:目標現場處理事務時,優先級應不低于目標Serivce要求的線程優先級,也
         *   不低于源線程的優先級,故設為兩者的較大值。
         */
        long    saved_priority;

};

11 結構體binder_transaction_data

binder_transaction_data 代表進程間通信所傳輸的數據:Binder對象的傳遞時通過binder_transaction_data來實現的,即Binder對象實際是封裝在binder_transaction_data結構體中

11.1 代碼位置

位置在
Linux的binder.h 217行

11.2 代碼注釋

struct binder_transaction_data {
         //target很重要,我下面重點介紹
         /* The first two are only used for bcTRANSACTION and brTRANSACTION,
         * identifying the target and contents of the transaction.
         */
         union {
               /* target descriptor of command transaction */
               __u32    handle;
               /* target descriptor of return transaction */
               binder_uintptr_t ptr;
         } target;
      
         //Binder實體帶有的附加數據
         binder_uintptr_t   cookie; /* target object cookie */
         // code是一個命令,描述了請求Binder對象執行的操作,表示要對目標對象請求的命令代碼
         __u32      code;       /* transaction command */

         /* General information about the transaction. */
         // 事務標志,詳細看transaction_flag結構體
         __u32          flags;
         // 發起請求的進程PID
         pid_t      sender_pid;
         // 發起請求的進程UID
         uid_t      sender_euid;
         // data.buffer緩沖區的大小,data見最下面的定義;命令的真正要傳輸的數據就保存data.buffer緩沖區
         binder_size_t  data_size;  /* number of bytes of data */
          // data.offsets緩沖區的大小
         binder_size_t  offsets_size;   /* number of bytes of offsets */

         /* If this transaction is inline, the data immediately
         * follows here; otherwise, it ends with a pointer to
         * the data buffer.
         */
         union {
               struct {
                        /* transaction data */
                        binder_uintptr_t    buffer;
                        /* offsets from buffer to flat_binder_object structs */
                        binder_uintptr_t    offsets;
               } ptr;
               __u8 buf[8];
         } data;
};

這里重點說兩個共用體target和data
target

  • 一個共用體target,當這個BINDER_WRITE_READ命令的目標對象是本地Binder的實體時,就用ptr來表示這個對象在本進程的地址,否則就使用handle來表示這個Binder的實體引用。
  • 只有目標對象是Binder實體時,cookie成員變量才有意義,表示一些附加數據。
  • 詳解解釋一下:傳輸的數據是一個復用數據聯合體,對于BINDER類型,數據就是一個Binder本地對象。如果是HANDLE類型,這個數據就是遠程Binder對象。很多人會說怎么區分本地Binder對象和遠程Binder對象,主要是角度不同而已。本地對象還可以帶有額外數據,保存在cookie中。

data

  • 命令的真正要傳輸的數據就保存在data.buffer緩沖區中,前面的一成員變量都是一些用來描述數據的特征。
  • data.buffer所表示的緩沖區數據分為兩類,一類是普通數據,Binder驅動程序不關心,一類是Binder實體或者Binder引用,這需要Binder驅動程序介入處理。
  • 為什么?因為如果一個進程A傳遞了一個Binder實體或Binder引用給進程B,那么,Binder驅動程序就需要介入維護這個Binder實體或者引用引用計數。
  • 防止B進程還在使用這個Binder實體時,A卻銷毀這個實體,這樣的話,B進程就會crash了。所以在傳輸數據時,如果數據中含有Binder實體和Binder引用,就需要告訴Binder驅動程序他們的具體位置,以便Binder驅動程序能夠去維護它們。data.offsets的作用就在這里,它指定在data.buffer緩沖區中,所以Binder實體或者引用的偏移位置。
  • 進程間傳輸的數據被稱為Binder對象(Binder Object),它是一個flat_binder_object。
  • Binder對象的傳遞時通過binder_transaction_data來實現的,即Binder對象實際是封裝在binder_transaction_data結構體中。

12 結構體transaction_flags

transaction_flags 描述傳輸方式,比如同步或者異步等

12.1 代碼位置

位置在
Linux的binder.h 210行

12.2 代碼注釋

enum transaction_flags {
         //當前事務異步,不需要等待
        TF_ONE_WAY  =  0x01,    /* this is a one-way call: async, no return */
        // 包含內容是根對象
        TF_ROOT_OBJECT  =  0x04,    /* contents are the component's root object */
        // 表示data所描述的數據緩沖區內 同時一個4bit的狀態碼
        TF_STATUS_CODE  =  0x08,    /* contents are a 32-bit status code */
        // 允許數據中包含文件描述
        TF_ACCEPT_FDS   =  0x10,    /* allow replies with file descriptors */
};

13 結構體flat_binder_object

flat_binder_object 描述進程中通信過程中傳遞的Binder實體/引用對象或文件描述符

13.1 代碼位置

位置在
Linux的binder.h 68行

13.2 代碼注釋

/*
 * This is the flattened representation of a Binder object for transfer
 * between processes.  The 'offsets' supplied as part of a binder transaction
 * contains offsets into the data where these structures occur.  The Binder
 * driver takes care of re-writing the structure type and data as it moves
 * between processes.
 */
struct flat_binder_object {
    struct binder_object_header hdr;
    __u32               flags;

    /* 8 bytes of data. */
    union {
        binder_uintptr_t    binder; /* local object */
        __u32           handle; /* remote object */
    };

    /* extra data associated with local object */
    binder_uintptr_t    cookie;
};

這個比較重要我們就一個一個來說,首先看下struct binder_object_header hdr;
里面涉及到一個結構體binder_object_header,這個結構體在Linux的binder.h 57行,代碼如下:

/**
 * struct binder_object_header - header shared by all binder metadata objects.
 * @type:   type of the object
 */
struct binder_object_header {
    __u32        type;
};

flat_binder_object通過type來區分描述類型,可選的描述類型如下:
代碼在
Linux的binder.h 30行

enum {
        //強類型Binder實體對象
    BINDER_TYPE_BINDER  = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),
        //弱類型Binder實體對象
    BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
         // 強類型引用對象
    BINDER_TYPE_HANDLE  = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
         // 弱類型引用對象
    BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
        // 文件描述符 
    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),
};

當描述實體對象時,cookie表示Binder實體對應的Service在用戶空間的本地Binder(BBinder)地址,binder表示Binder實體對應的當描述引用對象時,handle表示該引用的句柄值。

14 結構體binder_write_read

binder_write_read 描述進程間通信過程中所傳輸的數據

14.1 代碼位置

位置在
Linux的binder.h 165行

14.2 代碼注釋

/*
 * On 64-bit platforms where user code may run in 32-bits the driver must
 * translate the buffer (and local binder) addresses appropriately.
 */

struct binder_write_read {

        /** 輸入數據 從用戶控件傳輸到Binder驅動程序的數據
         *  數據協議代碼為命令協議碼,由binder_driver_command_protocol定義
         */
        // 寫入的大小
        binder_size_t       write_size; /* bytes to write */
        // 記錄了從緩沖區取了多少字節的數據
        binder_size_t       write_consumed; /* bytes consumed by driver */
        // 指向一個用戶控件緩沖區的地址,里面的內容即為輸入數據,大小由write_size指定
        binder_uintptr_t    write_buffer;
        
         /** 輸出數據,從Binder驅動程序,返回給用戶空間的數據
          *  數據協議代碼為返回協議代碼,由binder_driver_return_protocol定義
          */
        //讀出的大小
        binder_size_t       read_size;  /* bytes to read */
        // read_buffer中讀取的數據量
        binder_size_t       read_consumed;  /* bytes consumed by driver */
        // 指向一個用戶緩沖區一個地址,里面保存輸出的數據
        binder_uintptr_t    read_buffer;
};

里面涉及到兩個協議,我們就在這里詳細講解下

14.3 binder_driver_command_protocol協議

代碼在Linux的binder.h 366行

enum binder_driver_command_protocol {
      
        /**  下面這兩個命令數據類型為binder_transaction_data,是最常用到的 */
        /**一個Client進程請求目標進程執行某個事務時,會使用BC_TRANSACTION請求Binder驅
         *動程序將通信數據傳遞到Server目標進程
         * 使用者:Client進程   用處:傳遞數據
         */ 
        BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
         /** 當Server目標進程處理完事務后,會使用BC_REPLY請求Binder將結果返回給Client源進程
          * 使用者:Server進程  用處:返回數據
          */
        BC_REPLY = _IOW('c', 1, struct binder_transaction_data),


        /*
         * binder_transaction_data: the sent command.
         */
        //當前版本not support  在Linux中的binder.c就是這么寫的
        BC_ACQUIRE_RESULT = _IOW('c', 2, __s32),

        /*
         * not currently supported
         * int:  0 if the last BR_ATTEMPT_ACQUIRE was not successful.
         * Else you have acquired a primary reference on the object.
         */

        // 數據類型為int類型,指向Binder內部一塊內核緩沖區
        // 目標進程處理完源進程事務后,會使用BC_FREE_BUFFER來釋放緩沖區
        BC_FREE_BUFFER = _IOW('c', 3, binder_uintptr_t),
    /*
     * void *: ptr to transaction data received on a read
     */

        //通信類型為int類型,表示binder_ref的句柄值handle
        // 增加弱引用數
        BC_INCREFS = _IOW('c', 4, __u32),
        // 減少弱引用數
        BC_DECREFS = _IOW('c', 7, __u32),
        // 增加強引用數
        BC_ACQUIRE = _IOW('c', 5, __u32),
        // 減少強引用數
        BC_RELEASE = _IOW('c', 6, __u32),
    
        /*
         * int: descriptor
         */
        /** Service進程完成增加強/弱引用的計數后,會使用這兩個命令通知Binder */
        // 增加強引用計數后
        BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie),
        //增加弱引用計數后
        BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie),

        /*
         * void *: ptr to binder
         * void *: cookie for binder
         */
        //當前版本不支持 
        BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc),

        /*
         * not currently supported
         * int: priority
         * int: descriptor
         */
        // Binder驅動程序 請求進程注冊一個線程到它的線程池中,新建立線程會使用
        //BC_REGISTER_LOOPER來通知Binder準備就緒
        BC_REGISTER_LOOPER = _IO('c', 11),

        /*
         * No parameters.
         * Register a spawned looper thread with the device.
         */
        //一個線程自己注冊到Binder驅動后,會使用BC_ENTER_LOOPER通知Binder準備就緒
        BC_ENTER_LOOPER = _IO('c', 12),
        // 線程發送退出請求
        BC_EXIT_LOOPER = _IO('c', 13),

        /*
         * No parameters.
         * These two commands are sent as an application-level thread
         * enters and exits the binder loop, respectively.  They are
         * used so the binder can have an accurate count of the number
         * of looping threads it has available.
         */
        // 進程向Binder注冊一個死亡通知
        BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14,
                        struct binder_handle_cookie),
        /*
         * int: handle
         * void *: cookie
         */
         // 進程取消之前注冊的死亡通知
        BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15,
                        struct binder_handle_cookie),
        /*
         * int: handle
         * void *: cookie
         */
        // 數據指向死亡通知binder_ref_death的地址,進程獲得Service組件的死亡通知,
        // 會使用該命令通知Binder其已經處理完死亡通知
        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.
         */
};

在上述枚舉命令成員中,最重要的是BC_TRANSACTION和BC_REPLY命令,被作為發送操作的命令,其數據參數都是binder_transaction_data結構體。其中前者用于翻譯和解析將要被處理的事務數據,而后者則是事務處理完成之后對返回"結果數據"的操作命令。

14.4 binder_driver_return_protocol協議

Binder驅動的響應(返回,BR_)協議,定義了Binder命令的數據返回格式
代碼在Linux的binder.h 278行

enum binder_driver_return_protocol {
         // Binder驅動程序處理進程發送的請求時,發生異常,在返回BR_ERROR通知該進程
         // 數據類型為int,表示錯誤代碼
        BR_ERROR = _IOR('r', 0, __s32),
    /*
     * int: error code
     */

        // 表示通知進程成功處理了該事務
        BR_OK = _IO('r', 1),
    /* No parameters! */
        
        // 與上面的 BC_ 相對應
        //  Client進程向Server進程發送通信請求(BC_) ,Binder使用BR_TRANSACTION通知Server
        // 使用者 :   Binder驅動程序     用途:通知Server
        BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data),
        // Server處理完 請求 使用 BC_ 通知Binder,Binder使用BR_REPLY通知Client
        // 使用者:Binder驅動程序,用途:通知Client
        BR_REPLY = _IOR('r', 3, struct binder_transaction_data),
    /*
     * binder_transaction_data: the received command.
     */

         // 當前不支持 
        BR_ACQUIRE_RESULT = _IOR('r', 4, __s32),
    /*
     * not currently supported
     * int: 0 if the last bcATTEMPT_ACQUIRE was not successful.
     * Else the remote object has acquired a primary reference.
     */

        // Binder處理請求時,發現目標進程或目標線程已經死亡,通知源進程
        BR_DEAD_REPLY = _IO('r', 5),
    /*
     * The target of the last transaction (either a bcTRANSACTION or
     * a bcATTEMPT_ACQUIRE) is no longer with us.  No parameters.
     */

        // Binder 接收到BC_TRANSACATION或BC_REPLY時,會返回 BR_TRANSACTION_COMPLETE通知源進程命令已經接收
        BR_TRANSACTION_COMPLETE = _IO('r', 6),
    /*
     * No parameters... always refers to the last transaction requested
     * (including replies).  Note that this will be sent even for
     * asynchronous transactions.
     */

        // 增加弱引用計數
        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),
    /*
     * void *:  ptr to binder
     * void *: cookie for binder
     */

         //當前不支持
        BR_ATTEMPT_ACQUIRE = _IOR('r', 11, struct binder_pri_ptr_cookie),
    /*
     * not currently supported
     * int: priority
     * void *: ptr to binder
     * void *: cookie for binder
     */

        // Binder通過源進程執行了一個空操作,用以可以替換為BR_SPAWN_LOOPER
        BR_NOOP = _IO('r', 12),
    /*
     * No parameters.  Do nothing and examine the next command.  It exists
     * primarily so that we can replace it with a BR_SPAWN_LOOPER command.
     */

         // Binder發現沒有足夠的線程處理請求時,會返回BR_SPAWN_LOOPER請求增加新的新城到Binder線程池中
        BR_SPAWN_LOOPER = _IO('r', 13),
    /*
     * No parameters.  The driver has determined that a process has no
     * threads waiting to service incoming transactions.  When a process
     * receives this command, it must spawn a new service thread and
     * register it via bcENTER_LOOPER.
     */

         // 當前暫不支持
        BR_FINISHED = _IO('r', 14),
    /*
     * not currently supported
     * stop threadpool thread
     */


        /** Binder檢測到Service組件死亡時,使用BR_DEAD_BINDER通知Client進程,Client請求
         *  注銷之前的死亡通知,Binder完成后,返回BR_CLEAR_DEATH_NOTIFACTION_DONE
         */
        // 告訴發送方對象已經死亡
        BR_DEAD_BINDER = _IOR('r', 15, binder_uintptr_t),

    /*
     * void *: cookie
     */
        //清理死亡通知
        BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, binder_uintptr_t),
    /*
     * void *: cookie
     */

         // 發生異常,通知源進程
        BR_FAILED_REPLY = _IO('r', 17),
    /*
     * The the last transaction (either a bcTRANSACTION or
     * a bcATTEMPT_ACQUIRE) failed (e.g. out of memory).  No parameters.
     */
};

14.5 Binder通信協議流程

單獨看上面的協議可能很難理解,這里我們以一次Binder請求為過程來詳細看一下Binder協議是如何通信的,就比較好理解了。這幅圖的說明如下:

  • 1 Binder是C/S架構的,通信過程牽涉到Client、Server以及Binder驅動三個角色
  • 2 Client對于Server的請求以及Server對于Client的回復都需要通過Binder驅動來中轉數據
  • 3 BC_XXX命令是進程發送給驅動命令
  • 4 BR_XXX命令是驅動發送給進程的命令
  • 5 整個通信過程由Binder驅動控制

5713484-ece60dd742daca84.png

PS:這里補充說明一下,通過上面的Binder協議的說明,我們看到,Binder協議的通信過程中,不僅僅是發送請求和接收數據這些命令。同時包括了對于引用計數的管理和對于死亡通知的管理(告知一方,通訊的另外一方已經死亡)。這個功能的流程和上述的功能大致一致。

15 結構體binder_ptr_cookie

binder_ptr_cookie 用來描述一個Binder實體對象或一個Service組件的死亡通知

15.1 代碼位置

位置在
Linux的binder.h 257行

15.2 代碼注釋

struct binder_ptr_cookie {
    binder_uintptr_t ptr;
    binder_uintptr_t cookie;
};
  • 當描述Binder實體對象:ptr,cookie見binder_node
  • 當描述死亡通知:ptr指向一個Binder引用對象的句柄值,cookie指向接收死亡通知的對象地址

16 總結

5713484-ce28e70308d4a24b.png

如果以結構體為標的來看整個Binder傳輸過程則如下:


5713484-ad87a4845fed2cb1.png

參考

Android跨進程通信IPC之7——Binder相關結構體簡介

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,283評論 6 530
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 97,947評論 3 413
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,094評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,485評論 1 308
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,268評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,817評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,906評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,039評論 0 285
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,551評論 1 331
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,502評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,662評論 1 366
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,188評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,907評論 3 345
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,304評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,563評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,255評論 3 389
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,637評論 2 370

推薦閱讀更多精彩內容