Android开发Android开发经验谈

深入Android系统 Binder-3-原理

2020-09-08  本文已影响0人  Android进阶架构

Binder的实现原理

涉及到原理源码肯定是少不了的,9.0 binder 相关的源码分为三部分:

还有一点需要明确的是:

书中这两个名词没有做详细说明,一开始看的有点晕

源码在手,干啥都有

Binder设计相关的问题

Binder实现的远程调用是一种面向对象的远程调用,那么它和面向过程的远程调用区别在什么地方呢?

设计复杂也带来了功能的强大,正因为Binder面向对象的,我们可以创建多个Binder实体对象来服务不同的客户,每个对象有自己的数据。相互之间不会干扰。

为了系统中所有引用对象实体对象能相互关联:

参数的传递问题:

对于服务进程中Binder调用的执行:

Binder的线程模型

Binder 的线程池

Zygote进程启动时,会调用AppRuntimeonZygoteInit函数(书中第8章,还没看到),代码如下:

    virtual void onZygoteInit()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    }

所有Android应用都是从Zygote进程fork出来的。因此,这段代码对所有应用进程都有效。

 ProcessState::ProcessState(const char *driver)  > : mDriverName(String8(driver))
 , mDriverFD(open_driver(driver))
 //......
 {
  if (mDriverFD >= 0) {
     // mmap the binder, providing a chunk of virtual >   address space to receive transactions.
     mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
     if (mVMStart == MAP_FAILED) {
         // *sigh*
        ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
        close(mDriverFD);
         mDriverFD = -1;
         mDriverName.clear();
     }
   }
 }
   #define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
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());
   }
 }
  • spawnPooledThread函数的作用是创建加入线程池的线程
  • 函数中创建了一个PoolThread类,类的run函数会创建线程
  • 传入的参数为true,说明这个线程是线程池的第一个线程
  • 以后再创建的线程都是接到驱动通知后创建的,传入的参数为false,像这样
status_t IPCThreadState::executeCommand(int32_t cmd){
  //......
   case BR_SPAWN_LOOPER:
      mProcess->spawnPooledThread(false);
      break;
  //......
}
  • 我们再看下PoolThread类的业务实现threadLoop函数
protected:
virtual bool threadLoop()
{
  IPCThreadState::self()->joinThreadPool(mIsMain);
  return false;
}
      * 返回`false`代表执行一次,为什么是在`threadLoop()`中执行业务逻辑,可以看下[Android 中的`threadLoop`](https://blog.csdn.net/f2006116/article/details/89058397)
      * 具体调用细节大家阅读`frameworks`源码吧,路径应该是在:`frameworks/av/services/audioflinger/Threads.h`
      * `threadLoop`函数只是调用了`IPCThreadState`的`joinThreadPool`函数,这个函数后面单练它

好的,我们先来梳理下线程池这部分内容:

关于IPCThreadState,稍后详谈

调用Binder服务的线程

客户端Binder服务的调用是通过IBinder的transact函数完成的。这里的IBInder实际上是BpBinder对象,代码如下:

status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }
    return DEAD_OBJECT;
}

这部分代码做了:

status_t IPCThreadState::transact(int32_t handle,
                                  uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags)
{
    status_t err;
    flags |= TF_ACCEPT_FDS;
    //......
    //把要发送的数据放到类的成员变量mOut中
    err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
    if (err != NO_ERROR) {
        if (reply) reply->setError(err);
        return (mLastError = err);
    }
    if ((flags & TF_ONE_WAY) == 0) {// 同步调用方式
        //......
        if (reply) {
            // 调用者要求返回结果,此时向底层发送数据并等待返回值
            err = waitForResponse(reply);
        } else {
            // 调用者不需要返回值,但是还要等待远程执行完毕
            // 这里用fakeRely来接收返回的Parcel对象
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
       //......
    } else {// 异步调用方式,函数会立即返回
        err = waitForResponse(NULL, NULL);
    }
    return err;
}

对于一个进程而言,只有一个Binder驱动文件描述符,所有ioctl调用都是使用这个描述符。如果客户端有好几个线程同时执行远程调用,它们都将在同一个描述符的ioctl函数上等待。那么当数据到来时,哪个线程会受到回复呢?

好的,我们在对这部分做个小结:

Binder对象的传递

Binder对象作为参数传递是,会有两种情形:

Binder对象传递流程简介

Binder调用的参数传递是通过Parcel类来完成的。先来简单看下Binder实体对象转换成Binder引用对象的过程:

写入Binder对象的过程

有了上面的整体流程,我们来看下Binder对象的写入细节:

看下writeStrongBinder的代码:

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}
status_t flatten_binder(const sp<ProcessState>& /*proc*/,
    const sp<IBinder>& binder, Parcel* out)
{
    // flatten_binder 整个方法其实是在向obj这个结构体存放数据
    flat_binder_object obj;

    if (IPCThreadState::self()->backgroundSchedulingDisabled()) {
        /* minimum priority for all nodes is nice 0 */
        obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;
    } else {
        /* minimum priority for all nodes is MAX_NICE(19) */
        obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    }
    
    if (binder != NULL) {
        // 调用localBinder函数开区分是实体对象还是引用对象
        IBinder *local = binder->localBinder();
        if (!local) { //binder引用对象
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.hdr.type = BINDER_TYPE_HANDLE;
            obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
            obj.handle = handle;
            obj.cookie = 0;
        } else { // binder实体对象
            obj.hdr.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {
        obj.hdr.type = BINDER_TYPE_BINDER;
        obj.binder = 0;
        obj.cookie = 0;
    }

    return finish_flatten_binder(binder, obj, out);
}

flatten_binder整个方法其实是在向flat_binder_object这个结构体存放数据。我们看下flat_binder_object的结构:

struct flat_binder_object {
    /* 8 bytes for large_flat_header. */
    __u32       type;
    __u32       flags;
    /* 8 bytes of data. */
    union {
        binder_uintptr_t    binder; /* local object */
        __u32           handle; /* remote object */
    };
    /* extra data associated with local object */
    binder_uintptr_t    cookie;
};

我们看下flat_binder_object中的属性:

解析强引用Binder对象数据的过程

Parcel 类中解析数据的函数是unflatten_binder,代码如下:

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->hdr.type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

unflatten_binder的逻辑是:

Vector<handle_entry> mHandleToObject;
struct handle_entry {
  IBinder* binder;
  RefBase::weakref_type* refs;
};

IPCThreadState类

每个Binder线程都会有一个关联的IPCThreadState类的对象。IPCThreadState类主要的作用是和Binder驱动交互,发送接收Binder数据,处理和Binder驱动之间来往的消息。

我们在Binder线程模型中已经知道:

这两个函数都是定义在IPCThreadState类中,我们分别来看下这两个函数。

waitForResponse()函数

函数定义如下:

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    uint32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break;//和驱动通信
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;//没有数据,重新开始循环

        cmd = (uint32_t)mIn.readInt32();//读取数据
        //......
        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        //......省略部分case语句
        case BR_REPLY:
        // Binder调用返回的消息
        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }
    }

finish:
    //...... 错误处理
    return err;
}

waitForResponse()函数中是一个无限while循环,在循环中,重复下面的工作:

我们再仔细看下Binder调用收到的返回类型为BR_REPLY的代码:

        case BR_REPLY:
            {
                binder_transaction_data tr;
                //按照 binder_transaction_data 结构大小读取数据
                err = mIn.read(&tr, sizeof(tr));
                ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
                if (err != NO_ERROR) goto finish;

                if (reply) {
                    //reply 不为null,表示调用者需要返回结果
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        //binder 调用成功,把从驱动来的数据设置到reply对象中
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t),
                            freeBuffer, this);
                    } else {
                        //binder调用失败,使用freeBuffer函数释放驱动中分配的缓冲区
                        err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t), this);
                    }
                } else {
                    //调用者不需要返回结果,使用freeBuffer函数释放驱动中分配的缓冲区
                    freeBuffer(NULL,
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(binder_size_t), this);
                    continue;
                }
            }

针对BR_REPLY类型的处理流程是:

因为Binder提供了一块驱动和应用层共享的内存空间,所以在接收Binder数据不需要额外创建缓冲区再进行一次拷贝了,但是如果不及时通知驱动释放缓冲区中占用的无用内存,会很快会耗光这部分共享空间。

上面代码中的reply->ipcSetDataReference方法,在设置Parcel对象的同时,同样也把freeBuffer的指针作为参数传入到对象中,这样reply对象删除时,也会调用freeBuffer函数来释放驱动中的缓冲区。

waitForResponse()函数的作用是发送Binder调用的数据并等待返回值。为什么还需要使用循环的方式反复和驱动交互?原因有两点:

joinThreadPool函数

Binder线程池部分已经知道:应用启动时会伴随着启动Binder服务,而最后执行到的方法就是joinThreadPool函数。

我们看下函数定义:

void IPCThreadState::joinThreadPool(bool isMain)
{
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

    status_t result;
    do {
        processPendingDerefs();
        //now get the next command to be processed, waiting if necessary
        result = getAndExecuteCommand();//读取并处理驱动发送的消息
        if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
            abort();
        }
        // Let this thread exit the thread pool if it is no longer
        // needed and it is not the main process thread.
        if(result == TIMED_OUT && !isMain) {
            break;
        }
    } while (result != -ECONNREFUSED && result != -EBADF);

    mOut.writeInt32(BC_EXIT_LOOPER); //退出前向驱动发送线程退出消息
    talkWithDriver(false);
}

joinThreadPool函数的结构是一个while循环。

到这里,我们再来看下talkWithDriverexecuteCommand两个函数

talkWithDriver函数

talkWithDriver函数的作用是把IPCThreadState类中的mOut变量保存的数据通过ioctl函数发送到驱动,同时把驱动返回的数据放到类的mIn变量中。

talkWithDriver函数的代码如下:

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    if (mProcess->mDriverFD <= 0) {
        return -EBADF;
    }
    //ioctl 传输时所使用的的数据结构
    binder_write_read bwr;

    // Is the read buffer empty?
    // 判断 mIn 中的数据是否已经读取完毕,没有的话还需要继续读取
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    // We don't want to write anything if we are still reading
    // from data left in the input buffer and the caller
    // has requested to read the next data.
    // 英文描述的很详细了哟
    // 如果不需要读取数据(doReceive=false,needRead=true),那么就可以准备写数据了
    const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
    bwr.write_size = outAvail;//表示要写入的长度
    bwr.write_buffer = (uintptr_t)mOut.data();//要写入的数据的指针

    // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (uintptr_t)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;

    bwr.write_consumed = 0;//这个字段后面会被填上驱动读取到的数据长度(写入到驱动的数据长度)
    bwr.read_consumed = 0;// 这个字段后面会被填上从驱动返回的数据长度
    status_t err;
    do {
// 9.0增加了一些平台判断,可能以后要多平台去支持了吧
#if defined(__ANDROID__)
        // 用ioctl和驱动交换数据
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
        if (mProcess->mDriverFD <= 0) {
            // 这个情况应该是设备节点不可用
            err = -EBADF;
        }
    } while (err == -EINTR);

    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < mOut.dataSize())
                // 如果已经写入驱动的数据长度小于mOut中的数据长度
                // 说明还没发送完,把已经写入驱动的数据移除掉
                // 剩下的数据等待下次发送
                mOut.remove(0, bwr.write_consumed);
            else {
                // 数据已经全部写入驱动,复位mOut
                mOut.setDataSize(0);
                // 做一些指针的清理工作
                processPostWriteDerefs();
            }
        }
        if (bwr.read_consumed > 0) {
            // 说明从驱动中读到了数据,设置好mInt对象
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        return NO_ERROR;
    }

    return err;
}

executeCommand 函数

executeCommand 函数是一个大的switch语句,处理从驱动传递过来的消息。

我们前面遇到了一些消息,大概包括:

重点是BR_TRANSACTION,代码定义如下:

case BR_TRANSACTION:
        {
            binder_transaction_data tr;
            result = mIn.read(&tr, sizeof(tr));
            if (result != NO_ERROR) break; // 数据异常直接退出

            Parcel buffer;
            //用从驱动接收的数据设置Parcel对象Buffer
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);

            const pid_t origPid = mCallingPid;
            const uid_t origUid = mCallingUid;
            const int32_t origStrictModePolicy = mStrictModePolicy;
            const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
            // 从消息中取出调用者的进程ID和euid
            mCallingPid = tr.sender_pid;
            mCallingUid = tr.sender_euid;
            mLastTransactionBinderFlags = tr.flags;

            Parcel reply;
            status_t error;

            if (tr.target.ptr) {
                // We only have a weak reference on the target object, so we must first try to
                // safely acquire a strong reference before doing anything else with it.
                if (reinterpret_cast<RefBase::weakref_type*>(
                        tr.target.ptr)->attemptIncStrong(this)) {
                    // 如果ptr指针不为空,cookie保存的是BBinder的指针
                    // 调用cookie的transact函数
                    error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
                            &reply, tr.flags);
                    // 及时清除指针
                    reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
                } else {
                    error = UNKNOWN_TRANSACTION;
                }

            } else {
                //如果tr.target.ptr为0 表示是ServiceManager
                error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
            }
            // 如果是同步调用,则把reply对象发送回去,否则什么也不做
            if ((tr.flags & TF_ONE_WAY) == 0) {
                LOG_ONEWAY("Sending reply to %d!", mCallingPid);
                if (error < NO_ERROR) reply.setError(error);
                sendReply(reply, 0);
            } else {
                LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
            }

            mCallingPid = origPid;
            mCallingUid = origUid;
            mStrictModePolicy = origStrictModePolicy;
            mLastTransactionBinderFlags = origTransactionBinderFlags;
        }
        break;

BR_TRANSACTION消息的处理过程是:

上一篇下一篇

猜你喜欢

热点阅读