Binder的实现原理
涉及到原理源码肯定是少不了的,9.0 binder
相关的源码分为三部分:
- Java:
frameworks/base/core/java/android/os/Binder.java
- native:
frameworks/native/libs/binder/
- driver:
common/drivers/android/binder.c
还有一点需要明确的是:
-
用户进程
:针对内核
空间或者binder驱动
来说的,这里指的是向binder驱动
发送消息的进程 -
客户进程
:针对binder通信
的服务进程
来说的,这里指的是发起binder调用
的进程
书中这两个名词没有做详细说明,一开始看的有点晕
源码在手,干啥都有
Binder设计相关的问题
Binder
实现的远程调用是一种面向对象
的远程调用,那么它和面向过程
的远程调用区别在什么地方呢?
-
面向过程
的远程调用实现起来比较容易:
- 只需要通过某种方式把需要执行的
函数号
和参数
传递到服务进程
- 然后
服务进程
根据函数号
和参数
执行对应的函数就完成了
- 只需要通过某种方式把需要执行的
-
面向对象
的调用则比较复杂:
- 同一个服务类可以创建对个对象
- 调用时不但要通过
函数号
和参数
来识别要执行函数 - 同时还要指定具体的对象
- 调用时不但要通过
- 对象是有生命周期的,需要监听管理
- 服务中的
实体对象
死亡后,客户进程的引用对象
也需要删除 - 这个过程需要自动完成而不能由上层的客户程序协助完成
- 因此为了管理方便,客户进程中需要(ProcessState类的作用):
- 集中管理本进程中所有的
Binder引用对象
- 并负责它们的创建和释放
- 集中管理本进程中所有的
- 服务中的
- 同一个服务类可以创建对个对象
设计复杂也带来了功能的强大,正因为Binder
是面向对象
的,我们可以创建多个Binder实体对象
来服务不同的客户,每个对象有自己的数据。相互之间不会干扰。
为了系统中所有引用对象
和实体对象
能相互关联:
-
Binder
在驱动中建立了一张所有进程的引用对象
和实体对象
的关联表
。 - 有了关联,Binder又必须保证用户进程中实体对象和引用对象跟驱动中的数据一致
- 为了达到这个目标,
Binder
定义了自己的引用计数规则,而且这种规则是跨进程的。
- 为了达到这个目标,
参数的传递问题:
- 一般的对象作为参数传递没有太大问题,只需要
序列化
和反序列化
就能实现。 - 但是 Binder对象作为参数传递的时候,就会面临实体对象和引用对象相互转换的问题。
- 为了让上层应用使用方便,这种转换在驱动中自动完成
- 普通的 IPC 传递参数数据时,要经历两次数据复制的过程:
- 一次是从调用者的数据缓冲区复制到内核的缓冲区
- 一次是从内核的缓冲区复制到接受进程的读缓冲区
- Binder为了提高效率:
- 为每一个进程创建了一块缓存区,这块缓存区在内核和用户进程间共享
- 传输数据到驱动,需要:
- 从发送进程的用户空间缓存区复制到目标进程在驱动的缓存区
- 目标进程从驱动中读取数据就不需要从内核空间复制到用户空间了,而是直接从内核共享的缓存区中读取
- 这样减少了一次数据复制的过程
对于服务进程中Binder调用
的执行:
- 每次执行必须在一个线程中完成
- 如果线程不停地创建和释放,会带来很大的系统开销。
- 使用 线程池 来管理 Binder 调用的执行
- 在
Binder
的设计中,除了第一个线程是应用层主动创建的 - 线程池中的其他线程都是在驱动的请求下才创建的
- 这样将线程数量降到最低,并保证从驱动到来的
Binder调用
有线程可以使用。
- 在
Binder的线程模型
Binder 的线程池
在Zygote进程
启动时,会调用AppRuntime
的onZygoteInit
函数(书中第8章,还没看到),代码如下:
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
所有Android应用都是从Zygote进程
中fork
出来的。因此,这段代码对所有应用进程都有效。
- onZygoteInit 函数首先调用 self 函数来得到 ProcessState 类的实例,每个进程都只会有一个实例。ProcessState 构造函数如下:
ProcessState::ProcessState(const char *driver) > : mDriverName(String8(driver))
, mDriverFD(open_driver(driver))
//......
{
if (mDriverFD >= 0) {
// mmap the binder, providing a chunk of virtual > address space to receive transactions.
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
close(mDriverFD);
mDriverFD = -1;
mDriverName.clear();
}
}
}
-
ProcessState类做了两件事:
-
调用
open_driver()
函数打开Binder设备
-
调用 mmap() 函数在驱动中分配了一块内存空间
- 这块内存空间大小略小于1MB,定义如下:
-
#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
-
这块内存并不是给上层应用使用
-
这块内存用于Binder驱动中接受传递给本进程的Binder数据
-
这块内存在内核和本应用中共享
-
得到 ProcessState 类的实例后,调用它的 startThreadPool() 函数启动线程池,看下代码:
void ProcessState::startThreadPool(){
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
spawnPooledThread(true);
}
}
- 先判断
mThreadPoolStarted
是否为true
,不为true
才继续执行 - 然后把
mThreadPoolStarted
设置为true
,这说明startThreadPool()
在进程中只会运行一次 - 接着调用
spawnPooledThread
函数,参数为true
void ProcessState::spawnPooledThread(bool isMain)
{
if (mThreadPoolStarted) {
String8 name = makeBinderThreadName();
ALOGV("Spawning new pooled thread, name=%s\n", name.string());
sp<Thread> t = new PoolThread(isMain);
t->run(name.string());
}
}
spawnPooledThread
函数的作用是创建加入线程池的线程- 函数中创建了一个
PoolThread
类,类的run
函数会创建线程- 传入的参数为
true
,说明这个线程是线程池
的第一个线程- 以后再创建的线程都是接到驱动通知后创建的,传入的参数为
false
,像这样status_t IPCThreadState::executeCommand(int32_t cmd){ //...... case BR_SPAWN_LOOPER: mProcess->spawnPooledThread(false); break; //...... }
- 我们再看下
PoolThread
类的业务实现threadLoop
函数protected: virtual bool threadLoop() { IPCThreadState::self()->joinThreadPool(mIsMain); return false; } * 返回`false`代表执行一次,为什么是在`threadLoop()`中执行业务逻辑,可以看下[Android 中的`threadLoop`](https://blog.csdn.net/f2006116/article/details/89058397) * 具体调用细节大家阅读`frameworks`源码吧,路径应该是在:`frameworks/av/services/audioflinger/Threads.h` * `threadLoop`函数只是调用了`IPCThreadState`的`joinThreadPool`函数,这个函数后面单练它
好的,我们先来梳理下线程池这部分内容:
- 首先,应用启动时会执行
onZygoteInit
函数,这部分会打开Binder设备
并申请共享内存空间
- 然后,执行
ProcessState
的startThreadPool
创建线程池
- 然后,通过创建
PoolThread
的实例,创建线程池
中的第一个线程 - 最后,
PoolThread
只是简单调用了IPCThreadState
的joinThreadPool
函数
关于IPCThreadState
,稍后详谈
调用Binder服务的线程
客户端Binder服务的调用是通过IBinder的transact函数完成的。这里的IBInder实际上是BpBinder对象,代码如下:
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
这部分代码做了:
- 通过 IPCThreadState 的 self 静态函数获取 IPCThreadState 的指针
-
IPCThreadState
对象会和每个线程关联 -
self
函数会判断本线程是否有关联的IPCThreadState
对象 - 没有关联的对象则新创建一个
IPCThreadState
对象,并保存到当前线程中
-
- 得到
IPCThreadState
对象后,接着调用了IPCThreadState
的transact
,我们看下代码:
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err;
flags |= TF_ACCEPT_FDS;
//......
//把要发送的数据放到类的成员变量mOut中
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {// 同步调用方式
//......
if (reply) {
// 调用者要求返回结果,此时向底层发送数据并等待返回值
err = waitForResponse(reply);
} else {
// 调用者不需要返回值,但是还要等待远程执行完毕
// 这里用fakeRely来接收返回的Parcel对象
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
//......
} else {// 异步调用方式,函数会立即返回
err = waitForResponse(NULL, NULL);
}
return err;
}
-
IPCThreadState
的transact
首先执行writeTransactionData
包要传输的数据保存到mOut
变量中 - 然后通过
waitForResponse
把mOut
中的数据通过ioctl
发送给底层驱动
对于一个进程而言,只有一个Binder驱动
的文件描述符
,所有ioctl
调用都是使用这个描述符。如果客户端
有好几个线程同时执行远程调用,它们都将在同一个描述符的ioctl
函数上等待。那么当数据到来时,哪个线程会受到回复呢?
- 通常的
IO模型
,在同一个描述符上等待的多个线程会被随机唤醒一个 - 但是Android在 Binder驱动 中记录了每次Binder调用的信息,其中就包括 线程ID,因此Binder驱动知道返回值应该交给哪个线程
- 由驱动来处理线程的唤醒比在应用层做同样的事情要更简单,效率也更高
- 但是,从架构的角度来看,这种设计比较糟糕,应用层和驱动产生了耦合。
好的,我们在对这部分做个小结:
-
客户端
从某个线程中发起调用,将参数打包后,通过ioctl
函数传递给驱动 -
客户端
挂起并等待ioctl
返回函数的结果 -
binder驱动
记录下调用线程的信息,然后根据调用的binder对象
寻找Binder服务
所在的进程,也就是服务端
-
binder驱动
找到服务端
后先查看是否有空闲线程,没有则通知服务端
创建 -
服务端
得到空闲线程后,根据binder驱动
中保存的BBinder对象
的指针调用相应的函数 -
服务端
在函数返回后在通过ioctl
把结果打包传递给binder驱动
-
binder驱动
根据返回信息查找调用者线程 -
binder驱动
找到调用的线程后并唤醒它,并通过ioctl
函数把结果传递回去 -
客户端
的线程得到返回结果后继续运行
Binder对象的传递
当Binder对象
作为参数传递是,会有两种情形:
-
Binder实体对象
作为参数传递:非Binder对象
的传递是通过在接收端复制对象完成的,但是Binder实体对象
是无法复制的,因此需要在客户进程中创建一个Binder引用对象
来代替实体对象。 - Binder引用 对象作为参数传递:
- 如果传递的目的地是该引用对象对应实体对象所在的进程,那么:
-
Binder框架
必须把这个引用对象
转换成Binder实体对象
。 - 不能再创建一个新的
实体对象
- 必须找到并使用原来的
实体对象
-
- 如果传递的目的地是另一个客户端进程,那么:
- 不能简单的复制
引用对象
- 需要建立目的进程中的
引用对象
和实体对象
的关系 - 这个关系的建立是在
Binder驱动
中完成的
- 不能简单的复制
- 如果传递的目的地是该引用对象对应实体对象所在的进程,那么:
Binder对象传递流程简介
Binder调用
的参数传递是通过Parcel类
来完成的。先来简单看下Binder实体对象
转换成Binder引用对象
的过程:
- 在服务进程中将IBinder(BBinder)对象加入到Parcel对象后,Parcel对象会:
- 打包数据,并把数据类型标记为
BINDER_TYPE_BINDER
- 把
BpBinder
的指针放进cookie
字段 - 通过
ioctl
把Parcel对象
的数据传递到Binder驱动
中
- 打包数据,并把数据类型标记为
- Binder驱动会检查传递进来的数据,如果发现了标记为BINDER_TYPE_BINDER的数据:
- 会先查找和服务进程相关的Binder实体对象表:
- 如果表中还没有这个实体对象的记录,则创建新的节点,并保存信息。
- 然后驱动会查看客户进程的Binder对象引用表:
- 如果没有引用对象的记录,同样会创建新的节点
- 并让这个节点中某个字段指向服务进程的
Binder实体对象表
中的节点
- 接下来驱动对Parcel对象中的数据进行改动:
- 把数据从
BINDER_TYPE_BINDER
改为BINDER_TYPE_HANEL
- 同时把
handle
的值设为Binder对象引用表
中的节点
- 把数据从
- 最后,把改动的数据传到
客户进程
- 会先查找和服务进程相关的Binder实体对象表:
- 客户端接收到数据,发现数据中的Binder类型为BINDER_TYPE_HANEL后
- 使用handle值作为参数,调用ProcessState类中的函数getStrongProxyFoHandle来得到BpBinder对象
- 如果对象不存在则创建一个新的对象
- 这个
BpBinder对象
会一直保存在ProcessState
的mHandleToObject
表中
- 这样,客户端就得到了
Binder引用对象
- 使用handle值作为参数,调用ProcessState类中的函数getStrongProxyFoHandle来得到BpBinder对象
写入Binder对象的过程
有了上面的整体流程,我们来看下Binder对象
的写入细节:
- Parcel类中:
- 写入Binder对象的函数是:
-
writeStrongBinder
:写入强引用Binder对象
-
writeWeakBinder
:写入弱引用Binder对象
-
- 读取Binder对象的函数是:
- readStrongBinder:获取强引用Binder对象
- 强引用的Binder对象可以分为
实体对象
和引用对象
- 强引用的Binder对象可以分为
- readWeakBinder:获取弱引用Binder对象
- 弱引用则没有区分
实体对象
和引用对象
- 弱引用则没有区分
- readStrongBinder:获取强引用Binder对象
- 写入Binder对象的函数是:
看下writeStrongBinder
的代码:
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
return flatten_binder(ProcessState::self(), val, this);
}
status_t flatten_binder(const sp<ProcessState>& /*proc*/,
const sp<IBinder>& binder, Parcel* out)
{
// flatten_binder 整个方法其实是在向obj这个结构体存放数据
flat_binder_object obj;
if (IPCThreadState::self()->backgroundSchedulingDisabled()) {
/* minimum priority for all nodes is nice 0 */
obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
} else {
/* minimum priority for all nodes is MAX_NICE(19) */
obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;
}
if (binder != NULL) {
// 调用localBinder函数开区分是实体对象还是引用对象
IBinder *local = binder->localBinder();
if (!local) { //binder引用对象
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL) {
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
} else { // binder实体对象
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
}
} else {
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
}
return finish_flatten_binder(binder, obj, out);
}
flatten_binder
整个方法其实是在向flat_binder_object
这个结构体存放数据。我们看下flat_binder_object
的结构:
struct flat_binder_object {
/* 8 bytes for large_flat_header. */
__u32 type;
__u32 flags;
/* 8 bytes of data. */
union {
binder_uintptr_t binder; /* local object */
__u32 handle; /* remote object */
};
/* extra data associated with local object */
binder_uintptr_t cookie;
};
我们看下flat_binder_object
中的属性:
- type 的类型:
-
BINDER_TYPE_BINDER
:用来表示Binder实体对象 -
BINDER_TYPE_WEAK_BINDER
:用来表示Bindr实体对象的弱引用 -
BINDER_TYPE_HANDLE
:用来表示Binder引用对象 -
BINDER_TYPE_WEAK_HANDLE
:用来表示Binder引用对象的弱引用 -
BINDER_TYPE_FD
:用来表示一个文件描述符
-
-
flag
字段用来保存向驱动传递的标志 -
union.binder
在打包实体对象
时存放的是对象的弱引用指针
-
union.handle
在打包引用对象
时存放的是对象中的handle值
-
cookie
字段只用在打包实体对象
时,存放的是BBinder指针
解析强引用
Binder对象数据的过程
Parcel 类中解析数据的函数是unflatten_binder
,代码如下:
status_t unflatten_binder(const sp<ProcessState>& proc,
const Parcel& in, sp<IBinder>* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
switch (flat->hdr.type) {
case BINDER_TYPE_BINDER:
*out = reinterpret_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE:
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast<BpBinder*>(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
unflatten_binder
的逻辑是:
-
如果是
BINDER_TYPE_BINDER
类型的数据,说明接收到的数据类型是Binder实体对象
,此时cookie字段
存放的是本进程的Binder实体对象
的指针,可直接转化成IBinder
的指针 -
如果是 BINDER_TYPE_HANDLE 类型的数据,则调用 ProcessState类的 getStrongProxyForHandle函数来得到 BpBinder对象,函数代码如下:
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) { sp<IBinder> result; AutoMutex _l(mLock); // 根据handle查看进程中是否已经创建了引用对象 // 如果进程中不存在handle对应的引用对象,在表中插入新的元素并返回 handle_entry* e = lookupHandleLocked(handle); if (e != NULL) { IBinder* b = e->binder; //根据返回元素中的binder值来判断是否有引用对象 if (b == NULL || !e->refs->attemptIncWeak(this)) { if (handle == 0) { // handle为0 表示是ServiceManager的引用对象 Parcel data; //发送PING_TRANSACTION检查ServiceManager是否存在 status_t status = IPCThreadState::self()->transact( 0, IBinder::PING_TRANSACTION, data, NULL, 0); if (status == DEAD_OBJECT) return NULL; } b = BpBinder::create(handle); // 创建一个新的引用对象 e->binder = b;// 放入元素中的binder属性 if (b) e->refs = b->getWeakRefs(); result = b; } else { result.force_set(b);// 如果引用对象已经存在,放入到返回的对象result中 e->refs->decWeak(this); } } return result; }
getStrongProxyForHandle()函数会调用lookupHandleLocked()来查找handle在进程中对应的引用对象。所有进程的引用对象都保存在 ProcessState 的 mHandleToObject
变量中。 mHandleToObject 变量定义如下:
Vector<handle_entry> mHandleToObject;
-
mHandleToObject 是一个 Vector 集合类,元素类型 handle_entry
-
handle_entry
结构很简单:
-
struct handle_entry {
IBinder* binder;
RefBase::weakref_type* refs;
};
-
lookupHandleLocked()
函数就是使用handle
作为关键项
来查找对应的handle_entry
,没有则创建新的handle_entry
,并添加到集合中 -
当获得 handle_entry 后,如果 handle 值为0,表明要创建的是 ServiceManager 的 引用对象
- 并发送
PING_TRANSACTION
消息来检查ServiceManager
是否已经创建
- 并发送
IPCThreadState类
每个Binder线程
都会有一个关联的IPCThreadState类
的对象。IPCThreadState类
主要的作用是和Binder驱动
交互,发送接收Binder数据
,处理和Binder驱动
之间来往的消息。
我们在Binder线程模型
中已经知道:
-
Binder服务
启动时,服务线程调用了joinThreadPool()
函数 - 远程调用
Binder服务
时,客户线程调用了waitForResponse()
函数
这两个函数都是定义在IPCThreadState类
中,我们分别来看下这两个函数。
waitForResponse()
函数
函数定义如下:
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;//和驱动通信
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;//没有数据,重新开始循环
cmd = (uint32_t)mIn.readInt32();//读取数据
//......
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
//......省略部分case语句
case BR_REPLY:
// Binder调用返回的消息
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
//...... 错误处理
return err;
}
waitForResponse()
函数中是一个无限while循环
,在循环中,重复下面的工作:
- 调用
talkWithDriver
函数发送/接收
数据 - 如果有消息从驱动返回,会通过 switch语句处理消息。
- 如果收到错误消息或者调用返回的消息,将通过
goto
语句跳出while循环
- 如果还有未处理的消息,则交给
executeCommand
函数处理
- 如果收到错误消息或者调用返回的消息,将通过
我们再仔细看下Binder调用
收到的返回类型为BR_REPLY
的代码:
case BR_REPLY:
{
binder_transaction_data tr;
//按照 binder_transaction_data 结构大小读取数据
err = mIn.read(&tr, sizeof(tr));
ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
if (err != NO_ERROR) goto finish;
if (reply) {
//reply 不为null,表示调用者需要返回结果
if ((tr.flags & TF_STATUS_CODE) == 0) {
//binder 调用成功,把从驱动来的数据设置到reply对象中
reply->ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t),
freeBuffer, this);
} else {
//binder调用失败,使用freeBuffer函数释放驱动中分配的缓冲区
err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), this);
}
} else {
//调用者不需要返回结果,使用freeBuffer函数释放驱动中分配的缓冲区
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), this);
continue;
}
}
针对BR_REPLY
类型的处理流程是:
- 如果 Binder调用 成功返回,并且 调用者 也需要返回值
- 把接收到的数据放在
Parcel
对象reply
中返回
- 把接收到的数据放在
- 如果 Binder调用 不成功,或者 调用者不需要返回数据
- 通过
freeBuffer
释放驱动中分配的缓冲区
- 通过
因为Binder
提供了一块驱动和应用层共享的内存空间
,所以在接收Binder数据
时不需要额外创建缓冲区
并再进行一次拷贝
了,但是如果不及时通知驱动释放缓冲区中占用的无用内存,会很快会耗光这部分共享空间。
上面代码中的reply->ipcSetDataReference
方法,在设置Parcel对象
的同时,同样也把freeBuffer
的指针作为参数传入到对象中,这样reply对象
删除时,也会调用freeBuffer
函数来释放驱动中的缓冲区。
waitForResponse()
函数的作用是发送Binder调用的数据并等待返回值
。为什么还需要使用循环的方式反复和驱动交互?原因有两点:
- 一是消息协议中要求应用层通过 BC_TRANSACTION 发送 Binder 调用数据后:
- 驱动要先给应用层回复
BC_TRANSACTION_COMPLETE
消息,表示已经说到并且认可本次Binder调用数据
。 - 然后上层应用再次调用
talkWithDriver
来等待驱动返回调用结果 - 如果调用结果返回了,会收到
BR_REPLY
消息
- 驱动要先给应用层回复
- 二是等待调用返回期间,驱动可能会给线程发送消息,利用这个线程帮忙干点活。。。。
joinThreadPool
函数
在
Binder线程池
部分已经知道:应用启动时会伴随着启动Binder服务
,而最后执行到的方法就是joinThreadPool
函数。
我们看下函数定义:
void IPCThreadState::joinThreadPool(bool isMain)
{
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
status_t result;
do {
processPendingDerefs();
//now get the next command to be processed, waiting if necessary
result = getAndExecuteCommand();//读取并处理驱动发送的消息
if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
abort();
}
// Let this thread exit the thread pool if it is no longer
// needed and it is not the main process thread.
if(result == TIMED_OUT && !isMain) {
break;
}
} while (result != -ECONNREFUSED && result != -EBADF);
mOut.writeInt32(BC_EXIT_LOOPER); //退出前向驱动发送线程退出消息
talkWithDriver(false);
}
joinThreadPool
函数的结构是一个while循环。
-
传入的参数 isMain
- 如果为 true(通常是线程池第一个线程发起的调用),则向驱动发送 BC_ENTER_LOOPER 消息
- 发送
BC_ENTER_LOOPER
的线程会被驱动标记为“主”线程 - 不会在空闲时间被驱动要求退出
- 发送
- 否则,发送
BC_REGISTER_LOOPER
。 - 这两条消息都是告诉驱动:本线程已经做好准备接收驱动来的Binder调用了
- 如果为 true(通常是线程池第一个线程发起的调用),则向驱动发送 BC_ENTER_LOOPER 消息
-
进入循环,调用了 processPendingDerefs() 函数
-
用来处理 IPCThreadState 对象中 mPendingWeakDerefs
和 mPendingStrongDerefs 的 Binder对象 的引用计数
-
mPendingWeakDerefs
和mPendingStrongDerefs
都是Vector
集合
-
-
当接收到驱动发来的
BR_RELEASE
消息时,就会把其中的Binder对象
放到mPendingStrongDerefs
中 -
并在
processPendingDerefs()
函数中介绍对象的引用计数
-
-
调用 getAndExecuteCommand 函数
- 函数中调用
talkWithDriver
读取驱动传递的数据 - 然后调用
executeCommand
来执行
- 函数中调用
到这里,我们再来看下talkWithDriver
和executeCommand
两个函数
talkWithDriver
函数
talkWithDriver
函数的作用是把IPCThreadState
类中的mOut变量
保存的数据通过ioctl
函数发送到驱动,同时把驱动返回的数据放到类的mIn变量
中。
talkWithDriver
函数的代码如下:
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {
return -EBADF;
}
//ioctl 传输时所使用的的数据结构
binder_write_read bwr;
// Is the read buffer empty?
// 判断 mIn 中的数据是否已经读取完毕,没有的话还需要继续读取
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// We don't 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.
// 英文描述的很详细了哟
// 如果不需要读取数据(doReceive=false,needRead=true),那么就可以准备写数据了
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;//表示要写入的长度
bwr.write_buffer = (uintptr_t)mOut.data();//要写入的数据的指针
// This is what we'll read.
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
// Return immediately if there is nothing to do.
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;//这个字段后面会被填上驱动读取到的数据长度(写入到驱动的数据长度)
bwr.read_consumed = 0;// 这个字段后面会被填上从驱动返回的数据长度
status_t err;
do {
// 9.0增加了一些平台判断,可能以后要多平台去支持了吧
#if defined(__ANDROID__)
// 用ioctl和驱动交换数据
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
#else
err = INVALID_OPERATION;
#endif
if (mProcess->mDriverFD <= 0) {
// 这个情况应该是设备节点不可用
err = -EBADF;
}
} while (err == -EINTR);
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
// 如果已经写入驱动的数据长度小于mOut中的数据长度
// 说明还没发送完,把已经写入驱动的数据移除掉
// 剩下的数据等待下次发送
mOut.remove(0, bwr.write_consumed);
else {
// 数据已经全部写入驱动,复位mOut
mOut.setDataSize(0);
// 做一些指针的清理工作
processPostWriteDerefs();
}
}
if (bwr.read_consumed > 0) {
// 说明从驱动中读到了数据,设置好mInt对象
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
return NO_ERROR;
}
return err;
}
- 准备发送到驱动中的数据保存在成员变量
mOut
中 - 从驱动中读取到的数据保存在成员变量
mInt
中 - 调用 talkWithDriver 时,如果 mInt 还有数据
- 表示还没有处理完驱动发来的消息
- 本次函数调用将不会从驱动中读取数据
- ioctl 函数
- 使用的命令是
BINDER_WRITE_READ
- 需要
binder_write_read
结构体作为参数 - 驱动篇再看
- 使用的命令是
executeCommand
函数
executeCommand
函数是一个大的switch语句,处理从驱动传递过来的消息。
我们前面遇到了一些消息,大概包括:
-
BR_SPAWN_LOOP
:驱动通知启动新线程的消息 -
BR_DEAD_BINDER
:驱动通知Binder服务
死亡的消息 -
BR_FINISHED
:驱动通知线程退出的消息 -
BR_ERROR
,BR_OK
,BR_NOOP
:驱动简单的回复消息 -
BR_RELEASE
,BR_INCREFS
,BR_DECREFS
:驱动通知增加和减少Binder对象
跨进程的引用计数 -
BR_TRANSACTION
,:驱动通知进行Binder调用
的消息
重点是BR_TRANSACTION
,代码定义如下:
case BR_TRANSACTION:
{
binder_transaction_data tr;
result = mIn.read(&tr, sizeof(tr));
if (result != NO_ERROR) break; // 数据异常直接退出
Parcel buffer;
//用从驱动接收的数据设置Parcel对象Buffer
buffer.ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
const pid_t origPid = mCallingPid;
const uid_t origUid = mCallingUid;
const int32_t origStrictModePolicy = mStrictModePolicy;
const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
// 从消息中取出调用者的进程ID和euid
mCallingPid = tr.sender_pid;
mCallingUid = tr.sender_euid;
mLastTransactionBinderFlags = tr.flags;
Parcel reply;
status_t error;
if (tr.target.ptr) {
// We only have a weak reference on the target object, so we must first try to
// safely acquire a strong reference before doing anything else with it.
if (reinterpret_cast<RefBase::weakref_type*>(
tr.target.ptr)->attemptIncStrong(this)) {
// 如果ptr指针不为空,cookie保存的是BBinder的指针
// 调用cookie的transact函数
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags);
// 及时清除指针
reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
} else {
error = UNKNOWN_TRANSACTION;
}
} else {
//如果tr.target.ptr为0 表示是ServiceManager
error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
}
// 如果是同步调用,则把reply对象发送回去,否则什么也不做
if ((tr.flags & TF_ONE_WAY) == 0) {
LOG_ONEWAY("Sending reply to %d!", mCallingPid);
if (error < NO_ERROR) reply.setError(error);
sendReply(reply, 0);
} else {
LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
}
mCallingPid = origPid;
mCallingUid = origUid;
mStrictModePolicy = origStrictModePolicy;
mLastTransactionBinderFlags = origTransactionBinderFlags;
}
break;
BR_TRANSACTION
消息的处理过程是:
- 把消息解析出来后放置到
Parcel
类型的buffer
对象中 - 使用消息参数里的 BBinder 指针来调用 transact 函数
-
transact
函数会传入函数号
、buffer
、reply
-
transact
函数根据函数号
来调用对应的服务函数
-
服务函数
执行完后,结果保存在reply
对象中
-
- 如果是同步调用,使用
sendReply
函数返回reply
对象 - 如果是异步调用,则什么也不做,结束调用
网友评论