Binder驱动和内核其余模块同样,使用C语言实现,为面向对象的进程间通讯提供底层支持。
复制代码
3.0 Binder框架四个角色--类比互联网:java
并非全部进程中的binder都须要注册给SM广而告之。例如某个client进程能够经过已经创建的Binder链接,将本身的binder实体引用发送给Server进程。Server进程使用该引用请求Client进程,此过程进程双方的角色就发生了互换。例如应用启动过程当中的ApplicationThread
复制代码
基本格式:命令+数据。使用ioctl(fd,cmd,arg)交互。node
数据格式一样为:命令+数据。均存放在write_buffer域指向的内存空间,多条命令可连续存放。数据紧跟着命令后面,数据格式会有所不一样.
复制代码
Binder写操做命令字 | arg | |
---|---|---|
BC_TRANSACTION | 最经常使用命令之一,Client经过驱动向Server发送请求数据, | struct binder_transaction_data 利用binder_transaction_data中的flag域区分同步异步。flag域中的TF_ONE_WAY为1,则为异步,Client无需等待BC_REPLY数据包。不然须要等到数据包接收,才算完成一次交互。 |
BC_REPLY | 最经常使用命令之二Server经过驱动向Client发送应答数据 | |
BC_ACQUIRE_RESULT | ||
BC_ATTEMPT_ACQUIRE | ||
BC_FREE_BUFFER | 释放一块映射的内存 | |
BC_INCREFS BC_ACQUIRE BC_RELEASE BC_DECREFS | 管理Binder引用计数 | 32位Binder引用号 |
BC_INCREFS_DONE BC_ACQUIRE_DONE | Binder实体处理引用计数后,给Binder驱动的反馈 | |
BC_REGISTER_LOOPER | 通知binder驱动,Server端建立了一个线程 | Server端线程池管理 |
BC_ENTER_LOOPER | 通知binder驱动,Server端某个线程进入主循环,能够接收数据 | |
BC_EXIT_LOOPER | 通知binder驱动,Server端某个线程退出主循环,再也不接收数据 | |
BC_REQUEST_DEATH_NOTIFICATION | client要求binder驱动在Binder实体销毁时,client能获得通知 | 参数1:uint32 *ptr; 须要获得死亡通知的Binder引用 参数2:void * *cookie: 与死亡通知相关的信息,驱动会在发出死亡通知时返回给发出请求的进程。 |
BC_DEAD_BINDER_DONE | client收到实体死亡通知,删除引用,并告知驱动 |
格式与写操做一致,一样能连续存放。linux
Binder读操做命令字 | |
---|---|
BR_ERROR | 内部错误? |
BR_OK BR_NOOP |
操做成功,与BR_TRANSACTION_COMPLETE区别在哪? |
BR_SPAWN_LOOPER | 驱动发现接收方线程不够用,要求接收方建立线程 |
BR_TRANSACTION BR_REPLY |
|
BR_ACQUIRE_RESULT BR_ATTEMPT_ACQUIRE |
|
BRFINISHED | |
BR_DEAD_REPLAY | 交互过程当中若是发现对方进程或线程已经死亡则返回该消息 |
BR_TRANSACTION_COMPLETE | 驱动告知发送方“发送成功”,与接收方是否返回请求数据无关 |
BR_INCREFS BR_ACQUIRE BR_RELEASE BR_DECREFS |
引用与计数管理 |
BR_DEAD_BINDER BR_CLEAR_DEATH_NOTIFICATION_DONE |
告知Client进程,Binder实体死亡通知处理相关 |
BR_FAILED_REPLY | 发送非法引用号,则返回此 |
成员定义 | |
---|---|
union { size_t handle; void *ptr; } target; |
对于发送方 target指向目的地。target.handle(即句柄)存放binder引用 当数据包到达接收方时,target已被驱动赋予了binder实体对象内存的指针,存放在target.ptr |
void *cookie; | 发送方忽略该成员,binder实体建立时,接收方自定义的任意数值,与binder指针相关的额外信息,驱动也基本不关心该成员 |
unsigned int code; | Server端定义的公共接口函数编号(业务码?) |
unsigned int flags; | 交互相关标志位,其中最重要的是TF_ONE_WAY位 |
pid_t sender_pid; | 发送方进程ID和用户ID,由驱动负责填入 |
uid_t sender_euid; | |
size_t data_size; | Server定义的公共接口函数关心的数据相关,传输中的Binder,以flat_binder_object的形式包含在buffer中. buffer指向部分会copy到映射区 |
size_t offsets_size; | |
union{ struct { *buffer;*offsets} ptr; uint8_t buf[8]; }data; |
Binder存在于系统的这些部分:设计模式
在不一样的部分,Binder实现的功能不一样,表现形式也不同。咱们须要关心其扮演的角色和使用的数据结构。 缓存
Binder本质是底层通讯的方式,与具体服务无关。Server必须提供一套接口函数,以便Client远程访问。
这时一般采用Proxy设计模式,将接口函数定义在一个抽象类中安全
如何将Binder和Proxy设计模式结合是应用程序实现面向对象Binder通讯的根本问题 服务器
Binder实体类实现两个类的全部虚函数:markdown
Client中会有代理类实现这两个类:cookie
Binder传输结构为flat_binder_object 网络
属性 | 含义 |
---|---|
unsigned long type; | binder类型: BINDER_TYPE_BINDER 表示传递的是Binder实体,而且指向该实体的都是强引用; BINDER_TYPE_WEAK_BINDER 表示传递的是Binder实体,而且指向该实体的都是弱引用; BINDER_TYPE_HANDLE 表示传递的是Binder强引用; BINDER_TYPE_WEAK_HANDLE 表示传递的是Binder弱引用; BINDER_TYPE_FD 表示传递的是文件形式Binder; |
unsigned long flags | 只对第一次传递Binder实体时有效,由于是用于驱动在内核中建立Binder实体节点, 内容暂不关心了就。 |
union{ void * binder; signed long handle; }; |
void * binder; 指向Binder实体在Server进程中的内存地址,传递Binder实体时使用。 signed long handle; 存放Binder在进程中的引用号,传递Binder引用时使用。 |
void * cookie; | 只对Binder实体有效,存放Binder相关附加信息 |
不管是Binder实体仍是引用都从属某个进程,因此不能透明的在进程间传递,必须通过驱动“翻译”。
binder类型 | 在发送方的操做 | 在接收方的操做 |
---|---|---|
BINDER_TYPE_BINDER BINDER_TYPE_WEAK_BINDER | 只有实体所在的进程才能发送该类型的Binder。第一次发送时,驱动为其在内核中建立节点,并保存binder,cookie,flag域 | 若是是第一次接收该Binder则建立实体在内核中的引用;将handle域替换为新建的引用号;将type域替换为INDER_TYPE_(WEAK_)HANDLE |
BINDER_TYPE_HANDLE BINDER_TYPE_WEAK_HANDLE | 驱动根据handle域提供的引用号查找binder在内核中的引用。若是找不到,则不合法。 | 1.若是收到的binder实体位于接收进程:将ptr域替换为保存在节点中的binder值,cookie值替换,type替换为BINDER_TYPE_(WEAK_)BINDER 2.若是收到的Binder实体不在接收进程中:若是第一次接收则建立实体在内核中的引用;将handle域替换为新建的引用号 |
BINDER_TYPE_FD | 略 |
驱动是Binder通讯的核心:
Binder实体在驱动中,称为“节点”,由binder_node 结构表示。根据传输数据中的flat_binder_object,构建binder_node节点。
属性 | 含义 |
---|---|
int debug_id; | 用于调试 |
struct binder_work work; | ? |
union{ struct rb_node rb_node; struct hlist_node dead_node; }; |
内核为每一个进程维护一颗红黑树,存放此进程的全部binder实体的用户空间指针(ptr).rb_node即为binder_node在红黑树中的形式。 待销毁节点,待切断全部引用后,完全销毁。 |
struct binder_proc *proc; | 该节点所属进程 |
struct hlist_head refs; | 该节点全部引用队列 |
int internal_strong_refs; | 强指针计数器 |
int local_strong_refs; | 驱动为传输中的Binder设置的强引用计数 |
int local_weak_refs; | 驱动为传输中的Binder设置的弱引用计数 |
void __user * ptr; | 指向用户空间内存地址,来自flat_binder_object的binder成员 |
void __user *cookie; | |
unsigned pending_strong_ref; unsigned has_strong_ref; unsigned has_weak_ref; unsigned pending_weak_ref; |
引用计数相关 |
unsigned has_async_transaction; | 表面该节点在to-do队列中有异步交互还没有完成。to-do队列是驱动为接收进程或线程开辟的,用于暂存发往接收端的数据包。 若是to-do中有还没有完成的异步交互,则将新到的异步交互,存放在async队列中。为同步交互让路,避免长时间阻塞发送端 |
struct list_head aysnc_todo; | 异步交互等待队列;分流发往本节点的异步交互包 |
unsigned accept_fds; | 文件binder相关,略 |
int min_priority; | 设置处理请求的线程的最低优先级,值来自于flat_binder_object中的flags成员 区别在哪? |
表述为binder_ref,根据传输数据中的flat_binder_object构建。
属性 | 含义 |
---|---|
int debug_id; | 调试用 |
struct rb_node rb_node_desc; | 每一个进程有一棵红黑树,进程全部引用以引用号(即本结构的desc域)为索引添入该树中。本成员用作连接到该树的一个节点。 |
struct rb_node rb_node_node; | 每一个进程又有一棵红黑树,进程全部引用以节点实体在驱动中的内存地址(即本结构的node域)为所引添入该树中。本成员用作连接到该树的一个节点。 |
struct hlist_node node_entry; | 该域将本引用作为节点链入所指向的Binder实体结构binder_node中的refs队列 |
struct binder_proc *proc; | 本引用所属的进程 |
struct binder_node *node; | 本引用所指向的节点(Binder实体) |
uint32_t desc; | 本结构的引用号 |
int strong; | 强引用计数 |
int weak; | 弱引用计数 |
struct binder_ref_death *death; | 应用程序向驱动发送BC_REQUEST_DEATH_NOTIFICATION或BC_CLEAR_DEATH_NOTIFICATION命令从而当Binder实体销毁时可以收到来自驱动的提醒。该域不为空代表用户订阅了对应实体销毁的‘噩耗’。 |
Binder实体会有不少引用,这些引用分布在不一样的进程中。
Binder引用能够经过两个键值索引:
驱动建立于内核中的binder_node结构地址,非实体在用户进程中的内存地址。内核地址是惟一的,而用户进程地址可能会重合
驱动为引用分配的32位标识,Binder引用在用户进程中的句柄,单个进程内是惟一的。经过引用号在红黑树中找到binder_ref,经过ref的node域找到Binder实体相关信息。
Binder会预先建立一堆线程,这些线程会阻塞在等待队列上。一旦有数据请求,驱动会从队列中唤醒一个线程来处理。
Binder如何管理线程:
1.队列用以缓解请求与处理的“供需矛盾”:
2.数据包进入进程or线程to-do队列规则:
3.驱动递交同步交互和异步交互规则:
为了减小异步交互对同步发送端的阻塞。异步要为同步让步。即一旦to-do队列中有异步在处理,新提交的异步只能特制的async_todo队列。而新提交的同步,依然能够进入to-do队列
性能:单次拷贝<br /> 安全性:PID/UID可控<br /> 易用性:面向对象设计
复制代码