Android Binder框架实现之Native层addService详解之请求的发送
Android Binder机制(五) addService详解01之请求的发送 有了前面篇章的铺垫,我想读者对Binder应该有了一定的了解了,那么我们接着继续深入了解。本篇章终于要开始讲解Client-Server交互了,若标题所示,本文要讲解的是addService请求,即添加服务请求。本文选取的题材是MediaPlayerService服务通过addService请求注册到Serv..
Android Binder框架实现之Native层addService详解之请求的发送
Android Binder框架实现目录:
Android Binder框架实现之Binder的设计思想
Android Binder框架实现之何为匿名/实名Binder
Android Binder框架实现之Binder中的数据结构
Android Binder框架实现之Binder相关的接口和类
Android Binder框架实现之Parcel详解之基本数据的读写
Android Binder框架实现之Parcel read/writeStrongBinder实现
Android Binder框架实现之servicemanager守护进程
Android Binder框架实现之defaultServiceManager()的实现
Android Binder框架实现之Native层addService详解之请求的发送
Android Binder框架实现之Native层addService详解之请求的处理
Android Binder框架实现之Native层addService详解之请求的反馈
Android Binder框架实现之Binder服务的消息循环
Android Binder框架实现之Native层getService详解之请求的发送
Android Binder框架实现之Native层getService详解之请求的处理
Android Binder框架实现之Native层getService详解之请求的反馈
Android Binder框架实现之Binder Native Service的Java调用流程
Android Binder框架实现之Java层Binder整体框架设计
Android Binder框架实现之Framework层Binder服务注册过程源码分析
Android Binder框架实现之Java层Binder服务跨进程调用源码分析
Android Binder框架实现之Java层获取Binder服务源码分析
前言
有了前面篇章的铺垫,我想读者对Binder应该有了一定的了解了,那么我们接着继续深入了解。本篇章终于要开始讲解Client-Server交互了,如标题所示,本文要讲解的是addService请求,即添加服务请求。本文选取的切入点是MediaPlayerService服务通过addService请求注册到ServiceManager中。在这个请求中,MediaPlayerService是Client,而ServiceManager是Server。由于涉及到的过程比较复杂,这里会将addService请求分为3篇进行说明,这3篇的主题分别是:请求的发送,请求的处理,以及请求的反馈。和以往一样,在讲解详细的代码之前,先做个整体介绍。
注意:本文是基于Android 7.xx版本进行介绍的,其中涉及的源码路径如下:
frameworks/native/libs/binder/IServiceManager.cpp
frameworks/native/libs/binder/Static.cpp
frameworks/native/libs/binder/ProcessState.cpp
kernel/drivers/staging/android/binder.c
kernel/include/linux/list.h
kernel/drivers/staging/android/uapi/binder.h
external/kernel-headers/original/uapi/linux/android/binder.h
frameworks/native/include/binder/ProcessState.h
frameworks/native/include/binder/BpBinder.h
frameworks/native/libs/binder/BpBinder.cpp
frameworks/native/include/binder/IPCThreadState.h
frameworks/native/libs/binder/IPCThreadState.cpp
frameworks/native/include/binder/IInterface.h
frameworks/native/libs/binder/IInterface.cpp
frameworks/native/include/binder/IBinder.h
frameworks/native/libs/binder/Binder.cpp
frameworks/av/media/mediaserver/main_mediaserver.cpp
frameworks/native/cmds/servicemanager/service_manager.c
- 在正式开始进行源码分析前,先看下Native Binder服务注册整体示意图:
- 接着在正式开始进行源码分析前,先看看Native Binder类图如下:
一.addService流程的时序图
上面是addService流程的精简时序图,理解这个图的前提是理解图中的三种角色之间的关系:
(01) MediaPlayerService和ServiceManager是两个不同的进程。它们都位于用户空间,都有各自的内存单元,两者之间不能直接进行通信;因此,需要Binder驱动的帮助才能通信。
(02) Binder驱动位于内核空间,它映射到节点"/dev/binder"上。MediaPlayerService和ServiceManager都有通过open("/dev/binder")打开该节点,并通过mmap()将内存映射到各自所在的进程中;这也就是说MediaPlayerService能和Binder驱动通信,而且ServiceManager也能和Binder驱动通信。而在Binder驱动中,有一个全局变量,依靠这个全局变量,就能实现MediaPlayerService和ServiceManager之间的通信。 依靠的这个全局变量,就是 Android Binder框架实现之ServiceManager守护进程中介绍过的binder_context_mgr_node变量,它是ServiceManager的Binder实体。
搞清楚了上面三者之间的关系之后(不是三角恋),再回到时序图,来看看三者之间的交互命令:
(1) WAIT:这表示ServiceManager进程进入中断等待状态,等待客户端的到来。它进入等待状态的详细流程,在 Android Binder框架实现之ServiceManager守护进程篇有详细介绍过。
(2) BC_TRANSACTION:这是MediaPlayerService进程向ServiceManager发送addService请求对应的事务。这个事务是请求,而不是回复,因此BC开头,B代表Binder,而C代表Command。如果是回复,则会以BR开发,R表示Reply。Binder驱动在收到BC_TRANSACTION之后,会将分配内存,将请求数据保存到所分配的内存中。
(3) WAKE_UP:MediaPlayerService通过BC_TRANSACTION提交一个请求,该请求是交给ServiceManager来处理的。因此,Binder驱动在收到该请求后,会将其发送到ServiceManager的待处理事务队列中,并将ServiceManager唤醒。
(4) BR_TRANSACTION_COMPLETE:MediaPlayerService在发起了一个请求之后,它需要知道该请求是否发送成功。因此,Binder驱动在将该请求提交给ServiceManager之后,会反馈一个BR_TRANSACTION_COMPLETE给MediaPlayerService,表示MediaPlayerService发送的请求已经被Binder驱动收到了。
(5) WAIT:MediaPlayerService在知道自己的请求发送成功之后,就会进入中断等待状态,等待请求的反馈。
(6)BR_NOOP和BR_TRANSACTION:ServiceManager被唤醒之后,收到Binder驱动的BR_NOOP和BR_TRANSACTION指令。BR_NOOP指令什么也不会做;而对于BR_TRANSACTION指令时,ServiceManager在解析出该事务是添加服务请求,会将MediaPlayerService的相关信息保存到一个链表中。
(7) BC_FREE_BUFFER和BC_REPLY:ServiceManager在保存了MediaPlayerService的相关信息之后,便处理完毕了MediaPlayerService的请求。此时,它便反馈BC_FREE_BUFFER和BC_REPLY给Binder驱动。Binder驱动在收到BC_FREE_BUFFER之后,会释放保存请求数据所申请的内存;收到BC_REPLY之后,Binder驱动则知道ServiceManager已经处理完了MediaPlayerService的请求。
(8) WAKE_UP:Binder驱动发送WAKE_UP唤醒MediaPlayerService进程。
(9) BR_NOOP和BR_REPLY:Binder驱动唤醒MediaPlayerService后,继续BR_NOOP和BR_REPLY给MediaPlayerService,告诉MediaPlayerService请求已经处理完毕。
(10) BR_TRANSACTION_COMPLETE:同时,Binder驱动会发送一个BR_TRANSACTION_COMPLETE给ServiceManager,告诉ServiceManager该事务已经处理完毕。 MediaPlayerService在收到BR_REPLY反馈之后,知道addService请求已经成功处理;接着,它会再次进入等待状态,等待Client的请求。
(11) WAIT:最后,ServiceManager处理MediaPlayerService的请求之后,没有其他事务可处理,也再次进入了等待状态。
二.IMediaPlayerService的类图
本文是以MediaPlayerService为例,对addService进行解析。下面让我们一起看看MediaPlayerService相关联的类图(是不是觉得有点熟悉,在前面的篇章里面也有见过不是)。
MediaPlayerService的类图和 Android Binder框架实现之defaultServiceManager()的实现中IServiceManager的类图类似。这里就不再逐一对每个类进行介绍了。
需要知道的是,对于一个MediaPlayerService而言,它存在一个"远程BpBinder对象"和"本地BBinder对象"。
(1) 远程BpBinder对象的作用,是和Binder驱动进行交互。例如,当本文所讲到的addService请求,就是通过defaultServiceManager()调用到远程BpBinder对象的transact()方法,而该方法又会调用到IPCThreadState::transact()接口,通过IPCThreadState类来和Binder驱动交互。
(2) MediaPlayerService是"本地BBinder的子类"。当Client向MediaPlayerService发起请求时,会调用BBinder的onTransact()方法,而BnMediaPlayerService又重写了该方法,从而调用onTransact()完成对请求的处理。
三.addService请求发送代码详解
在前面的章节里面,我们从时序图和类图两个维度对addService有了一个轮廓上的认识,下面我们从代码层面来分析addService请求发送的流程。
3.1 MediaPlayerService的main()函数
本篇章是以MediaPlayerService添加到ServiceManager为例来说明addService的过程,所以得先从MediaPlayerService服务入手,通过源码我们知道该服务是一个Native Service所以先从函数入口main()分析起。
int main(int argc __unused, char **argv __unused)
{
...
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm(defaultServiceManager());
...
MediaPlayerService::instantiate();
ResourceManagerService::instantiate();
...
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
该源码的路径是frameworks/av/media/mediaserver/main_mediaserver.cpp中。这段代码比较精简(不是简单额),主要功能如下:
(1) ProcessState:self()是获取ProcessState对象,并赋值给proc。ProcessState::self(),详细的介绍在Android Binder框架实现之defaultServiceManager()的实现中已经介绍过了。
(2) defaultServiceManager()是获取IServiceManager对象,它的实现在Android Binder框架实现之defaultServiceManager()的实现中也有详细介绍。
(3) MediaPlayerService::instantiate()是初始化MediaPlayerService服务。
3.2 MediaPlayerService::instantiate()
void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService());
}
该代码在frameworks/av/media/libmediaplayerservice/MediaPlayerService.cpp中,可以使用一句话来概括就是将MediaPlayerService添加到ServiceManager。它会新建MediaPlayerService对象;然后调用defaultServiceManager()获取到的BpServiceManager的实例然后调用BpServiceManager的addService()方法,将MediaPlayerService对象添加到Service Manager中。MediaPlayerService服务的名称是"media.player"。可以通过service list命令查看:
λ adb shell "service list | grep media.player"
143 media.player: [android.media.IMediaPlayerService]
3.3 MediaPlayerService::MediaPlayerService()
MediaPlayerService::MediaPlayerService()
{
ALOGV("MediaPlayerService created");
mNextConnId = 1;
mBatteryAudio.refCount = 0;
for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
mBatteryAudio.deviceOn[i] = 0;
mBatteryAudio.lastTime[i] = 0;
mBatteryAudio.totalTime[i] = 0;
}
// speaker is on by default
mBatteryAudio.deviceOn[SPEAKER] = 1;
// reset battery stats
// if the mediaserver has crashed, battery stats could be left
// in bad state, reset the state upon service start.
BatteryNotifier::getInstance().noteResetVideo();
MediaPlayerFactory::registerBuiltinFactories();
}
MediaPlayerService的构造函数比较简单,就是进行一些变量的初始化。没有什么好说的,其次这也不是本篇探讨的重点。
3.4 BpServiceManager::addService()
class BpServiceManager : public BpInterface<IServiceManager>
{
public:
...
virtual status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated)
{
Parcel data, reply;
//见3.6章节分析
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
//见3.7章节分析
data.writeString16(name);
//见3.8章节分析
data.writeStrongBinder(service);
//见3.9章节分析
data.writeInt32(allowIsolated ? 1 : 0);
//见3.10章节分析
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
...
}
该代码在frameworks/native/libs/binder/IServiceManager.cpp中,通过前面的篇章我们知道defaultServiceManager最后通过层层的分解得到的是ServiceManager远程服务端代理BpServiceManager,最终调用BpServiceManager的addService()会先将MediaPlayerService服务的名称(“media.player”)以及它的实例等参数保存到data(Parcel对象)中,然后再调用remote()返回的BpBinder对象的transact()与Binder驱动进行交互。
(1)addService参数分析: name=“media.player”,即MediaPlayerService服务的名称;service就是MediaPlayerService对象,而IBinder是MediaPlayerService的父类;allowIsolated这个值默认为false,默认值的定义在frameworks/native/include/binder/IServiceManager.h的addService()函数声明中。
(2)数据写入分析: Parcel是Binder通信的数据存储结构,它的各个成员和函数在Android Binder机制(二) Binder中的数据结构中有详细说明。在向data中写入数据时,先通过writeInterfaceToken()写入数据头,这里的数据头是:int32的整形数+字符串(字符串是"android.os.IServiceManager")。writeString16(name)写入的是服务的名称,即"media.player"。writeStrongBinder(service)是将MediaPlayerService封装到flat_binder_object结构体中。最后的writeInt32()暂时不用关心。
下面,我们逐个对data的赋值进行介绍。
3.5 Parcel类分析
通过上面的分析可知,addService里面会创建两个Parcel的对象,下面我们来看看Parcel的构造函数。
Parcel::Parcel()
{
LOG_ALLOC("Parcel %p: constructing", this);
initState();
}
该代码定义在frameworks/native/libs/binder/Parcel.cpp,接着继续分析initState函数,该函数主要做一些初始化的工作。
void Parcel::initState()
{
mError = NO_ERROR;
mData = 0; //数据的地址指针
mDataSize = 0; //数据的大小
mDataCapacity = 0; //数据的容量
mDataPos = 0; //数据的位置
mObjects = NULL; //保存对象的地址指针
mObjectsSize = 0; //对象的个数
mObjectsCapacity = 0; //对象的容量
mNextObjectHint = 0;
mHasFds = false;
mFdsKnown = true;
mAllowFds = true;
mOwner = NULL;
mOpenAshmemSize = 0;
...
}
3.6 Parcel::writeInterfaceToken(getInterfaceDescriptor())
下面看看data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor())的实现。getInterfaceDescriptor()是通过宏IMPLEMENT_META_INTERFACE()实现的,该宏已经在Android Binder框架实现之defaultServiceManager()的实现中介绍过了;getInterfaceDescriptor()的返回是"android.os.IServiceManager",即data.writeInterfaceToken(“android.os.IServiceManager”)。下面看看writeInterfaceToken()的实现。
status_t Parcel::writeInterfaceToken(const String16& interface)
{
writeInt32(IPCThreadState::self()->getStrictModePolicy() |
STRICT_MODE_PENALTY_GATHER);
return writeString16(interface);
}
该函数先通过writeInt32()写入一个32位的int数到Parcel中,然后再通过writeString16()将字符串写入到Parcel中。它所写入的是数据头,ServiceManager中收到该数据之后,会先获取数据头,并根据数据头来判断数据的有效性! 下面继续分析该函数:
(1) IPCThreadState::self()返回IPCThreadState对象;然后,调用IPCThreadState::getStrictModePolicy(),返回的是mStrictModePolicy,mStrictModePolicy的初始值是0。因此,writeInt32()就可以简化为writeInt32(STRICT_MODE_PENALTY_GATHER)。
(2) writeString16(interface)是writeString16(“android.os.IServiceManager”)。
好了至此writeInterfaceToken分析完毕了,它从整体上可以划分为writeInt32和writeString16两个小部分。我们假设Parcel在内存中是以类似数组的存储空间进行存储的(这个只是假设,实际并不是如此),那么此时各个C_Parcel的位置相关的变量值的逻辑如下所示(是不是觉得Parcel还是很简单吗(到这里还是很简单,待分析完成了IBinder的打包和传输你就不会这么说了!),这里需要重点说明的是mData的值肯定不为0,因为内存中的0地址是给系统预留的,但是mDataPos的值是从0开始增长起来的。
3.6.1 Parcel::writeInt32()
status_t Parcel::writeInt32(int32_t val)
{
return writeAligned(val);
}
writeInt32函数会调用writeAligned()。
Parcel::writeAligned()
template<class T>
//模板函数
status_t Parcel::writeAligned(T val) {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));
if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
*reinterpret_cast<T*>(mData+mDataPos) = val;
return finishWrite(sizeof(val));
}
status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}
writeAligned()的作用是是写入数据,譬如同步相应的变量,下面渐进分析:
(1) 在函数writeAligned()中此时mDataPos的初始值=0,sizeof(val)=4,mDataCapacity的初始值=0。因此,if((mDataPos+sizeof(val)) <= mDataCapacity)为false。
(2) 接下来,会先调用growData(sizeof(val))来增加容量,然后再将数据写入到mData中。
Parcel::growData()
status_t Parcel::growData(size_t len)
{
if (len > INT32_MAX) {
return BAD_VALUE;
}
size_t newSize = ((mDataSize+len)*3)/2;
return (newSize <= mDataSize)
? (status_t) NO_MEMORY
: continueWrite(newSize);
}
接着继续分析growData(字面理解就可以看出是增加容量),通过代码可以看出Parcel增加容量时,是按1.5倍进行增长,因为此时mDataSize=0,而len=4;因此会执行continueWrite(6)。
Parcel::continueWrite()
//Parcel.cpp
status_t Parcel::continueWrite(size_t desired)
{
size_t objectsSize = mObjectsSize;
...
if (mOwner) {
...
} else if (mData) {
...
} else {
// This is the first data. Easy!
uint8_t* data = (uint8_t*)malloc(desired);
..
mData = data;
mDataSize = mDataPos = 0;
mDataCapacity = desired;
}
return NO_ERROR;
}
接着继续分析continueWrite,此时mObjectsSize的初始值为0,mOwner的初始值为NULL,mData非空;并且,desired=6,mDataCapacity=0。因此,会调用realloc()给mData重新分配内存大小为6字节。分配成功后,更新"数据地址mData"和"数据容量mDataCapacity=6"。接下来,回到writeAligned()中,它会跳转到restart_write标签处。先将int32_t的整形数保存到mData中,然后再调用finishWrite()进行同步。
Parcel::finishWrite()
status_t Parcel::finishWrite(size_t len)
{
if (len > INT32_MAX) {
return BAD_VALUE;
}
mDataPos += len;
if (mDataPos > mDataSize) {
mDataSize = mDataPos;
...
}
return NO_ERROR;
}
下面我们继续分析finishWrite(),在前面的步骤中已经将数据写入到mData中,现在就通过finishWrite()来改变数据的当前指针位置(方便下一次写入)和数据的大小。此时
(1) len是int32_t的大小,很显然是4个字节,len=4。所以,mDataPos=4。
(2) mDataPos=4,mDataSize=0;因此if(mDataPos>mDataSize)为true,所以,mDataSize=4。
到这里我们就已经分析完了writeInterfaceToken()中的writeInt32()的所有过程了。下面让我们再理一理此时Parcel里面各个变量的值。
mData:它的第0~3个字节保存了int32_t类型的数据STRICT_MODE_PENALTY_GATHER。
mDataPos:值为4,即下一个写入mData中的数据从第4个字节开始。
mDataSize:值为4,即mData中数据的大小。
mDataCapacity:值为6,即mData的数据容量为6字节。
此时,mData的数据如下图所示:
好了writeInt分析完毕了,我们假设Parcel在内存中是以类似数组的存储空间进行存储的(这个只是假设,实际并不是如此),这里需要重点说明的是mData的值肯定不为0,因为内存中的0地址是给系统预留的,但是mDataPos的值是从0开始增长起来的。
接下来,我们继续分析writeString16(“android.os.IServiceManager”),看它是如何将字符串写入到Parcel中。
3.6.2 writeString16(interface)
status_t Parcel::writeString16(const String16& str)
{
return writeString16(str.string(), str.size());
}
status_t Parcel::writeString16(const char16_t* str, size_t len)
{
if (str == NULL) return writeInt32(-1);
//将字符串长度写入Parcel中
status_t err = writeInt32(len);
if (err == NO_ERROR) {
len *= sizeof(char16_t);//重新计算len的长度
//在将字符串写入之前,增加mData的容量
uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
if (data) {
//将字符串拷贝到mData中
memcpy(data, str, len);
//字符串结束符
*reinterpret_cast<char16_t*>(data+len) = 0;
return NO_ERROR;
}
err = mError;
}
return err;
}
让我们回到writeInterfaceToken继续分析writeString16,此时会调用重载的writeString16函数,下面我们来逐步分析:
(1) writeString16(str, len)中,str=“android.os.IServiceManager”;len是由str.size()得来,虽然这里的字符串是String16类型(即每个字符占2个字节),但是str.size()是获取str中有效数据的个数(不包含字符串结束符),因此,len=26。
(2) 首先调用writeInt32(len)将字符串的长度写入到Parcel中,writeInt32()在前面已经介绍过了。当再次写入int32_t类型的数据时,数据容量不够,会再次增长为12,即mDataCapacity=12;而写入int32_t类型的数据之后,mDataPos和mDataSize都增长为8。 此时,mData的数据如下图所示:
在调用writeInt32(len)写入数据长度之后,再重新计算len的值为52,接着通过writeInplace()写入数据。
Parcel::writeInplace()
#define PAD_SIZE_UNSAFE(s) (((s)+3)&~3)
static size_t pad_size(size_t s) {
if (s > (SIZE_T_MAX - 3)) {
abort();
}
return PAD_SIZE_UNSAFE(s);
}
void* Parcel::writeInplace(size_t len)
{
if (len > INT32_MAX) {
return NULL;
}
//4字节对齐
const size_t padded = pad_size(len);
if (mDataPos+padded < mDataPos) {
return NULL;
}
if ((mDataPos+padded) <= mDataCapacity) {
restart_write:
uint8_t* const data = mData+mDataPos;
// 如果padded!=len,则根据大端法还是小端法进行地址对齐设置。
if (padded != len) {
...
}
finishWrite(padded);
return data;
}
status_t err = growData(padded);
if (err == NO_ERROR) goto restart_write;
return NULL;
}
重点备注一下此时的入参len为54而不是52这个地方不要弄错了,至于为什么大家可以从代码里面查找到原因。下面接着继续分析代码:
(1) pad_size()是4字节对齐的宏,所以pad_size(54)计算后的结果是56。
(2) 函数的初始值为padded=56,mDataPos=8,mDataCapacity=12。因此,会先调用growData(padded)来增加数据容量。growData()在前面已经介绍过;此时,它会将容量mDataCapacity增加至96。
(3) 接着会跳转到restart_write标签处,然后调用finishWrite(padded)来更新mDataPos和mDataSize。
至此,writeInplace()就分析完了,它的作用就是增加mData的容量,并返回即将写入数据的地址。接着,我们继续回到3.6.2 writeString16章节中,执行mmap(data, str, len)将数据拷贝到mData中;拷贝完毕之后,设置字符串的结束符为0。
status_t Parcel::writeString16(const char16_t* str, size_t len)
{
if (str == NULL) return writeInt32(-1);
//将字符串长度写入Parcel中
status_t err = writeInt32(len);
if (err == NO_ERROR) {
len *= sizeof(char16_t);//重新计算len的长度
//在将字符串写入之前,增加mData的容量
uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t));
if (data) {
//将字符串拷贝到mData中
memcpy(data, str, len);
//字符串结束符
*reinterpret_cast<char16_t*>(data+len) = 0;
return NO_ERROR;
}
err = mError;
}
return err;
}
分析至此,addService的data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor())就分析完了。此时让我们来捋一捋Parcel已经写入的数据,其中mData中数据如下图所示:
3.7 data.writeString16(name)
继续回到addService()中,接着会通过data.writeString16(name)将MediaPlayerService服务的名称写入到data中,此处的name=“media.player”。在前面已经详细介绍过writeString16(),这里执行完该语句后,mData中的数据分布结构如下:
接着,addService()会调用data.writeStrongBinder(service)将MediaPlayerService对象写入到data中。这个数据最重要,这个也是Binder设计的精华所在,现在让我们分析学习一下writeStrongBinder()的实现。
3.8 data.writeStrongBinder(service)
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
return flatten_binder(ProcessState::self(), val, this);
}
该函数调用flatten_binder()将数据打包(将服务拍扁打包),在Parcel里面有一个与之相对应的函数unflatten_binder,这个我们在后续的章节和篇章也会讲到的。
3.8.1 Parcel::flatten_binder()
status_t flatten_binder(const sp<ProcessState>& /*proc*/,
const sp<IBinder>& binder, Parcel* out)
{
flat_binder_object obj;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (binder != NULL) {
IBinder *local = binder->localBinder();
if (!local) {
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL) {
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
obj.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
} else {
//addService会走到该分支里面
obj.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
}
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = 0;
obj.cookie = 0;
}
return finish_flatten_binder(binder, obj, out);
}
下面让我们来分析分析该函数,该函数在addService的过程中会将MediaPlayerService对象打扁封装到结构体flat_binder_object中。Binder驱动认识flat_binder_object结构体类型的数据,在C++层将数据发送给Binder驱动后,Binder驱动能够解析该结构体。
(1) 先看看参数,proc是ProcessState对象,binder是MediaPlayerService对象,out是Parcel自己。
(2) binder不为NULL,因此,执行if(binder!=NULL)中的语句。binder->localBinder()返回的BBinder对象,即本地Binder对象。(BBinder是MediaPlayerService的父类,localBinder()函数在frameworks/native/libs/binder/Binder.cpp中实现)。因此,local不为NULL。此时的obj的赋值如下:
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; //标记
obj.type = BINDER_TYPE_BINDER; //类型
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs()); //MediaPlayerService的弱引用
obj.cookie = reinterpret_cast<uintptr_t>(local); // MediaPlayerService自身
从上面的赋值结果可以看出,MediaPlayerService添加服务时,发送给驱动的数据是MediaPlayerService的本地Binder对象,即BBinder实例。准确的来说,该数据是保存在obj.cookie中的,该数据的类型是BINDER_TYPE_BINDER。
(3) 最后调用finish_flatten_binder()将数据写入到Parcel中。
3.8.2 Parcel::finish_flatten_binder()
inline static status_t finish_flatten_binder(
const sp<IBinder>& /*binder*/, const flat_binder_object& flat, Parcel* out)
{
return out->writeObject(flat, false);
}
该函数比较简单,就是将flat_binder_object对象写入到Parcel中。
3.8.3 Parcel::writeObject()
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
const bool enoughObjects = mObjectsSize < mObjectsCapacity;
if (enoughData && enoughObjects) {
restart_write:
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
...
// val.binder非空
if (nullMetaData || val.binder != 0) {
//将地址偏移位置保存到mObjects[0]中
mObjects[mObjectsSize] = mDataPos;
acquire_object(ProcessState::self(), val, this, &mOpenAshmemSize);
// 增加mObjectsSize的值
mObjectsSize++;
}
return finishWrite(sizeof(flat_binder_object));
}
if (!enoughData) {
const status_t err = growData(sizeof(val));
if (err != NO_ERROR) return err;
}
if (!enoughObjects) {
//增加容量
size_t newSize = ((mObjectsSize+2)*3)/2;
//分配内存
if (newSize*sizeof(binder_size_t) < mObjectsSize) return NO_MEMORY;
binder_size_t* objects = (binder_size_t*)realloc(mObjects, newSize*sizeof(binder_size_t));
if (objects == NULL) return NO_MEMORY;
//设置mObjects的内存起始地址
mObjects = objects;
//设置mObjects对象的容量
mObjectsCapacity = newSize;
}
goto restart_write;
}
至此,让我回过头看看Parcel里面各个变量的值,此时,mDataPos=96, sizeof(val)=32, mDataCapacity=96,让我们带着这些数据来分析这段代码:
(1) 根据此时Parcel里面数据数值计算,因此,enoughData=false。mObjectsSize和mObjectsCapacity的初始值=0,因此,enoughObjects=false。
(2) 首先,执行if(!enoughData)部分,通过growData()将数据的容量增加至192(1.5倍)。即mDataCapacity=192。
(3) 接着,执行if(!enoughObjects)部分,该部分的目的是分配对象空间,并修改mObjects和mObjectsCapacity的值。增加之后的容量mObjectsCapacity=3。
(4) 然后,跳转到restart_write标签处。 reinterpret_cast<flat_binder_object>(mData+mDataPos) = val是保存val对象到mDataPos+mDataPos所指的地址中。
(5) mObjects[mObjectsSize]=mDataPos,此处的mObjectsSize=0;这里是将对象的地址偏移mDataPos保存到mObjects[0]中。随后执行mObjectsSize++增加mObjectsSize的值为1。
(6) 最后,调用finishWrite()更新mDataPos和mDataSize的值。
至此,data.writeStrongBinder()就分析完了。下面我们来捋一捋在将MediaPlayerService写入data之后,Parcel的数据如下图所示:
好了writeStrongBinder分析完毕了,我们假设Parcel在内存中是以类似数组的存储空间进行存储的(这个只是假设,实际并不是如此),那么此时各个C_Parcel的位置相关的变量值的逻辑如下所示这里需要重点说明的是mData的值肯定不为0,因为内存中的0地址是给系统预留的,但是mDataPos的值是从0开始增长起来的。
3.9 data.writeInt32(allowIsolated ? 1 : 0)
让我们继续回到addService源码里面,最后调用data.writeInt32(allowIsolated ? 1 : 0)。allowIsolated为false,因此,data.writeInt32(0)。执行该函数之后,data的数据如下图所示:
上述就是addService中data的数据的呈现和赋值,接下来执行remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply)。前面已经说过,remote()返回的是BpBinder对象,该BpBinder对象是在Android Binder机制(五) defaultServiceManager()的实现中调用defaultServiceManager()时初始化的,如果不太熟悉的可以跳转过去看看,下面查看BpBinder的transact()。
3.10 BpBinder::transact()
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
//初始值为1,在BpBinder构造的时候被赋值
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
该代码在frameworks/native/libs/binder/BpBinder.cpp中。由于mAlive的初始值为1,因此该函数会调用IPCThreadState::self()->transact()。我们知道,IPCThreadState::self()是获取全局IPCThreadState对象,因此最终会调用IPCThreadState::transact()。 下面我们继续分析IPCThreadState::transact()。
3.11 IPCThreadState::transact()
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err = data.errorCheck();
flags |= TF_ACCEPT_FDS;
...
if (err == NO_ERROR) {
...
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
...
if ((flags & TF_ONE_WAY) == 0) {
...
if (reply) {
err = waitForResponse(reply);
} else {
...
}
} else {
//当不需要驱动反馈的时候设置flag为TF_ONE_WAY,会节省一定时间,在某些场合可以使用
err = waitForResponse(NULL, NULL);
}
return err;
}
该代码在frameworks/native/libs/binder/IPCThreadState.cpp中。来,让我们开始我们的分析之路,疤丁解牛。
(1) 先看看函数的参数。handle是BpBinder中的mHandle对象,BpBinder中的mHandle是ServiceManager的句柄,值为0。code=ADD_SERVICE_TRANSACTION。data就是在addService中设置的Parcel对象。reply是用来接收Binder驱动反馈数据的Parcel对象。flags是默认值0。
(2) 该函数会先通过writeTransactionData()将数据打包。
(3) flags的初始化为0,并且reply非空。因此,将数据打包号之后,会调用waitForResponse()将数据发送给Binder驱动,然后等待Binder驱动反馈。
3.11.1 IPCThreadState::writeTransactionData()
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
binder_transaction_data tr;
tr.target.ptr = 0;
tr.target.handle = handle;
tr.code = code;
tr.flags = binderFlags;
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 0;
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
tr.data.ptr.offsets = data.ipcObjects();
} else if (statusBuffer) {
...
} else {
...
}
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
该函数会读取前面已经写入到Parcel中的数据,然后将其打包到binder_transaction_data结构体中(说实话这个函数名命名的太操蛋了,谷歌的工程师估计当时可能走神了,看到这个函数名还以为是将数据写入驱动然后发送了,然后并不是)。binder_transaction_data结构体是Binder驱动能够识别并对之进行解析的数据结构,在前面Android Binder框架实现之Binder中的常见数据结构中已经有关详细的介绍了,就不多赘述了。
ipcDataSize()是返回mDataSize,ipcData()是返回mData,ipcObjectsCount()是返回mObjectsSize,而ipcObjects则是返回mObjects。这些数据就是前面我们在addService中分析的Parcel对象的数据。下面给出初始化之后tr的值。
tr.target.handle = handler; //0, 即ServiceManager代理对象的默认句柄
tr.code = code; // ADD_SERVICE_TRANSACTION
tr.flags = binderFlags; // TF_ACCEPT_FDS
tr.cookie = 0;
tr.sender_pid = 0;
tr.data_size = data.ipcDataSize(); //数据大小(对应mDataSize)
tr.data.ptr.buffer = data.ipcData(); //数据的起始地址(对应mData)
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t); // data中保存的对象个数(对应mObjectsSize)
tr.data.ptr.offsets = data.ipcObjects(); // data中保存的对象的偏移地址数组(对应mObjects)
初始化结构体binder_transaction_data 完成之后,将cmd=BC_TRANSACTION和tr重新打包到mOut中。mOut中的数据将来会被以请求的方式发送给Binder驱动。重新打包后的数据如下图所示:
在上图中,mOut包含了"事务指令"+"binder_transaction_data"结构体对象。而具体的MediaPlayerService对象,则包含在binder_transaction_data的data数据区域;它是被封装在flat_binder_object结构体中的。
3.11.2 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;
...
if (mIn.dataAvail() == 0) continue;
cmd = (uint32_t)mIn.readInt32();
...
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
...
case BR_DEAD_REPLY:
...
case BR_FAILED_REPLY:
...
case BR_ACQUIRE_RESULT:
...
case BR_REPLY:
...
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
...
return err;
}
writeTransactionData()分析完毕之后,再看看waitForResponse()的代码,waitForResponse()会先调用talkWithDriver()和Binder驱动交互,然后根据反馈结果来分别进行处理。
3.11.3 IPCThreadState::talkWithDriver()
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
...
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
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;
}
...
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
...
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
...
} while (err == -EINTR);
if (err >= NO_ERROR) {
//清空已写的数据
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
//设置已读数据
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
...
return NO_ERROR;
}
return err;
}
talkWithDriver()会先初始化bwr(binder_write_read类型的变量),然后将bwr变量通过ioctl()发送给Binder驱动。该函数的参数doReceive的默认值为true。
(1) 现在,mIn中还没有被写入数据,因此它的值都是初始值。那么,mIn.dataPosition()返回mDataPos,它的值为0;mIn.dataSize()返回mDataSize,它的初始值也为0。因此,needRead=true。
(2) doReceive=true,但是needRead=true;因此,outAvail=mOut.dataSize,outAvail不为0。接下来,就对bwr进行初始化,关于bwr的介绍,请参考Android Binder框架实现之Binder中的常见数据结构篇章。bwr初始化完毕之后,各个成员的值如下:
bwr.write_size = outAvail; //mOut中数据大小,大于0
bwr.write_buffer = (long unsigned int)mOut.data(); // mOut中数据的起始地址
bwr.write_consumed = 0;
bwr.read_size = mIn.dataCapacity(); // 256
bwr.read_buffer = (long unsigned int)mIn.data(); // mIn.mData,实际上为空
bwr.read_consumed = 0;
(3) bwr初始化完成之后,调用ioctl(,BINDER_WRITE_READ,)和Binder驱动进行交互。
通过binder_write_read再次打包后的数据如下图所示:
如上图所示,ioctl()传输的数据包含"BINDER_WRITE_READ"+“binder_write_read结构体对象”。在binder_write_read的write_buffer中包含了事务数据;而在数据数据的data中又包含了flat_binder_object等数据。在flat_binder_object中就包含了需要传输的MediaPlayerService对象。下面要划重点了,在面试如果你的简历中有说对Binder比较熟悉的话,一般考官都会喜欢问上面的数据结构图看看你是否真的掌握了。
总体来看,数据经过了三次封装。下面我们继续分析在Binder驱动中是如何一层层将它们剖析开来的。
3.12 Binder驱动中binder_ioctl()的BINDER_WRITE_READ相关部分的源码
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
...
//中断等待函数
// 1. 当binder_stop_on_user_error < 2为true时;不会进入等待状态;直接跳过。
// 2. 当binder_stop_on_user_error < 2为false时,进入等待状态。
// 当有其他进程通过wake_up_interruptible来唤醒binder_user_error_wait队列,并且binder_stop_on_user_error < 2为true时;
// 则继续执行;否则,再进入等待状态。
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
...
binder_lock(__func__);
//在proc进程中查找该线程对应的binder_thread;若查找失败,则新建一个binder_thread,并添加到proc->threads中。
thread = binder_get_thread(proc);
...
switch (cmd) {
case BINDER_WRITE_READ: {
struct binder_write_read bwr;
...
//将binder_write_read从“用户空间”拷贝到“内核空间”
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto err;
}
...
//如果write_size>0,则进行写操作
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
...
}
//如果read_size>0,则进行读操作
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
...
}
...
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto err;
}
break;
}
...
}
ret = 0;
...
return ret;
}
关于该函数在Android Binder框架实现之ServiceManager守护进程中已经介绍过了。这里将binder_write_read从用户空间拷贝到内核空间之后,读取bwr.write_size和bwr.read_size都>0,因此先写后读,下面我们来逐个分析,各个击破。
3.12.1 Binder驱动中binder_thread_write()的源码
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)
{
uint32_t cmd;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
//读取binder_write_read.write_buffer中的内容。
//每次读取32bit(即四个字节)
while (ptr < end && thread->return_error == BR_OK) {
// 从用户空间读取32bit到内核中,并赋值给cmd。
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
...
switch (cmd) {
...
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
break;
}
...
}
//更新bwr.write_consumed的值
*consumed = ptr - buffer;
}
return 0;
}
读取出来的命令码是BC_TRANSACTION,所以此处走BC_TRANSACTION,在通过copy_from_user()将数据拷贝从用户空间拷贝到内核空间之后,就调用binder_transaction()进行处理。
3.12.2 Binder驱动中binder_transaction()的源码
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{
struct binder_transaction *t;
struct binder_work *tcomplete;
binder_size_t *offp, *off_end;
binder_size_t off_min;
struct binder_proc *target_proc;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
struct list_head *target_list;
wait_queue_head_t *target_wait;
struct binder_transaction *in_reply_to = NULL;
struct binder_transaction_log_entry *e;
uint32_t return_error;
...
//此时参数reply的值为0,所以会进入非replay分支
if (reply) {
...
} else {
//此时handle的值为0
if (tr->target.handle) {
...
} else {
//事务目标对象是ServiceManager得Binder实体
//即该事务是交给ServiceManager来处理的
target_node = binder_context_mgr_node;
...
}
...
//设置处理事务的目标进程
target_proc = target_node->proc;
...
}
if (target_thread) {
...
} else {
target_list = &target_proc->todo;
target_wait = &target_proc->wait;
}
...
//分配一个特处理事务t,t是binder事务(binder_transaction对象)
t = kzalloc(sizeof(*t), GFP_KERNEL);
...
//分配一个待完成的工作tcomplete,tcomplete是binder_work对象
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
...
t->debug_id = ++binder_last_id;
...
//设置from,表示该事务是MediaPlayerService发起的
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
else
t->from = NULL;
//下面的一些赋值是初始化事务t
t->sender_euid = proc->tsk->cred->euid;
//事务将交给target_proc进程进行处理
t->to_proc = target_proc;
//事务将交给target_thread线程进行处理
t->to_thread = target_thread;
//事务编码
t->code = tr->code;
//事务标志
t->flags = tr->flags;
//事务优先级
t->priority = task_nice(current);
...
//分配空间
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
...
t->buffer->allow_user_free = 0;
t->buffer->debug_id = t->debug_id;
//保存事务
t->buffer->transaction = t;
// 保存事务的目标对象(即处理该事务的binder对象)
t->buffer->target_node = target_node;
trace_binder_transaction_alloc_buf(t->buffer);
if (target_node)
binder_inc_node(target_node, 1, 0, NULL);
offp = (binder_size_t *)(t->buffer->data +
ALIGN(tr->data_size, sizeof(void *)));
// 将"用户空间的数据"拷贝到内核中
// tr->data.ptr.buffer就是用户空间数据的起始地址,tr->data_size就是数据大小
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
...
}
// 将"用户空间的数据中所含对象的偏移地址"拷贝到内核中
// tr->data.ptr.offsets就是数据中的对象偏移地址数组,tr->offsets_size就数据中的对象个数
// 拷贝之后,offp就是flat_binder_object对象数组在内核空间的偏移数组的起始地址
if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
...
}
...
// off_end就是flat_binder_object对象数组在内核空间的偏移地址的结束地址
off_end = (void *)offp + tr->offsets_size;
off_min = 0;
//将所有的flat_binder_object对象读取出来
//对MediaPlayerService而言,只有一个flat_binder_object对象。
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
...
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
off_min = *offp + sizeof(struct flat_binder_object);
switch (fp->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct binder_ref *ref;
//在proc中查找binder实体对应的binder_node
struct binder_node *node = binder_get_node(proc, fp->binder);
//若找不到,则新建一个binder_node;下次就可以直接使用了,妈的要是找女朋友有这么容易的话就好了,如果没有找到直接new一个那就完美了啊
if (node == NULL) {
node = binder_new_node(proc, fp->binder, fp->cookie);
if (node == NULL) {
...
}
node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
}
...
//在target_proc(即ServiceManager的进程上下文)中查找是否包行"该Binder实体的引用",
//如果没有找到的话,则将"该binder实体的引用"添加到target_proc->refs_by_node红黑树中。这样,就可以通过Service Manager对该Binder实体进行管理了。
ref = binder_get_ref_for_node(target_proc, node);
if (ref == NULL) {
...
}
//修改type
if (fp->type == BINDER_TYPE_BINDER)
fp->type = BINDER_TYPE_HANDLE;
else
fp->type = BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0;
// 修改handle。handle和binder是联合体,这里将handle设为引用的描述。
// 根据该handle可以找到"该binder实体在target_proc中的binder引用";
// 即,可以根据该handle,可以从Service Manager找到对应的Binder实体的引用,从而获取Binder实体。
fp->handle = ref->desc;
// 增加引用计数,防止"该binder实体"在使用过程中被销毁。
fp->handle = ref->desc;
fp->cookie = 0;
binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
&thread->todo);
trace_binder_transaction_node_to_ref(t, node, ref);
...
} break;
...
}
}
if (reply) {
...
} else if (!(t->flags & TF_ONE_WAY)) {
BUG_ON(t->buffer->async_transaction != 0);
t->need_reply = 1;
t->from_parent = thread->transaction_stack;
//将当前事务添加到当前线程的事务栈中
thread->transaction_stack = t;
} else {
...
}
//设置事务的类型为BINDER_WORK_TRANSACTION
t->work.type = BINDER_WORK_TRANSACTION;
//将事务添加到target_list队列中,即target_list的待处理事务中
list_add_tail(&t->work.entry, target_list);
// 设置待完成工作的类型为BINDER_WORK_TRANSACTION_COMPLETE
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
// 将待完成工作添加到thread->todo队列中,即当前线程的待完成工作中。
list_add_tail(&tcomplete->entry, &thread->todo);
//唤醒目标进程
if (target_wait)
wake_up_interruptible(target_wait);
return;
...
}
(1) 先对入参的参数分析一把,此时tr->target.handle=0且reply也为0,因此,会设置target_node为ServiceManager对应的Binder实体。下面是target_node,target_proc等值初始化之后的值。
target_node = binder_context_mgr_node; // 目标节点为Service Manager对应的Binder实体
target_proc = target_node->proc; // 目标进程为Service Manager对应的binder_proc进程上下文信息
target_list = &target_thread->todo; // 待处理事务队列
target_wait = &target_thread->wait; // 等待队列
(2) 分析至此,我们现在已经知道目标节点是Service Manager对应的Binder实体。这是指MediaPlayerService的addService()这个指令是来提交给Service Manager进行处理的,它最终会发送给Service Manager进行处理。
(3) 在初始化完target_node等目标节点之后,会新建一个待处理事务t和待完成的工作tcomplete,并对它们进行初始化。待处理事务t会被提交给目标(即ServiceManager对应的Binder实体)进行处理;而待完成的工作tcomplete则是为了反馈给MediaPlayerService服务,告诉MediaPlayerService它的请求Binder驱动已经收到了。注意,这里仅仅是告诉MediaPlayerService该请求已经被收到,而不是处理完毕!待ServiceManager处理完毕该请求之后,Binder驱动会再次反馈相应的消息给MediaPlayerService。
//分配一个特处理事务t,t是binder事务(binder_transaction对象)
t = kzalloc(sizeof(*t), GFP_KERNEL);
...
//分配一个待完成的工作tcomplete,tcomplete是binder_work对象
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
...
t->debug_id = ++binder_last_id;
...
//设置from,表示该事务是MediaPlayerService发起的
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
else
t->from = NULL;
//下面的一些赋值是初始化事务t
t->sender_euid = proc->tsk->cred->euid;
//事务将交给target_proc进程进行处理
t->to_proc = target_proc;
//事务将交给target_thread线程进行处理
t->to_thread = target_thread;
//事务编码
t->code = tr->code;
//事务标志
t->flags = tr->flags;
//事务优先级
t->priority = task_nice(current);
...
//分配空间
t->buffer = binder_alloc_buf(target_proc, tr->data_size,
tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
...
t->buffer->allow_user_free = 0;
t->buffer->debug_id = t->debug_id;
//保存事务
t->buffer->transaction = t;
// 保存事务的目标对象(即处理该事务的binder对象)
t->buffer->target_node = target_node;
trace_binder_transaction_alloc_buf(t->buffer);
if (target_node)
binder_inc_node(target_node, 1, 0, NULL);
(4) 在初始化完待处理事务t之后,接着将MediaPlayerService请求的数据拷贝到内核空间并解析出来。从数据中解析出MediaPlayerService请求数据中的flat_binder_object对象,只有一个flat_binder_object对象。该flat_binder_object对象的类型是BINDER_TYPE_BINDER,然后调用binder_get_node()在当前进程的上下文环境proc中查找fp->binder对应的Binder实体,fp->binder是Android的flatten_binder()中赋值的,它是MediaPlayerService对象的本地引用的描述(即MediaPlayerService对应的BBinder对象的描述);此外,在MediaPlayerService是初次与Binder驱动通信,因此肯定找不到该对象fp->binder对应的Binder实体;因此node=NULL。 接下来,就调用binder_new_node()新建fp->binder对应的Binder实体,这也就是MediaPlayerService对应的Binder实体。然后,调用binder_get_ref_for_node(target_proc, node)获取该Binder实体在target_proc(即ServiceManager的进程上下文环境)中的Binder引用,此时,在target_proc中肯定也找不到该Binder实体对应的引用;那么,就新建Binder实体的引用,并将其添加到target_proc->refs_by_node红黑树 和 target_proc->refs_by_desc红黑树中。 这样,Service Manager的进程上下文中就存在MediaPlayerService的Binder引用,Service Manager也就可以对MediaPlayerService进行管理了!然后,修改fp->type=BINDER_TYPE_HANDLE,并使fp->handle = ref->desc。这样,就将MediaPlayerService的请求数据解析出来,并且在Binder驱动中创建了MediaPlayerService对应的Binder实体,而且将该Binder实体添加到MediaPlayerService的进程上下文proc中。更重要的是,在ServiceManager的refs_by_node和refs_by_desc这两颗红黑树中创建了"MediaPlayerService对应的Binder实体的Binder引用"。这意味着,在Binder驱动中,已经能在ServiceManager的进程上下文中找到MediaPlayerService,其中该流程涉及的主要源码如下:
// 将"用户空间的数据中所含对象的偏移地址"拷贝到内核中
// tr->data.ptr.offsets就是数据中的对象偏移地址数组,tr->offsets_size就数据中的对象个数
// 拷贝之后,offp就是flat_binder_object对象数组在内核空间的偏移数组的起始地址
if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
...
}
...
// off_end就是flat_binder_object对象数组在内核空间的偏移地址的结束地址
off_end = (void *)offp + tr->offsets_size;
off_min = 0;
//将所有的flat_binder_object对象读取出来
//对MediaPlayerService而言,只有一个flat_binder_object对象。
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
...
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
off_min = *offp + sizeof(struct flat_binder_object);
switch (fp->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct binder_ref *ref;
//在proc中查找binder实体对应的binder_node
struct binder_node *node = binder_get_node(proc, fp->binder);
//若找不到,则新建一个binder_node;下次就可以直接使用了,妈的要是找女朋友有这么容易的话就好了,如果没有找到直接new一个那就完美了啊
if (node == NULL) {
node = binder_new_node(proc, fp->binder, fp->cookie);
if (node == NULL) {
...
}
node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
}
...
//在target_proc(即ServiceManager的进程上下文)中查找是否包行"该Binder实体的引用",
//如果没有找到的话,则将"该binder实体的引用"添加到target_proc->refs_by_node红黑树中。这样,就可以通过Service Manager对该Binder实体进行管理了。
ref = binder_get_ref_for_node(target_proc, node);
if (ref == NULL) {
...
}
//修改type
if (fp->type == BINDER_TYPE_BINDER)
fp->type = BINDER_TYPE_HANDLE;
else
fp->type = BINDER_TYPE_WEAK_HANDLE;
fp->binder = 0;
// 修改handle。handle和binder是联合体,这里将handle设为引用的描述。
// 根据该handle可以找到"该binder实体在target_proc中的binder引用";
// 即,可以根据该handle,可以从Service Manager找到对应的Binder实体的引用,从而获取Binder实体。
fp->handle = ref->desc;
fp->cookie = 0;
// 增加引用计数,防止"该binder实体"在使用过程中被销毁
binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
&thread->todo);
trace_binder_transaction_node_to_ref(t, node, ref);
...
} break;
...
}
}
如果你对上面牵涉的各种引用,实体还是迷糊的话,可以参见下下面的角色图(不是Cosplay图额),是Binder角色图,如果你理解了下面的图,我想对于上面的流程是手到擒来了。
(5) 然后,设置待处理事务的类型为BINDER_WORK_TRANSACTION,并将其添加到target_list中。即,添加事务到Service Manager对应的待处理事务队列中。
设置待完成工作的类型为BINDER_WORK_TRANSACTION_COMPLETE,并将其添加到当前线程的待完成工作中。此时,Binder驱动已经收到了MediaPlayerService的请求,这个所谓的待完成工作,就是用来让Binder驱动告诉MediaPlayerService,它的请求已经被处理了。
最后,target_wait是ServiceManager的等待队列,肯定不为空(因为前面刚刚将BINDER_WORK_TRANSACTION事务添加到待处理事务中)。因此,便会执行wake_up_interruptible(target_wait)唤醒Service Manager进程。
注意,此时都是运行在MediaPlayerService的进程中的!
//设置事务的类型为BINDER_WORK_TRANSACTION
t->work.type = BINDER_WORK_TRANSACTION;
//将事务添加到target_list队列中,即target_list的待处理事务中
list_add_tail(&t->work.entry, target_list);
// 设置待完成工作的类型为BINDER_WORK_TRANSACTION_COMPLETE
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
// 将待完成工作添加到thread->todo队列中,即当前线程的待完成工作中。
list_add_tail(&tcomplete->entry, &thread->todo);
//唤醒目标进程
if (target_wait)
wake_up_interruptible(target_wait);
return;
此时,MediaPlayerService进程还会继续运行,而且它也通过wake_up_interruptible()唤醒了ServiceManager进程。ServiceManager被唤醒后,所做的工作就是将MediaPlayerService注册到它的服务队列中进行管理;它的具体流程稍候再分析,现在还是先分析完MediaPlayerService进程。
分析至此,binder_transaction()就分析完了。在binder_transaction()中,我们主要进行了以下工作,下面我们总结概括一下:
- 解析出来MediaPlayerService的请求数据、
- 新建MediaPlayerService对应的Binder实体和Binder引用,并在ServiceManager的进程上下文中保存MediaPlayerService的Binder引用。
- 新建待处理事务,并将该事务添加到ServiceManager的待处理事务队列中。然后,唤醒ServiceManager来处理该事务。
- 新建了待完成工作,并将待完成工作添加到当前线程的待完成工作队列中中。
3.12.3 Binder驱动中binder_thread_write()的源码
分析完binder_transaction()后,跳出接着分析MediaPlayerService进程的工作,此时binder_thread_write()中执行binder_transaction()后,会更新*consumed的值,即bwr.write_consumed的值。意味着,Binder驱动已经驱动完成读取MediaPlayerService的请求数据。
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)
{
uint32_t cmd;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
//读取binder_write_read.write_buffer中的内容。
//每次读取32bit(即四个字节)
while (ptr < end && thread->return_error == BR_OK) {
// 从用户空间读取32bit到内核中,并赋值给cmd。
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
...
switch (cmd) {
...
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
break;
}
...
}
//更新bwr.write_consumed的值
*consumed = ptr - buffer;
}
return 0;
}
3.12.4 Binder驱动中binder_thread_read()的源码
让我们继续分析,接下来ioctl()会执行binder_thread_read()来设置反馈数据给MediaPlayerService进程。让我们带着愉悦的心情分析一番:
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)
{
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
int ret = 0;
int wait_for_proc_work;
//如果*consumed=0,则写入BR_NOOP到用户传进来的bwr.read_buffer缓存区,用来表示读取数据开始(这个是Binder驱动约定俗成的)
if (*consumed == 0) {
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
}
retry:
//等待proc进程的事务标记。
//当线程的事务栈为空 并且 待处理事务队列为空时,该标记位true。
wait_for_proc_work = thread->transaction_stack == NULL &&
list_empty(&thread->todo);
...
thread->looper |= BINDER_LOOPER_STATE_WAITING;
if (wait_for_proc_work)
proc->ready_threads++;
...
if (wait_for_proc_work) {
...
} else {
if (non_block) {
...
} else
ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
}
binder_lock(__func__);
if (wait_for_proc_work)
proc->ready_threads--;
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
...
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
struct binder_work *w;
struct binder_transaction *t = NULL;
//如果当前线程的"待完成工作"不为空,则取出待完成工作
if (!list_empty(&thread->todo))
w = list_first_entry(&thread->todo, struct binder_work, entry);
else if (!list_empty(&proc->todo) && wait_for_proc_work)
...
else {
if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */
goto retry;
break;
}
...
switch (w->type) {
...
case BINDER_WORK_TRANSACTION_COMPLETE: {
cmd = BR_TRANSACTION_COMPLETE;
// 将BR_TRANSACTION_COMPLETE写入到用户缓冲空间中
if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
binder_stat_br(proc, thread, cmd);
...
//待完成事务已经处理完毕,将其从待完成事务队列中删除
list_del(&w->entry);
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
} break;
...
}
if (!t)
continue;
...
//更新bwr.read_consumed的值
*consumed = ptr - buffer;
...
return 0;
}
代码虽然不多,但是信息量还是比较大的,下面让我们来逐层分析,步步为营:
(1) 先看看函数的参数,buffer是bwr.read_buffer,是反馈数据缓冲区。size是bwr.read_size,是缓冲区大小,为256字节;而consumed是指向bwr.read_consumed的,它的值是0,表示反馈数据还没有被MediaPlayerService读取过。non_block为0。
(2) consumed=0,因此会先将BR_NOOP从内核空间拷贝到用户空间,即拷贝到bwr.read_buffer中。
(3) 在binder_transaction()中,我们有添加待完成工作到thread的待完成工作队列中。因此,wait_for_proc_work是false。
(4) binder_has_thread_work(thread)为ture,因此wait_event_interruptible()不会进入中断等待状态,而是继续往下运行。
(5) 接着,进入while循环。list_empty(&thread->todo)为flase,执行list_first_entry(&thread->todo, struct binder_work, entry)从thread的待完成工作队列中取出待完成的工作t。
(6) 根据binder_transaction()中的分析可知,t->type的值为BINDER_WORK_TRANSACTION_COMPLETE。执行对应的case分支,会将数据cmd=BR_TRANSACTION_COMPLETE拷贝到用户空间,即bwr.read_buffer中。拷贝之后,即代表该工作已完成,然后从当前线程的工作队列中将该工作删除,并释放所分配的空间。
(7) 由于t=null,因此,会再次从头开始执行while循环。而此时,list_empty(&thread->todo)为true,并且list_empty(&proc->todo)也为true;因此会执行break跳出while循环。
(8) 在跳出while循环之后,会更新consumed的值。即,更新bwr.read_consumed的值。此时,由于写入了BR_NOOP和BR_TRANSACTION_COMPLETE两个指令,bwr.read_consumed=8。
接下来,回到binder_ioctl()中。将bwr数据拷贝到用户空间后返回。此时,bwr中各个参数的值如下:
bwr.write_size = outAvail;
bwr.write_buffer = (long unsigned int)mOut.data();
bwr.write_consumed = outAvail; // 等于write_size
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (long unsigned int)mIn.data(); // 存储了BR_NOOP和BR_TRANSACTION_COMPLETE两个返回指令
bwr.read_consumed = 8; // 等于read_size
bwr中的write参数是保存"MediaPlayerService发送给Binder驱动的请求内容的",而read则是保存"Binder驱动反馈给MediaPlayerService的内容的"。此时,write_consumed和write_size相同,意味着"Binder驱动已经将请求的内容都处理完毕了";而read_consumed>0,则意味着"Binder驱动有反馈内容给MediaPlayerService"。
回到talkWithDriver()中,看看ioctl()之后做了些什么?
3.13 重返客户端IPCThreadState::transact()
通过前面分析可知,此时代码已经从驱动层返回到了应用层,那么让我们一起跟着代码的脚步来到应用层继续分析吗(注意这里的标题客户端是相对于servicemanager进程来说的)。
3.13.1 IPCThreadState::talkWithDriver()
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
...
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
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;
}
...
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
...
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
...
} while (err == -EINTR);
if (err >= NO_ERROR) {
//清空已写的数据
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
//设置已读数据
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
...
return NO_ERROR;
}
return err;
}
ioctl()从驱动层返回以后的返回值为0,所以err=NO_ERROR,此时退出while循环。接着代码往下执行:
(1) bwr.write_consumed>0,并且bwr.write_consumed=mOut.dataSize。因此,调用mOut.setDataSize(0)将释放mOut的内存,并且将mOut的mDataSize和mObjectsSize设为0。
(2) bwr.read_consumed>0,因此调用mIn.setDataSize()为mIn分配空间,并将mIn的mDataSize设为=bwr.read_consumed。然后,将位置mDataPos初始化为0。
之后,跳出talkWithDriver(),返回到waitForResponse()中。
3.13.2 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;
...
if (mIn.dataAvail() == 0) continue;
cmd = (uint32_t)mIn.readInt32();
...
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
...
case BR_DEAD_REPLY:
...
case BR_FAILED_REPLY:
...
case BR_ACQUIRE_RESULT:
...
case BR_REPLY:
...
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
}
finish:
...
return err;
}
此时函数从从talkWithDriver()正常返回,接着执行代码读取mIn中的数据。而mIn中的数据就是Binder驱动返回的"BR_NOOP和BR_TRANSACTION_COMPLETE两个指令"。先读出的指令是BR_NOOP,因此这里执行executeCommand(cmd)。(读者可以缓一缓,上个厕所干个啥的,内容有点多啊)
3.13.3 IPCThreadState::executeCommand()
status_t IPCThreadState::executeCommand(int32_t cmd)
{
BBinder* obj;
RefBase::weakref_type* refs;
status_t result = NO_ERROR;
...
switch (cmd) {
case BR_ERROR:
...
case BR_OK:
...
case BR_NOOP:
break;
default:
...
}
if (result != NO_ERROR) {
mLastError = result;
}
return result;
}
此时入参的参数cmd=BR_NOOP没有进行任何的操作,直接返回。继续回到waitForResponse()中,重新开始while循环,执行talkWithDriver()。
3.13.4 IPCThreadState::talkWithDriver()
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
...
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
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;
}
...
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
...
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
...
} while (err == -EINTR);
if (err >= NO_ERROR) {
//清空已写的数据
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
//设置已读数据
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
...
return NO_ERROR;
}
return err;
}
弯弯绕绕又回到了这里,你说我们容易吗。好吗,废话有点多,让我们接着继续分析:
(1) 此时,因为在waitForResponse()中已经通过mIn.readInt32()读取了4个字节,因此mIn.dataPosition()=4,而此时mIn.dataSize()=8;因此,needRead=false。
(2) 此时needRead=false,而doRecive=true,所以计算得出outAvail=0。最终,由于 bwr.write_size和bwr.read_size都为0,因此直接返回NO_ERROR。
(3) 再次回到waitForResponse()中,此时读出的cmd为BR_TRANSACTION_COMPLETE。此时,由于reply不为NULL,不会跳出while循环,因此再次重新执行while循环,调用talkWithDriver()。
(4) 此时,已经读取了mIn中的全部数据,因此mIn.dataPosition()=8,而mIn.dataSize()=8;因此,needRead=true。
(5) outAvail=mOut.dataSize(),前面已经将mOut清空,因此outAvail=0。bwr初始化完毕之后,各个成员的值如下:
bwr.write_size = 0;
bwr.write_buffer = (long unsigned int)mOut.data();
bwr.write_consumed = 0;
bwr.read_size = mIn.dataCapacity(); // 256字节
bwr.read_buffer = (long unsigned int)mIn.data();
bwr.read_consumed = 0;
(7) 分析到这里,此时MediaPlayerService已经处理完"addService()这个请求,包括已经处理完了该请求的反馈"。对MediaPlayerService而言,它已经成功的注册到Service Manager中;接下来,就是等待Client的请求了。
那么如何去等待Client的请求呢?这和前面分析Service Manager服务启动之后等待Client的请求类似。MediaPlayerService服务,会通过ioctl()给Binder驱动发送读写请求,而此时的bwr.write_size=0,意味着不会进行写;bwr.read_size>0,意味着会进行读。这样,Binder驱动就会执行读取动作,进而去查看"MediaPlayerService在Binder驱动中的待处理事务队列"是否有事务需要处理,有的话,就进行事务处理;否则,就进入中断等待状态,等待Client的请求。能有啥办法呢,只能接着继续分析了。
3.14 再探Binder驱动
各位读者请系好安全带,抓紧扶手,振作精神,我们得继续Binder驱动层深入分析了。木有办法Binder深入就是有这么的折腾。
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
...
//中断等待函数
// 1. 当binder_stop_on_user_error < 2为true时;不会进入等待状态;直接跳过。
// 2. 当binder_stop_on_user_error < 2为false时,进入等待状态。
// 当有其他进程通过wake_up_interruptible来唤醒binder_user_error_wait队列,并且binder_stop_on_user_error < 2为true时;
// 则继续执行;否则,再进入等待状态。
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
...
binder_lock(__func__);
//在proc进程中查找该线程对应的binder_thread;若查找失败,则新建一个binder_thread,并添加到proc->threads中。
thread = binder_get_thread(proc);
...
switch (cmd) {
case BINDER_WRITE_READ: {
struct binder_write_read bwr;
...
//将binder_write_read从“用户空间”拷贝到“内核空间”
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto err;
}
...
//如果write_size>0,则进行写操作
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread, bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
...
}
//如果read_size>0,则进行读操作
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
...
}
...
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto err;
}
break;
}
...
}
ret = 0;
...
return ret;
此时,bwr.write_size=0,因此只读不写,不会执行binder_thread_write()。而此时bwr.read_size>0,因此会调用binder_thread_read()进行读取动作。
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
void __user *buffer, int size,
signed long *consumed, int non_block)
{
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
int ret = 0;
int wait_for_proc_work;
// 如果*consumed=0,则写入BR_NOOP到用户传进来的bwr.read_buffer缓存区
if (*consumed == 0) {
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
// 修改指针位置
ptr += sizeof(uint32_t);
}
retry:
// 等待proc进程的事务标记。
// 当线程的事务栈为空 并且 待处理事务队列为空时,该标记位true。
wait_for_proc_work = thread->transaction_stack == NULL &&
list_empty(&thread->todo);
...
if (wait_for_proc_work) {
...
// 设置当前线程的优先级=proc->default_priority。
// 即,当前线程要处理proc的事务,所以设置优先级和proc一样。
binder_set_nice(proc->default_priority);
if (non_block) {
...
} else
// 阻塞式的读取,则阻塞等待事务的发生。
ret = wait_event_interruptible_exclusive(proc->wait, binder_has_proc_work(proc, thread));
} else {
...
}
...
}
此时的我有点凌乱,有点紧张因为本篇章快要结束了。继续分析
(1) 此时,bwr.read_consumed=0,意味着*consumed=0。因此,还是会先将BR_NOOP写入到bwr.read_buffer中。
(2) 此时,当前线程的事务栈和待处理事务队列都是空,因此wait_for_proc_work=true。
(3) 在调用binder_set_nice()设置当前线程的优先级之后,就会调用wait_event_interruptible()。而此时binder_has_proc_work()为false,因此当前线程会进入中断等待状态。当Service Manager处理完MediaPlayerService的请求之后,就会将其唤醒。
总结
此时的你是不是有点小激动,我的妈呀我竟然看到这里了!你值得骄傲,能看到此处的都是好汉。至此,MediaPlayerService进程的addService的请求发送部分就讲解完了。在继续了解请求的处理之前,先回顾一下本部分的内容。
如上图所示,MediaPlayerService发送一个BC_TRANSACTION事务给Binder驱动。Binder驱动收到该事务之后,对请求数据进行解析,在Kernel中新建了MediaPlayerService对应的Binder实体,并将在ServiceManager的进程上下文中添加了该Binder实体的Binder引用。解析完数据之后,新增一个待处理事务并提交到ServiceManager的待处理事务列表中;接着,就唤醒了ServiceManager。与此同时,Binder驱动还反馈了一个BR_TRANSACTION_COMPLETE给MediaPlayerService,告诉MediaPlayerService它的addService请求已经发送成功;MediaPlayerService在解析完BR_TRANSACTION_COMPLETE之后,就进入等待状态,等待ServiceManager的处理完请求之后反馈结果给它。
有了一个好的开头,那么在接下来的篇章中,我们将继续分析ServiceManager被唤醒后,具体都做了些什么工作!聪明的你应该可以想得到做了些什么,让我们下个篇章继续分析 Android Binder框架实现之Native层addService详解之请求的处理。
写在最后
至此,Android Binder框架实现之Native层addService详解之请求的发送就介绍完了。如果小伙们对整个过程还是很迷糊或者困惑,也不要感到伤心和难过,因为Binder不是一蹴而就的事情,这个是一个考验耐心耐力的的知识点学习,容不得半点马虎的,多看源码,多分析我相信终有一天你会突然开窍的。好了就到这里了,欢迎小伙伴们评论和点赞!本篇博客到这里真的要说再见了。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)