Binder 机制前前后后看了好久,刚开始两眼一抹黑,完全不知所云。后来,随着工作变化,对 Android 的理解也不断加深,这块内容又重新拾起来。
所以,写 Binder 这个系列是有原因的,一是记录自己的学习总结,二是想把学习的心得和经验分享给大家,因为每个人的技术背景和学习方式不一样,导致每个人理解的方式也不一样。
虽然现在网上介绍Binder 的文章不少,但是可能并不适合自己。比如有些细节自己在看的时候没有理解,但很多文章中又没有讲解这一块,就会搞得自己很困惑。所以,把自己学习过程总结分享,大家看得多了,理解自然也会深一点。
特别要感谢那些无私奉献的博客专家和大神,没有他们细致入微的讲解和分析 ,有可能到现在还没有弄懂 Binder。
最后,有些地方讲解可能不对,大家都可以指出,期望这个系列的文章能给大家带来帮助。
Android Binder 通信机制深入浅出(一)
Android Binder 通信机制深入浅出(二)
Android Binder 通信机制深入浅出(三)
一、概述
1.1 进程、用户空间和内核空间的关系
Linux中每个进程都会有自己的用户空间,每个进程只能在自己的空间范围内活动,这一点大家应该都理解。但是内核空间是却是多个进程共享,如果想实现进程间通信,用户空间只需要把通信的内容传递到内核空间,内核在去传递给其它进程即可。用户空间和内核空间相互传递数据通过 copy_from_user() 和 copy_to_user() 两个方法。
进程、用户空间和内核空间的关系2.1 为什么选择 Binder
<效率>
Binder 效率比传统的 IPC 要高效,内核资源竞争在传统的 IPC 中,read 和 write 操作都需要对内核中的读写的 buffer 进行资源竞争,解决并发方面的问题,一方面加锁操作消耗了一定的资源,另一方面各个操作需要排队等待。Binder 为了解决内核中 buffer 的竞争,采用了为每个线程单独分配空间,避免了内核态的等待。
但是内核态不需要等待,用户空间的线程不一定能够及时的操作,所以 Binder 又在用户态引用了多线程支持,从内核态和用户态解决了资源访问等待的问题,这是Binder 效率高的真正原因。
<安全性>
Android 作为一个开放性的平台,安全极其重要,传统的 IPC 没有任何安全措施,完全依赖上层协议来确保。
首先传统的 IPC 接收方无法获得对方可靠的进程用户ID/进程ID(UID/PID),从而无法鉴别对方身份。Android 为每个安装好的 APP 分配了自己的 UID,故而进程的 UID 是鉴别进程身份的重要标志。传统的 IPC 只能由用户在数据包中填入 UID/PID,但这样不可靠,容易被恶意程序利用,可靠的身份标识只有由 IPC 机制在内核中添加。
其次传统的 IPC 访问接入点是开放的,只要知道这些接入点的程序都可以和对端建立连接,不管怎样都无法阻止恶意程序通过猜测接收方地址获得连接。而 Binder 不仅支持实名 Binder,而且还支持匿名 Binder,安全性高。
总结:
优势 | 描述 |
---|---|
性能 | 只需要一次数据拷贝,性能上仅次于共享内存 |
稳定性 | 基于 C/S 架构,职责明确、架构清晰,因此稳定性好 |
安全性 | 为每个 APP 分配 UID,进程的 UID 是鉴别进程身份的重要标志 |
二、Binder 通信流程
2.1 Binder 通信四个步骤
要想进行 Binder 通信,首先需要打开binder驱动、映射内核地址空间等,Java 层在创建应用进程的时候系统默认已经初始化好,但是 native 层进程需要开发者自己实现。
如图所示,Binder 通信整体过程大体可以概括为 4 步:
1.
Client 端向 Binder 驱动发送数据;2.
Biner 驱动接受 Client 端数据后转发给 Server端;
3.
Server 端执行完相应的操作,把数据发给 Biner 驱动;4.
最后 Binder 驱动把 Server 端的数据发送的数据返回给 Client 端。
这样一次完整的 Binder 通信就结束了,下面我们就从 Binder 的初始化和四个通信步骤来揭开它的神秘面纱。
2.2 Binder 初始化
2.2.1 每个进程只有一个的 ProcessSate
先来看 ProcessState 对象的创建,它是在 media 进程 main() 中调用ProcessState::self() 实现初始化。
int main(int argc __unused, char **argv __unused)
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm(defaultServiceManager());
ALOGI("ServiceManager: %p", sm.get());
......
MediaPlayerService::instantiate();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
sp<ProcessState> ProcessState::self()
{
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) { //全局变量,每个进程有且只有一个
return gProcess;
}
gProcess = new ProcessState;
return gProcess;
}
ProcessState::ProcessState()
: mDriverFD(open_driver()) //打开binder设备
, mVMStart(MAP_FAILED)
......
{
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 /dev/binder failed: unable to mmap transaction memory.\n");
close(mDriverFD);
mDriverFD = -1;
}
}
}
static int open_driver()
{
int fd = open("/dev/binder", O_RDWR);
if (fd >= 0) {
......
size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
if (result == -1) {
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
}
return fd;
}
2.3 客户端:向 Binder 驱动写数据
2.31 BpBinder 创建和作用
我们来看一段经典的 binder 通信中代码。
1.
首先,通过 ServiceManager 获得MediaplayerService的客户端代理,
2.
然后再调用它相关的业务方法。
- frameworks/av/media/libmedia/mediarecorder.cpp
MediaRecorder::MediaRecorder(const String16& opPackageName)
{
const sp<IMediaPlayerService> service(getMediaPlayerService());
if (service != NULL) {
mMediaRecorder = service->createMediaRecorder(opPackageName);
}
.......
}
- frameworks/av/media/libmedia/IMediaDeathNotifier.cpp
IMediaDeathNotifier::getMediaPlayerService()
{
if (sMediaPlayerService == 0) {
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder;
do {
// 通过servicemanager获得MediaPlayerService的BpBinder对象
binder = sm->getService(String16("media.player"));
if (binder != 0) {
break;
}
ALOGW("Media player service not published, waiting...");
usleep(500000); // 0.5 s
} while (true);
......
// 通过上面获得的 BpBinder 对象构造 Client 端的BpMediaPlayerService对象
sMediaPlayerService =1、interface_cast<IMediaPlayerService>(binder);
}
return sMediaPlayerService;
}
Client 端发起跨进程调用最终都是通过以下形式,这里有两点需要特别注意:
1.
两个Parcel 类型的对象: data:封装发送数据, replay:封装返回数据
2.
remote() 返回的对象
接下来我们重点来看上面涉及一些关键方法的实现,这一块代码比较绕,是 Binder 机制重点和难点。
1、interface_cast<>(binder) 的实现
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
// 把 IMediaPlayerService 和 binder 代入,转化为如下:
inline sp<IMediaPlayerService> interface_cast(const sp<IBinder>& binder)
{
return IMediaPlayerService::asInterface(binder);
}
可以看出,interface_cast()
最终返回的是 IMediaPlayerService::asInterface(binder)
,那么asInterface(binder)
方法又是返回的什么呢?
代码里找一圈,IMediaPlayerService 并没这个方法,查看其父类好像也没有。。。其实这个方法并没有在代码中直接定义,而是需要宏转换,如下:
- frameworks/native/include/binder/IInterface.h
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
const android::String16 I##INTERFACE::descriptor(NAME); \
const android::String16& \
I##INTERFACE::getInterfaceDescriptor() const { \
return I##INTERFACE::descriptor; \
} \
android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
const android::sp<android::IBinder>& obj) \
{ \
android::sp<I##INTERFACE> intr; \
if (obj != NULL) { \
intr = static_cast<I##INTERFACE*>( \
obj->queryLocalInterface( \
I##INTERFACE::descriptor).get()); \
if (intr == NULL) { \
intr = new Bp##INTERFACE(obj); \
} \
} \
return intr; \
} \
I##INTERFACE::I##INTERFACE() { } \
I##INTERFACE::~I##INTERFACE() { }
在IMediaplayerService.cpp 调用:
IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.media.IMediaPlayerService");
此句转换后相当于以下这段代码:
const android::String16 IMediaPlayerService::descriptor(“android.hardware.IMediaPlayerService”);
const android::String16& IMediaPlayerService::getInterfaceDescriptor() const
{
return IMediaPlayerService::descriptor;
}
android::sp<IMediaPlayerService> IMediaPlayerService::asInterface(const android::sp<android::IBinder>& obj)
{
android::sp<IMediaPlayerService> intr;
if (obj != NULL)
{
intr = static_cast<IMediaPlayerService*>(obj->queryLocalInterface(IMediaPlayerService::descriptor).get());
if (intr == NULL)
{
intr = new BpMediaPlayerService(obj);
}
}
return intr;
}
IMediaPlayerService::IMediaPlayerService() { }
IMediaPlayerService::~IMediaPlayerService() { }
经过转换后,可以看到这里asInterface() 返回的就是 BpMediaPlayerService(binder)
对象,终于离终点近了一步。
为什么需要这么做呢,我猜是因为这块代码比较公共,google 直接把这块代码抽离出来,让开发者自己去调用,这样确实能减少不少代码量,但代码的可读性就大打折扣。
2、remote() 的实现
首先,来看它的定义:
class BpRefBase : public virtual RefBase
{
......
//可以看到remote() 是BpRefBase类中的一个方法,返回的IBinder* 类型的对象 mRemote,我们继续看mRemote是如何创建的
inline IBinder* remote() { return mRemote; }
IBinder* const mRemote;
......
}
mRemote 的赋值其实也比较坎坷,它是在 BpMediaPlayerService 的构造方法中调用父类的构造方法中赋值的,而且特别隐蔽,隐蔽到一不小心就被忽略。。。
1. 来看BpMediaPlayerService的构造方法,其什么都没做,只是调用了父类的构造方法
class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
{
public:
BpMediaPlayerService(const sp<IBinder>& impl)
: BpInterface<IMediaPlayerService>(impl)
{
}
......
}
2. 继续往下走,查看BpInterface的构造方法,其也什么都没走,继续调用自己父类的构造方法
template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
: BpRefBase(remote) // 注意,这里的remote就是一开始传入的 BpBinder 对象,一直传递到这里
{
}
3. 最后,来看 BpRefBase 构造方法,它里面最终对 mRemote 赋值,把刚刚BpBinder 赋值给mRemote,o.get() 返回的就是BpBinder
BpRefBase::BpRefBase(const sp<IBinder>& o)
: mRemote(o.get()), mRefs(NULL), mState(0)
{
......
}
我们来看一下 BpMediaPlayerService 族谱,如下图:
从图得知 BpMediaPlayerService 继承 BpInterface、BpRefBase、IMediaPlayerService,所以 BpRefBase中的 mRemote 对象 BpMediaPlayerService 中可以直接使用。
到这里,
remote()
方法终于告一段落,大家看完感受如何,是不是有点晕,不过没关系,再难啃的代码多啃几次也就透了,与大家一起共勉~
2.32 IPCThreadState 实例
上一节面我们知道 remote() 返回的是一个 BpBinder
对象,remote()->transact() 就是 BpBinder->transact(),接下来我们继续看 BpBinder
中transact()
方法的实现:
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;
可以看到 transact() 方法又调用 IPCThreadState 的transact(),这里就带出我们这一节的重点 IPCThreadState,它就是最终和 binder 驱动交互的类,重要性不言而喻。
之前我们讲到 ProcessState 是每个进程只有一个,这里 IPCTreadState 对象是每个线程有一个,也就是说一个 ProcessState 会对应多个 IPCTreadState,那么如何确保每个线程独有一个 IPCTreadState 实例呢,简单介绍一下,IPCThreadState 保存在每个线程的局部存储 TLS 中,具体实现如下:
IPCThreadState* IPCThreadState::self()
{
if (gHaveTLS) {
restart:
const pthread_key_t k = gTLS;
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
if (st) return st;
return new IPCThreadState;
}
......
if (!gHaveTLS) {
int key_create_value = pthread_key_create(&gTLS, threadDestructor);
if (key_create_value != 0) {
pthread_mutex_unlock(&gTLSMutex);
ALOGW("IPCThreadState::self() unable to create TLS key, expect a crash: %s\n",
strerror(key_create_value));
return NULL;
}
gHaveTLS = true;
}
pthread_mutex_unlock(&gTLSMutex);
goto restart;
}
2.33 IPCThreadState 作用
IPCThreadState 对象是每个线程都有一个,保存在线程的局部存储 TLS 中,主要负责 Binder 的读取、写入和请求处理。
刚刚说到 BpBinder.cpp 中 transact() 其实调用的是 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();
......
if (err == NO_ERROR) {
err = 1. writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
if ((flags & TF_ONE_WAY) == 0) {
if (reply) {
err = 2. waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
......
} else {
err = waitForResponse(NULL, NULL);
}
return err;
}
这个方法中有调用了两个重要的方法:
1.
writeTransactionData()
2.
waitForResponse()
这里贴出 writeTransactionData()
方法的代码,他的主要作用把发送的数据转换为 Binder 驱动识别的 binder_transaction_data
结构体:
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; /* Don't pass uninitialized stack data to a remote process */
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) {
tr.flags |= TF_STATUS_CODE;
*statusBuffer = err;
tr.data_size = sizeof(status_t);
tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
tr.offsets_size = 0;
tr.data.ptr.offsets = 0;
} else {
return (mLastError = err);
}
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
注意:接下来我们就要分析 Binder 架构中很重要的一个方法 waitForResponse()
,为什么说这个方法重要呢?因为这个方法涉及到 Client 端和 Server 端,和 Binder 驱动数据的交互都是由它完成。
先来分析跟 Client 端发送数据相关的实现,后面继续分析其它部分。
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 BR_REPLY:
{
......
}
goto finish;
......
}
......
return err;
}
这里和发送数据相关的只有 talkWithDriver()
方法,顾名思义,从名字就可以看出这个方法的作用,是用来和驱动交互的。
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {
return -EBADF;
}
binder_write_read bwr;
// Is the read buffer empty?
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.
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 {
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
} while (err == -EINTR);
......
return err;
}
总结一下,transact() 方法干了如下几件事情:
-
通过
writeTransactionData()
把要发送的数据转换为 Binder 驱动识别的binder_transaction_data
结构体,然后写入mOut
。 -
通过
waitForResponse()
方法把mOut
的数据发送给Binder
驱动,并监听Binder
驱动返回的数据。
这里多说一些,Binder 通信涉及到客户端和服务端,这点和网络通信类似,必然会有类似 TCP 一样的通信协议,不过 Binder 协议比较简单,这里写入:
来自<gityuan>CMD:BC_TRANSACTION
就是协议规定的一种,代表客户端向 Binder 驱动发送数据。当然,有发送数据必然会有接受数据,接受:CMD:BR_REPLY
。
其实 Client 端或 Server 端在向 Binder 驱动发送完命令后,驱动都会返回一个BR_TRANSACTION_COMPLETE
指令告知上层发送成功,这点和 HTTP 协议真的很像。
2.4 Server 端:接受 Binder 驱动发来的数据
上一章讲到 Client 端
如何一步步最终通过ioctl()
把数据发给 Binder 驱动,这一章我们就从 Server 端
接受驱动发送过来的数据讲起,然后再介绍业务层面的内容。
再说一些题外话,很多人刚看binder时觉得头昏眼花,绕来绕去,其实是因为binder机制把业务部分和通信逻辑部分交织在一起,导致看起来很迷糊。。。我刚开始看binder我也觉得很困惑,后面看多了才弄明白,所以在介绍binder时,刻意把两部分分开介绍,这样条理更清晰一些。
2.4.1 Binder 主线程创建
之前已经提到 Server 端
接受驱动发过来的数据也是通过 ioctl()
这个方法,但是是Server 端
哪个线程来接受数据这也是很有讲究。一般 Binder 发送线程就是Client 端
执行业务所在线程,但是服务端有谁来接受呢?答案是由 Binder 驱动来控制,下面我们从Server 端
创建 Binder 主线程讲起。
为什么叫主线程呢?因为是自己主动创建的,具体创建是由 startThreadPool()
完成。
- frameworks/native/libs/binder/ProcessState.cpp
void ProcessState::startThreadPool()
{
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
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());
}
}
// PoolThread 继承Thread.cpp,在其 Threadpool() 方法中会执行joinThreadPool()
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);// 此时传入的参数为true
return false;
}
2.4.2 joinThreadPool() 方法介绍
上面我们仅仅是创建了一个线程,并执行
joinThreadPool()
方法,可以猜想与 Binder 驱动通信关键应该在这里,下面我们重点介绍这个关键方法。
- frameworks/native/libs/binder/IPCThreadState.cpp
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
1、result = getAndExecuteCommand();
......
// 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) {//binder 主线程这个参数为true,所以会一直运行
break;
}
} while (result != -ECONNREFUSED && result != -EBADF);
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
}
先来看第一个方法 getAndExecuteCommand()
status_t IPCThreadState::getAndExecuteCommand()
{
......
2、 result = talkWithDriver();
if (result >= NO_ERROR) {
size_t IN = mIn.dataAvail();
if (IN < sizeof(int32_t)) return result;
cmd = mIn.readInt32();
......
3、result = executeCommand(cmd);
......
}
return result;
}
我们再具体来看服务端 talkWithDriver()
的实现:
- frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {
return -EBADF;
}
binder_write_read bwr;
// Is the read buffer empty?
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.
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();
// 重点在这里,mIn 的数据是 talkWithDriver() 中通过把 mIn 的 data 地址赋值给 bwr.read_buffer, 然后把它传给驱动,由 binder 驱动负责向这个地址中写入
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 {
//通过 ioctl() 一直阻塞在这里,直到有数据可读或可写才返回,
// 这里我们主要讲有数据可读,即binder 驱动有数据发送给服务端
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else {
err = -errno;
}
} while (err == -EINTR);
......
return err;
}
总结:
- 首先,
getAndExecuteCommand()
调用talkWithDriver()
,把 mIn 的 data 地址赋值给bwr.read_buffer
传给驱动,并监听驱动是否写入数据 - 然后,驱动若有数据写入,
mIn.readInt32()
读取cmd,然后通过executeCommand(cmd)
根据不同的cmd
执行不同的操作。
mOut 和 mIn 的作用
它们都是parcel 类型,负责和 Binder 驱动数据的传输
-
mOut:
Client 端线程 writeTransactionData() 已向 Parcel 数据类型的 mOut 写入数据,然后通过 ioctl() 发给 Binder 驱动,此时mIn还没有数据;(Server 端也是通过 mOut 向 Binder 驱动写回数据) -
mIn:
Binder 驱动返回的数据写入 mIn,再根据收到的不同响应码,执行相应的操作。
mIn 的数据是 talkWithDriver() 中通过把 mIn 的 data 地址赋值给 bwr.read_buffer 后传给驱动,然后 Binder 驱动负责向这个地址写入。
最后我们来分析 executeCommand()
方法:
- frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::executeCommand(int32_t cmd)
{
BBinder* obj;
RefBase::weakref_type* refs;
switch ((uint32_t)cmd) {
......
case BR_TRANSACTION:
{
binder_transaction_data tr;
result = mIn.read(&tr, sizeof(tr));
if (result != NO_ERROR) break;
Parcel buffer;
//把 tr 中的内容写入到 Parcel 类型 buffer中,之后会发给 server 端具体业务执行方法
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;
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)) {
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 {
error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
}
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);
}
......
}
break;
......
}
return result;
}
executeCommand() 总结:
可以看到,executeCommand()
会根据传入 cmd 来执行不同的操作,我们重点看BR_TRANSACTION
,这个就表示客户端有数据传过来,和客户端的 BC_TRANSACTION
想对应,这里面会调用 transact()
方法,此方法在Binder.cpp
中,会调用 onTransact()
,它是一个虚方法,最终执行的就是服务端自己实现的 onTransact()
。
例如:
MediaplayerService.java
是服务端,它继承 BnMediaPlayerService.java
,最终就会调用 BnMediaPlayerService
中的 onTransact()
。
- frameworks/native/libs/binder/Binder.cpp
status_t BBinder::transact()
{
......
switch (code) {
case PING_TRANSACTION:
reply->writeInt32(pingBinder());
break;
default:
err = onTransact(code, data, reply, flags);
break;
}
......
return err;
}
具体业务逻辑的实现:
- frameworks/av/media/libmedia/IMediaPlayerService.cpp
status_t BnMediaPlayerService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
case CREATE: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
sp<IMediaPlayerClient> client =
interface_cast<IMediaPlayerClient>(data.readStrongBinder());
audio_session_t audioSessionId = (audio_session_t) data.readInt32();
sp<IMediaPlayer> player = create(client, audioSessionId);
reply->writeStrongBinder(IInterface::asBinder(player));
return NO_ERROR;
} break;
// 以client端请求创建media recorder 为例,server端收到client端的请求后,
// 调用createMediaRecorder() 方法创建recorder对象,然后再写入reply,通过sendReply() 方法发送给client端
case CREATE_MEDIA_RECORDER: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
const String16 opPackageName = data.readString16();
sp<IMediaRecorder> recorder = createMediaRecorder(opPackageName);
reply->writeStrongBinder(IInterface::asBinder(recorder));
return NO_ERROR;
} break;
case CREATE_METADATA_RETRIEVER: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
sp<IMediaMetadataRetriever> retriever = createMetadataRetriever();
reply->writeStrongBinder(IInterface::asBinder(retriever));
return NO_ERROR;
} break;
......
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
到这里我们已经分析完这一章开始将到一次 Binder 通信四个步骤中的 1、2 两步,接着分析 3、4 两步的实现。
2.5 Server 端:向 Binder 驱动发送数据
- frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::sendReply(const Parcel& reply, uint32_t flags)
{
status_t err;
status_t statusBuffer;
// 是不是很熟悉的方法,上一章介绍客户端向binder 驱动写入数据流程时也遇到这个它
// 这个方法就是把数据打包成binder_transaction_data结构,再写入Parcel:reply ,
// 注意这次的响应码变成 BC_REPLY,表示Server 端返回的数据
err = writeTransactionData(BC_REPLY, flags, -1, 0, reply, &statusBuffer);
if (err < NO_ERROR) return err;
//这个方法上一章也重点介绍过,主要是调用talkwithDriver()向驱动写数据
// 注意此时的参数都为 null,意味着不需要等待客户端的返回值
return waitForResponse(NULL, NULL);//注意这里传入的参数为NULL
}
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
......
while (1) {
if ((err=talkWithDriver()) < 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;
......
}
}
总结:
sendReply() 作用:
·executeCommand()
处理完 BR_TRANSACTION
消息后,会调用 sendReply()
把 Server 端
处理完的结果写入 Binder 驱动
来发回给 Client 端
。
2.6 Client 端:接受 Binder 驱动返回的数据
再让我们回到 Client 端
给 Binder 驱动
发送数据的 transact()
方法,在 waitForResponse() 的 talkWithDriver() 方法给 Binder 驱动发送完数据就会阻塞在 ioctl() 处,直到 Binder 驱动有数据返回
。
- frameworks/native/libs/binder/IPCThreadState.cpp
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
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 {
err = waitForResponse(NULL, NULL);
}
return err;
}
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
......
while (1) {
//当binder驱动有数据返回时,此处会接着往下执行
if ((err=talkWithDriver()) < NO_ERROR) break;
......
if (mIn.dataAvail() == 0) continue;
//读取响应码为BR_REPLY
cmd = (uint32_t)mIn.readInt32();
......
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
if (!reply && !acquireResult) goto finish;
break;
......
case BR_REPLY:
{
binder_transaction_data tr;
//从协议缓冲区mIn的内容读到一个binder_transaction_data结构体tr中
err = mIn.read(&tr, sizeof(tr));
if (reply) {
if ((tr.flags & TF_STATUS_CODE) == 0) {//走这里
//将保存在结构体tr中的进程间通信结果保存在Parcel对象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);
......
}
总结:
-
startThreadPool()
开启新的线程并执行了joinThreadPool()
方法,而joinThreadPool() 方法通过调用 talkWithDriver() 来读取 Binder 设备
。 - 通过
startThreadPool()
创建的线程都是Binder 主线程
,用来接收Binder 驱动
发送回来的消息,而且joinThreadPool() 有阻塞线程的作用,防止主线程在main() 方法执行完后退出
。
到底有多少个线程在为 Service端 服务呢?目前看来是两个:
-
startThreadPool()
中新启动的线程通过joinThreadPool()
读取Binder
设备,查看是否有请求。 - 主线程也调用
joinThreadPool()
读取Binder
设备,查看是否有请求。
默认情况下,线程池中没有线程,由于本身已经有了2个线程可用,一般情况下,能满足要求。但是,当有多个并发的 IPC 请求时,可能会触发内核增加更多的IPC通信线程来服务这些请求。当进程接受到 Binder 驱动从内核中发出的 BR_SPAWN_LOOPER 命令时,会启动一个非 Binder 主线程。
最后通过一张图,来总结 Binder 通信的整个流程:
网友评论