Binder 驱动学习笔记

来源:互联网 发布:淘宝网汉服女装 编辑:程序博客网 时间:2024/05/18 08:30

转自 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 处于用户空间。

  • 传递参数给内核:用户态填写好参数内容后, 交给内核态读取;
  • 内核态传回参数:内核态改写bwr内部,交给用户态通过 ioctl 读取。
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 定义的命令中, 只有

  • BC_TRANSACTION
  • BC_REPLY
  • BR_TRANSACTION
  • BR_REPLY

四种能够完成传递数据。

无论是 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_transaction_data的字段解释

至此, 基于 Binder 的数据传输, 可以表现为如下形式:
基于 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 在用户态的一些概念, 提供一种内核态的辅助手段。

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,从而可以加快查找




Binder 驱动的实现

在系统里生成一个 /dev/binder 字符设备文件, 不对应任何硬件, 内存虚拟出来的设备。

BINDER_WRITE_READ
把用户态传递到 Binder 驱动的 binder_write_read 数据结构,从用户态拷贝到内核态, 根据 bwr 中的 read_size 和 write_size 是否大于 0 决定后续操作。两个值可能都存在, 所以一个 ioctl 周期里, 可能同时 读写。

  • write_size > 0 , binder_thread_write 用户态 写入 内核态
  • read_size > 0, binder_thread_read 用户态 读取 内核态的数据
  • 都不大于0, binder 无法正常交互, 返回错误

binder_thread_write()
解析 binder_ioctl 得到的 BC_* 命令, 然后再根据命令作出处理。
binder_transaction
binder_thread_read()



Binder 驱动总结

Binder 驱动在 Binder IPC 过程中, 是一种中间容器, 把一个进程的请求, 转发给另一个进程。
1. 高效。 竭力见效内存拷贝的开销, 使用红黑树等算法建立索引。
2. 面向对象。整个 Binder 驱动, 只是 libbinder 的一种辅助手段,继承了其 面向对象的特点。
3. 开销小。Binder 驱动在不使用时开销几乎可以忽略。每次数据的读写都是通过 binder_transcation 这样的元数据来记录,只记录数据变动。 对于大数据量的读写, 使用从用户空间偷来的的内存空间, 只针对进程有效, 不会影响到整个系统的内存分配, 只有一些内部维护的开销。
4. 面向进程。 Binder IPC 传输的本质便是面向进程, 可以从 binder_transaction 的结构中看出来, 并且只使用进程用户空间来处理大数据 buffer , 进一步加强了 binder 的进程化。如果处于 binder 传输过程中的进程出错死掉, 不会带来系统及开销。
5. 简洁。

0 0
原创粉丝点击