美文网首页Android高阶Android进程间通信
Android进程间通信之8——Binder相关结构体简介

Android进程间通信之8——Binder相关结构体简介

作者: Sophia_dd35 | 来源:发表于2018-08-14 14:18 被阅读102次

    后面就要讲解Binder源码了,会涉及到很多struct,因此这里先铺垫一下,说几个常用的数据结构,内容如下:

    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、总结

    一、结构体binder_work

    1、位置

    Linux的binder.c 240行

    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;  //描述工作项的类型
    };
    

    二、结构体binder_thread

    1、代码位置

    Linux的binder.c 368行

    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
    };
    

    三、结构体binder_stats

    binder_stats 代表的是Binder线程相关统计数据

    1、代码位置

    Linux的binder.c 173行

    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];
    };
    

    四、结构体binder_proc

    binder_proc 代表的是一个正在使用Binder进程通信的进程,binder_proc为管理其信息的记录体,当一个进程open /dev/binder 时,Binder驱动程序会为其创建一个binder_proc结构体,用以记录进程的所有相关信息,并把该结构体保存到一个全局的hash表中

    1、代码位置

    Linux的binder.c 322行

    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;
    };
    

    五、结构体binder_node

    binder_node 代表的是Binder实体对象,每一个service组件或者ServiceManager在Binder驱动程序中的描述,Binder驱动通过强引用和弱引用来维护其生命周期,通过node找到空间的Service对象.

    1、代码位置

    Linux的binder.c 252行

    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来描述。

    3、异步事物处理

    异步事务处理,目的在于为同步交互让路,避免长时间阻塞发送送端
    异步事务定义:(相对于同步事务)单向进程间通信要求,即不需要等待应答的进程间通信请求
    Binder驱动程序认为异步事务的优先级低于同步事务,则在同一时刻,一个Binder实体对象至多只有一个异步事物会得到处理。而同步事务则无此限制。
    Binder将事务保存在一个线程binder_thread的todo队列中,表示由该线程来处理该事务。每一个事务都关联Binder实体对象(union target),表示该事务的目标处理对象,表示要求该Binder实体对象对应的Service组件在制定线程中处理该事务,而如果Binder发现一个事务时异步事务,则会将其保存在目标Binder对象的async_todo的异步事务中等待处理

    六、结构体binder_ref

    binder_ref 代表的是Binder的引用对象,每一个Clinet组件在Binder驱动中都有一个Binder引用对象,用来描述它在内核中的状态。

    1、代码位置

    Linux的binder.c 281行

    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;
    };
    

    七、结构体binder_ref_death

    binder_ref_death 一个死亡通知的结构体
    我们知道Client组件无法控制它所引用的Service组件的生命周期,由于Service组件所在的进程可能意外崩溃。Client进程需要能够在它所引用的Service组件死亡时获的通知,进而进行响应。则Client进程就需要向Binder驱动注册一个用来接收死亡通知的对象地址(这里的cookie)

    1、代码位置

    Linux的binder.c 276行

    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
    • 2、Client进程向Binder驱动注册一个死亡接收通知时,如果它所引用的Service组件已经死亡,Binder会立即发送通知给Client进程。这种情况binder_work类型为BINDER_WORK_DEAD_BINDER
    • 3、当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中

    八、结构体binder_state

    binder_state 代表着binder设备文件的状态

    1、代码位置

    /frameworks/native/cmds/servicemanager/binder.c 89行

    2、代码注释

    struct binder_state
    {
        //打开 /dev/binder之后得到的文件描述符
        int fd;
        //mmap将设备文件映射到本地进程的地址空间,映射后的到地址空间中,映射后得到的地址空间地址,及大小。
        void *mapped;
        // 分配内存的大小,默认是128K
        size_t mapsize;
    };
    

    九、结构体binder_buffer

    binder_buffer 内核缓冲区,用以在进程间传递数据。binder驱动程序管理这个内存映射地址空间方法,即管理buffer~(buffer+buffer_size)这段地址空间的,这个地址空间被划分为一段一段来管理,每一段是结构体struct binder_buffer来描述。每一个binder_buffer通过其成员entry从低到高地址连入到struct binder_proc中的buffers表示链表中去

    1、代码位置

    Linux的binder.c 298行

    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;
    };
    

    十、结构体binder_transaction

    binder_transaction 描述Binder进程中通信过程,这个过程称为一个transaction(事务),用以中转请求和返回结果,并保存接收和要发送的进程信息

    1、代码位置

    Linux的binder.c 383行

    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;
    
    };
    

    十一、结构体binder_transaction_data

    binder_transaction_data 代表进程间通信所传输的数据:Binder对象的传递时通过binder_transaction_data来实现的,即Binder对象实际是封装在binder_transaction_data结构体中

    1、代码位置

    Linux的binder.h 217行

    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结构体中。

    十二、结构体transaction_flags

    transaction_flags 描述传输方式,比如同步或者异步等

    1、代码位置

    Linux的binder.h 210行

    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 */
    };
    

    十三、结构体flat_binder_object

    flat_binder_object 描述进程中通信过程中传递的Binder实体/引用对象或文件描述符

    1、代码位置

    Linux的binder.h 68行

    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表示该引用的句柄值。

    十四、结构体binder_write_read

    binder_write_read 描述进程间通信过程中所传输的数据

    1、代码位置

    Linux的binder.h 165行

    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;
    };
    

    里面涉及到两个协议,我们就在这里详细讲解下

    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结构体。其中前者用于翻译和解析将要被处理的事务数据,而后者则是事务处理完成之后对返回"结果数据"的操作命令。

    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.
         */
    };
    

    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驱动控制
    Binder通信过程.png

    PS:这里补充说明一下,通过上面的Binder协议的说明,我们看到,Binder协议的通信过程中,不仅仅是发送请求和接收数据这些命令。同时包括了对于引用计数的管理和对于死亡通知的管理(告知一方,通讯的另外一方已经死亡)。这个功能的流程和上述的功能大致一致。

    十五、结构体binder_ptr_cookie

    binder_ptr_cookie 用来描述一个Binder实体对象或一个Service组件的死亡通知

    1、代码位置

    Linux的binder.h 257行

    2、代码注释

    struct binder_ptr_cookie {
        binder_uintptr_t ptr;
        binder_uintptr_t cookie;
    };
    
    • 当描述Binder实体对象:ptr,cookie见binder_node
    • 当描述死亡通知:ptr指向一个Binder引用对象的句柄值,cookie指向接收死亡通知的对象地址

    十六、总结

    结构体就是这样的



    如果以结构体为标的来看整个Binder传输过程则如下:


    结构体为标的.png

    参考文献

    Android跨进程通信IPC之7——Binder相关结构体简介

    相关文章

      网友评论

        本文标题:Android进程间通信之8——Binder相关结构体简介

        本文链接:https://www.haomeiwen.com/subject/ikbybftx.html