内容纲要

Binder

状态:仅分析了binder,且Binder源码分析未完成

原理

https://blog.csdn.net/carson_ho/article/details/73560642

Binder机制,CS通信方式。

同一个Server进程可以同时运行多个组件来向Client进程提供服务,这些组件成为Service组件,同时,同一个Client进程也可以同时向多个Service组件请求服务,每一个请求都对应有一个Client组件(或称为Service代理对象)。

Binder进程间通信机制的每一个Server进程和Client进程都维护一个Binder线程池来处理进程间的通信请求,因此Server进程和Client进程可以并发地提供和访问服务。

通信要依靠运行在内核空间的Binder驱动程序来进行。

Binder驱动程序向用户空间暴露了一个设备文件/dev/binder,使得应用程序进程可以间接地通过它来建立通信通道。

Service组件在启动时,会将自己注册到一个Service Manager组件中,以便Client组件可以通过Service Manager组件找到它。

image-20211008212820107

image-20211010195258070

image-20211010195515474

image-20211010195641261

示意图

image-20211010200133822

image-20211010200441661

1次用户层到内核层的数据发送,通过映射传递信息实现跨进程通信

示意图

注册服务:Server进程向Binder驱动发起服务注册请求,Binder转发给Service Manager进程,Service Manager添加该Server进程

获取服务:Client进程向Binder驱动发起获取服务的请求,传递要获取的服务名称,Binder转发给Service Manager,Service Manager查找该Server返回给Client

image-20211010201851043

Binder进程间通信应用实例

https://github.com/yocosqamaq/Binder_demo

一些问题

1)报错buiid-tools的问题,但是通过project structure修改也没改正,原因是每个模块都有build.gradle,一个build包错误,所有的都需要修改。同理manifest.xml文件如此,sdk12以上需要加上android:exported

2)绑定组件,无法setText,一运行就闪退

位置绑定错误,在绑定layout前绑定组件,会指向空指针

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.button);
mShowText = (TextView) findViewById(R.id.textView);

binder源码分析

资源来源

Binder source code

知识点来源《Android系统源代码分析》

struct binder_work

// 用来描述待处理的工作项,这些工作项可能属于一个进程或者线程。
struct binder_work {
    // entry:   用来将该结构体嵌入到一个宿主结构中
    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:    用来描述工作项的类型
    // 根据type值, 可以判断出binder_work结构体嵌入到了什么类型的宿主结构
    } type;
};

struct binder_node

// 用来描述一个Binder实体对象
// 每一个Service组件在Binder驱动程序中都对应着一个Binder实体对象, 用来描述它在内核中的状态
// Binder驱动程序通过强引用计数和弱引用计数来维护它们的生命周期
struct binder_node {
    // debug_id: 来用标志一个Binder实体对象的身体,用来调试用的
    int debug_id;
    // 当一个Binder实体对象的引用计数0->1或者1->0时,Binder驱动程序会请求相应的Service增加或
    // 减少其引用计数; 这时,Binder驱动会将该引用计数修改操作封装成binder_node的工作项,work变量
    // 的值将被设为BINDER_WORK_NODE并添加到相应的todo队列中等待处理
    struct binder_work work;
    union {
        // rb_node:     红黑树中的一个节点
        struct rb_node rb_node;
        // dead_node:   如果宿主进程已经死亡,dead_node将被保存在一个全局hash列表中
        struct hlist_node dead_node;
    };
    // proc:        指向一个Binder实体对象的宿主进程
    // [这些宿主进程通过一个binder_proc结构体来描述,宿主进程使用一个红黑树来维护它内部所有的Binder实体对象]
    struct binder_proc *proc;
    // refs:        binder_ref hash列表,通过这个变量可以知道哪些Client组件引用了同一个Binder实体对象
    struct hlist_head refs;
    // internal_strong_refs/local_strong_refs: Binder实体对象强引用计数
    int internal_strong_refs;
    // local_weak_refs: 弱引用计数
    int local_weak_refs;
    int local_strong_refs;
    // ptr:         指向用户空间中的Service组件内部的引用计数对象的地址
    void __user *ptr;
    // cookie:      指向用户空间中的Service组件地址
    void __user *cookie;
    unsigned has_strong_ref : 1;
    // pending_strong_ref/pending_weak_ref: 正在增加或减少引用计数会被设置为1
    unsigned pending_strong_ref : 1;
    // has_strong_ref/has_weak_ref: 当Binder实体对象请求Service组件执行操作时会被设置为1
    unsigned has_weak_ref : 1;
    unsigned pending_weak_ref : 1;
    // has_async_transaction: 是否在执行异步事务
    // [Binder驱动程序会将事务保存在一个线程的todo队列中,表示要由该线程来处理的事务,每一个事务都关联着一个
    // Binder实体对象,表示该事务的目标处理对象,即要求与该Binder实体对象对应的Service组件在指定的线程中处理该事务,
    // 当Binder驱动发现一个事务是异步的,就会将它保存在目标Bindr实体对象的一个异步事务队列中]
    unsigned has_async_transaction : 1;
    // accept_fds:  用来描述一个Binder实体对象是否可以接受包含有文件描述符的进程间通信数据
    // [如果允许,当一个进程向另一个进程发送数据中含有文件描述符时,Binder驱动会自动在目标进程中打开一个相同文件]
    unsigned accept_fds : 1;
    // min_priority: Binder实体对象在处理一个来自Client进程的请求时,它的对应Server进程中的进程应该具备的最小线程优先级
    // 这样能保证该Binder实体对象对应的Service组件在一个有一定优先级的线程中处理来自Client进程的请求
    int min_priority : 8;
    // async_todo:  异步事务队列; 即单向的进程间通信请求,不需要等待应答
    // [异步事务的优先级低于同步事务,同一时刻,一个Binder实体对象的所有异步事务至多只有一个会得到处理
    // 同步事务没有这个限制]
    struct list_head async_todo;
};

image-20211008234240614

image-20211008235034057

image-20211008235124439

struct binder_ref_death

// 用来描述一个Service组件的死亡接收通知
// 正常情况下,Service组件被其他Client进程引用时是不可被销毁的,然而,Client进程是无法控制它所引用的Service组件的生命周期。
// 如果Service组件意外崩溃死亡,Client进程将会收到Service组件死亡的通知
// [当驱动要向Client发送死亡通知时,会将一个binder_ref_death结构体封装
// 成一个工作项,根据实际情况来设置work变量的值,最后发送到Client的todo队列中]
//
// 死亡通知的2种情况:BINDER_WORK_DEAD_BINDER
// 1. 驱动程序检测到了Service组件死亡:binder_node->refs [-] binder_ref_death
// 2. Client注册死亡通知时,如果Service已经死亡
//
// 注销死亡通知时也会向Client进程todo队列发送一个类型为binder_ref_death的工作项
// 1.注销时Service没有死亡,驱动会找到之前注册的binder_ref_death结构体,将work修改为
// BINDER_WORK_CLEAR_DEATH_NOTIFICATION, 然后再封装成工作项添加到Client的todo队列
// 2. 注销时Service已经死亡,驱动会找到binder_ref_death结构体,将work修改为
// BINDER_WORK_DEAD_BINDER_AND_CLEAR. 然后再封装成工作项添加到Client的todo队列
struct binder_ref_death {
    // 用来标志一个具体的死亡通知类型; 通过它也能够区分注销的结果
    struct binder_work work;
    // 用来保存负责接收死亡通知的对象的地址
    void __user *cookie;
};

image-20211009000114351

image-20211009000220358

struct binder_ref

// 同一个Binder实体对象可能会同时被多个Client组件引用
// 使用binder_ref来描述这些引用关系
// 将引用了同一个Binder实体对象的所有引用都保存在一个hash列表中
//
// 用来描述一个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) */
    // 用来标志Binder应用对象的身份,帮助调试用
    int debug_id;
    // 一个宿主进程使用两个红黑树来保存它内部所有的引用对象
    // 分别以句柄值和对应的Binder实体对象的地址作为关键字来保存Binder引用对象
    // rb_node_desc, rb_node_node 为这两个红黑树的节点
    struct rb_node rb_node_desc;
    struct rb_node rb_node_node;
    // Binder实体对象hash列表的节点
    struct hlist_node node_entry;
    // 引用对象的宿主进程
    struct binder_proc *proc;
    // 描述一个Binder引用对象所引用的Binder实体对象
    struct binder_node *node;
    // 句柄值描述符,用来描述一个Binder引用对象
    // 句柄值在进程范围内唯一,不同进程见同一句柄值可能代表这不同Service组件
    //
    // 在Client进程的用户空间中,访问Service组件通过以下顺序
    // Client->句柄值->binder_ref->binder_node->Service
    uint32_t desc;
    // 强弱引用计数,用来维护引用对象的生命周期
    int strong;
    int weak;
    // 指向死亡接收通知
    // 当Client进程向驱动注册死亡通知时,会创建一个binder_ref_death结构体并保存在death变量中
    struct binder_ref_death *death;
};

struct binder-buffer