Binder可以实现进程与进程之间的通信(IPC), Binder是Android底层系统的一个特色了,它很好地解决了进程间通讯的问题。
可能很多小伙伴对Binder
感觉有点儿陌生,但是Binder
在Android系统中无处不在,比如:
Binder是Android独有的跨线程通讯机制,它的运行机制和现实中的一个例子很像,我们来看一张图
这张图很形象的提现了Binder的运行机制,有Client(个人电脑)
,Server(应用服务器)
,Binder(路由器)
,ServiceManager(DNS服务器)
Binder
对服务端(Server)而言相当于服务端提供特定服务的接入点
,想要对接该服务就要从这个接入点
入手 对于客户端(Client)而言,Binder相当于通向服务端(Server)管道
的入口,要想和服务端(Server)某个服务通讯,必须先建立管道,并获得管道的入口,也就是接入点
ServiceManager
相当于DNS服务器
注:
这里只是举个形象的栗子,具体是怎样的,都做了什么,下面会慢慢讲~
为什么Android会采用Binder做IPC(进程间通讯)呢?这也是Binder
的由来,首先Linux
中是有多种跨进程通讯的方式,但是它们不太适用于Android的跨进程通讯的场景,我们大概来看下:
所以Binder
是应需求而生,前面三种方式只是说了不是和Android的进程建通讯,那么Binder为什么适合呢?
主要是两个方面
安全性
Binder协议支持对通讯双方的身份信息进行较校验,既支持匿名
的Binder也支持实名
的Binder,像传统的Socket通讯,并没有严格的身份校验,只要知道ip地址就可以访问,在Android中每个应用安装成功都会分配一个唯一的UID
,而每个进程都有一个PID
,例如在Android9.0源码中startActivity()
会对UID
和PID
做校验,下面会提到~性能
Binder机制在进程间通讯时,数据只需Copy一次
,而传统的通讯方式,比如管道
的方式需要Copy
两次,性能方面仅次于共享内存的方式~另外还有一点是为什么Binder设计的是Client/Server
的形式,因为系统提供了一个服务,可能很多app都需要使用该服务,所以是一个一对多
的场景,所以Binder采用的是Client/Server
的形式
如图(管道方式需要两次Copy操作):
Client
和 Server
之间的桥梁,Client
可以通过ServiceManager
拿到Server
中Binder
实体的引用Client
、Server
和ServiceManager
的桥梁,Android重很多系统服务是通过Binder
拿到的,比如context.getSystemService (Context.AUDIO_SERVICE)
获取音量的服务
其中前三者Client
、Server
、ServiceManager
都属于用户空间,而Binder驱动
属于内核空间 注意用户空间是不可以进程间通讯的,内核空间是可以进程间通讯的 这里需要主要的是Binder驱动
它是有个线程池的存在,有可能是并发, 这个线程池是由Binder驱动
管理的,一个进程的Binder线程数默认是16
,超过这个数会阻塞等待~
首先需要简单说下AIDL
: AIDL是Android Interface definition language 安卓接口定义语言,是Binder
中Client进程
和Server进程
通讯的语言,是为了Binder
简化代码的架构
AIDL
定义的接口Java层的
Binder类,代表的是Binder的本地对象,有个重要的内部类BinderProxy
Stub
的静态内部类,继承自Binder
,是个抽象类,具体实现Iinterface
的接口的具体逻辑,开发者自己实现先看两张图,Binder通讯流程图:
如图: Binder通讯流程首先是,Client需要发送数据,做了(只做一次)copy from user
到BinderProxy
,BinderProxy
是可以操作内核的缓存区,内核的缓存区和Binder创建的内存映射(Binder创建的接收缓存区)是存在映射关系的,而服务端是与内存映射(Binder创建得接收缓存区)是存在直接的内存映射关系,所以只需要一次copy
操作,相当于这一次复制,直接将数据复制到了Server进程
的内存空间中去了。当然这中间室友校验的,比如: descriptor
的Binder实体的引用
和Binder实体是否匹配
等
详细流程图:
源码分析
下面从源码角度简单分析内核层主要做的以下步骤:
我们从Android源码中都可以看到这些,下面代码以Android9.0
为例:
有兴趣的小伙伴可以自行翻阅:Android在线源码阅读
首先我们看下ServiceManager启动,ServiceManager
是在Android系统启动时就就会唤起的服务可见system/core/rootdir/init.rc
的407
行:
start servicemanager
ServiceManager
会完成打开binder设备和开辟内存映射 (128K)的动作,可见device/google/cuttlefish_kernel/4.4-x86_64/System.map
的25306行
(该文件需要下载查看,不支持在线浏览):
ffffffff815dbf50 t binder_mmap
在frameworks/native/cmds/servicemanager/service_manager.c
的main()
方法中有:
if (argc > 1) {
driver = argv[1];
} else {
//打开Binder设备文件,返回文件描述符
driver = "/dev/binder";
}
//Binder的buffer创建,用于进程间数据传输,开启128k大小的内存映射,路径见下方
bs = binder_open(driver, 128*1024);
其实Service
的注册也是在service_manager
中的do_add_service()
方法中完成的,这个不是Binder
的核心知识,简单提下,感兴趣的可以看下
//权限检查
if (!svc_can_register(s, len, spid, uid)) {
ALOGE("add_service('%s',%x) uid=%d - PERMISSION DENIED\n",
str8(s, len), handle, uid);
return -1;
}
//根据服务名在svclist链表上查找,看服务是否已经注册
si = find_svc(s, len);
if (si) {
if (si->handle) {
//注册过
ALOGE("add_service('%s',%x) uid=%d - ALREADY REGISTERED, OVERRIDE\n",
str8(s, len), handle, uid);
svcinfo_death(bs, si);
}
si->handle = handle;
} else {
//没注册,分配一个服务管理的结构svcinfo,并将其添加到链表的list当中
si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
if (!si) {
ALOGE("add_service('%s',%x) uid=%d - OUT OF MEMORY\n",
str8(s, len), handle, uid);
return -1;
}
si->handle = handle;
si->len = len;
memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
si->name[len] = '\0';
si->death.func = (void*) svcinfo_death;
si->death.ptr = si;
si->allow_isolated = allow_isolated;
si->dumpsys_priority = dumpsys_priority;
//将代表该服务的结构插入到链表
si->next = svclist;
svclist = si;
}
//增加Binder的应用计数
binder_acquire(bs, handle);
//该服务退出需要通知ServiceManager
binder_link_to_death(bs, handle, &si->death);
return 0;
打开Binder设备驱动是在frameworks/native/cmds/servicemanager/binder.c
的binder_open()
方法中有这么一行代码:
//打开Binder设备驱动的时候,开启128k大小的内存映射是在这里执行的
bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
打包Parcel,数据写入binder设备,copy_from_user可见frameworks/native/libs/binder/IServiceManager.cpp
的addService()
方法: 这里会将Service
相关信息打包成Parcel
对象,并且调用remote()->transact()
方法往下一步传输
virtual status_t addService(const String16& name, const sp& service,
bool allowIsolated, int dumpsysPriority) {
Parcel data, reply;
//Parcel对象打包过程
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
data.writeInt32(dumpsysPriority);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
数据写入binder设备的过程在frameworks/native/libs/binder/IPCThreadState.cpp
中实现的writeTransactionData()
方法,
//这里主要是将`Parcel`对象中的信息封装成结构体,并且写入到`mOut`当中
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
...
//将数据写入Binder的设备当中,并等待返回结果
err = waitForResponse(reply);
另外从Binder设备中不停地读写的实现方式,是通过线程池
的方式(上文有提到),不停地去读写,具体可见: frameworks/native/libs/binder/IPCThreadState.cpp
的joinThreadPool()
方法,主要是定义了一个主线程中的线程池,
//将对象设为当前线程的私有
pthread_setspecific(gTLS, this);
clearCaller();
//输入buffer预分配256大小的空间
mIn.setDataCapacity(256);
//输出buffer预分配256大小的空间
mOut.setDataCapacity(256);
对Binder设备数据的读写,主要的工作就是循环的对mIn
和mOut
进行IO
的读写,然后发送到Binder的设备中 对于数据是否需要读取/写的
// Is the read buffer empty?,是否有读的请求
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// We dont want to write anything if we are still reading
// from data left in the input buffer and the caller
// has requested to read the next data.
// 是否有写的请求
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
...
//将读写的请求数据发送到Binder设备中
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
最后在这里我分享自己收录在整理的Android学习知识点文档,里面有Handler相关面试题和其他知识点的讲解,希望可以帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,可以分享给身边好友一起学习
有需要的朋友可以点赞+评论+转发,关注我,然后私信我【666】获取,也可以点击《Android学习PDF+架构视频+面试文档》查看更多;