Android进程间通信机制Binder
注:本文大部分代码来自安卓11
● 从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备驱动,它的设备驱动是/dev/binder,该通信方式以前在linux中没有
● 从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager)和相应ManagerService的桥梁
● 从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当你bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务(本地和远程)
为什么需要进程间通信?
在操作系统中,进程与进程之间的内存、调度优先级等是不共享(都运行与用户空间,彼此独立)的,进程之间无法直接访问对方的数据,因而进程间的数据交互需要采用特殊的方式(需要内核空间支持)进行,也就是进程间通信。
为什么不用已有的进程间通信机制?
Linux系统本身有许多IPC手段,为什么Android要重新设计一套Binder机制呢?
为什么选用Binder,我们知道Android也是基于Linux内核开发的,Linux现有的进程通信手段有以下几种:
a. 管道:在创建时分配一个page大小的内存,缓存区大小比较有限;
b. 消息队列:信息复制两次,额外的CPU消耗;不合适频繁或信息量大的通信;
c. 共享内存:无须复制,共享缓冲区直接付附加到进程虚拟地址空间,速度快;但进程间的同步问题操作系统无法实现,必须各进程利用同步工具解决;
d. 套接字(Socket):作为更通用的接口,传输效率低,主要用于不同机器或跨网络的通信;
e. 信号量:常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
既然有现有的IPC方式,为什么重新设计一套Binder机制呢。主要是出于以上三个方面的考量:
- 从性能上:从数据拷贝次数来看Binder只需要进行一次内存拷贝,而管道、消息队列、Socket都需要两次,共享内存不需要拷贝,Binder的性能仅次于共享内存。
- 从稳定性:上面说到共享内存的性能优于Binder,那为什么不适用共享内存呢,因为共享内存需要处理并发同步问题,控制负责,容易出现死锁和资源竟争,稳定性较差。而Binder基于C/S架构,客户端与服务端彼此独立,稳定性较好。
- 安全性:Binder会为你注册的服务分配一个UID,用来作为鉴别进程的重要标志,如果你的服务是恶意软件启动的服务,那么我们就可以通过UID找到这个服务并进行禁止。传统IPC是由服务自己往数据包里填入UID/PID去告知内核,这个标记完全是在用户空间控制的,没有放在内核空间,因此有被恶意篡改的可能,篡改后就无法通过此uid找到具体的恶意服务了。因此Binder的安全性更高。
而管道、消息队列、Socket都需要两次数据拷贝,共享内存不需要拷贝,指的是:
答:用户空间的两个进程是无法直接进行数据交互的,它们之间的通信需要内核空间提供支持
从用户空间(发送数据方的用户空间进程)拷贝到内核空间算一次,
再从内核空间拷贝到用户空间(接受数据方的用户空间进程)算第二次
共享内存直接在内核空间中,发送方和接受方直接访问,不需要拷贝
Binder因为不是Linux内核的一部分,它是通过一种Linux的动态内核可加载模块这一机制来实现的。
动态内核可加载模块:是一种可以单独编译,但是不能独立运行的模块,它在运行时会被链接到内核作为内核的一部分运行。
Android系统就是通过动态添加一个内核模块在内核空间运行,然后以这个内核模块为桥梁实现的进程间通信。这个内核模块就是我们平时所说的Binder驱动(Binder Drivers)
基本原理
步骤 过程描述
虚线就是代表Client、Server不是直接与ServiceManager直接交互,而是通过Binder驱动
Linux内存映射:
内存映射可以将用户空间的一块内存区域映射到内核空间,用户空间对这块区域的修改可以直接映射到内核空间,
反之内核空间对这块内存区域的修改也可以映射到用户空间。实际上在操作系统的表示是一个物理页被内核空间
和用户空间同时引用
前置知识
Android智能指针
在Android系统的应用程序框架层中,有相当一部分代码是使用C++语言开发的。C++指针使用不当,轻则造成内存泄漏,重则造成莫名其妙的逻辑错误,甚至系统崩溃。因此,Android系统为我们提供了C++智能指针,通过引用计数来维护对象的生命周期,Binder对象就是如此。
引用计数技术存在造成循环引用的问题,解决方法是将对象分为强引用和弱引用两种进行引用计数。在使用强引用计数和弱引用计数的解决方案中,一般将有关联的对象划分为“父-子”和“子一父”关系。在“父-子”关系中,“父”对象通过强引用计数来引用“子”对象;而在“子-父”关系中,“子”对象通过弱引用计数来引用“父”对象。这样就可以解决由于相互引用而造成对象不能释放的问题了。被弱引用引用的对象释放不受影响,被强引用引用的对象释放受约束,不可用随便回收释放。
Binder对象引用计数技术
其实在Client进程和Server进程的一次通信过程中,涉及了四种类型的对象,它们分别是位于Binder驱动程序中的Binder实体对象(binder_node)和Binder引用对象(binder_ref),以及位于Binder库中的Binder本地对象(BBinder)和Binder代理对象(BpBinder),它们的交互过程如图5-17所示。
它们的交互过程可以划分为五个步骤,如下所示。
(1)运行在Client进程中的Binder代理对象通过Binder驱动程序向运行在Server进程中的Binder本地对象发出一个进程间通信请求,Binder驱动程序接着就根据Client进程传递过来的Binder代理对象的句柄值来找到对应的Binder引用对象(查找引用对象的红黑树的过程)。
(2)Binder驱动程序根据前面找到的Binder引用对象找到对应的Binder实体对象(通过该节点的node域找到对应的binder_node节点,因为binder_ref和binder_node都处于binder驱动的地址空间中,所以是可以用指针直接指向的),并且创建一个事务(binder_transaction)来描述该次进程间通信过程。
(3)Binder驱动程序根据前面找到的Binder实体对象(从属服务进程的)来找到运行在Server进程中的Binder本地对象(通过binder_node节点的cookie域),并且将Client进程传递过来的通信数据发送给它处理。
(4)Binder本地对象处理完成Client进程的通信请求之后,就将通信结果返回给Binder驱动程序,Binder驱动程序接着就找到前面所创建的一个事务。
(5)Binder驱动程序根据前面找到的事务的相关属性来找到发出通信请求的Client进程,并且通知Client进程将通信结果返回给对应的Binder代理对象处理。
从这个过程就可以看出,Binder代理对象依赖于Binder引用对象,而Binder引用对象又依赖于Binder实体对象,最后,Binder实体对象又依赖于Binder本地对象。这样,Binder进程间通信机制就必须采用一种技术措施来保证,不能销毁一个还被其他对象依赖着的对象。为了维护这些Binder对象的依赖关系,Binder进程间通信机制采用引用计数技术来维护每一个Binder对象的生命周期。
Binder_ref和binder_node是红黑树结构,说明了进程之间通信的很复杂,进程A可能作为Client与B,C通信, 同时也可能作为Server与D,E通信,和不同的进程通信又是一个不同的Binder_ref和Binder_node节点
架构分析
从不同层次进行分析
从内核空间(Kernel)与用户空间(Java、C程序库)角度看
- 框架层:框架是一个中间层,它对接了底层驱动的实现,封装了复杂的内部逻辑,并提供对外使用的
内部接口,它分为C++和Java两个部分,为了实现功能的复用,中间用JNI衔接。
Binder Java
Binder JNI
Binder C - 驱动层:驱动层位于Linux内核中,它提供了最底层的数据传递、对象标识、线程管理、调度过程控制 等功能。
接下来我们看Binder体系各个类之间的关系。Java层、JNI层、C++层
这个图比较全面,我将不同的模块打上了不同的颜色。
● 黄色:IInterface接口,包含Java层的接口和C++层的接口,它们的功能是一样的(下层封装给上层用)。
● 绿色:IBinder接口,包含Java层的接口和C++层的接口,它们的功能是一样的(下层封装给上层用)。
● 红色:本地Binder (同一进程时用)
● 灰色:远程调用的代理Binder (不同进程时用)
● 紫色:上面都是在说接口,这一块就是可以由我们用户实现的位置。这里则是指两个具体的服务(Client与Server),包含Java层的服务ActivityManagerService(使用Binder Java层实现IPC,底层调用Binder Native层)
Binder体系的核心实现都在C++层,最为核心的就是BpBinder和BBinder了。各个类的作用如下所示:
Java层:
frameworks/base/core/java/android/os
frameworks/base/core/java/com/android/internal/os
● Ilnterface:供Java层Binder服务继承的接口。
● IBinder:Java层的IBinder类,提供transact()方法来调用远程的服务。
● Binder:实现了IBinder接口,封装了JNI的实现,Java层Binder服务的基类。
● BinderProxy:实现了IBinder接口,封装了JNI实现,提供remote()方法调用远程服务。
● Parcel:Java层的数据包装器,底层实现在C++层。
当是跨进程时ActivityManagerProxy就是通过ActivityManagerNative的asInterface进行asInterface(BinderProxy)转化来的,当不是跨进程时,则是直接就能用的
BinderProxy代表从ServiceManager拿到的一个远程服务代理对象
IActivityManager代表一个aidl接口类
ActivityManagerNative代表生成的中间服务类(旧方式我们手写的,新方式会根据你写的aidl文件自动生成的中间类,里面就包含这些了,具体后面Binder通信的Jav接口讨论)
C++层:
frameworks/native/include/binder/Binder.h
frameworks/native/include/binder/
frameworks/native/libs/binder/
frameworks/native/cmds/servicemanager/
● RefBase:继承了RefBase类的子类对象均可以通过强指针和弱指针来维护它们的生命周期(是一个在引用计数依赖中解决循环依赖的方法,这也说明了BInder本地对象是通过引用计数技术来维护生命周期的。)
————————————
● BpRefBase:RefBase的子类,提供remote方法获取远程Binder
BpRefase类继承了RefBase类,因此它的子类对象,即Binder代理对象也可以通过强指针和弱指针来维护生命周期。BpRefBase类有一个重要的成员变量mRemote,它指向一个BpBinder对象,可以通过成员函数remote来获取。BpBinder类实现了BpRefBase类的进程间通信接口
————————————
● IInterfatce:Binder服务接口的基类,Binder服务通常需要同时提供本地接口Bnlnterface和远程接口BpInterface,这两个接口都继承于Ilnterface,拥有相同的方法,本地接口实现服务,远程接囗提供给Client调用。
○ onAsBinder():由子类实现,在本地对象的实现类中返回本地对象。在远程对象的实现类中返回
远程对象。onAsBinder()会被静态方法asBinder()调用
● BpInterface:远程接口的基类,远程接口是供Client调用的接口集。
● BnInterface:本地接口的基类,本地接口是需要服务中真正实现的接口集。
● IBinder:Binder对象的基类,这个接口定义了与远程对象交互的协议。
————————————
● BpBinder:远程Binder(Client端的Binder),这个类提供handle()方法返回指向Binder服务实现者的句柄,提供transact()方法来发送请求,BpXXX实现中会用到。
BpBinder类的成员变量mHandle是一个整数,它表示一个Client组件的句柄值,可以通过成员函数handle来获取。每一个Client组件在Binder驱动程序中都对于一个Binder引用对象,而每一个Binder引用对象都有一个句柄值,其中,Client组件就是通过这个句柄值来和Binder驱动程序中的Binder引用对象建立对于关系的。
————————————
● BBinder:本地Binder(Server端的Binder),服务实现方的基类,提供了onTransact()接口来接收请求,和BpBinder的transact()方法对应。
BBinder类重要的成员函数onTransact,它负责分发于业务相关的进程间通信请求。事实上,与业务相关的进程间通信请求是由Binder本地对象类的子类,即Service组件来负责处理的。
而BpBinder的成员函数transact用来向运行在Server进程中的Service组件发送进程间通信请求,这是通过Binder驱动程序间接实现的。BpBinder类的成员函数transact会把BpBinder类的成员变量mHandle,以及进程间通信数据发送给Binder驱动程序,这样Binder驱动程序就能根据这个句柄值来找到对应的Binder引用对象,继而找到对应的Binder实体对象,最后就可以将进程间通信数据发送给对应的Service组件了(这里指实现BBinder的子类那些)
————————————
● ProcessState:代表了使用Binder的进程。它是一个单例类,一个进程只有一个实例。
● IPCThreadState:代表了使用Binder的线程,这个类封装了与BInder驱动通信的逻辑。它是一个单例类,一个线程有一个实例。
○ transact():公开接口,供Proxy发送数据到驱动,并读取返回结果。
○ sendReply():供Server端写回请求的返回结果给驱动。
○ waitForResponse():发送请求后等待响应结果。
○ talkWithDriver():通过iotcl BINDER_WRITE_READ来与驱动通信。
○ writeTransactionData():写入一次事务的数据。
○ executeCommand():处理binder_driver_return_protocol协议命令。
○ freeBuffer():通过BC_FREE_BUFFER命令释放Buffer。
● Parcel:在Binder上传递数据的包装器。
无论是BBinder类,还是BpBInder类,它们都是通过IPCThreadState类来和Binder驱动程序交互的,每一个使用了Binder进程间通信机制的进程都有一个Binder线程池,用来处理进程间通信请求。对于每一个Binder线程来说,它的内部都有一个IPCThreadState对象,我们可以通过IPCThreadState类的静态成员函数self()来获取,并且调用它的成员函数transact来和Binder驱动程序交互。在IPCThreadState类的成员函数transanct内部,与Binder驱动程序的交互操作又是通过调用函数talkWithDriver来实现的,它一方面负责向Binder驱动发送进程间通信请求,另一方面又负责接收来自Binder驱动程序的进程间通信请求。
IPCThreadState类有一个成员变量mProcess,它指向一个ProcessState对象。对于每一个使用了Binder进程间通信机制的进程来说,它的内部都有一个ProcessState对象,它负责初始化Binder设备,即打开设备文件/dev/binder,以及将设备文件/dev/binder映射到进程的地址空间。由于这个ProcessState对象在进程范围内是唯一的,因此,Binder线程池中的每一个线程都可以通过它来和Binder驱动程序建立连接。
进程中的ProcessState对象可以通过ProcessState类的静态成员函数self来获取。第一次调用ProcessState类的静态成员函数self时,Binder库就会为进程创建一个ProcessState对象,并且调用函数open来打开设备文件/dev/binder,接着又调用函数mmap将它映射到(用户)进程的地址空间,即请求Binder驱动程序为进程分配内核缓冲区。设备文件/dev/binder映射到(用户)进程的地址空间后,得到的内核缓冲区的用户地址就保存在其成员变量mVMStar中。
——————————————————
Kernel层
/bsp/kernel/kernel4.14/drivers/android/binder.c
原理分析
Binder通信流程
我们上面以及聊过,Binder的通信流程是先由Client发出,经过Binder Framework到达Kernel,再经过Kernel转发,最终通过Binder Framework到达Server。整体的通信流程有点像网络通信。
我们以常见的启动四大组件为例来描述Binder的通信流程,如下所示:
1、当我们在Activity里启动一个Service,这个调用会经过层层传递到ActivityManagerProxy,然后ActivityManagerProxy会通过Binder发起跨进程调用。
2、接着Client就会向Binder Driver发起binder ioctl请求,在IPCTreadState::waitForResponse里执行While循环,在While循环中调用IPCThreadState::talkWithDriver()与驱动交互,然后Client线程等待驱动回复
binder驱动收到BC_TRANSACTION事件后的应答消息:
- BR_TRANSACTION_COMPLETE:对于oneway transaction(非阻塞通信、单向),当收到该消息,则完成了本次Binder通信
- BR_DEAD_REPLY:回复失败,往往是线程或节点为空,则结束本次通信Binder
- BR_FAlLED_REPLY:回复失败,往往是transaction出错导致,则结束本次通信Binder
- BR_REPLY:对于非oneway transaction时,当收到该消息,则完整地完成本次Binder通信
3、上述命令除了BR_TRANSACTION_COMPLETE:其他回复Client端的进程都会接着调用IPCThreadState::executeCommand()处理驱动返回的命令。
4、对于Server端而言,会调用IPCThreadState::joinThreadPool循环执行IPCThreadState::getAndExecuteCommand,最终调用ActivityManagerService.startService方法。如果需要回复Client会调用talkWithDriver与驱动通信
Client在与Service的通信过程按照是否需要Server返回数据可以分为两种方式:
- 单向模式:不需要Server返回数据
- 双向模式:需要Server返回数据
对以上的代码做简单分析:
binder_ioctl -> binder_ioctl_write_read -> binder_thread_write
```java
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
...
switch (cmd) {
case BINDER_WRITE_READ:
ret = binder_ioctl_write_read(filp, cmd, arg, thread)
...
}
——————————————————
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
...
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) { //把用户空间数据ubuf拷贝到bwr
ret = -EFAULT;
goto out;
...
if (bwr.write_size > 0) { //写缓冲区有数据时,binder_thread_write
ret = binder_thread_write(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
...
}
if (bwr.read_size > 0) { //读缓冲区有数据时,binder_thread_read
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
filp->f_flags & O_NONBLOCK);
trace_binder_read_done(ret);
binder_inner_proc_lock(proc);
...
}
...
//将内核数据bwr拷贝到用户空间ubuf
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
}
Client端
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
if (reply) {
//等待响应
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
...
} else {
//oneway,则不需要等待 reply 的场景
err = waitForResponse(nullptr, nullptr);
}
—————————————————————
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
int32_t cmd;
int32_t err;
//在while循环中做下面的事情:
while (1) {
//循环中通过talkWithDriver与驱动通信
if ((err=talkWithDriver()) < NO_ERROR) break;
...
if (mIn.dataAvail() == 0) continue;
cmd = mIn.readInt32();
switch (cmd) {
... //循环中等待Binder驱动回复,执行executeCommand
err = executeCommand(cmd);
...
}
}
...
return err;
}
—————————————————
status_t IPCThreadState::executeCommand(int32_t cmd)
{
...
switch ((uint32_t)cmd) { //switch判断
case BR_ERROR:
result = mIn.readInt32();
break;
case BR_OK:
break;
case BR_ACQUIRE:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
ALOG_ASSERT(refs->refBase() == obj,
"BR_ACQUIRE: object %p does not match cookie %p (expected %p)",
refs, obj, refs->refBase());
obj->incStrong(mProcess.get());
IF_LOG_REMOTEREFS() {
LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj);
obj->printRefs();
}
mOut.writeInt32(BC_ACQUIRE_DONE);
mOut.writePointer((uintptr_t)refs);
mOut.writePointer((uintptr_t)obj);
break;
case BR_RELEASE:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
...
Server端
void IPCThreadState::joinThreadPool(bool isMain)
{
...
do { //循环里面不断的读取命令数据,然后解析命令数据,并给出相应的响应。
processPendingDerefs();
// now get the next command to be processed, waiting if necessary
result = getAndExecuteCommand();
...
}
—————————————————————
status_t IPCThreadState::getAndExecuteCommand()
{
status_t result;
int32_t cmd;
result = talkWithDriver();
if (result >= NO_ERROR) {
size_t IN = mIn.dataAvail();
if (IN < sizeof(int32_t)) return result;
cmd = mIn.readInt32();
IF_LOG_COMMANDS() {
alog << "Processing top-level Command: "
<< getReturnString(cmd) << endl;
}
pthread_mutex_lock(&mProcess->mThreadCountLock);
mProcess->mExecutingThreadsCount++;
if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&
mProcess->mStarvationStartTimeMs == 0) {
mProcess->mStarvationStartTimeMs = uptimeMillis();
}
pthread_mutex_unlock(&mProcess->mThreadCountLock);
//解析来自驱动的命令
result = executeCommand(cmd);
pthread_mutex_lock(&mProcess->mThreadCountLock);
mProcess->mExecutingThreadsCount--;
if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads &&
mProcess->mStarvationStartTimeMs != 0) {
int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;
if (starvationTimeMs > 100) {
ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms",
mProcess->mMaxThreads, starvationTimeMs);
}
mProcess->mStarvationStartTimeMs = 0;
}
pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
return result;
}
Binder驱动
这一块可以理解为是用户线程与Binder这个驱动设备交互的过程(用户态切换到内核态的系统调用过程)
Binder是一个miscellaneous(混杂的,各种各样的)类型的驱动,本身不对应任何硬件,所有的操作都在软件层,Binder核心操作包含三个函数。
open:打开Binder设备。
mmap:进行内存映射。
ioctl:进行实际的通信操作。
binder_open
任何进程使用Binder之前都需要通过open(“/dev/binder”)打开Binder设备,驱动程序会为进程创建一个唯一的结构体binder_proc,该结构体持有进程的pid,与打开binder设备文件的进程相关联,然后将该结构体加入全局hash队列binder_procs中。该队列保存了所有正在使用binder驱动的进程的binder_proc结构体,也就是说通过遍历该队列可以知道当前有多少个进程在使用binder进程间通信。
Binder驱动核心是维护一个binder_proc类型的双向链表,里面记录了servicemanager在内的所有service信息,当client去请求某个service时,binder驱动去binder_proc中查找相应的service返回给client,同时增加service的引用个数。
binder_procs里面有许多binder_proc:
对上面的基础数据结构进行介绍
/bsp/kernel/kernel4.14/drivers/android/binder.c
**struct binder_proc:**每个进程调用open()打开binder驱动都会创建该结构体,用于管理IPC所需的各种信息。
struct binder_proc {
//挂载在全局binder_procs链表中的节点。
struct hlist_node proc_node;
//使用红黑树来保存使用Binder机制通信的进程的Binder线程池的线程、关联binder_thread->rb_node
struct rb_root threads;
//binder_proc进程内的binder实体组成的红黑树(关联binder_node->rb_node)
struct rb_root nodes;
//binder_proc进程内的binder引用组成的红黑树,以引用句柄的值来排序(关联binder_ref->rb_node_desc)
struct rb_root refs_by_desc;
//binder_proc进程内的binder引用组成的红黑树,该引用以它对应的binder实体的地址来排序(关联binder_ref->rb_node)
struct rb_root refs_by_node;
//空闲Binder线程会睡眠在wait描述的等待队列中
struct list_head waiting_threads;
//进程pid
int pid;
//保存内核缓冲区在用户空间的地址
struct vm_area_struct *vma;
//保存使用Binder机制通信的进程信息
struct task_struct *tsk;
struct files_struct *files;//打开文件结构体
//挂载在全局延迟工作项链表binder_deferred_list中的节点
struct hlist_node deferred_work_node;
//描述延迟工作项的具体类型
int deferred_work;
//表示要映射的物理内存在内核空间中的起始位置;
void *buffer;
//它表示的是内核使用的虚拟地址与进程使用的虚拟地址之间的差值
ptrdiff_t user_buffer_offset;
//指向被划分为若干小块的内核缓冲区
struct list_head buffers;
//指向没有分配物理页面的空闲小块内核缓冲区,用红黑树来组织,是为了分配合适的缓冲区能提高效率
struct rb_root free_buffers;
//指向已经分配了物理页面正在使用的小块内核缓冲区
struct rb_root allocated_buffers;
//保存当前可以用来保存异步事务数据的内核缓冲区的大小
size_t free_async_space;
//为内核缓冲区分配的物理页面
struct page **pages;
//保存Binder驱动程序为进程分配的内核缓冲区的大小
size_t buffer_size;
//保存空闲内核缓冲区的大小
uint32_t buffer_free;
//进程待处理工作项队列
struct list_head todo;
//空闲Binder线程会睡眠在wait描述的等待队列中
wait_queue_head_t wait;
//统计进程接收到的进程间通信请求次数
struct binder_stats stats;
//死亡通知队列
struct list_head delivered_death;
//保存Binder驱动程序最多可以主动请求进程注册的线程数量
int max_threads;
//记录请求注册的线程个数
int requested_threads;
//记录响应请求的线程个数
int requested_threads_started;
//保存进程当前空闲的Binder线程数目
int ready_threads;
//设置进程优先级
long default_priority;
struct dentry *debugfs_entry;
};
进程打开设备文件/dev/binder之后,还必须调用函数mmap将它映射到进程的地址空间来,实际上是请求Binder驱动程序为它分配一看内核缓冲区,以便用来接收进程间通信数据。Binder驱动程序为进程分配的内核缓冲区的大小保存在成员变量buffer_size中。**这些内核缓冲区有内核空间地址和用户空间地址两个地址,内核空间地址保存在成员变量void buffer;中,用户空间地址保存在成员变量vm_area_struct vma;中,用户空间程序通过用户空间地址来访问内核缓冲区,内核缓冲区的内核空间地址和用户空间地址之间相差一个固定值,保存在成员变量user_buffer_offset中。这样就可以通过一个用户空间地址或内核空间地址来计算出另外一个地址的大小。这两个地址都是虚拟地址,对应的物理页面保存在成员变量pages中,Binder驱动程序开始时只为内核缓冲区分配一个物理页面。
Binder驱动为了方便管理内核缓冲区,会将它划分成若干小块,使用binder_buffer来描述这些内核缓冲区,并且按地址从小到大保存在成员变量buffers指向的链表中。当使用这些内核缓冲区时就会为其分配物理页面,正在使用的内核缓冲区保存在allocated_buffers红黑树中,而空闲的内核缓冲区保存在free_buffers红黑树中。
结构体binder_proc的成员变量threads是一个红黑树的根节点,它以线程ID作为关键字来组织一个进程的Binder线程池。进程可以调用函数ioctl将一个线程注册到Binder驱动程序中。
当进程接收到一个进程间通信请求时,Binder驱动程序就将该请求封装成一个工作项,并且加入到进程的待处理队列todo中,Binder线程池中的空闲Binder线程会睡眠在wait所描述的一个等待队列中,当它们的宿主进程的待处理工作项队列增加了新的工作项之后,Binder驱动程序会唤醒这些线程去处理新的工作项,同时线程优先级被设置为default_priority值。
这里的threads、nodes这些其实是binder_thread、binder_node结构体的成员变量rb_node,而proc里面描述binder_thread等的数据类型是rb_root,也就是红黑树的节点。
**struct binder_node:**用来描述一个Binder实体对象。每一个Service组件(一个进程内有多个Service组件)在Binder驱动程序中都对应有一个Binder实体对象,用来描述它在内核的状态。Binder驱动程序通过强弱引用计数来维护它们的生命周期。
struct binder_node {
//调试id
int debug_id;
//描述一个待处理的工作项
struct binder_work work;
union {
//挂载到宿主进程binder_proc的成员变量nodes红黑树的节点
struct rb_node rb_node;
//当宿主进程死亡,该binder实体对象将挂载到全局binder_dead_nodes链表中
struct hlist_node dead_node;
};
//指向该binder线程的宿主进程
struct binder_proc *proc;
//保存所有引用该binder实体对象的binder引用对象列表
struct hlist_head refs;
//binder实体对象的强引用计数
int internal_strong_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;
//binder实体对象的弱引用计数
int local_weak_refs;
//指向用户空间service组件内部的引用计数对象wekref_impl的地址
void __user *ptr;
//保存用户空间的service组件地址
void __user *cookie;
//标示该binder实体对象是否正在处理一个异步事务
unsigned has_async_transaction:1;
//设置该binder实体对象是否可以接收包含有文件描述符的IPC数据
unsigned accept_fds:1;
//binder实体对象要求处理线程应具备的最小线程优先级
unsigned min_priority:8;
//异步事务队列
struct list_head async_todo;
};
宿主进程使用一个红黑树来维护它内部所有的Binder实体对象,而每一个Binder实体对象的成员变量rb_node就正好是这个红黑树中的一个节点。如果一个Binder实体对象的宿主进程已经死亡了,那么这个Binder实体对象就会通过它的成员变量dead_node保存在一个全局的hash列表中。
一个Binder实体对象可以被多个Binder引用对象引用的。
struct binder_ref:
struct binder_ref {
//调试id
int debug_id;
//挂载到宿主对象binder_proc的红黑树refs_by_desc中的节点,引用句柄来排序
struct rb_node rb_node_desc;
//挂载到宿主对象binder_proc的红黑树refs_by_node中的节点,binder实体的地址来排序
struct rb_node rb_node_node;
//挂载到Binder实体对象的refs链表中的节点
struct hlist_node node_entry;
//Binder引用对象的宿主进程binder_proc
struct binder_proc *proc;
//Binder引用对象所引用的Binder实体对象
struct binder_node *node;
//Binder引用对象的句柄值
uint32_t desc;
//强引用计数
int strong;
//弱引用计数
int weak;
//注册死亡接收通知
struct binder_ref_death *death;
};
结构体binder_ref用来描述一个Binder引用对象。每一个Client组件在Binder驱动程序中都对应有一个Binder引用对象,用来描述它在内核中的状态。Binder驱动程序通过强引用计数和弱引用计数技术来维护它们的生命周期。
成员变量 *node用来描述一个Binder引用对象所引用的Binder实体对象。前面在介绍结构体binder_node时提到,每一个Binder实体对象都有一个hash列表,用来保存那些引用了它的Binder引用对象,而这些Binder引用对象(可以理解为Cient端的binder_proc结构体里面的binder引用)的成员变量node_entry正好是这个hash列表的节点。
上面binder_node和binder_ref的关系如下图所示:
**struct binder_buffer:**描述一个个内核缓存区,它是用来在进程间传输数据的,可以被划分交给某个事务使用
**binder_transaction:**描述一个进程间通信的过程,这个过程称为一个事务。
struct binder_transaction {
int debug_id;
struct binder_work work; //描述这个事务的是做什么工作的
struct binder_thread *from;
struct binder_transaction *from_parent; //描述一个事务所依赖的另外一个事务
struct binder_proc *to_proc; //指向负责处理的进程
struct binder_thread *to_thread;//指向负责处理的线程
struct binder_transaction *to_parent; //目标线程下一个需要处理的事务
unsigned need_reply:1; //1表示这是同步通信,0表示异步
struct binder_buffer *buffer; //内核缓冲区
unsigned int code;
unsigned int flags;
struct binder_priority priority;
struct binder_priority saved_priority;
bool set_priority_called;
kuid_t sender_euid;
binder_uintptr_t security_ctx;
spinlock_t lock;
};
成员变量from_parent和to_parent分别描述一个事务所依赖的另外一个事务,以及目标线程下一个需要处理的事务。假设线程A发起了一个事务T1,需要由线程B来处理;线程B在处理事务T1时,又需要线程C先处理事务T2;线程C在处理事务T2时,又需要线程A先处理事务T3。这样,事务T1就依赖于事务T2,而事务T2又依赖于事务T3,它们的关系如下:
T2->from_parent = T1
T3->from_parent = T2
对于线程A来说,它需要处理的事务有两个,分别是T1和T3,它首先要处理事务T3,然后才能处理事务T1,因此,事务T1和T3的关系如下:
T3->to_parent = T1
考虑这样一个情景:如果线程C在发起事务T3给线程A所属的进程来处理时,Binder驱动程序选择了该进程的另外一个线程D来处理该事务,这时候会出现什么情况呢?这时候线程A就会处于空闲等待状态,什么也不能做,因为它必须要等线程D处理完成事务T3后,它才可以继续执行事务T1。在这种情况下,与其让线程A闲着,还不如把事务T3交给它来处理,这样线程D就可以去处理其他事务,提高了进程的并发性。
现在,关键的问题又来了——Binder驱动程序在分发事务T3给目标进程处理时,它是如何知道线程A属于目标进程,并且正在等待事务T3的处理结果的?当线程C在处理事务T2时,就会将事务T2放在其事务堆栈transaction_stack的最前端。这样当线程C发起事务T3给线程A所属的进程处理时,Binder驱动程序就可以沿着线程C的事务堆栈transaction_stack向下遍历,即沿着事务T2的成员变量from_parent向下遍历,最后就会发现事务T3的目标进程等于事务T1的源进程,并且事务T1是由线程A发起的,这时候它就知道线程A正在等待事务T3的处理结果了。
**binder_thread:**描述Binder线程池中的一个线程,内部也有todo队列
**binder_work:**描述一个工作项,里面有枚举类型值,比如这个工作项是属于一个进程还是线程
struct binder_work {
struct list_head entry;
enum binder_work_type {
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_RETURN_ERROR,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
} type;
};
binder_mmap
在打开Binder设备之后,进程还会通过mmap(“mmap是一种内存映射文件的方法”)进行内存映射。
mmap()的作用主要有两个:
● 申请一块内存空间,用来接受Binder通信过程中的数据。
● 将这块内存进行地址映射到用户空间,以便将来访问。
binder_ioctl
ioctl是一种专用于设备输入输出的系统调用,该调用传入一个跟设备有关的请求码,系统调用的功能完全取决于请求码。
binder_ioctl()函数对应了ioctl系统的处理,它根据ioctl命令来确定进一步的处理逻辑,如下所示:
ioctl(文件描述符,ioctl 命令,数据类型);
1、打开Binder设备时的fd
2、具体的操作码,通信协议的操作码
3、存储请求数据,类型是binder_write_read,内部封装了一个binder_transaction_data结构体,该结构体包含了发出请求者的标识,请求的目标对象以及请求所需要的参数。
通信协议(通信命令),Binder协议可分为控制协议和驱动协议两种:
1、Binder控制协议是进程通过ioctl(“/dev/binder”)与Binder设备进行通讯的协议。
操作码有下面这些
2、Binder的驱动协议描述了对于Binder驱动的具体使用过程。驱动协议又可以分为两类:
● binder_driver_command_protocol:描述了进程发送给Binder驱动的命令,以BC_开头
● binder_driver_return_protocol:描述了Binder驱动发送给进程的命令,BR_开头
binder_driver_command_protocol包含的命令,分别是:
binder_driver_return_protocol包含的命令,分别是:
我们前面提到的2个重要函数binder_thread_write和binder_thread_read就是对这些请求吗进行处理
binder_thread_write(){
while (ptr < end && thread->return_error == BR_OK) {
...
get_user(cmd, (uint32_t __user *)ptr);//获取 IPC 数据中的 Binder 协议(BC 码)
...
switch (cmd) {
case BC_INCREFS: ...
case BC_ACQUIRE: ...
case BC_RELEASE: ...
case BC_DECREFS: ...
case BC_INCREFS_DONE: ...
case BC_ACQUIRE_DONE: ...
case BC_FREE_BUFFER: ... break;
case BC_TRANSACTION:
case BC_REPLY:
case BC_REGISTER_LOOPER: ...
case BC_ENTER_LOOPER: ...
case BC_EXIT_LOOPER: ...
case BC_REQUEST_DEATH_NOTIFICATION: ...
case BC_CLEAR_DEATH_NOTIFICATION: ...
case BC_DEAD_BINDER_DONE: ...
}
}
}
————————————
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
while (1) {
switch (w->type) { // 取出封装的工作项binder_work的类型,再处理请求吗
case BINDER_WORK_TRANSACTION: {} break;
case BINDER_WORK_RETURN_ERROR: {} break;
case BINDER_WORK_TRANSACTION_COMPLETE: {
binder_inner_proc_unlock(proc);
cmd = BR_TRANSACTION_COMPLETE;
...
} break;
case BINDER_WORK_NODE: {
...
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_INCREFS, "BR_INCREFS");
...
} break;
case BINDER_WORK_DEAD_BINDER:
case BINDER_WORK_DEAD_BINDER_AND_CLEAR:
case BINDER_WORK_CLEAR_DEATH_NOTIFICATION: {
...
cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;
}break;
...
}
}
}
Binder线程池
Binder线程池:每个进程在启动时会创建一个binder线程池(比如app进程、systemserver),并向其中注册一个Binder线程
在Zygote进程中初始化该APP进程的时候,会调用到Native层的app_main.cpp中的onZygoteInit()。
frameworks/base/cmds/app_process/app_main.cpp:
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool(); //开启线程池
}
为当前进程创建ProcessState的时候,通过mMaxThreads传入了Binder驱动可创建的最大Binder线程数,默认为15个。
frameworks/native/libs/binder/ProcessState.cpp
#define DEFAULT_MAX_BINDER_THREADS 15
...
ProcessState::ProcessState(const char *driver)
: mDriverName(String8(driver))
, mDriverFD(open_driver(driver))
, mVMStart(MAP_FAILED)
...
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
...
{
...
}
之后Server进程也可以向binder线程池注册新的线程,或者Binder驱动在探测到没有足够的binder线程来处理Client请求时会主动向Server进程要求注册新的的binder线程去处理请求
下面的代码为Binder驱动在探测到没有足够的binder线程来处理Client请求时会主动向Server进程要求注册新的的binder线程去处理请求的情况
frameworks/source/drivers/android/binder.c
done:
*consumed = ptr - buffer;
binder_inner_proc_lock(proc);
//当proc->requested_threads == 0: 正在注册的已经为0
//list_empty(&thread->proc->waiting_threads): 空闲的Binder线程队列为0,表示都在处理请求中
//.proc->requested_threads_started < proc->max_threads:已经启动的用来处理线程数 < 设定最大值数
if (proc->requested_threads == 0 &&
list_empty(&thread->proc->waiting_threads) &&
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
/*spawn a new thread if we leave this out */) {
proc->requested_threads++; //请求注册+1,就是代表注册一个Binder线程用来处理请求
binder_inner_proc_unlock(proc);
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BR_SPAWN_LOOPER\n",
proc->pid, thread->pid);
//发送一个创建线程的请求给应用程序
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
} else
binder_inner_proc_unlock(proc);
return 0;
}
上面注册成功后的Binder线程并不是马上变为了requested_threads_started。
当某个Client发送请求过来,进行写操作时,通过binder_ioctl再到binder_thread_write,我们这边Server端的进程会执行proc->requested_threads–-。如下:
static int binder_thread_write(struct binder_proc *proc,struct binder_thread *thread,binder_uintptr_t binder_buffer, size_t size,binder_size_t *consumed)
case BC_REGISTER_LOOPER:
/*注册后未使用的线程数-1*/
proc->requested_threads--;
/*已经启动的线程数目+1*/
proc->requested_threads_started++;
表示新的线程已经进入了循环体。
对于底层Binder驱动,通过binder_procs链表记录所有创建的binder_proc结构体,binder驱动层的每一个 binder_proc结构体都与用户空间的一个用于binder通信的进程一一对应,且每个进程有且只有一ProcessState 对象,这是通过单例模式来保证的。
在每个进程中可以有很多个线程,每个线程对应一个IPCThreadState对象,IPCThreadState对象也是单例模式,即一个线程对应一个IPCThreadState对象,在Binder驱动层与之相对应的结构Binder_thread结构体。在 binder_proc结构体中通过成员变量rb_root threads,来记录当前进程内所有的binder_thread。
Binder实体对象和Binder本地对象之间
Binder本地对象是一个类型为BBinder的对象,它是在用户空间中创建的,并且运行在Server进程中。Binder本地对象一方面会被运行在Server进程中的其他对象引用,另一方面也会被Binder驱动程序中的Binder实体对象引用。由于BBinder类继承了RefBase类,因此,Server进程中的其他对象可以简单地通过智能指针来引用这些Binder本地对象,以便可以控制它们的生命周期。
由于Binder驱动程序中的Binder实体对象是运行在内核空间的,它不能够通过智能指针来引用运行在用户空间的Binder本地对象,因此,Binder驱动程序就需要和Server进程约定一套规则来维护它们的引用计数,避免它们在还被Binder实体对象引用的情况下销毁。
Server进程将一个Binder本地对象注册到Sevice Manager时,Binder驱动程序就会为它创建一个Binder实体对象。接下来,当Client进程通过Service Manager来查询一个Binder本地对象的代理对象接口时,Binder驱动程序就会为它所对应的Binder实体对象创建一个Binder引用对象(指某个Client端的)。
接着再使用BR_INCREFS和BR_ACQUIRE协议来通知对应的Server进程增加对应的Binder本地对象的弱引用计数和强引用计数。保证Binder代理对象在引用一个Binder本地对象的时候,本地对象不会被销毁
当没有任何Binder代理对象引用一个Binder本地对象时,Binder驱动程序就会使用BR_DECREFS和BR_RELEASE协议来通知对应的Server进程减少对应的Binder本地对象的弱引用计数和强引用计数。
下面的情况是由Server进程要断开连接的情况:
总结来说,Binder驱动程序就是通过BR_INCREFS、BR_ACQUIRE、BR_DECREFS和BR_RELEASE协议来引用运行在Server进程中的Binder本地对象的,相关的代码实现在binder.c文件函数binder_thread_read中
当Binder驱动程序要和目标进程或者目标线程通信时,它就会把一个工作项加入到它的todo队列中。目标进程或者目标线程会不断地调用Binder驱动程序中的函数binder_thread_read()来检查它的todo队列中有没有新的工作项。如果有,目标进程或者目标线程就会将它取出来,并且返回到用户空间去处理。
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
......
while (1) {
uint32_t cmd;
struct binder_transaction_data_secctx tr;
struct binder_transaction_data *trd = &tr.transaction_data;
struct binder_work *w = NULL;
struct list_head *list = NULL;
struct binder_transaction *t = NULL;
struct binder_thread *t_from;
size_t trsize = sizeof(*trd);
...
case BINDER_WORK_NODE: { //这个即是为一个BINDER_WORK_NODE类型的工作项
struct binder_node *node = container_of(w, struct binder_node, work);
int strong, weak;
binder_uintptr_t node_ptr = node->ptr;
binder_uintptr_t node_cookie = node->cookie;
int node_debug_id = node->debug_id;
int has_weak_ref;
int has_strong_ref;
void __user *orig_ptr = ptr;
...
if (weak && !has_weak_ref) {
node->has_weak_ref = 1;
node->pending_weak_ref = 1;
node->local_weak_refs++;
}
if (strong && !has_strong_ref) {
node->has_strong_ref = 1;
node->pending_strong_ref = 1;
node->local_strong_refs++;
}
if (!strong && has_strong_ref)
node->has_strong_ref = 0;
if (!weak && has_weak_ref)
node->has_weak_ref = 0;
...
if (weak && !has_weak_ref)
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_INCREFS, "BR_INCREFS");
if (!ret && strong && !has_strong_ref)
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_ACQUIRE, "BR_ACQUIRE");
if (!ret && !strong && has_strong_ref)
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_RELEASE, "BR_RELEASE");
if (!ret && !weak && has_weak_ref)
ret = binder_put_node_cmd(
proc, thread, &ptr, node_ptr,
node_cookie, node_debug_id,
BR_DECREFS, "BR_DECREFS");
......
return 0;
}
如果Binder驱动程序已经请求运行Server进程中的一个Binder本地对象为它内部的一个Binder实体对象增加了强引用计数和弱引用计数,那么Binder驱动程序就会将该Binder实体对象的成员变量has_strong_ref和has_weak_ref的值设置为1;否则,就设置为0。
从31-69行的if语句就根据变量strong和weak的值,以及目标Binder实体对象的成员变量has_strong_ref和has_weak_ref的值,来判断通知目标进程增加还是减少一个对应的Binder本地对象的强引用计数或者弱引用计数,判断规则如下所示。
(1)如果变量weak的值等于1(表示有一个Binder引用对象引用我了),但我们Binder实体对象的成员变量has_weak_ref的值是等于0的,那么就说明该Binder实体对象已经引用了一个Binder本地对象,但是还没有增加它的弱引用计数。因此,第32行到第34行和第50到53行代码就使用BR_INCREFS协议来请求增加对应的Binder本地对象的弱引用计数以及将协议内容写入到由Server进程所提供的一个用户空间缓冲区(写入的不是数据,已经映射好了),然后返回到Server进程的用户空间。
Server进程是通过Binder库提供的IPCThreadState接口来处理Binder驱动程序发送给它的协议的,具体来说,就是在IPCThreadState类的成员函数executeCommand中处理BR_INCREFS、BR_ACQUIRE、BR_DECREFS和BR_RELEASE这四个协议,如下所示。
status_t IPCThreadState::executeCommand(int32_t cmd)
{
BBinder* obj;
RefBase::weakref_type* refs;
status_t result = NO_ERROR;
switch ((uint32_t)cmd) {
...
case BR_ACQUIRE:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
...
obj->incStrong(mProcess.get());
...
mOut.writeInt32(BC_ACQUIRE_DONE);
mOut.writePointer((uintptr_t)refs);
mOut.writePointer((uintptr_t)obj);
break;
case BR_RELEASE:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
...
mPendingStrongDerefs.push(obj);
break;
case BR_INCREFS:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
refs->incWeak(mProcess.get());
mOut.writeInt32(BC_INCREFS_DONE);
mOut.writePointer((uintptr_t)refs);
mOut.writePointer((uintptr_t)obj);
break;
case BR_DECREFS:
refs = (RefBase::weakref_type*)mIn.readPointer();
obj = (BBinder*)mIn.readPointer();
...
mPendingWeakDerefs.push(refs);
break;
...
}
......
return result;
}
当Binder驱动程序请求Server进程增加或者减少一个Binder本地对象的强引用计数或者弱引用计数时,会将该Binder本地对象内部的一个弱引用计数对象(weakref_type),以及该Binder本地对象的地址传递过来,因此,IPCThreadState类的成员函数executeCommand就可以知道要修改的是哪一个Binder本地对象的引用计数了。
Binder驱动程序传递给Server进程的数据保存在IPCThreadState类的成员变量mln中,第10行和第11行、第20行和第21行、第27行和第28行,以及第36行和第37行就分别从它里面取出上述两个地址,并且分别将它转换为一个weakref_type对象refs和一个BBinder对象obj。
对于BR_ACQUIRE和BR_INCREFS协议,Server进程会马上增加目标Binder本地对象的强引用计数和弱引用计数,如第15行和第30行代码所示,并且使用BC_ACQUIRE_DONE和BC_INCREFS_DONE协议来通知Binder驱动程序,它已经增加相应的Binder本地对象的引用计数了。
Bindeer引用对象与Binder实体对象之间
实体对像和引用对象在同一个层次,直接调用内部函数即可实现引用计数
Binder代理对象与Binder引用对象之间
需要协议,属于跨层次
Binder对象死亡通知机制
Client进程和Server进程内部错误而异常退出,会导致运行在它们里面的Binder代理对象和Binder本地对象就会意外死亡。而对象死亡通知机制可以监控到Binder本地对象的死亡事件,然后通知那些引用了它的Binder代理对象。
死亡通知机制中,首先是Binder代理对象将一个死亡接收通知注册到Binder驱动程序中,然后当Binder驱动程序监控到它引用的Binder本地对象死亡时,Binder驱动程序就会向它发送一个死亡通知。当然,这个通知也是可以注销的。
注册死亡接受通知
Binder库定义了死亡通知接收者必须要继承的基类DeathRecipient,它的实现如下所示。自定义的死亡通知接收者必须要重写父类DeathRecipient的成员函数binderDied。当Binder驱动程序通知一个Binder代理对象它所引用的Binder本地对象已经死亡时,就会调用它所指定的死亡通知接收者的成员函数binderDied()
class IBinder : public virtual RefBase
{
public:
...
class DeathRecipient : public virtual RefBase
{
public:
virtual void binderDied(const wp<IBinder>& who) = 0;
};
...
};
Service Manager
ServiceManager是Binder进程间通信机制的核心组件之一,它扮演着Binder进程间通信机制上下文管理者(Context Manager)的角色,同时负责管理系统中的Service组件,并且向Client组件提供获取Service代理对象的服务。
Service Manager运行在一个独立的进程中,因此,Service组件和Client组件也需要通过进程间通信机制来和它交互。Client组件在使用Service组件提供的服务之前,需要通过Service Manager来获取Service组件的代理对象,由于Service Manager也是一个Service组件,所以,其他Service组件和Client组件也需要先获得Service Manager的代理对象。
然后Server组件就可以向ServiceManager的mNameToService集合中缓存注册的服务,Client端需要先拿到Sevice Manager的代理对象proxy后,才能通过ServiceManager到mNameToService集合中查询所需服务并返回Server端的代理
Service Manager初始化
在Android的源码中主要有两种不同的方式来注册系统服务:
● ServiceManager.addService()
● SystemServiceManager.startService()
获取:
● context.getSystemService:context.getSystemService 方法用于获取缓存在 ContextImpl 中的系统服务 Manager。
● ServiceManager.getService:从ServiceManager的mNameToService集合中获取缓存注册的服务
入口:
frameworks/native/cmds/servermanager/main.cpp
int main(int argc, char** argv) {
if (argc > 2) {
LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
}
const char* driver = argc == 2 ? argv[1] : "/dev/binder";
sp<ProcessState> ps = ProcessState::initWithDriver(driver);
//servicemanager是单线程模型,所以限定了Binder线程池线程数为0,也不会去开启线程池
ps->setThreadPoolMaxThreadCount(0);
ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);
sp<ServiceManager> manager = new ServiceManager(std::make_unique<Access>());
if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
LOG(ERROR) << "Could not self register servicemanager";
}
IPCThreadState::self()->setTheContextObject(manager);
ps->becomeContextManager(nullptr, nullptr);
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
BinderCallback::setupTo(looper);
ClientCallbackCallback::setupTo(looper, manager);
//进入无限循环,处理client端发来的请求
while(true) {
looper->pollAll(-1);
}
// should not be reached
return EXIT_FAILURE;
}
ps->becomeContextManager(nullptr, nullptr);
frameworks/native/libs/binder/ProcessState.cpp
bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData)
{
AutoMutex _l(mLock);
mBinderContextCheckFunc = checkFunc;
mBinderContextUserData = userData;
flat_binder_object obj {
.flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
};
int result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);
// fallback to original method
if (result != 0) {
android_errorWriteLog(0x534e4554, "121035042");
int dummy = 0;
result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
}
if (result == -1) {
mBinderContextCheckFunc = nullptr;
mBinderContextUserData = nullptr;
ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno));
}
return result == 0;
}
获取服务
frameworks/native/cmds/servicemanager/ServiceManager.cpp
ServiceManager::getService
Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) {
*outBinder = tryGetService(name, true);
// returns ok regardless of result for legacy reasons
return Status::ok();
}
————————
sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfNotFound) {
auto ctx = mAccess->getCallingContext();
sp<IBinder> out;
Service* service = nullptr;
//mNameToService.find(name);查找缓存服务
if (auto it = mNameToService.find(name); it != mNameToService.end()) {
service = &(it->second);
...
out = service->binder;
}
...
//没有找到缓存的服务,这里去开启
if (!out && startIfNotFound) {
tryStartService(name);
}
...
return out;
}
frameworks/native/cmds/servicemanager/ServiceManager.h
mNameToService是一个Map结构
using ServiceMap = std::map<std::string, Service>;
...
ServiceMap mNameToService;
Binder进程通信机制的JAVA接口
从我们Java层调用为起点,说一说大致过程。首先实现方式有两种,我这里选择用aidl方式
从我们Java层调用为起点,说一说大致过程
一、Java接口的定义和解析
二、Java服务的调用过程。
(1)Java接口的定义和解析,因为我是在之前练习dagger的项目改写的,所以这里大家不要在意daager这个包名
通过Android接口描述语言(Android lnterface DefinitionLanguage,AIDL)来定义Java服务接口。AIDL是一种Binder进程间通信接口的描述语言,通过它来定义的Java服务接口具有执行Binder进程间通信的能力。
定义文件IRemoteService.aidl中
interface IRemoteService {
int getPid();
MyData getMyData();
}
编译该文件后,out目录会生成一个中间文件IRemoteService.java
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.example.daagerapplication;
// Declare any non-default types here with import statements
public interface IRemoteService extends android.os.IInterface
{
///
/** Default implementation for IRemoteService. */
public static class Default implements com.example.daagerapplication.IRemoteService
{
@Override public int getPid() throws android.os.RemoteException
{
return 0;
}
@Override public com.example.daagerapplication.MyData getMyData() throws android.os.RemoteException
{
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
///
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.daagerapplication.IRemoteService
{
private static final java.lang.String DESCRIPTOR = "com.example.daagerapplication.IRemoteService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.daagerapplication.IRemoteService interface,
* generating a proxy if needed.
*/
public static com.example.daagerapplication.IRemoteService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.daagerapplication.IRemoteService))) {
return ((com.example.daagerapplication.IRemoteService)iin);
}
return new com.example.daagerapplication.IRemoteService.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getPid:
{
data.enforceInterface(descriptor);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getMyData:
{
data.enforceInterface(descriptor);
com.example.daagerapplication.MyData _result = this.getMyData();
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
**********************************
private static class Proxy implements com.example.daagerapplication.IRemoteService
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public int getPid() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getPid();
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public com.example.daagerapplication.MyData getMyData() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.daagerapplication.MyData _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getMyData, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getMyData();
}
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.example.daagerapplication.MyData.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.daagerapplication.IRemoteService sDefaultImpl;
}
**********************************
static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getMyData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public static boolean setDefaultImpl(com.example.daagerapplication.IRemoteService impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.daagerapplication.IRemoteService getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
///
public int getPid() throws android.os.RemoteException;
public com.example.daagerapplication.MyData getMyData() throws android.os.RemoteException;
}
文件会生成一个Java抽象类IRemoteService.Stub和一个Java类IRemoteService.Stub.Proxy
抽象类IRemoteService.Stub用来描述一个Java服务,而抽象类IRemoteService.Stub.Proxy类用来描述一个Java服务代理对象。
IRemoteService.Stub类的静态成员函数asInterface通常用来将一个Java服务代理对象,即一个BinderProxy对象(从ServiceManager拿到的)封装成一个IRemoteService.Stub.Proxy对象。
IRemoteService.Stub类的成员函数onTransact是用来接收和分发进程间通信请求的。
IRemoteService.Stub.Proxy类内部有一个成员变量mRemote,它指向的是一个Java服务代理对象,即一个BinderProxy对象,用来向一个实现了IRemoteService接口的Java服务发送进程间通信请求
(2)Java服务的调用过程。
获取了Java代理对象之后,就可以调用这个实现了IRemoteService接口的Java代理对象的成员函数getPid();
和getMyData();
public class MainActivity extends AppCompatActivity {
...
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteService = IRemoteService.Stub.asInterface(service);
String pidInfo = null;
try {
MyData myData = mRemoteService.getMyData();
pidInfo = "pid="+ mRemoteService.getPid() +
", data1 = "+ myData.getData1() +
", data2="+ myData.getData2();
} catch (RemoteException e) {
e.printStackTrace();
}
Log.i(TAG, "[ClientActivity] onServiceConnected "+pidInfo);
mCallBackTv.setText(pidInfo);
Toast.makeText(MainActivity.this, "R.string.remote_service_connecte d", Toast.LENGTH_SHORT).show();
}
...
}
}
mRemoteService.getMyData() 过程
public interface IRemoteService extends android.os.IInterface
{
...
@Override public com.example.daagerapplication.MyData getMyData() throws android.os.RemoteException
{
//2个包,一个带数据过去,一个收回复的数据
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.daagerapplication.MyData _result;
try {
//首先将通信数据写入到一个Parcel对象_data中,然后mRemote.transact发起进程间通信
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getMyData, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getMyData();
}
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.example.daagerapplication.MyData.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
mRemote.transact的请求最后是JavaBBinder的Binder本地对象的成员函数onTransact进行处理
JavaBBinder类的成员变量mObject指向的是一个Java服务,这个java服务就是我们的RemoteService
frameworks/base/core/jni
...
class JavaBBinder : public BBinder
virtual status_t onTransact(
uint32_t code, const Parcel6 data, Parcel* reply, uint32_t flags = 0)
{
...
JNIEnv* env = javavm_to_jnienv(mVM);
jboolean rea = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, (int32_t) &data, (int32_t)reply, flags);
...
return res I= JNI_FALSE ? NO_ ERROR : UNKNOWN_TRANSAOTION;
}
...
第8行代码转给了Binder类的execTransact,因为子类没有重写这个方法,所以执行的是父类
frameworks/base/core/java/android/os
public class Binder implements IBinder {
...
// Entry point from android_util_Binder.cpp's onTransact
@UnsupportedAppUsage
private boolean execTransact(int code, long dataObj, long replyObj,
...
return execTransactInternal(code, dataObj, replyObj, flags, callingUid);
...
}
private boolean execTransactInternal(int code, long dataObj, long replyObj, int flags,
int callingUid){
...
res = onTransact(code, data, reply, flags);
...
}
会转给由子类重写的成员函数onTransact,实际上就是IRemoteService.Stub类的成员函数onTransact,因为public static abstract class Stub extends android.os.Binder
case TRANSACTION_getMyData:
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
...
case TRANSACTION_getPid:
{
data.enforceInterface(descriptor);
int _result = this.getPid();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
case TRANSACTION_getMyData:
{
data.enforceInterface(descriptor);
com.example.daagerapplication.MyData _result = this.getMyData();
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
com.example.daagerapplication.MyData _result = this.getMyData();最后转给实际服务RemoteService执行
package com.example.daagerapplication;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
//写法2:public class RemoteService 直接 extends IRemoteService.Stub,这里测试用extends Service方式也可以
//写法3:public class RemoteService extends systemServer的方式
public class RemoteService extends Service {
/** * 实现 IRemoteService.aidl 中定义的方法 */
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public int getPid() throws RemoteException {
Log.i(TAG,"[RemoteService] getPid()="+android.os.Process.myPid());
return android.os.Process.myPid();
}
@Override
public MyData getMyData() throws RemoteException {
Log.i(TAG,"[RemoteService] getMyData() "+ mMyData.toString());
return mMyData;
}
/**此处可用于权限拦截**/
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return super.onTransact(code, data, reply, flags);
}
};
/** * 初始化 MyData 数据 **/
private void initMyData() {
mMyData = new MyData();
mMyData.setData1(10);
mMyData.setData2(20);
}
}
通信常见异常
1、Binder leak,Binder内存泄漏,binder_buffer大概为4M(应用空间限制为1M,所以和4M其实关系不大),如果在通信过程中数据没有有效的释放,会造成空间越来越少。这个释放动作由Binder驱动做,一般很少出错
2、binder某个进程线程池处理线程达到上限(16个),可能会导致ANR。解决方法找log其他地方看是谁一直在消耗binder或者是有死锁发生
3、Binder线程池死锁,分析trace文件
4、Client端异常比如传输数据量超过1M,Service端异常比如提供的方法不可用,解决方法改代码
自问自答:
binder_procs是谁创建的?
答:Binder驱动初始化的时候会创建这个binder_procs链表。
可以不通过ServiceManager进行Binder跨进程通信嘛?
答:可以的,不是所有的Binder都需要注册给ServiceManager的(比如我上面写的例子)。Server端可以通过已经建立的Binder连接将创建的Binder实体传给Client,当然这条已经建立的Binder连接必须是通过实名Binder实现。由于这个Binder没有向ServiceManager注册名字,所以是匿名Binder。Client将会收到这个匿名Binder的引用,通过这个引用向位于Server中的实体发送请求。匿名Binder为通信双方建立一条私密通道,只要Server没有把匿名Binder发给别的进程,别的进程就无法通过穷举或猜测等任何方式获得该Binder的引用,向该Binder发送请求。文章来源:https://www.toymoban.com/news/detail-434863.html
ServiceManager是单线程的,为什么它不担心处理不过来的问题或者说它为什么是被设计为单线程?而不是像其他进程有Binder线程池?
答:请求量较少的原因吧,单线程绰绰有余。文章来源地址https://www.toymoban.com/news/detail-434863.html
到了这里,关于Binder通信原理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!