美文网首页Android 进程间通信 - Binder 机制深入浅出
Binder 进程间通信机制详解(Native 篇)

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

作者: 搬砖写Bug | 来源:发表于2018-07-07 15:25 被阅读62次

    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>

    相关文章

      网友评论

        本文标题:Binder 进程间通信机制详解(Native 篇)

        本文链接:https://www.haomeiwen.com/subject/iwgwuftx.html