[Binder深入学习二]Binder驱动——基础数据结构二
Userspace和KernelSpace进行交互时,大部分命令是通过 ioctl 实现的,在这个过程中,最重要的一个便是 BINDER_WRITE_READ 命令了。
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
/* * 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_size_t write_size; /* bytes to write */ binder_size_t write_consumed; /* bytes consumed by driver */ binder_uintptr_t write_buffer; binder_size_t read_size; /* bytes to read */ binder_size_t read_consumed; /* bytes consumed by driver */ binder_uintptr_t read_buffer; };
write_size,write_consumed,wirte_buffer是描述输入数据。
read_size,read_consumed,read_buffer是描述输出数据。
成员变量 write_buffer 指向一个用户空间缓冲区的地址,里面保存的内容即为要输入到Binder驱动程序的数据。缓冲区大小为 write_size, write_consumed用来描述
Binder驱动程序从缓冲区write_buffer中处理来多少个字节的数据。
read_buffer,read_size, read_consumed同理类似。
缓冲区 write_buffer 和 read_buffer 的数据格式:
它们是一个数组,数组的每一个元素都是由一个通信协议代码及通信数据组成的。协议代码又分为两种类型,其中一种类型是在输入缓冲区write_buffer中是用的,称为命令协议码。
另一种是在输出缓冲区read_buffer中使用的,称为返回协议码。
enum binder_driver_command_protocol { BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data), BC_REPLY = _IOW('c', 1, struct binder_transaction_data), /* * binder_transaction_data: the sent command. */ 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. */ BC_FREE_BUFFER = _IOW('c', 3, binder_uintptr_t), /* * void *: ptr to transaction data received on a read */ BC_INCREFS = _IOW('c', 4, __u32), BC_ACQUIRE = _IOW('c', 5, __u32), BC_RELEASE = _IOW('c', 6, __u32), BC_DECREFS = _IOW('c', 7, __u32), /* * int: descriptor */ 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 */ BC_REGISTER_LOOPER = _IO('c', 11), /* * No parameters. * Register a spawned looper thread with the device. */ 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. */ 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 */ BC_DEAD_BINDER_DONE = _IOW('c', 16, binder_uintptr_t), /* * void *: cookie */ };
命令协议代码BC_TRANSACTION和BC_REPLY后面跟的通信数据使用一个结构体binder_transaction_data来描述。当一个进程请求另外一个进程执行某一个操作时,源进程就使用命令协议代码BC_TRANSACTION来请求Binder驱动程序将通信数据传递到目标进程;当目标进程处理完成源进程所请求的操作之后,它就使用命令协议代码BC_REPLY来请求Binder驱动程序将结果数据传递给源进程。
命令协议代码BC_ACQUIRE_RESULT在当前的Binder驱动程序实现中不支持。
命令协议代码BC_FREE_BUFFER后面跟的通信数据是一个整数,它指向了在Binder驱动程序内部所分配的一块内核缓冲区。Binder驱动程序就是通过这个内核缓冲区将源进程的通信数据传递到目标进程的。当目标进程处理完成源进程的通信请求之后,它就会使用命令协议代码BC_FREE_BUFFER来通知Binder驱动程序释放这个内核缓冲区。
命令协议代码BC_INCREFS、BC_ACQUIRE、BC_RELEASE和BC_DECREFS后面跟的通信数据是一个整数,它描述了一个Binder引用对象的句柄值,其中,命令协议代码BC_INCREFS和BC_DECREFS分别用来增加和减少一个Binder引用对象的弱引用计数;而命令协议代码BC_ACQUIRE和BC_RELEASE分别用来增加和减少一个Binder引用对象的强引用计数。
命令协议代码BC_INCREFS_DONE和BC_ACQUIRE_DONE后面跟的通信数据使用一个结构体binder_ptr_cookie来描述。Binder驱动程序第一次增加一个Binder实体对象的强引用计数或者弱引用计数时,就会使用返回协议代码BR_ACQUIRE或者BR_INCREFS来请求对应的Server进程增加对应的Service组件的强引用计数或者弱引用计数。当Server进程处理完成这两个请求之后,就会分别使用命令协议代码BC_INCREFS_DONE和BC_ACQUIRE_DONE将操作结果返回给Binder驱动程序。
命令协议代码BC_ATTEMPT_ACQUIRE在当前的Binder驱动程序实现中不支持。
命令协议代码BC_REGISTER_LOOPER、BC_ENTER_LOOPER和BC_EXIT_LOOPER后面不需要指定通信数据。一方面,当一个线程将自己注册到Binder驱动程序之后,它接着就会使用命令协议代码BC_ENTER_LOOPER来通知Binder驱动程序,它已经准备就绪处理进程间通信请求了;另一方面,当Binder驱动程序主动请求进程注册一个新的线程到它的Binder线程池中来处理进程间通信请求之后,新创建的线程就会使用命令协议代码BC_REGISTER_LOOPER来通知Binder驱动程序,它准备就绪了。最后,当一个线程要退出时,它就使用命令协议代码BC_EXIT_LOOPER从Binder驱动程序中注销,这样它就不会再接收到进程间通信请求了。
命令协议代码BC_REQUEST_DEATH_NOTIFICATION和BC_CLEAR_DEATH_NOTIFICATION后面跟的通信数据使用一个结构体binder_ptr_cookie来描述。一方面,如果一个进程希望获得它所引用的Service组件的死亡接收通知,那么它就需要使用命令协议代码BC_REQUEST_DEATH_NOTIFICATION来向Binder驱动程序注册一个死亡接收通知;另一方面,如果一个进程想注销之前所注册的一个死亡接收通知,那么它就需要使用命令协议代码BC_CLEAR_DEATH_NOTIFICATION来向Binder驱动程序发出请求。
命令协议代码BC_DEAD_BINDER_DONE后面跟的通信数据是一个void类型的指针,指向一个死亡接收通知结构体binder_ref_death的地址。当一个进程获得一个Service组件的死亡通知时,它就会使用命令协议代码BC_DEAD_BINDER_DONE来通知Binder驱动程序,它已经处理完成该Service组件的死亡通知了。
enum binder_driver_return_protocol { BR_ERROR = _IOR('r', 0, __s32), /* * int: error code */ BR_OK = _IO('r', 1), /* No parameters! */ BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data), 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. */ 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. */ 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 */ 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. */ 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 */ 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. */ };
返回协议代码BR_ERROR后面跟的通信数据是一个整数,用来描述一个错误代码。Binder驱动程序在处理应用程序进程发出的某个请求时,如果发生了异常情况,它就会使用返回协议代码BR_ERROR来通知应用程序进程。
返回协议代码BR_OK后面不需要指定通信数据。Binder驱动程序成功处理了应用程序进程发出的某一个请求之后,它就会使用返回协议代码BR_OK来通知应用程序进程。
返回协议代码BR_TRANSACTION和BR_REPLY后面跟的通信数据使用一个结构体binder_transaction_data来描述。当一个Client进程向一个Server进程发出进程间通信请求时,Binder驱动程序就会使用返回协议代码BR_TRANSACTION通知该Server进程来处理该进程间通信请求;当Server进程处理完成该进程间通信请求之后,Binder驱动程序就会使用返回协议代码BR_REPLY将进程间通信请求结果数据返回给Client进程。
返回协议代码BR_ACQUIRE_RESULT在当前的Binder驱动程序实现中不支持。
返回协议代码BR_DEAD_REPLY后面不需要指定通信数据。Binder驱动程序在处理进程间通信请求时,如果发现目标进程或者目标线程已经死亡,它就会使用返回协议代码BR_DEAD_REPLY来通知源进程。
返回协议代码BR_TRANSACTION_COMPLETE后面不需要指定通信数据。当Binder驱动程序接收到应用程序进程给它发送的一个命令协议代码BC_TRANSACTION或者BC_REPLY时,它就会使用返回协议代码BR_TRANSACTION_COMPLETE来通知应用程序进程,该命令协议代码已经被接收,正在分发给目标进程或者目标线程处理。
返回协议代码BR_INCREFS、BR_ACQUIRE、BR_RELEASE和BR_DECREFS后面跟的通信数据使用一个结构体binder_ptr_cookie来描述,其中,命令协议代码BR_INCREFS和BR_DECREFS分别用来增加和减少一个Service组件的弱引用计数;而命令协议代码BR_ACQUIRE和BR_RELEASE分别用来增加和减少一个Service组件的强引用计数。
返回协议代码BR_ATTEMPT_ACQUIRE在当前的Binder驱动程序实现中不支持。
返回协议代码BR_NOOP后面不需要指定通信数据。Binder驱动程序使用返回协议代码BR_NOOP来通知应用程序进程执行一个空操作,它的存在是为了方便以后可以替换为返回协议代码BR_SPAWN_LOOPER。
返回协议代码BR_SPAWN_LOOPER后面不需要指定通信数据。当Binder驱动程序发现一个进程没有足够的空闲Binder线程来处理进程间通信请求时,它就会使用返回协议代码BR_SPAWN_LOOPER来通知该进程增加一个新的线程到Binder线程池中。
返回协议代码BR_FINISHED在当前的Binder驱动程序实现中不支持。
返回协议代码BR_DEAD_BINDER和BR_CLEAR_DEATH_NOTIFICATION_DONE后面跟的通信数据是一个void类型的指针,它指向一个用来接收Service组件死亡通知的对象的地址。当Binder驱动程序监测到一个Service组件的死亡事件时,它就会使用返回协议代码BR_DEAD_BINDER来通知相应的Client进程。当Client进程通知Binder驱动程序注销它之前所注册的一个死亡接收通知时,Binder驱动程序执行完成这个注销操作之后,就会使用返回协议代码BR_CLEAR_DEATH_NOTIFICATION_DONE来通知Client进程。
返回协议代码BR_FAILED_REPLY后面不需要指定通信数据。当Binder驱动程序处理一个进程发出的BC_TRANSACTION命令协议时,如果发生了异常情况,它就会使用返回协议代码BR_FAILED_REPLY来通知源进程。
介绍完Binder驱动程序提供的命令协议代码和返回协议代码之后,接下来我们继续分析这些协议所使用的两个结构体binder_ptr_cookie和binder_transaction_data的定义。
struct binder_ptr_cookie { binder_uintptr_t ptr; binder_uintptr_t cookie; };
结构体binder_ptr_cookie用来描述一个Binder实体对象或者一个Service组件的死亡接收通知。当结构体binder_ptr_cookie描述的是一个Binder实体对象时,成员变量ptr和cookie的含义等同于前面所介绍的结构体binder_node的成员变量ptr和cookie;当结构体binder_ptr_cookie描述的是一个Service组件的死亡接收通知时,成员变量ptr指向的是一个Binder引用对象的句柄值,而成员变量cookie指向的是一个用来接收死亡通知的对象的地址。
struct binder_transaction_data { /* 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_uintptr_t cookie; /* target object cookie */ __u32 code; /* transaction command */ /* General information about the transaction. */ __u32 flags; pid_t sender_pid; uid_t sender_euid; binder_size_t data_size; /* number of bytes of data */ 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; };
结构体binder_transaction_data用来描述进程间通信过程中所传输的数据。
成员变量target是一个联合体,用来描述一个目标Binder实体对象或者目标Binder引用对象。如果它描述的是一个目标Binder实体对象,那么它的成员变量ptr就指向与该Binder实体对象对应的一个Service组件内部的一个弱引用计数对象(weakref_impl)的地址;如果它描述的是一个目标Binder引用对象,那么它的成员变量handle就指向该Binder引用对象的句柄值。
成员变量cookie是由应用程序进程指定的额外参数。当Binder驱动程序使用返回命令协议BR_TRANSACTION向一个Server进程发出一个进程间通信请求时,这个成员变量才有实际意义,它指向的是目标Service组件的地址。
成员变量code是由执行进程间通信的两个进程互相约定好的一个通信代码,Binder驱动程序完全不关心它的含义。
成员变量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 */ };
目前,只使用了TF_ONE_WAY、TF_STATUS_CODE和TF_ACCEPT_FDS这三个标志值。如果成员变量flags的TF_ONE_WAY位被设置为1,就表示这是一个异步的进程间通信过程;如果成员变量flags的TF_ACCEPT_FDS位被设置为0,就表示源进程不允许目标进程返回的结果数据中包含有文件描述符;如果成员变量flags的TF_STATUS_CODE位被设置为1,就表示成员变量data所描述的数据缓冲区的内容是一个4字节的状态码。
成员变量sender_pid和sender_euid表示发起进程间通信请求的进程的PID和UID。这两个成员变量的值是由Binder驱动程序来填写的,因此,目标进程通过这两个成员变量就可以识别出源进程的身份,以便进行安全检查。
成员变量data_size和offsets_size分别用来描述一个通信数据缓冲区以及一个偏移数组的大小。成员变量data是一个联合体,它指向一个通信数据缓冲区。当通信数据较小时,就直接使用联合体内静态分配的数组buf来传输数据;当通信数据较大时,就需要使用一块动态分配的缓冲区来传输数据了。这块动态分配的缓冲区通过一个包含两个指针的结构体来描述,即通过联合体内的成员变量ptr来描述。结构体ptr的成员变量buffer指向一个数据缓冲区,它是真正用来保存通信数据的,它的大小由前面所描述的成员变量data_size来指定。当数据缓冲区中包含有Binder对象时,那么紧跟在这个数据缓冲区的后面就会有一个偏移数组offsets,用来描述数据缓冲区中每一个Binder对象的位置。有了这个偏移数组之后,Binder驱动程序就可以正确地维护其内部的Binder实体对象和Binder引用对象的引用计数。
数据缓冲区中的每一个Binder对象都使用一个flat_binder_object结构体来描述。下面我们就通过一个例子来描述数据缓冲区的内存布局,如图5-3所示。
图5-3 缓Binder进程间通信数据缓冲区的内存布局
在图5-3所描述的数据缓冲区中,有两个Binder对象,相应地,偏移数组的大小就等于2,里面保存的就是这两个Binder对象在数据缓冲区中的位置n1和n2。
下面我们继续分析结构体flat_binder_object的定义。
/* * 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 { /* 8 bytes for large_flat_header. */ __u32 type; __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; };
结构体flat_binder_object除了可以描述一个Binder实体对象和一个Binder引用对象之外,还可以用来描述一个文件描述符,它们是通过成员变量type来加以区别的。
成员变量type的取值范围如下所示。
enum { BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE), 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_BINDER和BINDER_TYPE_WEAK_BINDER都是用来描述一个Binder实体对象的,前者描述的是一个强类型的Binder实体对象,而后者描述的是一个弱类型的Binder实体对象;BINDER_TYPE_HANDLE和BINDER_TYPE_WEAK_HANDLE用来描述一个Binder引用对象,前者描述的是一个强类型的Binder引用对象,而后者描述的是一个弱类型的Binder引用对象;BINDER_TYPE_FD用来描述一个文件描述符。
成员变量flags是一个标志值,只有当结构体flat_binder_object描述的是一个Binder实体对象时,它才有实际意义。目前只用到该成员变量的第0位到第8位。其中,第0位到第7位描述的是一个Binder实体对象在处理一个进程间通信请求时,它所运行在的线程应当具有的最小线程优先级;第8位用来描述一个Binder实体对象是否可以将一块包含有文件描述符的数据缓冲区传输给目标进程,如果它的值等于1,就表示允许,否则就不允许。
成员变量binder和handle组成了一个联合体。当结构体flat_binder_object描述的是一个Binder实体对象时,那么就使用成员变量binder来指向与该Binder实体对象对应的一个Service组件内部的一个弱引用计数对象(weakref_impl)的地址,并且使用成员变量cookie来指向该Service组件的地址;当结构体flat_binder_object描述的是一个Binder引用对象时,那么就使用成员变量handle来描述该Binder引用对象的句柄值。