转自 http://blog.csdn.net/21cnbao/article/details/8087354
有如下特殊能力:
- 面向对象。Binder 设计目的是进程间传递对象
- 面向进程。文件描述符 + 进程 ID
- 简单。内核态驱动仅有一个 c 文件
- 高性能。再驱动层可以更高校的分发消息,最高效
- 自动化内存管理。容易构建出自动化垃圾回收
- 灵活。实现、拓展都很容易, 用于多种场景
只通过 ioctl() 系统调用完成读写, 通过 binder_write_read 数据结构来描述详细的操作。
binder_write_read bwr;
if(ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >=0)
err = NO_ERROR;
else
err = -errno;
参数一:fd 文件描述符,通过 open() 调用得到。
参数二:ioctl 码, 定义于 binder.h 。
参数三:用户态与内核态进行交互的数据结构, bwr 处于用户空间。
Binder IOCTL码 | 作用 |
---|---|
BINDER_WRITE_READ | 完成对 /dev/binder 的读写, 这回事最频繁的 Binder IOCTL 操作 |
BINDER_SET_IDLE_TIMEOUT | 设置 IDLE 超时 |
BINDER_SET_MAX_THREADS | 设置当前Binder实例可用的最大线程数 |
BINDER_SET_IDLE_PRIORITY | 设置Binder在进程处于IDLE时的进程优先级 |
BINDER_SET_CONTEXT_MGR | 让自己成为Binder的管理者,只有servicemanager会用到 |
BINDER_THREAD_EXIT | 退出Binder处理的内核线程 |
BINDER_VERSION | 返回Binder驱动的当前版本 |
BC_* 系列: Binder Command 的简称,说明用户态给 Binder 驱动发出的命令。
BR_* 系列: Binder Return 的简称, 说明是 Binder 驱动给用户态发回的操作结果反馈。
在 Binder 定义的命令中, 只有
四种能够完成传递数据。
无论是 TRANSACTION 或 REPLY , 通过 ioctl 操作时使用的参数都是一个 binder_transaction_data 的数据结构指针。
struct binder_transaction_data {
union {
size_t handle; /* target descriptor of command transaction */
void *ptr; /* target descriptor of return transaction */
}target;
void *cookie; /* target objectcookie */
unsigned int code; /* transaction command */
unsigned int flags;
pid_t sender_pid;
uid_t sender_euid;
size_t data_size; /* number of bytesof data */
size_t offsets_size; /* number of bytes ofoffsets */
union {
struct {
const void __user *buffer;
const void __user *offsets;
}ptr;
uint8_t buf[8];
}data;
};
至此, 基于 Binder 的数据传输, 可以表现为如下形式:
binder_transaction_data : data.ptr.buffer - 指向一个线性区域, 区域中存放的是用户态 libbinder 通过 Paracelable 接口处理过的对象 (flat_binder_object)。
binder_transaction_data : data.ptr.offsets - 上述线性区域中每个 Paracel 对象的索引
Binder 驱动里处理 Parcel 映射的数据结构 flat_binder_object, 其结构如下:
struct flat_binder_object {
unsigned long type;
unsigned long flags;
union {
void __user *binder; /* local object */
signed long handle; /* remote object */
};
void __user *cookie;
};
对于本地对象的引用使用 用户态指针 _ _ user *binder
对于远程对象, 使用 long 类型的 handle
Binder 驱动的局部数据结构, 更多的是用来映射 Binder 在用户态的一些概念, 提供一种内核态的辅助手段。
Binder 驱动变量 | 映射的用户态概念 | 作用 |
---|---|---|
binder_proc | ProcessState | 用于描述当前操作Binder的进程的上下文状态,比如内存信息、线程信息、进程优先级等。Binder驱动的open()函数被调用后就会创建一个binder_proc结构,这样的结构针对于每个进程都是唯一的,跟ProcessState一样 |
binder_thread | IPCThreadState | 虽然被命名为binder_thread,但这名字会有误导,实际上跟内核线程没任务关系,只是用于描述用户态Binder线程状态,也就是IPCThreadState的状态。比如当前Binder线程有多少任务需要完成,是否有失败等 |
binder_transaction | binder_transaction_data | binder_transaction_data只是操作的外部接口,内部描述则是使用binder_transaction。在binder_transaction里虽然也只存储元数据,记录如何进行数据操作,但由于需要通过binder_transaction来完成具体的传输,它会包含更多的信息,比如这一transaction所关联的进程、线程、调度优先级等信息。 |
bindernode | flat_binder_object | Binder传输使用过的对象,都会通过binder_node维护一个引用计数,对于任何操作过的对象,在binder_node构成的检索树里,都会保存引用记录,从而可以快速查找到这一对象,并进行所需要的处理。binder_node只用于索引用户态的Binder对象 |
binder_buffer | - | 对象被使用过后,就可以通过binder_buffer存储起来,当下次被用到时,可以绕开内存拷贝的需求 |
binder_ref | - | Binder的内部对象,可索引到binder_node、binder_proc以及用于垃圾回收的binder_ref_death,从而可以加快查找 |
在系统里生成一个 /dev/binder 字符设备文件, 不对应任何硬件, 内存虚拟出来的设备。
BINDER_WRITE_READ
把用户态传递到 Binder 驱动的 binder_write_read 数据结构,从用户态拷贝到内核态, 根据 bwr 中的 read_size 和 write_size 是否大于 0 决定后续操作。两个值可能都存在, 所以一个 ioctl 周期里, 可能同时 读写。
binder_thread_write()
解析 binder_ioctl 得到的 BC_* 命令, 然后再根据命令作出处理。
binder_transaction
binder_thread_read()
Binder 驱动在 Binder IPC 过程中, 是一种中间容器, 把一个进程的请求, 转发给另一个进程。
1. 高效。 竭力见效内存拷贝的开销, 使用红黑树等算法建立索引。
2. 面向对象。整个 Binder 驱动, 只是 libbinder 的一种辅助手段,继承了其 面向对象的特点。
3. 开销小。Binder 驱动在不使用时开销几乎可以忽略。每次数据的读写都是通过 binder_transcation 这样的元数据来记录,只记录数据变动。 对于大数据量的读写, 使用从用户空间偷来的的内存空间, 只针对进程有效, 不会影响到整个系统的内存分配, 只有一些内部维护的开销。
4. 面向进程。 Binder IPC 传输的本质便是面向进程, 可以从 binder_transaction 的结构中看出来, 并且只使用进程用户空间来处理大数据 buffer , 进一步加强了 binder 的进程化。如果处于 binder 传输过程中的进程出错死掉, 不会带来系统及开销。
5. 简洁。