Binder 进程间通信机制详解(Native 篇)

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 通信流程

如图所示,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(),接下来我们继续看 BpBindertransact() 方法的实现:

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() 方法干了如下几件事情:

  1. 通过 writeTransactionData() 把要发送的数据转换为 Binder 驱动识别的 binder_transaction_data 结构体,然后写入mOut

  2. 通过 waitForResponse() 方法把 mOut 的数据发送给 Binder 驱动,并监听 Binder 驱动返回的数据。

这里多说一些,Binder 通信涉及到客户端和服务端,这点和网络通信类似,必然会有类似 TCP 一样的通信协议,不过 Binder 协议比较简单,这里写入: CMD:BC_TRANSACTION 就是协议规定的一种,代表客户端向 Binder 驱动发送数据。当然,有发送数据必然会有接受数据,接受:CMD:BR_REPLY
其实 Client 端或 Server 端在向 Binder 驱动发送完命令后,驱动都会返回一个 BR_TRANSACTION_COMPLETE 指令告知上层发送成功,这点和 HTTP 协议真的很像。

来自<gityuan>

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;
}

总结:

  1. 首先,getAndExecuteCommand() 调用 talkWithDriver() ,把 mIn 的 data 地址赋值给 bwr.read_buffer 传给驱动,并监听驱动是否写入数据
  2. 然后,驱动若有数据写入,mIn.readInt32()读取cmd,然后通过executeCommand(cmd) 根据不同的 cmd 执行不同的操作。

mOut 和 mIn 的作用
它们都是parcel 类型,负责和 Binder 驱动数据的传输

  1. mOut: Client 端线程 writeTransactionData() 已向 Parcel 数据类型的 mOut 写入数据,然后通过 ioctl() 发给 Binder 驱动,此时mIn还没有数据;(Server 端也是通过 mOut 向 Binder 驱动写回数据)
  2. 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);
          ......
 }

总结:

  1. startThreadPool() 开启新的线程并执行了 joinThreadPool() 方法,而 joinThreadPool() 方法通过调用 talkWithDriver() 来读取 Binder 设备
  2. 通过 startThreadPool() 创建的线程都是 Binder 主线程,用来接收 Binder 驱动发送回来的消息,而且 joinThreadPool() 有阻塞线程的作用,防止主线程在main() 方法执行完后退出

到底有多少个线程在为 Service端 服务呢?目前看来是两个:

  1. startThreadPool() 中新启动的线程通过 joinThreadPool() 读取 Binder 设备,查看是否有请求。
  2. 主线程也调用 joinThreadPool() 读取 Binder 设备,查看是否有请求。
图像传输慢,通过 startThreadPool() 新建一个线程,速度就快很多

默认情况下,线程池中没有线程,由于本身已经有了2个线程可用,一般情况下,能满足要求。但是,当有多个并发的 IPC 请求时,可能会触发内核增加更多的IPC通信线程来服务这些请求。当进程接受到 Binder 驱动从内核中发出的 BR_SPAWN_LOOPER 命令时,会启动一个非 Binder 主线程。

最后通过一张图,来总结 Binder 通信的整个流程:

来自 <Light.Moon>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容