binder是什么?
进程间通信机制
是一个驱动
Binder.java–>实现了Ibinder–夸进程能力
linux进程间通讯有哪些?
管道、socket、信号量、共享内存…
共享内存逻辑控制太复杂,易用性差,通信的时候直接读取,不同拷贝
传统IPC例如socket需要拷贝两次,基于c/s架构,传输效率低,开销大
上面两种都是基于上层协议,访问接入点是开放的,不安全。
Binder,只需拷贝一次,基于c/s架构,易用性高,为每个APP分配UID,同时支持匿名和实名。
进程间是怎么通讯的?
进程A想调用进程B的方法?
内存划分:内存被操作系统划分为两块,用户空间和内核空间,用户空间是用户程序代码运行的地方,内核空间是内核代码运行的地方。
为了安全,它们之间是隔离的,即使用户的程序崩溃了,内核也不受影响。
上面AB两个进程间的通信,两次拷贝就是把A的物理内存拷贝从A的用户空间拷贝到内核空间的数据缓存区,再拷贝到B的用户空间。
而Binder的一次拷贝实际上是通过mmap这个技术把A的数据从A用户空间拷贝到内核空间,这个地方通过mmap的映射给B,B可以直接调用,所以才说是一次拷贝。
MMAP:linux通过将一个虚拟内存区域与磁盘上的一个对象关联起来,以初始化这块虚拟内存的区域的内容,这个过程称之为内存映射。
先上一张结构图:
客户端的BpBinder是服务端BBinder的代理对象,通过服务返回获取到的。
所有的流程都没有贴源码,都是C、C++源码,了解大概的流程原理即可,看不懂前面贴的流程没关系,后面看完java层的例子和总结再回来看看流程基本就能理解七七八八了。
驱动层:
binder_init
1、分配内存
2、初始化设备
3、放入链表binder_devices 驱动列表
binder_open(谁调用就是谁,相当于把调用者的信息存储到列表中)
1、创建binder_proc对象
2、当前进程信息,保存到proc
3、filp->private_data = proc;
4、添加到binder_procs链表中
binder_mmap
vma–>进程的虚拟内存,4m,驱动定的,应用定的是1m-8k,binder传输超过1m就报错,intent就是通过binder去传输的,所以不能超过1m。
流程:
1、通过用户空间的虚拟内存大小–》分配一块内核的虚拟内存,一样大小
2、分配了一块物理内存,4kb,初始值,因为还没有真正工作,都不知道最终需要多大的空间,初始值先分配4kb大小来存放数据。
3、把这块物理内存映射到上面用户和内核的两个虚拟内存,关联起来。
binder_ioctl
读写操作,通过判断对应的case,当read_size>0或者write_size>0,则进行相应的读写操作。
serviceManager–sm 注册
1、打开驱动,内存映射,设置大小(128K)跟一般的系统服务的内存大小不一样
2、设置sm为大管家(设置为守护线程) 作用,为了管理系统服务 调用looper不停地循环检测,默认handler == 0的标志指令就是调用binder
1、创建binder_node结构体
2、proc–>binder_node
3、创建work和todo–》l类似messageQueue
3、BC_ENTER_LOOPER命令 开启for循环
1、写入状态Loop
2、去读数据 ret = wait… (这个时候没有数据)wait等待
sm获取–native获取
获取sm的情况
1、注册服务到sm–native
2、通过sm去获取服务–java
- ProcessState::self()->getContextObject(NULL)、
- ProcessState::self()
- 打开驱动:binder
- 设置线程最大数目:15个 不包括主线程
- mmap – 设置共享内存大小 — (1M-8K)到这里的时候内存大小改变了,初始128K
- getContextObject
- 创建一个BpBinder — 客户端的对象
- ProcessState::self()
- interface_cast
- new BpServiceManager(new BpBinder) --》 new Proxy(binder==BinderProxy)
- remote.transact -->远程调用
- remote == BpBinder
- java 层 — ServiceManager.addService
- new ServiceManagerProxy(new BinderProxy)
- mRemote == BinderProxy
- BinderProxy.mObject == BpBinder
- mRemote.transact == BpBinder.transact
服务的注册和获取,线程池管理
java–AMS如何注册到SM中
getIserviceManager().addService(name,service,false)
- getIServiceManager — new ServiceManagerProxy(new BinderProxy())
- ServiceManagerNative.asInterface(BinderInternal.getContextObject())
- BinderInternal.getContextObject — 返回 BinderProxy 对象
- ProcessState::self()->getContextObject:创建一个BpBinder
- javaObjectForIBinder – BinderProxy 和 BpBinder 互相绑定
- ServiceManagerNative.asInterface
- 返回 ServiceManagerProxy
#NAME?
- 返回 ServiceManagerProxy
- BinderInternal.getContextObject — 返回 BinderProxy 对象
- data.writeStrongBinder(service); – service == AMS — 将AMS 放入 data中
- mRemote.transact — mRemote == BinderProxy
- 获取BpBinder — IPCThreadState::transact
- 1.writeTransactionData — out 写入命令 --write — cmd == BC_TRANSACTION
- 2.waitForResponse
- talkWithDriver – 源码核心 — 代码非常长
- binder_transaction
- handle == 0 --》 sm
-
- target_node
- proc
- todo,wait
- 创建t,tcomplete,
- 数据拷贝
- binder_transaction_binder --> handle
- thread->transaction_stack = t; —> 方便sm找到client
- t->work.type = BINDER_WORK_TRANSACTION; – 给sm – 做事
- tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; – 给client–挂起
- wake_up_interruptible 唤醒sm
- binder_transaction
- talkWithDriver – 源码核心 — 代码非常长
- client挂起
- BR_NOOP ,BR_TRANSACTION_COMPLETE
- wait_event_freezable — 挂起
- sm处理添加服务
- BINDER_WORK_TRANSACTION — 要处理 cmd == BR_TRANSACTION
-
- reply初始化
- res = func(bs, txn, &msg, &reply); — 函数指针 — svcmgr_handler作用:获取或者添加 service
- sm是用 svclist 保存所有服务的
- binder_send_reply — bc_reply
- t->work.type = BINDER_WORK_TRANSACTION; — 给Client
list_add_tail(&t->work.entry, target_list);
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; – 给SM — 被挂起
list_add_tail(&tcomplete->entry, &thread->todo); - wake_up_interruptible(target_wait); – 唤醒 Client
- client 被唤醒
- BINDER_WORK_TRANSACTION — cmd = BR_REPLY;
- 获取BpBinder — IPCThreadState::transact
- ServiceManagerNative.asInterface(BinderInternal.getContextObject())
SM 处理 onTransact
-
IPCThreadState::executeCommand
-
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags); -
JavaBBinder.onTransact — C++
-
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, reinterpret_cast(&data), reinterpret_cast(reply), flags); – Binder.java.execTransact 方法
-
整个binder机制大概需要理解下面这两张图
1、客户端拿的是服务端给的代理对象:ServiceManagerProxy,里面包含BinderProxy,又包含BpBinder。
2、BpBinder实际上是服务端BBinder的代理对象,BBinder实际上是注册服务返回的SM。
3、调用是通过SM来进行的,SM有一个默认指令handler == 0即是调用binder,它是binder的大管家,调用的时候会把当前服务添加到SM里面,它维护了一个列表,例如上面距离的AMS,当你在获取AMS的时候不仅实例化了AMS,还会调用addService添加到SM中。
4、SM中主要通过ioctl跟binder驱动互动来实现读写。
命令流程:
1、客户端注册完SM的时候SM实际上是wait状态
2、客户端通过发送1的指令给binder驱动,binder接收到指令后产生两条指令2和3。
3、客户端接收到指令2进行挂起,servive接收到指令3进行数据读写,同时被唤醒。
4、service读写玩返回指令4,binder驱动又发出了两条指令。
5、service接收到5进行挂起休眠
6、客户端接收指令6,同时被唤醒,进行指令6的操作读写(可能有返回值)。
上面从java层到native到binder驱动层的一个图解。
1、java层的客户端进行通信,是绿色路线,从binderProxy到native的BpBinder到驱动,驱动调用service的native的BBinder指向JavaBinder最终指向java层的Binder。
2、如果是native层自己的通讯,则直接从客户端的BpServiceManager调用BpBinder到驱动层再到Service的BnServiceManager。
名字有点多,看起来也有一点乱,native层就是SM在管理,所有的调用都是最终调用native的SM,然后SM返回给你代理对象,SM再自己调用驱动层。
其实上面就是java层获取SM和和native获取SM的不同的对象来源路劲,很明显,native层获取SM比java层少。
如果是我们自己去实现这种binder的调用流程会比较麻烦,所以android为我们提供了AIDL。
AIDL需要注意一些事项:
定义自己的接口方法,AIDL文件
如果是自定义类,记住实现序列化
下面文件是AIDL
// TestAidl.aidl
package com.example.startservicefromaotherapp;
//即使在同样的包中也需要导包
import com.example.startservicefromaotherapp.Info;
// Declare any non-default types here with import statements
interface TestAidl {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void addInfo(in Info info);
List<Info> getInfoList();
}
// Info.aidl
package com.example.startservicefromaotherapp;
// Declare any non-default types here with import statements
parcelable Info;
在你的main目录下创建一个Folder的AIDL文件,复制包路劲创建一个Package,然后创建你需要的接口,是aidl文件,自定义类需要序列化传输,最后把你的AIDL和info全部在另一端复制,记住路劲要一致。
@Override
public IBinder onBind(Intent intent) {
return binder;
}
private IBinder binder = new TestAidl.Stub() {
@Override
public void addInfo(Info info) throws RemoteException {
}
@Override
public List<Info> getInfoList() throws RemoteException {
return null;
}
};
上面的addInfo和getInfoList就是我们定义的方法啦,通过这种service的绑定把我们服务端的代理对象给客户端
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (iBinder == null) {
iBinder = TestAidl.Stub.asInterface(service);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
iBinder = null;
}
}
绑定之后随便用,跟我们寻常的binderService简直不要太相似。
看一下AIDL为什么生成的方法asInterface
public static com.example.startservicefromaotherapp.TestAidl asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
//判断是否在同一个进程内,如果是则直接返回普通binder对象
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.startservicefromaotherapp.TestAidl))) {
return ((com.example.startservicefromaotherapp.TestAidl)iin);
}
//否则返回Proxy的binder的代理对象,区别就是能跨进程传输,通过native获取到的
return new com.example.startservicefromaotherapp.TestAidl.Stub.Proxy(obj);
}
obj就是onServiceConnected方法,服务端返回的binder代理对象,说明Proxy持有了它
@Override public void addInfo(com.example.startservicefromaotherapp.Info info) throws android.os.RemoteException
{
//包装的数据集 data是发送的数据 reply接收返回的数据
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((info!=null)) {
_data.writeInt(1);
info.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
//transact会挂起,最终调用到onTransact
boolean _status = mRemote.transact(Stub.TRANSACTION_addInfo, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addInfo(info);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
这里的互相调用其实就是上面说的指令命令,AIDL其实自动我们做了一些工作也不复杂,我们完全可以自己书写,具体有兴趣了解的朋友可以自己写一个简单AIDL查看源码,代码并不多。对于binder的认知暂时就介绍到这里了。