Android笔记 - Binder之数据结构(二)

上篇介绍完了 Binder 驱动内部会使用的数据结构,本文继续介绍 Binder 驱动和用户空间都会使用的数据结构。这部分数据结构有一个显著的特点,就是用于进程间通信过程所传输数据的封装,使得通信数据在用户空间和 Binder 驱动之间能够高效传输。

本文涉及到 Binder 通信模型的一些基本概念,如果还不太了解的话,可以参考Binder之基本概念这篇文章了解一下基本的概念。

进程间通信数据在 Client 进程用户空间的封装过程如下图所示:
Android笔记 - Binder之数据结构(二)_第1张图片
上图主要涉及到三个结构体,下面按照数据封装顺序介绍:

1.1 struct flat_binder_object

struct flat_binder_object {
    /* 8 bytes for large_flat_header. */
    unsigned long       type;                                             [1]
    unsigned long       flags;                                            [2]

    /* 8 bytes of data. */
    union {
        void __user *binder;    /* local object */                        [3]
        signed long handle;     /* remote object */                       [4]
    };

    /* extra data associated with local object */
    void __user     *cookie;                                              [5]
};

结构体 flat_binder_object 主要用于描述一个 Binder 实体对象或者一个 Binder 引用对象,会被封装在结构体 binder_transaction_data 的成员变量 data中。
[1] type 用于说明 flat_binder_object 结构体表示的是一个 Binder 实体对象还是一个 Binder 引用对象。
[2] flags 只有在 flat_binder_object 结构体表示一个 Binder 实体对象时才有意义。目前只使用了它的第0位到第8位,其中第0位到第7位表示 Binder 实体所在线程应该具有的最小线程优先级;第8位表示 Binder 实体对象是否接收文件描述符。
[3] binder 用于指向对应 Service 组件内部的一个弱引用计数对象的地址,当 flat_binder_object 结构体表示一个 Binder 实体对象时用到。
[4] handle 用于表示 Binder 引用对象的句柄值,当 flat_binder_object 结构体表示一个 Binder 引用对象时用到。

binder 和 handle 是联合体中的成员变量,由此可知结构体 flat_binder_object 将传输 Binder 实体对象和 Binder 引用对象统一起来了。

[5] cookie 在 flat_binder_object 结构体表示一个 Binder 实体对象时才有意义,指向对应 Service 组件的地址。

1.2 struct binder_transaction_data

struct binder_transaction_data {
    /* The first two are only used for bcTRANSACTION and brTRANSACTION,
     * identifying the target and contents of the transaction.
     */
    union {
        size_t  handle; /* target descriptor of command transaction */    [1]
        void    *ptr;   /* target descriptor of return transaction */     [2]
    } target;
    void        *cookie;    /* target object cookie */                    [3]
    unsigned int    code;       /* transaction command */                 [4]

    /* General information about the transaction. */
    unsigned int    flags;
    pid_t       sender_pid;                                               [5]
    uid_t       sender_euid;                                              [6]
    size_t      data_size;  /* number of bytes of data */                 [7]
    size_t      offsets_size;   /* number of bytes of offsets */          [8]

    union {
        struct {
            /* transaction data */
            const void __user   *buffer;                                  
            /* offsets from buffer to flat_binder_object structs */
            const void __user   *offsets;                                
        } ptr;
        uint8_t buf[8];                                                  
    } data;
};

结构体 binder_transaction_data 用于描述用户空间和 Binder 驱动之间的事务通信数据,会被封装在结构体 binder_write_read 的成员变量 write_buffer 或者 read_buffer 指向的地址中。
[1] handler 用于表示一个 Binder 引用对象的句柄值,只有命令事务(command transaction)中才会用到。
[2] ptr 用于指向对应 Service 组件内部的一个弱引用计数对象的地址,只有返回事务(return transaction)中才会用到。

可以通过 ptr 是否为空来判断 Service 组件是否为 ServiceManager。

[3] cookie 用于指向实体对象对应 Service 组件的地址。
[4] flags 用来描述进程间通信事务的特征。flags 取值由枚举 transaction_flags 来定义:

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 */
    TF_STATUS_CODE  = 0x08, /* contents are a 32-bit status code */
    TF_ACCEPT_FDS   = 0x10,     /* allow replies with file descriptors */
};

[5] sender_pid 用于表示发起进程间通信请求的进程的 PID。
[6] sender_euid 用于表示发起进程间通信请求的进程的 UID。
[7] data_size 用来表示通信数据缓存区的大小
[8] offsets_size 用来表示偏移数组的大小,大于0说明缓存区中包含 Binder 对象。

当通信数据量少于等于8时,直接使用联合体内的 buf 数组传输数据;否则,使用一块动态分配的缓存区来传输数据,该存储区使用结构体 ptr 的成员变量 buffer 和 offsets 来表示。buffer 指向缓存区的首地址,用来保存通信数据,如果缓存区中包含 Binder 对象,则使用偏移数组 offsets 来说明数据缓存区中每个 Binder 对象的位置。

1.3 struct binder_write_read

struct binder_write_read {
    signed long write_size; /* bytes to write */                          [1]
    signed long write_consumed; /* bytes consumed by driver */            [2]
    unsigned long   write_buffer;                                         [3]
    signed long read_size;  /* bytes to read */                           [4]
    signed long read_consumed;  /* bytes consumed by driver */            [5]
    unsigned long   read_buffer;                                          [6]
};

结构体 binder_write_read 用来描述用户空间和 Binder 驱动之间的通信数据,用户空间通过 ioctl 系统调用将 write_buffer 中的内容写入 Binder 驱动, ioctl 系统调用完成后从 read_buffer 中读取返回的内容。
[1] write_size 表示需要写入 Binder 驱动中的数据大小
[2] write_consumed 表示已经写入 Binder 驱动中的数据大小
[3] write_buffer 保存用户空间缓存区的地址,该缓存区内容为需要写入 Binder 驱动的通信数据
[4] read_size 表示需要从 Binder 驱动中读取的数据大小
[5] read_consumed 表示已经从 Binder 驱动中读取的数据大小
[6] read_buffer 保存用户空间缓存区的地址,该缓存区保存从 Binder 驱动返回用户空间的通信数据

进程间通信过程肯定少不了通信协议,Binder 通信协议分为两类,一类是 Binder Command 协议,描述用户空间向 Binder 驱动程序发送通信请求,通过 binder_driver_command_protocol 枚举来定义;另一类是 Binder Return 协议,描述 Binder 驱动程序返回通信请求处理结果到用户空间,通过 binder_driver_return_protocol 枚举来定义。

2.1 enum binder_driver_command_protocol

协议 功能
BC_TRANSACTION 请求 Binder 驱动将通信数据传递给 Server 进程
BC_REPLY 请求 Binder 驱动将处理结果传递给 Client 进程
BC_FREE_BUFFER 请求 Binder 驱动程序释放内核缓存区
BC_INCREFS 增加一个 Binder 引用对象的弱引用计数
BC_ACQUIRE 增加一个 Binder 引用对象的强引用计数
BC_RELEASE 减少一个 Binder 引用对象的强引用计数
BC_DECREFS 减少一个 Binder 引用对象的弱引用计数
BC_REGISTER_LOOPER 线程通知 Binder 驱动已经准备就绪
BC_ENTER_LOOPER 线程通知 Binder 驱动已经准备就绪
BC_EXIT_LOOPER 线程通知 Binder 驱动需要退出

BC_TRANSACTION 和 BC_REPLY 协议后面带的通信数据使用结构体 binder_transaction_data 来描述。

强引用计数可以控制对象的生命周期,而弱引用计数不能控制对象的生命周期,因此使用弱引用计数所引用对象时需要先把弱引用计数升级为强引用计数,如果升级失败,说明所引用对象已经被释放了。

2.2 enum binder_driver_return_protocol

协议 功能
BR_ERROR 通知应用程序出现异常情况
BR_OK 通知应用程序请求处理完成
BR_TRANSACTION Binder 驱动通知 Server 进程处理通信请求
BR_REPLY Binder 驱动将请求处理结果通知 Client 进程
BR_TRANSACTION_COMPLETE 通知应用程序 BR_TRANSACTION 和 BR_REPLY 协议内容已经被 Binder 驱动接收
BR_INCREFS 增加一个 Service 组件的弱引用计数
BR_ACQUIRE 增加一个 Service 组件的强引用计数
BR_RELEASE 减少一个 Service 组件的强引用计数
BR_DECREFS 减少一个 Service 组件的弱引用计数
BR_SPAWN_LOOPER 通知 Server 进程增加一个新线程到 Binder 线程池

BR_TRANSACTION 和 BR_REPLY 协议后面带的通信数据也使用结构体 binder_transaction_data 来描述。

当一个 Client 进程向一个 Server 进程发送进程间通信请求时,使用的主要通信协议如下所示:
Android笔记 - Binder之数据结构(二)_第2张图片

本文和Binder之数据结构(一)对 Binder 通信机制中使用的主要数据结构进行了描述。只有了解了这些数据结构的作用和它们之间的联系,才能更好地了解 Binder 通信机制。当然,最好的方法是结合代码来学习,这样才能理解的更深刻。

参考资料:
1. Android 系统源代码情景分析的第5章 - Binder进程间通信系统
2. Service与Android系统设计(7)— Binder驱动

你可能感兴趣的:(Binder,学习,android,数据结构,binder,进程间通信)