• [Binder深入学习一]Binder驱动——基础数据结构


      具体代码路径:

        kernel/drivers/staging/android/binder.c

        kernel/drivers/staging/android/binder.h

    /*
     * binder_work是用来描述待处理的工作项,这些工作项可能属于同一个进程,也可能属于一个进程中的某一个线程
    */
    struct binder_work {
        struct list_head entry;    // 将之以链表的形式嵌入到其他结构体中
        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通信的过程就是Client和Service之间的通信,
    * 在用户空间的 Service 组件在内核空间使用 Binder实体对象 binder_node 来描述,
    * 在用户空间的 Client 组件在内核空间使用 Binder实体对象的引用 binder_ref 来描述, * Binder实体对象 ——> binder_node * Binder实体对象的引用 ——> binder_ref
    */ struct binder_node { int debug_id; struct binder_work work; union { struct rb_node rb_node; struct hlist_node dead_node; }; struct binder_proc *proc; struct hlist_head refs; int internal_strong_refs; int local_weak_refs; int local_strong_refs; binder_uintptr_t ptr; binder_uintptr_t cookie; unsigned has_strong_ref:1; unsigned pending_strong_ref:1; unsigned has_weak_ref:1; unsigned pending_weak_ref:1; unsigned has_async_transaction:1; unsigned accept_fds:1; unsigned min_priority:8; struct list_head async_todo; }; /*
    * debug_id : 标志Binder实体对象的身份,帮助调试。
    * proc : 指向一个Binder实体对象的宿主进程。Binder驱动会使用binder_proc来描述使用Binder通信的进程。
    * 一个使用Binder通信的进程可能会有多个Service或Client组件,而每个Service组件都对应一个binder_node,
    所以binder_proc需要维护所有的Binder实体对象,这是通过一棵红黑树来实现的。成员遍历rb_node和dead_node即完成这一工作
    * rb_node : 红黑树的一个节点,连入binder_proc中维护所有属于该进程的Binder实体对象的红黑树中 * dead_node : 如果一个Binder实体对象的宿主进程已经死亡,那么这个Binder实体对象就会通过它的成员变量dead_node保存在一个全局的hash列表中 * refs : 一个Binder实体对象可能被多个Client组件引用,那么Binder实体对象就必须维护引用它的Binder引用对象 ,同样使用hash列表维护 * internal_strong_refs : 强引用计数 * local_weak_refs : 弱引用计数 * local_strong_refs : 强引用计数 * 当一个Binder实体对象请求一个Service组件来执行某一操作时,会增加该Service组件的强引用计数或弱引用计数,
    * 相应的,Binder实体对象会将其成员变量has_strong_ref和has_weak_ref的值设置为1。
    * 当一个Service组件完成一个Binder实体对象所请求的操作后,Binder实体对象就会请求减少该Service组件的强引用计数或弱引用计数。
    * Binder实体对象在请求一个Service组件增加或减少强引用计数或弱引用计数的过程中,会将其成员变量pending_strong_reg或pending_weak_ref设置为1;
    * 而当Service组件增加或减少了强引用计数或弱引用计数后,Binder实体对象就会将这两个成员变量的值设置为0.
    * ptr : 指向用户空间Service组件内部的一个引用计数(类型为weakref_impl)的地址
    * cookie : 指向用户空间Service组件的地址
    *      所以ptr和cookie指向用户空间地址,它们用来描述用户空间的一个Service组件
    * has_async_transaction : 描述一个Binder实体对象是否是在处理一个异步事务,如果是置为1,否则为0。
    * 一般情况,Binder驱动程序都是将一个事务保存在一个线程的todo队列中,表示由该线程来处理该事务。每一个事务都关联这一个Binder实体对象,
    * 表示该事务的目标处理对象,即要求与该Binder实体对象对应的Service组件在指定的线程中处理该事务。然而,当Binder驱动程序发现一个事务
    * 是异步事务时,就会将它保存在目标Binder实体的一个异步事务队列中,这个异步事务队列就是由该目标Binder实体对象的async_todo来描述的。
    * 异步事务的定义是那些单向的进程间通信请求,即不需要等待应答的进程间通信请求。由于不需要等待应答,所以Binder驱动程序认为异步事务的优先级
    *                 低于同步事务,具体表现为:同一时刻,一个Binder实体对象的所有异步事务至多只有一个会得到处理,其余的异步事务等待在异步事务队列中。
    *    当一个Binder实体对象的引用技术由0变为1或者由1变为0,Binder驱动程序就会请求相应的Service组件增加或减小其引用计数。这时候Binder Driver就会将该引用计数修改操作
    * 封装成一个类型为 binder_node 的工作项,即将一个Binder实体对象的成员变量work的值设置为BINDER_WORK_NODE,并且将它添加到相应进程的todo队列中等待处理。
    * min_priority : 一个Binder实体对象在处理一个来自Client进程的请求时,它所要求的处理线程即Server进程中的一个线程,应该具备的最小线程优先级,这样就保证了该Binder实体
    * 对象对应的Service组件可以在一个具有一定优先级的线程中处理一个来自Client进程的通信请求.一个Service组件在将自己注册到Binder Driver时可以指定该值.
    * accept_fds : 描述一个Binder实体对象是否可以接收包含有文件描述符的进程间通信数据。
    *        当一个进程向另一个进程发送的数据含有文件描述符时,Binder Driver就会自动在目标进程中打开一个相同的文件。
    */
    /*
     * 描述一个Service组件的死亡接收通知。
     * 一个Service组件在被其他Client进程引用时,它是不可以销毁的。然而,Client进程是无法控制它所引用的Service组件的生命周期的,Service组件是可能会意外奔溃的。
    * 所以Client进程需要获取它所引用Service的死亡消息,以便做出相应的处理,这样Client进程就需要将一个用来接收死亡通知的对象的地址注册到Binder Driver中
    */ struct binder_ref_death { struct binder_work work; binder_uintptr_t cookie; };
    /*
    * cookie : 用来保存负责接收死亡通知的对象的地址
    * work : 可能取值为 BINDER_WORK_DEAD_BINDER,BINDER_WORK_CLEAR_DEATH_NOTIFICATION,BINDER_WORK_DEAD_BINDER_AND_CLEAR,标志死亡通知类型
    */

      Binder驱动程序要想一个Client进程发送一个Service组件的死亡通知时,会将binder_ref_death结构体封装成一个工作项,并且根据实际情况来设置结构体的成员变量work的值,

    最后,奖这个工作项加入到Client进程的todo队列中去等待处理。

      在下面两种情况下,Binder驱动程序会向Client进程发送一个Service组件的死亡通知:

      (1) 当Binder驱动程序监测到一个Service组件死亡时,它就会找到该Service组件对应的Binder实体对象,然后通过Binder实体对象的成员变了refs就可以找到所有引用了它的

    Client进程,最后就找到这些Client进程所注册的死亡接收通知,即一个binder_ref_death结构体。这时候Binder驱动程序将死亡通知的类型设置为BINDER_DEAD_BINDER.

      (2) 当Client进程在想Binder驱动程序注册一个死亡通知时,如果它所引用的Service组件已经死亡,那么Binder驱动程序就会马上发送一个死亡通知到Client进程。在这种情况下,

    Binder驱动程序会将死亡通知的类型设置为BINDER_WORK_DEAD_BINDER.

      另外,当Client进程向Binder驱动程序注销一个死亡通知时,Binder驱动程序也会向该Client进程的todo队列发送一个类型为 binder_ref_death 的工作项,用来表示注销结果。这时又需要分为两种情况:

      (1) 如果Client进程在注销一个死亡通知时,相应的Service组建还没有死亡,那么Binder驱动程序就会找到之间注册的一个binder_ref_death结构体,并且将它的work类型修改为BINDER_WORK_CLEAR_DEATH_NOTIFICATION,然后再将该binder_ref_death结构体封装成一个工作项添加到Client进程的todo队列中去等待处理。

      (2) 如果Client进程在注销一个死亡通知时,相应的的Service组件已经死亡,那么Binder驱动程序就会找到之间注册的一个binder_ref_death结构体,并且将它的work类型修改为BINDER_WORK_DEAD_BINDER_AND_CLIEAR,然后再将该binder_ref_death结构体封装成一个工作项添加到该Client进程的todo队列中去等待处理。

    /*
     * binder_ref描述一个Binder引用对象
     * 每一个Client组件在Binder驱动中都对应一个Binder引用对象,用它来描述它在内核中的状态
    */
    struct binder_ref {
        /* Lookups needed: */
        /*   node + proc => ref (transaction) */
        /*   desc + proc => ref (transaction, inc/dec ref) */
        /*   node => refs + procs (proc exit) */
        int debug_id;
        struct rb_node rb_node_desc;
        struct rb_node rb_node_node;
        struct hlist_node node_entry;
        struct binder_proc *proc;
        struct binder_node *node;
        uint32_t desc;
        int strong;
        int weak;
        struct binder_ref_death *death;
    };
    /*
    * debug_id : 标志引用对象的身份,帮助调试
    * node : Binder引用对象所引用的Binder实体对象 * node_entry : 每一个Binder实体对象都使用一个hash列表来保存所有引用它的引用对象,node_entry就是Binder实体对象hash列表的节点
    * desc : 句柄值,描述符。用来描述一个Binder引用对象。在Client进程的用户空间中,一个Binder引用对象是使用一个句柄值来描述的。
    * 因此,当Client进程的用户空间通过Binder驱动访问Service组件时,需要指定一个句柄值,Binder驱动程序可以通过该句柄值找到对于的Binder引用对象,然后根据该Binder引用对象的node成员找到对应的Binder实体对象,最后就可以通过实体对象访问Service组件
    * proc : 指向Binder引用对象的宿主进程。一个宿主进程使用两棵红黑树保存它内部所有的Binder引用对象
    * rb_node_desc : 以句柄值为关键字保存Binder引用对象到宿主进程中的红黑树上
    * rb_node_node : 以对应的Binder实体对象的地址作为关键字保存这些Binder引用对象到宿主进程中的红黑树上
    * strong : 强引用计数
    * weak : 弱引用计数
    * death : 死亡接收通知,指向Client进程注册死亡接收通知时创建的binder_ref_death结构体
    */
    /*
     * 描述一个内核缓冲区,用来完成在进程间的数据传输
     * 每一个使用Binder进程间通信机制的进程在Binder驱动程序中都会保存一个内核缓冲区列表,用来保存Binder驱动程序为它所分配的内核缓冲区,成员变量entry就是这个缓冲区链表的一个节点
    */
    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;
        unsigned allow_user_free:1;
        unsigned async_transaction:1;
        unsigned debug_id:29;
    
        struct binder_transaction *transaction;
    
        struct binder_node *target_node;
        size_t data_size;
        size_t offsets_size;
        uint8_t data[0];
    };
    /*
     * entry    : 宿主进程内核缓冲区链表节点
    * rb_node : 宿主进程会使用两棵红黑树分别保存那些正在使用的缓冲区和空闲的缓冲区,rb_node就是这两个红黑树上的节点
    * free : 如果该缓冲区是空闲的,该变量就为1
    * transaction : 描述内核缓冲区正在交给哪一个事务使用
    * targe_node : 描述内核缓冲区正在交给哪一个Binder实体对象使用
    * Binder驱动程序使用binder_transaction结构体描述一个事务,每一个事务都关联一个目标Binder实体对象。
    * Binder驱动程序将事务数据保存在一个内核缓冲区中,然后将它交给目标Binder实体对象处理,而目标Binder实体对象再将该内核缓冲区的内容交给Service组件处理。
    * Service组件在处理完事务后,如果发现传递给它的内核缓冲区的成员变量 allow_user_free 的值为1,那么该Service组件就会请求Binder驱动程序释放该内核缓冲区. * async_transaction : 如果内核缓冲区关联的是一个异步事务,则设置该值为1.
    * Binder驱动程序限制了分配给异步事务的内核缓冲区的大小,这样做的目的是为了保证同步事务可以优先得到内核缓冲区,以便快速的对同步事务进行处理
    * data : 指向一块大小可变的数据缓冲区,它是用来保存真正的通信数据
    * 数据缓冲区保存的数据可分为两类,一种是普通数据,一种是Binder对象。Binder驱动程序不关心数据缓冲区的普通数据,但必须知道里面的Binder对象,因为它需要根据它们
    * 来维护内核中的Binder实体对象和Binder引用对象的生命周期。在数据缓冲区的后面,有一个偏移数组,它记录了数据缓冲区中每一个Binder对象在数据缓冲区中的位置。
    * 偏移数组的大小保存在成员变量 offsets_size 中,而数据缓冲区的大小保存在变量 data_size 中。
    * data_size : 如上所述
    * offset_size : 如上所述
    */
    /*
     * 描述一个正在使用Binder进程间通信机制的进程
    */
    struct binder_proc {
        struct hlist_node proc_node;
        struct rb_root threads;
        struct rb_root nodes;
        struct rb_root refs_by_desc;
        struct rb_root refs_by_node;
        int pid;
        struct vm_area_struct *vma;
        struct mm_struct *vma_vm_mm;
        struct task_struct *tsk;
        struct files_struct *files;
        struct hlist_node deferred_work_node;
        int deferred_work;
        void *buffer;
        ptrdiff_t user_buffer_offset;
    
        struct list_head buffers;
        struct rb_root free_buffers;
        struct rb_root allocated_buffers;
        size_t free_async_space;
    
        struct page **pages;
        size_t buffer_size;
        uint32_t buffer_free;
        struct list_head todo;
        wait_queue_head_t wait;
        struct binder_stats stats;
        struct list_head delivered_death;
        int max_threads;
        int requested_threads;
        int requested_threads_started;
        int ready_threads;
        long default_priority;
        struct dentry *debugfs_entry;
    };
    /*
     * proc_node : Binder驱动程序会将所有 binder_proc 维护在一个全局hash列表中
    * pid : 进组组ID
    * tsk : 任务控制块
    * files : 打开文件结构体数组
    *   进程打开了设备文件 /dev/binder 后,还必须调用mmap将它映射到进程地址空间来,实际上是请求Binder驱动程序为它分配一个内核缓冲区,以便可以用来在进程间传递数据。
    * Binder驱动程序分配的内核缓冲区的大小保存在成员变量 buffer_size 中。这些内核缓冲区有两个地址,其中一个是内核空间地址,另一个是用户空间地址。内核空间地址是在Binder
    * 驱动程序内部使用的,保存在成员变量 buffer 中,而用户空间地址是在应用程序进程内部使用的,保存在成员变量 vma 中。这两个地址相差一个固定的值,保存在成员变量
    * user_buffer_offset 中。这样,给定一个用户空间地址或者内核空间地址,Binder驱动程序就可以计算出另一个地址。
    *   成员变量 buffer 指向的是一块大的内核缓冲区,Binder驱动程序为了方便的对它进行管理,会将它划分才若干个小块。这些小块的内核缓冲区就是使用 binder_buffer 来描述的,
    * 它们保存在一个列表中,按照地址值从小到大的顺序来排列。成员变量 buffers 指向的便是该列表的头部。列表中的小块内核缓冲区有正在使用的,即已经分配了的物理页面;有空闲的,即还没有
    * 分配的物理页面,它们分别组织在两个红黑树中,其中,前者保存在成员变量 allocated_buffers 所描述的红黑树中,而后者保存在成员变量 free_buffers 所描述的红黑树中。此外,成员变量
    * buffer_free 保存了空闲内核缓冲区的大小,而成员变量 free_async_space 保存了当前可以用来保存异步事务数据的内核缓冲区的大小。
    *   前面已提到,每一个使用了Binder进程间通信机制的进程都有一个线程池,用来处理进程间通信请求,这个Binder线程池是有Binder驱动程序来维护的。结构体 binder_proc 的成员变量
    * threads 是一个红黑树的根节点,它以线程ID作为关键字来组织一个进程的Binder线程池。进程可以调用函数 ioctl 将一个线程注册到Binder驱动程序中,同时,当前进程没有足够的空闲线程
    * 来处理进程间通信请求时,Binder驱动程序也可以主动要求进程注册更多的线程到Binder线程池中。Binder驱动程序最多可以主动注册请求进程注册的线程的数量保存在成员变量 max_threads
    * 中,而成员变量 ready_threads 表示当前进程的空闲Binder线程数。
    * 注意:
    *    成员变量 max_threads 并不表示Binder线程池中的最大线程数,进程本身可以主动注册任意数目的线程到Binder线程池中。Binder驱动程序每一次主动请求进程注册一个线程时,都会
    * 将成员变量 request_threads 的值加1;而当进程响应这个请求后,Binder驱动程序就会将成员变量 requested_threads 的值减1,而且将成员变量 requested_threads_started 的
    * 值加1,表示Binder驱动程序已经主动请求进程注册了多少个线程到Binder线程池中。
    *   当进程收到一个进程间通信请求时,Binder驱动程序就将该请求封装成一个工作项,并且加入到进程的待处理工作项队列中,这个队列使用成员变量 todo 来描述。Binder线程池中的空闲
    * Binder线程会睡眠在由成员变量 wait 所描述的一个等待队列中,当他们的宿主进程的待处理工作项队列增加了新的工作项后,Binder驱动程序就会唤醒这些线程,以便它们可以去处理新的
    * 工作项。成员变量 default_priority 的值被初始化为进程的优先级。档一个线程处理一个工作项时,它的线程优先级有可能被设置为其宿主进程的优先级,即设置为成员变量 default_priority
    * 的值,这是由于线程是代表其宿主进程来处理一个工作项的。线程处理一个工作项时的优先级还会收到其他因素的影响,后面在详述。
    *   一个进程内部包含了一系列的Binder实体对象和Binder引用对象,进程使用三个红黑树来组织它们,其中,成员变量 nodes 是来组织Binder实体对象的,它以Binder实体对象的成员变量ptr
    * 作为关键字;而成员变量 refs_by_desc 和 refs_by_node 所描述的红黑树是用来组织Binder引用对象的,前者以Binder引用对象的成员变量desc为关键字,而后者以Binder引用对象的成员
    * 变量 node 作为关键字。
    *   defered_work_node是一个hash列表,用来保存进程可以延迟的工作项。这些延迟的工作项有三种类型,如下所示
    */
    enum binder_deferred_state {
        BINDER_DEFERRED_PUT_FILES    = 0x01,
        BINDER_DEFERRED_FLUSH        = 0x02,
        BINDER_DEFERRED_RELEASE      = 0x04,
    };
    /*
    *   Binder驱动程序为进程分配内核缓冲区时,会为这个缓冲区创建一个文件描述符,进程可以通过这个文件描述符将该内核缓冲区映射到自己的地址空间。当进程不在需要使用Binder进程间
    * 通信机制时,它就会通知Binder驱动程序关闭文件描述符,并且释放之前分配的缓冲区。由于这不是一个马上就需要完成的操作,因此,Binder驱动程序就会创建一个BINDER_DEFFERED_PUT_FILES
    * 类型的工作项来延迟执行该操作。
    *   前面已提到,Binder线程池中的空闲Binder线程是睡眠在一个等待队列中的,进程可以通过调用函数 flush 来唤醒这些线程,以便他们可以检查是否有新的工作项需要处理。这时候Binder
    * 驱动程序就会创建一个BINDER_DEFFERRED_FLUSH类型的工作项,以便可以延迟指向唤醒空闲Binder线程的操作。
    *   当进程不再使用Binder进程间通信机制时,就会调用close函数来关闭设备文件/dev/binder,这时Binder驱动程序就会释放它分配的资源,例如,释放 binder_proc, binder_node,
    * binder_ref等。由于资源的释放操作是一个比较耗时的操作,因此,Binder驱动程序会创建一个BINDER_DEFFERRED_RELEASE类型的事务来延迟它们。
    *   Binder驱动程序将所有的延迟执行的工作项保存在一个hash列表中。如果一个进程有延迟执行的工作项,那么成员变量deferred_work_node就刚好是该hash列表中的一个节点,并且使用成员
    * 变量 defferred_work 来描述该延迟工作项的具体类型。
    *   当一个进程所使用的Service组件死亡时,Binder驱动程序就会向该进程发送一个死亡通知。这个正在发生的死亡通知被封装成一个类型为BINDER_WORK_DEAD_BINDER或者BINDER_WORK_DEAD_
    * AND_CLEAR的工作项,并且保存在由成员变量 delivered_death 所描述的一个队列中,表示Binder驱动程序正在向进程发送死亡通知。当进程接收到这个死亡通知后,它便会通知Binder驱动程序,这
    * 时候Binder驱动程序就会将对应的工作项从成员变量 delivered_death 所描述的队列中删除。
    *   最后,成员变量 stats 是用来统计进程数据的,例如,进程接收到的进程间通信请求的次数。
    */
    /*
    * 描述Binder线程池中的一个线程
    */
    struct
    binder_thread { struct binder_proc *proc; struct rb_node rb_node; int pid; int looper; struct binder_transaction *transaction_stack; struct list_head todo; 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 */ wait_queue_head_t wait; struct binder_stats stats; };
    /*
    * proc : 指向其宿主进程
    * rb_node : 宿主进程结构体binder_proc是使用红黑树来组织其Binder线程池中的线程
    * pid : Binder线程ID
    * looper : Binder线程状态,其取值如下:

    */
    enum {
        BINDER_LOOPER_STATE_REGISTERED  = 0x01,
        BINDER_LOOPER_STATE_ENTERED     = 0x02,
        BINDER_LOOPER_STATE_EXITED      = 0x04,
        BINDER_LOOPER_STATE_INVALID     = 0x08,
        BINDER_LOOPER_STATE_WAITING     = 0x10,
        BINDER_LOOPER_STATE_NEED_RETURN = 0x20
    };
    /*
    *   一个线程注册到Binder驱动程序时,Binder驱动程序会为它创建一个binder_thread结构体,并且将它的状态初始化为 BINDER_LOOPER_STATE_NEED_RETURN,表示该线程需要马上返回
    * 到用户空间。由于一个线程在注册为Binder线程时可能还没有准备好去处理进程间通信请求时,因此,最好返回到用户空间去做准备工作。此外,当进程调用函数 flush 来刷新它的Binder线程池
    * 时,Binder线程池中的线程状态也会被重置为BINDER_LOOPER_STATE_NEED_RETURN.
    *   一个线程注册到Binder驱动程序后,它接着就会通过BC_REGISTER_LOOPER或者BC_ENTER_LOOPER协议来通知Binder驱动程序,它可以处理进程间通信请求了,这时候Binder驱动程序
    * 就会将它的状态设置为BINDER_LOOPER_STATE_REGISTERED或者BINDER_LOOPER_STATE_ENTERED.如果一个线程是应用程序主动注册的,那么它就通过BC_ENTER_LOOPER协议来通知Binder
    * 驱动程序,它已经准备就绪处理进程间通信请求了;如果一个线程是Binder驱动程序请求创建的,那么它就通过BC_REGISTER_LOOPER协议来通知Binder驱动程序,这时候Binder驱动程序就会
    * 增加它所请求进程创建的Binder线程数目。
    *   当一个Binder线程处于空闲状态时,Binder驱动程序就会把它的状态设置为BINDER_LOOPER_STATE_WAITING;而当一个Binder线程退出时,它就会通过BC_EXIT_LOOPER协议来通知Binder
    * 驱动程序,这时候Binder驱动程序就会将它的状态设置为BINDER_LOOPER_STATE_EXITED.在异常情况下,一个Binder线程的状态会被设置为BINDER_LOOPER_STATE_INVALID,例如,当该线程
    * 已经处于BINDER_LOOPER_STATE_REGITERED状态时,如果它又再次通过BC_ENTER_LOOPER协议来通知Binder驱动程序它已经准备就绪,那么Binder驱动程序就会将它的状态设置为BINDER_LOOPER_STATE_INVALID.
    *   当一个来自Client进程的请求指定要由某一个Binder线程处理时,这个请求就会加入到相应的binder_thread结构体的成员变量todo所表示的队列中,并且唤醒这个线程来处理。
    *   当Binder驱动程序决定将一个事务交给一个Binder线程处理时,它就会将该事务封装成一个 binder_transaction 结构体,并且将它添加到线程结构体 binder_thread 的成员变量 transaction_stack
    * 所描述的一个事务堆栈中。
    *   一个Binder线程在处理一个事务时,如果出现了异常情况,那么Binder驱动程序就会将相应的错误码保存在 return_error 和 return_error2 中,这时候线程就会将错误码返回给用户空间应用程序处理。

    *   最后,stats是用来统计Binder线程数据的。例如,Binder线程接收到的进程间通信请求的次数。
    */

    /*
     * 描述进程间通信过程,这个过程又称为一个事物.
    */
    struct binder_transaction {
        int debug_id;
        struct binder_work work;
        struct binder_thread *from;
        struct binder_transaction *from_parent;
        struct binder_proc *to_proc;
        struct binder_thread *to_thread;
        struct binder_transaction *to_parent;
        unsigned need_reply:1;
        /* unsigned is_dead:1; */    /* not used at the moment */
    
        struct binder_buffer *buffer;
        unsigned int    code;
        unsigned int    flags;
        long    priority;
        long    saved_priority;
        kuid_t    sender_euid;
    };
    /*
     * debug_id   : 标志一个事务结构体身份,帮助调试
    * need_reply : 区分异步事务还是同步事务。同步事务需要等待对方回复,所以need_reply的值为1
    * from : 发起事务的线程,即源线程
    * to_proc : 负责处理该事务的进程,即目标进程
    * to_thread : 负责处理该事务的线程,即目标线程
    *        当Binder驱动程序为目标进程或者目标线程创建一个事务时,就会将该事务的成员变量 work 的值设置为 BINDER_WORK_TRANSACTION,并且将它添加到目标进程或者目标线程的todo队列中等待处理
    * priority : 源线程的优先级
    * sender_euid : 源线程的用户ID
    *         一个线程在处理一个事务时,Binder驱动程序需要修改它的线程优先级,以便满足源线程和目标Service组件的要求。Binder驱动程序在修改一个线程的优先级之前,会将它原来的
    *       线程优先级保存在 saved_priority 中,以便处理完该事务后可以恢复到原来的优先级。
    *         目标线程在处理一个事务时,它的线程优先级不能低于目标Service组件所要求的线程优先级,而且也不能低于源线程的优先级。这时候Binder驱动程序就会将这二者中较大值设置为
    *      目标线程的优先级。
    * buffer : 指向Binder驱动程序为该事务分配的一块缓冲区,里面保存率进程间通信的数据
    * code : 直接从进程间通信数据中拷贝过来的
    * flags :
    直接从进程间通信数据中拷贝过来的
    * from_parent : 一个事务所依赖的另一个事务
    * to_parent : 目标线程下一个需要处理的事务

    */
    /*
    * 假设线程A发起了一个事务T1,需要由线程B来处理;线程B在处理事务T1时,又需要线程C先处理事务T2;线程C在处理事务T2时,又需要线程A处理事务T3.这样,事务T1依赖于事务T2,而事务T2又依赖于事务T3,
    *   T2->from_parent = T1;
    *   T3->from_parent = T2;
    *   对于线程A累说,它需要处理的事务有两个,分别是T1和T3,它首先要处理事务T3,然后才能处理事务T1,因此,事务T1和T3的关系如下:
    *     T3->to_parent = T1;
    *   考虑这样一个场景:
    如果线程C在发起事务T3给线程A所属的进程来处理时,Binder驱动程序选择了该进程的另外一个线程D来处理该事务,这时候会出现什么情况呢?
    * 这时候线程A就会处于空闲等待状态,什么也不能做,因为它必须要等线程D处理完成事务T3后,它才可以继续执行事务T1。在这种情况下,与其让线程A闲着,还不如把事务T3交给它来处理,
    * 这样线程D就可以去处理其他事务,提高了进程的并发性。
    *   
    现在,关键的问题又来了——Binder驱动程序在分发事务T3给目标进程处理时,它是如何知道线程A属于目标进程,并且正在等待事务T3的处理结果的?
    *

    *   当线程C在处理事务T2时,就会将事务T2放在其事务堆栈transaction_stack的最前端。这样当线程C发起事务T3给线程A所属的进程处理时,
    * Binder驱动程序就可以沿着线程C的事务堆栈transaction_stack向下遍历,即沿着事务T2的成员变量from_parent向下遍历,最后就会发现事务T3的目标进程等于事务T1的目标进程,
    * 并且事务T1是由线程A发起来的,这时候它就知道线程A正在等待事务T3的处理结果了。
    */

      关于这点,不是很理解!!

      本文摘自《Android系统源码情景分析》——罗升阳

    直接从进程间通信数据中拷贝过来的
  • 相关阅读:
    Displaying XML in a Swing JTree
    jdom解析xml
    How to display XML in a JTree using JDOM
    关于XML文档和JAVA中的JTree之间如何转换的问题
    java swing 学习
    硅谷创业教父Paul Graham:如何获得创业idea
    如何成为一位优秀的创业CEO
    硅谷归来7点分享:创业者,做你自己
    The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 C Buy Watermelon
    The Preliminary Contest for ICPC Asia Xuzhou 2019 徐州网络赛 B so easy
  • 原文地址:https://www.cnblogs.com/ronnydm/p/6750590.html
Copyright © 2020-2023  润新知