Binder是Android系统IPC通信的一块基石,不管是AMS、WMS都可以看到Binder 的身影,如果搞不清楚的话,看Android源码的时候就会搞不清,比如为什么需要各种代理?怎么突然就跳跃了?
Binder内部细节太多了,然后个人只想搞清楚整体的机制而又不想深究内部的细节。不过一些东西又必须要弄清楚,比如IBinder、BBinder、BPBinder、Binder Proxy、ServiceManager等,所以尽可能以流程图的形式帮助理解,但是也包含一些关键数据结构。
同时,以下是个人学习binder的一个记录,有很多不足的地方,望谅解。
1. IPC机制
IPC(Inter-Process Communication), 进程之间相互通信的机制,目前 Android支持的一些IPC的手段有:Bundle(四大组件通信)、文件共享、AIDL、Messneger、ContentProvider、Socket。
2. Binder的优势
Client-Server 结构
安全性高,带有调用方的Pid、Uid
传输性能高,利用匿名共享内存,数据拷贝只要一次,
参考:https://www.zhihu.com/question/39440766/answer/89210950
3. Binder的使用:AIDL
在Android中,可以很方便的使用AIDL和Service来建立一个Binder跨进程通信,文章希望可以从AIDL入手,从Binder的应用,慢慢往内部实现层层剖析,加深对Binder的理解。
定义一个AIDL接口:
interface IPlusService {
int add(int a, int b);
}
IPlusService有一个add方法,相加获取结果,我们需要在A进程调用B进程的add服务,并且获取得到结果。
在这里我们把服务的调用方称为客户端,就是A;服务的提供方称为服务端,就是B。
使用Android Studio的build,默认会帮助我们构造一个文件IPlusService.java,打开看一下:
public interface IPlusService extends IInterface {
// Stub供服务端实现
public static abstract class Stub extends Binder implements IPlusService {
// binder的唯一标识
private static final String DESCRIPTOR = "com.jscheng.sweather.IPlusService";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
// 提供给客户端:将IBinder转化成IPlusService接口
public static IPlusService asInterface(IBinder obj) {
if ((obj==null)) {
return null;
}
// 如果是客户端跟服务端同一个进程,直接返回server端的binder对象
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof IPlusService))) {
return ((IPlusService)iin);
}
// 若不同进程,则生成一个binder的代理
return new IPlusService.Stub.Proxy(obj);
}
// 底层对应BBinder
@Override
public IBinder asBinder() {
return this;
}
// 运行在服务端:客户端调用transact(),会引发服务端onTransact()
// code 表示客户端请求方法标志
// data 表示参数
// reply 表示写入返回值
// 返回值:客户端请求结果,可以做权限控制
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSACTION_add:
data.enforceInterface(DESCRIPTOR);
// 从Parcel读取客户端传过来的数据
int _arg0 = data.readInt();
int _arg1 = _arg1 = data.readInt();
// 调用服务端的add()
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
// 写入返回给客户端的数据
reply.writeInt(_result);
return true;
}
return super.onTransact(code, data, reply, flags);
}
// 客户端:服务的代理类
private static class Proxy implements IPlusService {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
// 底层对应 BinderProxy
@Override
public IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
// 客户端:打包数据,调用代理binder的transact(),会引发服务端transact()
@Override
public int add(int a, int b) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(a);
_data.writeInt(b);
// 代理类,调用transact()
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (IBinder.FIRST_CALL_TRANSACTION + 0);
}
// 真正实现服务的地方
public int add(int a, int b) throws RemoteException;
}
在Activity中,作为binder的客户端,我们这样使用:
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binderProxy) {
try {
// 将Service传过来的IBinder转化成IPlusService
IPlusService plusServiceProxy = IPlusService.Stub.asInterface(binderProxy);
// 调用add()并同步得到结果
int result = plusServiceProxy.add(1, 2);
} catch (RemoteException e) {
e.printStackTrace();
}
}
...
};
而在Service端,作为Binder的服务端实现起来也很方便,记得要在Manifest文件中,给MainService的声明加上 process=”:remote”,表示当前的Service启动在另一个进程。
public class MainService extends Service {
@Override
public IBinder onBind(Intent intent) {
return new PlusServiceImpl();
}
public class PlusServiceImpl extends IPlusService.Stub {
@Override
public int add(int a, int b) throws RemoteException {
return a + b;
}
}
}
AIDL的使用起来很简单,因为工具帮我们生成了代码框架,我们只需要重写一下服务端的接口和客户端的调用,中间的传输细节基本透明化了。
整个IPC的调用流程:
-
客户端获取IBinder接口,即Binder的代理对象,调用add服务。
-
客户端会将参数打包成parcel对象,Binder的代理对象会调用transact(),同时会阻塞当前线程,等待transact()结束,返回结果。
-
服务端会在onTransact()收到调用方的调用,通过code知道调用的是add服务,通过传过来的parcel解析出参数,最后返回服务结果给客户端。
从上面的Binder 的使用流程我们可以知道几个事情:
- 服务端和客户端是两个进程,客户端调用transact(),服务端实现onTransact()
- Binder的调用,在客户端看来是同步的,transact()从调用到获取结果的过程。
- Binder的被调用,在服务端看来是异步,支持多个客户端调用,服务端底层是线程池。所以可能需要注意同步的问题。
- 如果服务端和客户端在同一个进程,是不会发生Binder通信的,而是在asInterface返回服务端的对象。
- onTransact可以做权限验证,拒绝调用。
- 客户端在调用add的时候,内部是调用transact,在等待回应的时候将会被挂起,所以若是耗时操作的话需要开启子线程。
Parcelable 和 Parcel
Parcelable是一个接口、用来实现序列化,实现了Parcelable的类就可以在进程间通过binder传输,比如Intent;
Parcel用于在进程间传递数据,在内存上序列和反序列化,实现Parcelable的类可以调用writeToParcel()把Parcelable数据写入到Parcel中,调用createFromParcel读取数据。
参考:https://www.jianshu.com/p/f5e103674953
IInterface、IBinder、Binder、BinderProxy、BBinder、BPBinder
IBinder表示具备跨进程传输的能力,只要实现了IBinder接口可以进行跨进程传递这个对象,由驱动底层支持的。
IInterface对应的是AIDL声明的接口,表示提供怎么样的服务。
Binder类代表Binder本地对象,继承自IBinder,具有跨进程传输的能力。
BinderProxy类是Binder类的内部类,代表远程进程的Binder对象的代理,继承自IBinder,具有跨进程传输的能力。
跨进程通信中,Binder驱动会自动抓换Binder和BinderProxy。
public interface IInterface {
public IBinder asBinder();
}
声明的binder service接口必须继承自IInterface,它提供了将服务或者服务代理类转为IBinder类型
public interface IBinder {
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
throws RemoteException;
}
IBinder接口是提供远程调用的接口,描述了与远程对象进行交互的抽象协议。如果我们需要定制一个服务,建议继承Binder类,而不是直接实现这个接口。
public class Binder implements IBinder {
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
...
boolean r = onTransact(code, data, reply, flags);
...
}
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
}
}
Binder 实现了IBinder接口,是真正的binder对象,内部有两个方法,transact()和onTransact(),其实transact()内部也只是转化调用了onTransact()
public class BinderProxy implements IBinder {
public boolean transact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
}
}
BinderProxy 是 Binder的内部类,同时也实现了IBinder接口,它表示远程Binder 的本地代理。
这里我们把client端持有的是Binder的本地代理BinderProxy,调用了BinderProxy.transact(),这时候远程server的Binder.transact()是响应了Client发出的远程调用,调用onTransact()处理任务。
也许你会很奇怪,这里怎么突然冒出了个BinderProxy?
因为Binder在客户端和服务端的表现是不一样的。
在server端的IPlusService.Stub的asBinder()返回的IBinder,底层jni对应的是BBinder对象。
而在client端的IPluService.Stub.Proxy的asBinder()返回的mRemote,其实是BinderProxy,它是由底层的jni BpBinder创建的一个Java服务对象,并保存在BpBinder中,一个BpBinder关联多个BinderProxy。
下面就更奇怪了,怎么突然又出现了BBinder和BpBinder了呢?
BBinder和BpBinder都是IBinder的实现类,在server端表现为BBinder,是真正的binder,而在client端是binder的引用对象BpBinder,为了提供给上层使用生成了代理类BinderProxy,这样client端通过引用对象就可以通知到远程的server端了。
小结:
下面问题来了BpBinder怎么跟BBinder通信的呢?
4. Binder的总体概览
BInder采用了CS的结构,Client和Server是存在于用户空间,Client和Server通信实现是由Binder驱动在内核的实现的。
从Binder组件中,包含Client、Server、ServiceManager以及binder驱动。
1. ServiceManager
其中ServiceManager负责管理系统中的各种服务,比如Client需要某个服务,可以通过ServiceManager查询获取服务,Server也可以向ServiceManager注册服务。ServiceManager是Android进程间通信机制Binder的守护进程。
ServiceManager也是在一个独立的进程中,那么Client进程或者Server进程怎么跟ServiceManager通信呢?其实也是利用了binder机制。
ServiceManager进程在启动的时候,调用Binder驱动时,使用使BINDER_SET_CONTEXXT_MGR命令将自己注册成ServiceManager时,Binder驱动会自动创建Binder实体。
即使ServiceManager在驱动中注册了binder实体,那其他client怎么拿到他的binder的引用呢?因为从上面的流程来看,server端有binder实体BBinder,client端需要拿到BpBinder,就可以实现通信。
这里有一个机巧的设置,client端中handle值为0的binder引用(BpBinder)就是指向ServiceManager,这样一来client端就直接拿到BpBinder。
2.Server向ServiceManager注册服务
Server 利用handle值为0的引用向ServiceManager,注册Binder的引用。ServiceManager在表中增加Service的名字和Binder 的引用。
3.Client从ServiceManager中获得Service
Client也利用了handle值为0的引用向ServiceManager请求访问某个Service。ServiceManager在查找表中找到Binder的引用打包发送给Client。这样Clinet就拿到了对应服务的BpBinder。
小结
ServerManager做为守护进程,在启动的时候注册成为ServerManager。
Server通过ServerManager注册服务。
Client通过ServerManger获取得到服务。
5. 重要的数据结构
其实,以上还是没有讲述到BBinder和BpBinder怎么实现通信的。果然还是要从内核驱动的视角出发去理解。
binder_proc
struct binder_proc {
//上述全局hash表中一个节点,用以标记该进程
struct hlist_node proc_node;
// 进程组ID
int pid;
// 任务控制模块
struct task_struct *tsk;
// 文件结构体数组
struct files_struct *files;
/** Binder线程池每一个Binder进程都有一个线程池,由Binder驱动来维护,Binder线程池中所有线程由一个红黑树来组织,RB树以线程ID为关键字 */
//上述红黑树的根节点
struct rb_root threads;
/** 一系列Binder实体对象(binder_node)和Binder引用对象(binder_ref) */
/** 在用户控件:运行在Server端称为Binder本地对象,运行在Client端称为Binder代理对象*/
/** 在内核空间:Binder实体对象用来描述Binder本地对象,Binder引用对象来描述Binder代理对象 */
// Binder实体对象列表(RB树),关键字 ptr
struct rb_root nodes;
// Binder引用对象,关键字 desc
struct rb_root refs_by_desc;
// Binder引用对象,关键字 node
struct rb_root refs_by_node;
// 这里有两个引用对象,是为了方便快速查找
/** 进程可以调用ioctl注册线程到Binder驱动程序中,当线程池中没有足够空闲线程来处理事务时,Binder驱动可以主动要求进程注册更多的线程到Binder线程池中 */
// Binder驱动程序最多可以请求进程注册线程的最大数量
int max_threads;
// Binder驱动每主动请求进程添加注册一个线程的时候,requested_threads+1
int requested_threads;
// 进程响应Binder要求后,requested_thread_started+1,request_threads-1,表示Binder已经主动请求注册的线程数目
int requested_threads_started;
// 进程当前空闲线程的数目
int ready_threads;
// 线程优先级,初始化为进程优先级
long default_priority;
//进程的整个虚拟地址空间
struct mm_struct *vma_vm_mm;
/** mmap 内核缓冲区*/
// mmap——分配的内核缓冲区 用户控件地址(相较于buffer)
struct vm_area_struct *vma;
// mmap——分配内核缓冲区,内核空间地址(相交于vma) 两者都是虚拟地址
void *buffer;
// mmap——buffer与vma之间的差值
ptrdiff_t user_buffer_offset;
/** buffer指向的内核缓冲区,被划分为很多小块进行性管理;这些小块保存在列表中,buffer就是列表的头部 */
// 内核缓冲列表
struct list_head buffers;
// 空闲的内存缓冲区(红黑树)
struct rb_root free_buffers;
// 正在使用的内存缓冲区(红黑树)
struct rb_root allocated_buffers;
// 当前可用来保存异步事物数据的内核缓冲区大小
size_t free_async_space;
// 对应用于vma 、buffer虚拟机地址,这里是他们对应的物理页面
struct page **pages;
// 内核缓冲区大小
size_t buffer_size;
// 空闲内核缓冲区大小
uint32_t buffer_free;
/** 进程每接收到一个通信请求,Binder将其封装成一个工作项,保存在待处理队列to_do中 */
//待处理队列
struct list_head todo;
// 等待队列,存放一些睡眠的空闲Binder线程
wait_queue_head_t wait;
// hash表,保存进程中可以延迟执行的工作项
struct hlist_node deferred_work_node;
// 延迟工作项的具体类型
int deferred_work;
//统计进程相关数据,具体参考binder_stats结构体
struct binder_stats stats;
// 表示 Binder驱动程序正在向进程发出死亡通知
struct list_head delivered_death;
// 用于debug
struct dentry *debugfs_entry;
// 连接 存储binder_node和binder_context_mgr_uid以及name
struct binder_context *context;
};
binder_proc 表示进程信息,是内核驱动创建的,表示当前binder所在的进程。
这里我们需要关注的,binder_proc 包含了以下这些重要的信息:
- Binder实体对象binder_node,表示当前进程提供了哪些binder服务;
- Binder引用对象binder_ref,表示哪些进程持有当前进程的binder的引用;
- Binder线程池,每一个进程都有一个Binder线程池,由Binder驱动来维护,线程接受驱动的命令;
- todo工作项,驱动会将binder_transaction,加入todo列表,被唤醒之后的线程就可以取出处理;
binder_node
struct binder_node {
// debug调试用的
int debug_id;
struct binder_work work; //binder驱动中进程要处理的工作项
/** 每一个binder进程都由一个binder_proc来描述,binder进程内部所有Binder实体对象,
由一个红黑树来进行组织(struct rb_root nodes) ; rb_node 则对应nodes的一个节点 */
union {
//用于本节点连接红黑树
struct rb_node rb_node;
// 如果Binder 实体对象对应的进程死亡,销毁节点时需要将rb_node从红黑树中删除,
//如果本节点还没有引用切断,则用dead_node将其隔离到另一个链表中,
//直到通知所有进程切断与该节点的引用后,该节点才能销毁
struct hlist_node dead_node;
};
// 指向该Binder实体对象 对应的进程,进程由binder_proc描述
struct binder_proc *proc;
// 该 Binder实体对象可能同时被多个Client组件引用,所有指向本实体对象的引用都
//保存在这个hash队列中refs表示队列头部;这些引用可能隶属于不同进程,遍历该
//hash表能够得到这些Client组件引用了这些对象
struct hlist_head refs;
//远程强引用计数
int internal_strong_refs; //实际上代表了一个binder_node与多少个binder_ref相关联
//本地弱引用技数
int local_weak_refs;
//本地强引用计数
int local_strong_refs;
unsigned has_strong_ref:1;
unsigned pending_strong_ref:1;
unsigned has_weak_ref:1;
unsigned pending_weak_ref:1;
/** 用来描述用户控件中的一个Service组件 */
// 描述用户控件的Service组件,对应Binder实体对应的Service在用户控件的(BBinder)的引用
binder_uintptr_t ptr;
// 描述用户空间的Service组件,Binder实体对应的Service在用户控件的本地Binder(BBinder)地址
binder_uintptr_t cookie;
// 异步事务处理,单独讲解
unsigned has_async_transaction:1;
struct list_head async_todo;
// 表示该Binder实体对象能否接收含有该文件描述符的进程间通信数据。当一个进程向
//另一个进程发送数据中包含文件描述符时,Binder会在目标进程中打开一个相同的文件
//故设为accept_fds为0 可以防止源进程在目标进程中打开文件
unsigned accept_fds:1;
// 处理Binder请求的线程最低优先级
unsigned min_priority:8;
};
binder_node 是binder实体,由驱动创建,对应着每个服务,内部包含进程信息binder_proc,还有binder的引用binder_ref。
binder_ref
struct binder_ref {
//debug 调试用的
int debug_id;
/** binder_proc中使用红黑树(对应两个rb_root变量) 来存储器内部所有引用对象,
*下面的rb_node则是红黑树中的节点
*/
//Binder引用的宿主进程
struct binder_proc *proc;
//对应 refs_by_desc,以句柄desc索引 关联到binder_proc->refs_by_desc红黑树
struct rb_node rb_node_desc;
//对应refs_by_node,以Binder实体对象地址作为关键字关联到binder_proc->refs_by_node红黑树
struct rb_node rb_node_node;
/** Client通过Binder访问Service时,仅需指定一个句柄,Binder通过该desc找到对应的binder_ref,
* 再根据该binder_ref中的node变量得到binder_node(实体对象),进而找到对应的Service组件
*/
// 对应Binder实体对象中(hlist_head) refs引用对象队列中的一个节点
struct hlist_node node_entry;
// 引用对象所指向的Binder实体对象
struct binder_node *node;
// Binder引用的句柄值,Binder驱动为binder驱动引用分配一个唯一的int型整数(进程范围内唯一)
// ,通过该值可以在binder_proc->refs_by_desc中找到Binder引用,进而可以找到Binder引用对应的Binder实体
uint32_t desc;
// 强引用 计数
int strong;
// 弱引用 计数
int weak;
// 表示Service组件接受到死亡通知
struct binder_ref_death *death;
};
Binder的引用对象,每一个Clinet在驱动中都有一个binder_ref和他对应,内部有对应的进程信息binder_proc和binder_node信息。
client端拿着BpBinder引用对象,在驱动层对应着binder_ref,就可以找到binder_proc和binder_node信息,找到指定的进程可以实现交互。
binder_buffer
struct binder_buffer {
//entry对应内核缓冲区列表的buffers(内核缓冲区列表)
struct list_head entry; /* free and allocated entries by address */
//结合free,如果free=1,则rb_node对应free_buffers中一个节点(内核缓冲区)
//如果free!=1,则对应allocated_buffers中的一个节点
struct rb_node rb_node; /* free entry by size or allocated entry */
/* by address */
unsigned free:1;
/** Binder将事务数据保存到一个内核缓冲区(binder_transaction.buffer),然后交由Binder
* 实体对象(target_node) 处理,而target_node会将缓冲区的内容交给对应的Service组件
* (proc) 来处理,Service组件处理完事务后,若allow_user_free=1,则请求Binder释放该
* 内核缓冲区
*/
unsigned allow_user_free:1;
// 描述一个内核缓冲区正在交给那个事务transaction,用以中转请求和返回结果
struct binder_transaction *transaction;
// 描述该缓冲区正在被那个Binder实体对象使用
struct binder_node *target_node;
//表示事务时异步的;异步事务的内核缓冲区大小是受限的,这样可以保证事务可以优先放到缓冲区
unsigned async_transaction:1;
//调试专用
unsigned debug_id:29;
/**
* 存储通信数据,通信数据中有两种类型数据:普通数据与Binder对象
* 在数据缓冲区最后,有一个偏移数组,记录数据缓冲区中每一个Binder
* 对象在缓冲区的偏移地址
*/
// 数据缓冲区大小
size_t data_size;
// 偏移数组的大小(其实也是偏移位置)
size_t offsets_size;
// 用以保存通信数据,数据缓冲区,大小可变
uint8_t data[0];
// 额外缓冲区大小
size_t extra_buffers_size;
};
进程间,用户空间的数据不可共享,所以用户空间 = 不可共享空间
进程间,内核空间的数据可共享,所以内核空间 = 可共享空间
binder利用mmap()将用户空间映射到内核空间,使得数据的交互只需要一次。client在每次发起请求的时候会构造一个binder_buffer,放置在binder_transaction.buffer。
binder_thread
进程在启动时会创建一个binder线程池,并向其中注册一个Binder线程;驱动发现没有空闲binder线程时会向Server进程注册新的的binder线程。对于一个Server进程有一个最大Binder线程数限制,默认为16个binder线程。对于所有Client端进程的binder请求都是交由Server端进程的binder线程来处理的。
binder_work
一次进程请求的工作,会被封装在transaction里面,放置在对应进程的todo中。
binder_transaction
struct binder_transaction {
//调试调用
int debug_id;
// 用来描述的处理的工作事项,这里会将type设置为BINDER_WORK_TRANSACTION,具体结构看binder_work
struct binder_work work;
/** 源线程 */
// 源线程,即发起事务的线程
struct binder_thread *from;
// 源线程的优先级
long priority;
//源 线程的用户 ID
kuid_t sender_euid;
/** 目标线程*/
// 目标进程:处理该事务的进程
struct binder_proc *to_proc;
// 目标线程:处理该事务的线程
struct binder_thread *to_thread;
// 表示另一个事务要依赖事务(不一定要在同一个线程中)
struct binder_transaction *from_parent;
// 目标线程下一个需要处理的事务
struct binder_transaction *to_parent;
// 标志事务是同步/异步;设为1表示同步事务,需要等待对方回复;设为0异步
unsigned need_reply:1;
/* unsigned is_dead:1; */ /* not used at the moment */
/* 参考binder_buffer中解释,指向Binder为该事务分配内核缓冲区
* code与flag参见binder_transaction_data
*/
struct binder_buffer *buffer;
unsigned int code;
unsigned int flags;
/** 目标线程设置事务钱,Binder需要修改priority;修改前需要将线程原来的priority保存到
* saved_priority中,用以处理完事务回复到原来优先级
* 优先级设置:目标现场处理事务时,优先级应不低于目标Serivce要求的线程优先级,也
* 不低于源线程的优先级,故设为两者的较大值。
*/
long saved_priority;
};
transact() 调用在驱动里都会生产一个binder_transaction,放在对应server进程的todo队列里,然后唤醒server的binder线程来最终完成onTransact()调用。我们这里说的进程间通信其实就是发送端把binder_transaction,放到目标进程或其子线程的todo队列中,等目标进程或线程不断循环地从todo队列中取出数据并进行相应的操作。
binder_transaction_data
struct binder_transaction_data {
union {
/* target descriptor of command transaction */
__u32 handle;
/* target descriptor of return transaction */
binder_uintptr_t ptr;
} target;
//Binder实体带有的附加数据
binder_uintptr_t cookie; /* target object cookie */
// code是一个命令,描述了请求Binder对象执行的操作,表示要对目标对象请求的命令代码
__u32 code; /* transaction command */
// 事务标志,详细看transaction_flag结构体
__u32 flags;
// 发起请求的进程PID
pid_t sender_pid;
// 发起请求的进程UID
uid_t sender_euid;
// data.buffer缓冲区的大小,命令的真正要传输的数据就保存data.buffer缓冲区
binder_size_t data_size; /* number of bytes of data */
// data.offsets缓冲区的大小
binder_size_t offsets_size; /* number of bytes of offsets */
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 是进程一次传输的数据,data.buffer可能是普通数据或者是flat_binder_object(封装了binder引用或者binder对象)。binder驱动需要对此做处理。
flat_binder_object
struct flat_binder_object {
unsigned long type;
unsigned long flags;
union {
void *binder; //BBinder,可以找到对应的binder_node
signed long handle; //BpBinder,可以找到对应的binder_ref
};
void *cookie;
};
在进程将传输过程中,binder对象会被封装到flat_binder_object进行传输。
驱动根据type来判断,如果是BINDER_TYPE_BINDER,表示传输的是binder实体,会在内核创建binder_node,并创建binder_ref的flat_binder_object给目标进程,因为给远程的只能是binder_ref;如果是BINDER_TYPE_HANDLE,表示传输的是binder引用。
以上注释的资料来自:https://www.jianshu.com/p/5740a8447324
6. Binder的基本流程
这里不罗列Binder的大部分代码,都是以流程的形式讲述。
-
binder驱动初始化
- 每个进程启动的时候,都会打开binder驱动
在binder驱动中会创建对应的进程的binder_proc对象,并把当前进程等信息保存到binder_proc对象,并加入到全局链表 - 调用mmap()分配内存映射空间,驱动层创建binder_buffer,并放入当前binder_proc的buffers 链表
- 创建一个线程池(Binder主线程),负责处理Binder驱动发送上来的一些请求或返回值,线程的创建和销毁是在用户空间进行的,但是线程是受驱动层控制。可以说,这些线程就是真正负责干活的。
- 如果是ServerManager进程,驱动会创建binder_node,通过BINDER_SET_CONTEXT_MGR命令,成为ServerManager,ServiceManager进入循环,不断监听客户端的请求,没有请求时ioctl将阻塞,有请求到达是就分析请求类型,做出相应的处理
- 每个进程启动的时候,都会打开binder驱动
-
Server怎么注册服务
- server 通过flat_binder_object(内部含有binder对象),经内核驱动时会创建一个binder_node,挂载到对应的进程的binder_proc;
- 内核会创建一个binder_ref,挂在binder_node下,同时提供给ServiceManager,注册完成;
- server 的线程会在binder_proc的wait队列上等待,等待binder_work。
-
Client怎么调用服务
- 我们先假设client跟ServiceManager拿到了Server的BpBinder了,
- client调用transact()时候,在内核驱动会根据BpBinder指向的binder_ref,找到binder_node,创建了一个binder_transaction插入到对应server进程binder_prc的todo队列,唤醒server进程下的binder_thread。
- 同时将client的调用线程阻塞放在wait队列,等待结果
- server端的thread从binder_proc的todo列表取出,最终执行on_transact(),
- 处理完成以后,向内核发送命令,内核构造一个binder_transaction放在client调用线程的todo中,同时唤醒client调用线程。
- 被唤醒的client线程,处理todo的binder_transaction。
那么其实client同ServiceManager查询服务也是一样的道理,通过0号引用,默认拿到了ServiceManager的BpBinder了,后续的流程也是类似的。
不足
本篇文章写得很凌乱,主要有两个原因:
- 个人对binder的了解不够深入;
- binder涉及的东西太多了,还有很多东西没有涉及到;
后续要再好好学习,重新整理。
参考
https://www.moyokoo.com/p/25/
http://gityuan.com/2015/11/28/binder-summary/
https://www.jianshu.com/p/3c71473e7305
https://www.cnblogs.com/everhad/p/6246551.html
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/6293.html