Android 重学系列 Binder 服务的初始化以及交互原理(上)

前言

经过前面三篇binder驱动的初始化阐述,我大致上稍微复习一边linux内核的基础知识,也对binder的理解更加深刻。接下来我们来看看binder 的服务是怎么注册到service_manager。
如果遇到问题请到:https://www.jianshu.com/p/04e53fd86ca2

正文

很多文章一开始直接放出了binder在framework中类的之间的uml图。我先不放出,因为一开始就直接上这种图的确不怎么好理解。等我们探索完binder 服务初始化架构之后再放出来。

ServiceManager 注册service

还记得我之前写的SystemServiceManager那一章节。我稍微的提及到了服务的注册,但是我并没有深入源码。这一次我连同binder一起解析一次。
文件:/frameworks/base/services/java/com/android/server/SystemServer.java

 wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
                    DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);

在这里为系统添加了windowManagerService的服务。这个服务Android应该十分熟悉,用来控制窗口的服务。我之后会专门取出一节来详细解析其中的代码。

让我们看看ServiceManager.addService方法。
文件:/frameworks/base/core/java/android/os/ServiceManager.java

    public static void addService(String name, IBinder service, boolean allowIsolated,
            int dumpPriority) {
        try {
            getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
    }

看这个代表着远程的异常,我们猜测这里是不是通过某种手段进行进程间通信,把IBinder添加到远程端中。

    private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative
                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
        return sServiceManager;
    }

首先判断IServiceManager 这个接口对象是否存在。不存在则从从远程端获取这个实例。

妙啊妙,我在Binder第一节就探讨过进程间通信,无一不是通过两端通过协议进行交流,而在这里的IPC通行居然就能抽象着两个远程端直接继承一个接口,进行通信。但是还记得吗?进程之间的地址都有自己保护,我们不可能真的抽离一个接口让两个进程之间通信,只是在这个过程中把进程间通信透明化了。

这么棒的设计,让我们看看Google工程师是怎么设计的。
文件:/frameworks/base/core/java/android/os/ServiceManagerNative.java

    static public IServiceManager asInterface(IBinder obj)
    {
        if (obj == null) {
            return null;
        }
        IServiceManager in =
            (IServiceManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ServiceManagerProxy(obj);
    }

ProcessState 初始化

此时通过传入的IBinder查询IServiceManager这个接口对象。那么这个IBinder对象是怎么生成的?看看下面这个方法:

public static final native IBinder getContextObject();

这个native方法究竟做了什么?

static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
    sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
    return javaObjectForIBinder(env, b);
}

这里可以看到一个很关键的类。我们可以见到在Binder 服务中十分重要的一个类ProcessState。我们先看看这个类的self方法。
文件:/frameworks/native/libs/binder/ProcessState.cpp

sp<ProcessState> ProcessState::self()
{
    Mutex::Autolock _l(gProcessMutex);
    if (gProcess != NULL) {
        return gProcess;
    }
    gProcess = new ProcessState("/dev/binder");
    return gProcess;
}

从这里我们可以的得知这是一个单例,如果在这个进程的ProcessState没有实例化则会实例化ProcessState。

接着我们看看这个构造函数。

ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
//注意这里
    , mDriverFD(open_driver(driver))
    , mVMStart(MAP_FAILED)
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
    , mStarvationStartTimeMs(0)
    , mManagesContexts(false)
    , mBinderContextCheckFunc(NULL)
    , mBinderContextUserData(NULL)
    , mThreadPoolStarted(false)
    , mThreadPoolSeq(1)
{
    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();
        }
    }

    LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened.  Terminating.");
}

这里的操作和service_manager十分相似,先调用open打开binder驱动,再进行映射。

static int open_driver(const char *driver)
{
    int fd = open(driver, O_RDWR | O_CLOEXEC);
    if (fd >= 0) {
        int vers = 0;
        status_t result = ioctl(fd, BINDER_VERSION, &vers);
        if (result == -1) {
            ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
            close(fd);
            fd = -1;
        }
        if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
          ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
                vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
            close(fd);
            fd = -1;
        }
        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));
        }
    } else {
        ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
    }
    return fd;
}

这里的初始化和service_manager的初始化极其相似,首先写入Binder_Version,接着写入BINDER_SET_MAX_THREADS 设置最大线程数。

经过ProcessState构造函数之后,我们也把当前这个类已经映射到了binder驱动中。实际上这个类我在第一篇启动系统中已经惊鸿一现,系统启动的时候,zygote进程孵化的SystemServer进程也会映射到binder驱动中。

细节不继续赘述了,我们继续看看getContextObject。

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
    return getStrongProxyForHandle(0);
}

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
    sp<IBinder> result;

    AutoMutex _l(mLock);

    handle_entry* e = lookupHandleLocked(handle);

    if (e != NULL) {
        // We need to create a new BpBinder if there isn't currently one, OR we
        // are unable to acquire a weak reference on this current one.  See comment
        // in getWeakProxyForHandle() for more info about this.
        IBinder* b = e->binder;
        if (b == NULL || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {
                Parcel data;
                status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);
                if (status == DEAD_OBJECT)
                   return NULL;
            }

            b = BpBinder::create(handle);
            e->binder = b;
            if (b) e->refs = b->getWeakRefs();
            result = b;
        } else {
            // This little bit of nastyness is to allow us to add a primary
            // reference to the remote proxy when this team doesn't have one
            // but another team is sending the handle to us.
            result.force_set(b);
            e->refs->decWeak(this);
        }
    }

    return result;
}

lookupHandleLocked 就算是第一次来查找,控制引用的mHandleToObject的vector对象还是会自己添加一份进去。所以一般情况下不用担心找不到引用,至于为什么说是引用,放到之后再说。
因此,此时我们会走到b==null的分支当中,调用了BpBinder::create(handle)的方法生成一个远程端的binder代理对象。

IPCThreadState 初始化

文件:/frameworks/native/libs/binder/IPCThreadState.cpp

此时handle为0,会走到下面的方法:

status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);

此时出现了另一个十分关键的类IPCThreadState。看到这个调用,我们也可以知道这是一个单例设计。

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 (gShutdown) {
        ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n");
        return NULL;
    }

    pthread_mutex_lock(&gTLSMutex);
    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;
}

从这这里可以清楚实际上,这个IPCThreadState的单例是做到线程单例。把IPCThreadState实例存储到线程的TLS中。这个做法实际上和Java的ThreadLocal思想相似,如果不熟悉pthread相关的操作,不妨可以代入思考。

IPCThreadState 和binder驱动交互

我们接下来看看transact方法。

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;

    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 0
        if (code == 4) { // relayout
...
        } else {
...
        }
        #endif
        if (reply) {
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }
        #if 0
        if (code == 4) { // relayout
...
        } else {
...
        }
        #endif

...
    } else {
        err = waitForResponse(NULL, NULL);
    }

    return err;
}

这就是整个Binder server和client在framework层中向binder驱动传递数据最为核心的的机制。
这里我们分为两段来讨论。注意此时flags & TF_ONE_WAY (0x10)将用来控制整个流程是否是等待远程端的回应标志位。此时flag为而0,因此会等待。当flag为1的时候则不等待。在这里的表现就是aidl中oneway开头的方法。

writeTransactionData

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
    int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
    binder_transaction_data tr;
//是这远程端的目标
    tr.target.ptr = 0; /* 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_buffer中offset代表着元数据的偏移量以及data_size代表着有效数据的大小。其实是在Parcel中已经做好了管理了。在Parcel中mData对象代表着普通数据,而mObjects是一个偏移数组,它保存了在数据缓冲区mData中所有Binder对象的位置。Binder会通过这个对象来寻找Binder对象。

此时需要进去的数据是:

  • cmd:BC_TRANSACTION
  • tr : binder_transaction_data
  • code: IBinder::PING_TRANSACTION
    BC_TRANSACTION 代表此时有个事务处理,binder_transaction_data代表事务数据。

这里为mOut Parcel设置了第一段数据。

waitForResponse(reply)

我们接着看循环第二段。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;

....
}
talkWithDriver

在这个循环里的talkWithDriver函数,看到名字就知道是和binder驱动进行沟通。

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 defined(__ANDROID__)
        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.remove(0, bwr.write_consumed);
            else {
                mOut.setDataSize(0);
                processPostWriteDerefs();
            }
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }


    return err;
}

在这里我们又看到了binder_write_read这个结构体。
这个结构体的读取部分设置了Parcel全局对象mIn,写入部分设置Parcel全局对象mOut。当童星结束之后将会清空全局的Parcel变量mOut以及mIn中的写入/读取标记位置。

binder_transaction

文件:/drivers/staging/android/binder.c

binder_transaction 虽然之前已经提过,但是这个方法过于长,这里我们继续只关注我们需要的。
此时我们通过ioctl通信到Binder驱动中。我们这边直接进入binder驱动的对应方法看看。
方法 binder_thread_write.

    case BC_TRANSACTION:
        case BC_REPLY: {
            struct binder_transaction_data tr;

            if (copy_from_user(&tr, ptr, sizeof(tr)))
                return -EFAULT;
            ptr += sizeof(tr);
            binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
            break;

此时cmd是BC_TRANSACTION而不是BC_REPLY

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
{
    struct binder_transaction *t;
    struct binder_work *tcomplete;
    binder_size_t *offp, *off_end;
    binder_size_t off_min;
    struct binder_proc *target_proc;
    struct binder_thread *target_thread = NULL;
    struct binder_node *target_node = NULL;
    struct list_head *target_list;
    wait_queue_head_t *target_wait;
    struct binder_transaction *in_reply_to = NULL;
    struct binder_transaction_log_entry *e;
    uint32_t return_error;

...
//1.通过handle查找对应的binder引用
    if (reply) {
        ...
    } else {
        if (tr->target.handle) {
...
        } else {
            target_node = binder_context_mgr_node;
            if (target_node == NULL) {
                return_error = BR_DEAD_REPLY;
                goto err_no_context_mgr_node;
            }
        }
        e->to_node = target_node->debug_id;
        target_proc = target_node->proc;
...
//寻找事务依赖
        if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
            struct binder_transaction *tmp;

            tmp = thread->transaction_stack;
            if (tmp->to_thread != thread) {
                binder_user_error("%d:%d got new transaction with bad transaction stack, transaction %d has target %d:%d\n",
                    proc->pid, thread->pid, tmp->debug_id,
                    tmp->to_proc ? tmp->to_proc->pid : 0,
                    tmp->to_thread ?
                    tmp->to_thread->pid : 0);
                return_error = BR_FAILED_REPLY;
                goto err_bad_call_stack;
            }
            while (tmp) {
                if (tmp->from && tmp->from->proc == target_proc)
                    target_thread = tmp->from;
                tmp = tmp->from_parent;
            }
        }
    }
//2.设置当前的目标等待队列以及目标todo 队列
    if (target_thread) {
        e->to_thread = target_thread->pid;
        target_list = &target_thread->todo;
        target_wait = &target_thread->wait;
    } else {
        target_list = &target_proc->todo;
        target_wait = &target_proc->wait;
    }
    e->to_proc = target_proc->pid;
//申请binder_transaction
    /* TODO: reuse incoming transaction for reply */
    t = kzalloc(sizeof(*t), GFP_KERNEL);
    if (t == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_alloc_t_failed;
    }
    binder_stats_created(BINDER_STAT_TRANSACTION);
//申请binder_work
    tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
    if (tcomplete == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_alloc_tcomplete_failed;
    }
    binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
...
    if (!reply && !(tr->flags & TF_ONE_WAY))
        t->from = thread;
    else
        t->from = NULL;
    t->sender_euid = task_euid(proc->tsk);
    t->to_proc = target_proc;
    t->to_thread = target_thread;
    t->code = tr->code;
    t->flags = tr->flags;
    t->priority = task_nice(current);

    trace_binder_transaction(reply, t, target_node);
//为事务分配一个binder_buffer
    t->buffer = binder_alloc_buf(target_proc, tr->data_size,
        tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
    if (t->buffer == NULL) {
        return_error = BR_FAILED_REPLY;
        goto err_binder_alloc_buf_failed;
    }
    t->buffer->allow_user_free = 0;
    t->buffer->debug_id = t->debug_id;
    t->buffer->transaction = t;
    t->buffer->target_node = target_node;
    trace_binder_transaction_alloc_buf(t->buffer);
    if (target_node)
        binder_inc_node(target_node, 1, 0, NULL);

    offp = (binder_size_t *)(t->buffer->data +
                 ALIGN(tr->data_size, sizeof(void *)));
//拷贝
    if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
               tr->data.ptr.buffer, tr->data_size)) {
        ...
    }
    if (copy_from_user(offp, (const void __user *)(uintptr_t)
               tr->data.ptr.offsets, tr->offsets_size)) {
...
    }
...

这里分为以下几步来说明:

  • 1.通过handle查找对应的binder引用。在这里我们的cmd是BC_TRANSCATION。因此会直接拿binder_context_mgr_node。而这个就是我在上一篇说过的service_manager的binder实体。此时我们也相当于拿到了service_manager进程映射的地址了。

  • 2.找到我们需要的binder实体之后,我们看看该进程的binder_thread对应的事务栈transaction_stack。我们需要通过这个栈去寻找,当前分发下来的事务是否有依赖其他事务。

            while (tmp) {
                if (tmp->from && tmp->from->proc == target_proc)
                    target_thread = tmp->from;
                tmp = tmp->from_parent;
            }

因此,binder驱动会通过一个循环,直接从栈顶开始查找依赖的事务的线程(binder_transaction->from)。会去查找依赖的事务所对应的binder_proc是不是当前的目标进程。是则说明有线程也在使用这个目标进程,我们需要把把这个binder_thread取出来,先把依赖的事务给处理了。

  • 3.判断此时有没有找到目标的binder_thread。找到则设置目标binder_thread的todo list和等待队列。不然则是binder_proc的todo list和等待队列。

  • 4.申请内存 t 和 tcomplete 事务对象binder_transaction,工作项 binder_work。并且把相关的数据如目标进程,目标binder_thread,code,flag等写入binder_transaction。并且通过binder_alloc_buf 为这一次事务在目标进程中申请一段binder_buffer(内核缓冲区).

  • 5.通过copy_from_user把binder中普通数据拷贝到binder_transaction_data的data中,元数据拷贝到offsets中。

为了弄清楚这一点,我们看看binder_transaction_data数据结构

struct binder_transaction_data {
  union {
//引用
    __u32 handle;
//对应用户空间binder_transaction_data 的ptr
    binder_uintptr_t ptr;
  } target;
//对应用户空间binder_transaction_data的cookie
  binder_uintptr_t cookie;
//对应用户空间binder_transaction_data的code
  __u32 code;
//对应用户空间binder_transaction_data的flags
  __u32 flags;
  pid_t sender_pid;
  uid_t sender_euid;
//数据大小
  binder_size_t data_size;
//偏移量大小
  binder_size_t offsets_size;
//元数据
  union {
//binder_buffer
    struct {
      binder_uintptr_t buffer;
//binder数组
      binder_uintptr_t offsets;
    } ptr;
//数据
    __u8 buf[8];
  } data;
};

此时拷贝tr->data.ptr.buffer到了binder_transaction的内核缓冲区的数据区。tr->data.ptr.offsets拷贝到offp 地址中。而这个offp地址是这么计算的

offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
这么计算的结果是把binder_transaction的binder_buffer的末尾地址计算出来,相当于定位了mData中Binder偏移数组的起始位置。

    off_end = (void *)offp + tr->offsets_size;
    off_min = 0;
    for (; offp < off_end; offp++) {
        struct flat_binder_object *fp;

...
//获取flat_binder_object 结构体
        fp = (struct flat_binder_object *)(t->buffer->data + *offp);
        off_min = *offp + sizeof(struct flat_binder_object);
        switch (fp->type) {
//处理flat_binder_object 中type为 BINDER_TYPE_BINDER BINDER_TYPE_WEAK_BINDER
        case BINDER_TYPE_BINDER:
        case BINDER_TYPE_WEAK_BINDER: {
            struct binder_ref *ref;
            struct binder_node *node = binder_get_node(proc, fp->binder);
//生成一个新的binder对象
            if (node == NULL) {
                node = binder_new_node(proc, fp->binder, fp->cookie);
                if (node == NULL) {
                    return_error = BR_FAILED_REPLY;
                    goto err_binder_new_node_failed;
                }
                node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
                node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
            }
//cookie中保存着本地binder对象
            if (fp->cookie != node->cookie) {
                binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
                    proc->pid, thread->pid,
                    (u64)fp->binder, node->debug_id,
                    (u64)fp->cookie, (u64)node->cookie);
                return_error = BR_FAILED_REPLY;
                goto err_binder_get_ref_for_node_failed;
            }
    if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
                return_error = BR_FAILED_REPLY;
                goto err_binder_get_ref_for_node_failed;
            }
//为binder对象添加引用
            ref = binder_get_ref_for_node(target_proc, node);
            if (ref == NULL) {
                return_error = BR_FAILED_REPLY;
                goto err_binder_get_ref_for_node_failed;
            }
//判断是不是弱引用的binder type
            if (fp->type == BINDER_TYPE_BINDER)
                fp->type = BINDER_TYPE_HANDLE;
            else
                fp->type = BINDER_TYPE_WEAK_HANDLE;
            fp->handle = ref->desc;
//找到对应的binder 引用并且增加
            binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
                       &thread->todo);

            trace_binder_transaction_node_to_ref(t, node, ref);
            binder_debug(BINDER_DEBUG_TRANSACTION,
                     "        node %d u%016llx -> ref %d desc %d\n",
                     node->debug_id, (u64)node->ptr,
                     ref->debug_id, ref->desc);
        } break;
//处理flat_binder_object 中type为 BINDER_TYPE_HANDLE BINDER_TYPE_WEAK_HANDLE
        case BINDER_TYPE_HANDLE:
        case BINDER_TYPE_WEAK_HANDLE: {
//获取binder 引用
            struct binder_ref *ref = binder_get_ref(proc, fp->handle);

            if (ref == NULL) {
                binder_user_error("%d:%d got transaction with invalid handle, %d\n",
                        proc->pid,
                        thread->pid, fp->handle);
                return_error = BR_FAILED_REPLY;
                goto err_binder_get_ref_failed;
            }
            if (security_binder_transfer_binder(proc->tsk, target_proc->tsk)) {
                return_error = BR_FAILED_REPLY;
                goto err_binder_get_ref_failed;
            }
//如果引用对象刚好是目标进程,转化为Binder 本地
            if (ref->node->proc == target_proc) {
                if (fp->type == BINDER_TYPE_HANDLE)
                    fp->type = BINDER_TYPE_BINDER;
                else
                    fp->type = BINDER_TYPE_WEAK_BINDER;
                fp->binder = ref->node->ptr;
                fp->cookie = ref->node->cookie;
                binder_inc_node(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
                trace_binder_transaction_ref_to_node(t, ref);
                binder_debug(BINDER_DEBUG_TRANSACTION,
                         "        ref %d desc %d -> node %d u%016llx\n",
                         ref->debug_id, ref->desc, ref->node->debug_id,
                         (u64)ref->node->ptr);
            } else {
                struct binder_ref *new_ref;
//不是则获取目标进程中的引用
                new_ref = binder_get_ref_for_node(target_proc, ref->node);
                if (new_ref == NULL) {
                    return_error = BR_FAILED_REPLY;
                    goto err_binder_get_ref_for_node_failed;
                }
                fp->handle = new_ref->desc;
                binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
                trace_binder_transaction_ref_to_ref(t, ref,
                                    new_ref);
                binder_debug(BINDER_DEBUG_TRANSACTION,
                         "        ref %d desc %d -> ref %d desc %d (node %d)\n",
                         ref->debug_id, ref->desc, new_ref->debug_id,
                         new_ref->desc, ref->node->debug_id);
            }
        } break;
//透传文件
        case BINDER_TYPE_FD: {
...
        } break;

        default:
            binder_user_error("%d:%d got transaction with invalid object type, %x\n",
                proc->pid, thread->pid, fp->type);
            return_error = BR_FAILED_REPLY;
            goto err_bad_object_type;
        }
    }
    if (reply) {
        BUG_ON(t->buffer->async_transaction != 0);
        binder_pop_transaction(target_thread, in_reply_to);
    } else if (!(t->flags & TF_ONE_WAY)) {
        BUG_ON(t->buffer->async_transaction != 0);
        t->need_reply = 1;
        t->from_parent = thread->transaction_stack;
        thread->transaction_stack = t;
    } else {
        BUG_ON(target_node == NULL);
        BUG_ON(t->buffer->async_transaction != 1);
        if (target_node->has_async_transaction) {
            target_list = &target_node->async_todo;
            target_wait = NULL;
        } else
            target_node->has_async_transaction = 1;
    }
//切换工作状态,添加到目标进程的工作队列
    t->work.type = BINDER_WORK_TRANSACTION;
    list_add_tail(&t->work.entry, target_list);
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    list_add_tail(&tcomplete->entry, &thread->todo);
    if (target_wait)
        wake_up_interruptible(target_wait);
    return;

...

}

增加引用计数

在这个循环中有一个关键的结构体,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 在Parcel中也有一个一模一样的数据结构,这个结构体实际上就是Binder偏移数组中的数据。很有意思,当我们尝试着写内核和用户空间交互数据的时候,我们往往可以在两侧构造一样的的数据结构再进行copy。

fp = (struct flat_binder_object *)(t->buffer->data + *offp);

但是这是怎么回事?为什么这样子能够强转flat_binder_object对象。这个东西我研究了老半天?
文件:/frameworks/native/libs/binder/Parcel.cpp

status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
    const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
    const bool enoughObjects = mObjectsSize < mObjectsCapacity;
    if (enoughData && enoughObjects) {
restart_write:
        *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;

原来如此,这里提前说一下,Binder经常借助Parcel传递消息,而Binder的传递恰好就是调用这个方法。而在framework层换算flat_binder_object对象,也是通过普通数据+偏移量转型为flat_binder_object并且赋值。实际上这里的内核算法只是和用户空间的算法保持一致。

在这个循环中,主要处理了三种flat_binder_object 对应的5种type。首先看看flat_binder_object 是怎么获得的。这些type

  • 1.BINDER_TYPE_BINDER/BINDER_TYPE_WEAK_BINDER
    该标志位是在Parcel中设置,判断如果是本地binder对象则,加入此标志位。此时binder驱动接受到之后,说明此时是本地端发起的交互,因此此时需要获取目标进程的引用,并且增加引用计数,同时把状态转化为BINDER_TYPE_HANDLE / BINDER_TYPE_WEAK_HANDLE。

  • 2.BINDER_TYPE_HANDLE / BINDER_TYPE_WEAK_HANDLE
    该标志位也是在Parcel中设置。判断到此时不是binder本地而是远程端,则会加入此标记。当binder驱动接受到,说明此时是远程端发起的交互。需要通过handle获取本地端的引用。当handle获取到的引用刚好事目标进程,则把标志位设置为BINDER_TYPE_BINDER/BINDER_TYPE_WEAK_BINDER 。不是则直接从目标进程查找引用。

  • 3.BINDER_TYPE_FD
    该标志控制着一个文件描述符,并且读写进数据,做跨进程的数据透传。

细节:binder_ref的处理
static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
                          struct binder_node *node)
{
    struct rb_node *n;
    struct rb_node **p = &proc->refs_by_node.rb_node;
    struct rb_node *parent = NULL;
    struct binder_ref *ref, *new_ref;
//从binder_proc中寻找合适ref
    while (*p) {
        parent = *p;
        ref = rb_entry(parent, struct binder_ref, rb_node_node);

        if (node < ref->node)
            p = &(*p)->rb_left;
        else if (node > ref->node)
            p = &(*p)->rb_right;
        else
            return ref;
    }
//没找到则新建一个
    new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
    if (new_ref == NULL)
        return NULL;
    binder_stats_created(BINDER_STAT_REF);
    new_ref->debug_id = ++binder_last_id;
    new_ref->proc = proc;
    new_ref->node = node;
//把binder实体添加到binder_proc的rb_node_node
    rb_link_node(&new_ref->rb_node_node, parent, p);
    rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
//处理binder_proc中所有的引用描述(句柄)
    new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1;
    for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
        ref = rb_entry(n, struct binder_ref, rb_node_desc);
        if (ref->desc > new_ref->desc)
            break;
        new_ref->desc = ref->desc + 1;
    }
//把引用添加到rb_node_desc
    p = &proc->refs_by_desc.rb_node;
    while (*p) {
        parent = *p;
        ref = rb_entry(parent, struct binder_ref, rb_node_desc);

        if (new_ref->desc < ref->desc)
            p = &(*p)->rb_left;
        else if (new_ref->desc > ref->desc)
            p = &(*p)->rb_right;
        else
            BUG();
    }
    rb_link_node(&new_ref->rb_node_desc, parent, p);
    rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);
    return new_ref;
}

做的事情分为以下几个步骤:

  • 1.从refs_by_node红黑树查找查找,当前的node是否有binder的引用,因为此时要做的行为是要把binder往对端的用户空间传递。此时不可能做到把binder实体交给另外的进程,因此会传递一个引用。
  • 2.没有找到binder引用说明此时这个binder是新加入的,需要需要把当前生成的新的binder_node(binder实体)添加到binder_proc的refs_by_node。
  • 3.把当前的binder_ref的描述设置为1(如果当前的binder的实体是service_manager则设置为0)。循环后面的引用描述(句柄),当遇到引用(句柄)比当前新的大则跳出,否则则新的引用是老引用+1.实际上就是就是调整整个算法。因为在整个过程是获取refs_by_desc第二个管理binder引用的红黑树(一个以引用描述为key的)的后继,比较句柄大小,找到当前最大的那个往后加1。这个break的存在就是为了对付加入一直加的普通的binder对象,突然添加了service_manager的对象,此时我们必须要保证这个binder引用描述为0才设计。

  • 4.把引用以desc 为key添加到refs_by_desc中。这个红黑树更加重要,因为我们在上层的handle就是对应这个描述符,通过这个描述符去查找对应的引用,从而传输binder对象引用。

对于binder来说,是不会直接使用binder实体类,因为binder实体里包含这个其他进程的地址,会导致无法访问的情况。因此,需要一层binder引用包裹。

小总结

为什么要做这一步?对于智能指针来讲,每一次减少意味这个这个binder引用可能有被析构的可能,反过来当我们不需要这个引用的时候,我们也可以把这个计数减少的步骤向后挪移也没问题。所以对于这种情况来说,我们在离开本次作用域之前需,把需要传递的binder引用对象新增加一个引用计数,来防止其被析构。
更为关键的的是,在Parcel读取的时候,会通过这个type,BINDER_TYPE_BINDER转型为BBinder(代表本地端对象),还是BINDER_TYPE_HANDLE转型为BpBinder(代表远程端对象)。

唤醒目标进程

    if (reply) {
        BUG_ON(t->buffer->async_transaction != 0);
        binder_pop_transaction(target_thread, in_reply_to);
    } else if (!(t->flags & TF_ONE_WAY)) {
        BUG_ON(t->buffer->async_transaction != 0);
        t->need_reply = 1;
        t->from_parent = thread->transaction_stack;
        thread->transaction_stack = t;
    } else {
        BUG_ON(target_node == NULL);
        BUG_ON(t->buffer->async_transaction != 1);
        if (target_node->has_async_transaction) {
            target_list = &target_node->async_todo;
            target_wait = NULL;
        } else
            target_node->has_async_transaction = 1;
    }
//切换工作状态,添加到目标进程的工作队列
    t->work.type = BINDER_WORK_TRANSACTION;
    list_add_tail(&t->work.entry, target_list);
    tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
    list_add_tail(&tcomplete->entry, &thread->todo);
    if (target_wait)
        wake_up_interruptible(target_wait);
    return;

第一段是判断当前是否是BC_REPLY,这里先不去研究。当时t->flags & TF_ONE_WAY为0的时候,则设置当前的binder_transaction的need_reply为1,设置依赖事务为当前的binder_thread的transaction_stack。不然则是选择todo list为目标binder实体的异步队列。

最后把工作加入到目标工作(todo)队列。最后调用wake_up_interruptible唤醒目标进程。tcomplete添加到当前线程的todo中。

回到当前场景

当前场景,我们发送了的mOut中实际上没有设置任何的data数据。因此在整个binder_transaction(事务交易)中,循环offsets的那部分直接跳过,将会直接寻找目标对象,设置好目标binder_thread以及工作队列还有等待队列。事务项的type设置为BINDER_WORK_TRANSACTION。tcomplete设置BINDER_WORK_TRANSACTION_COMPLETE。

还记得,此时我们唤醒的target_wait是谁吗?就是指service_manager。此时service_manager在binder_thread_read方法被阻塞了。我们看看binder_thread_read方法吧。唤醒的同时,ServiceManager调用者本身的进程也在继续进行。我们在这里记住,我们继续回去看看IPCThreadState的waitForResponse方法。实际上这是两个进程同时进行的事务工作。

ServiceManager调用者进程的talkWithDriver的情景分析

还记得吗。此时talkwithDriver的doneed默认为true,因此会走binder_thread_read的阻塞。等待service_manager的binder_thread_read的返回。

service_manager的binder_thread_read

while (1) {
        uint32_t cmd;
        struct binder_transaction_data tr;
        struct binder_work *w;
        struct binder_transaction *t = NULL;
// 获取todo列表的首部
        if (!list_empty(&thread->todo)) {
            w = list_first_entry(&thread->todo, struct binder_work,
                         entry);
        } else if (!list_empty(&proc->todo) && wait_for_proc_work) {
            w = list_first_entry(&proc->todo, struct binder_work,
                         entry);
        } else {
            /* no data added */
            if (ptr - buffer == 4 &&
                !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
                goto retry;
            break;
        }

        if (end - ptr < sizeof(tr) + 4)
            break;
//判断当前工作项的type
        switch (w->type) {
        case BINDER_WORK_TRANSACTION: {
            t = container_of(w, struct binder_transaction, work);
        } break;
...
        }

        if (!t)
            continue;
//如果对应的目标node不为空
        if (t->buffer->target_node) {
            struct binder_node *target_node = t->buffer->target_node;

            tr.target.ptr = target_node->ptr;
            tr.cookie =  target_node->cookie;
            t->saved_priority = task_nice(current);
            if (t->priority < target_node->min_priority &&
                !(t->flags & TF_ONE_WAY))
                binder_set_nice(t->priority);
            else if (!(t->flags & TF_ONE_WAY) ||
                 t->saved_priority > target_node->min_priority)
                binder_set_nice(target_node->min_priority);
            cmd = BR_TRANSACTION;
        } else {
//为空,则设置cmd为BR_REPLY
            tr.target.ptr = 0;
            tr.cookie = 0;
            cmd = BR_REPLY;
        }
        tr.code = t->code;
        tr.flags = t->flags;
        tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
//设置pid
        if (t->from) {
            struct task_struct *sender = t->from->proc->tsk;

            tr.sender_pid = task_tgid_nr_ns(sender,
                            task_active_pid_ns(current));
        } else {
            tr.sender_pid = 0;
        }
//设置参数
        tr.data_size = t->buffer->data_size;
        tr.offsets_size = t->buffer->offsets_size;
        tr.data.ptr.buffer = (binder_uintptr_t)(
                    (uintptr_t)t->buffer->data +
                    proc->user_buffer_offset);
        tr.data.ptr.offsets = tr.data.ptr.buffer +
                    ALIGN(t->buffer->data_size,
                        sizeof(void *));
//拷贝数据到用户空间
        if (put_user(cmd, (uint32_t __user *)ptr))
            return -EFAULT;
        ptr += sizeof(uint32_t);
        if (copy_to_user(ptr, &tr, sizeof(tr)))
            return -EFAULT;
        ptr += sizeof(tr);

        trace_binder_transaction_received(t);
        binder_stat_br(proc, thread, cmd);
...
        list_del(&t->work.entry);
        t->buffer->allow_user_free = 1;
        if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
            t->to_parent = thread->transaction_stack;
            t->to_thread = thread;
            thread->transaction_stack = t;
        } else {
            t->buffer->transaction = NULL;
            kfree(t);
            binder_stats_deleted(BINDER_STAT_TRANSACTION);
        }
        break;
    }

当ServiceManager对应的进程唤醒当前service_manager的时候,函数将会继续往下走,此时就会进入到这个循环。

  • 1.可以看到的是,此时会从service_manager进程中获取出todo列表的头部,优先获取binder_thread的todo双向队列。这样就能拿到我们就能拿到从ServiceManager加入进去的工作项。
    如果binder_thread没有则去binder_proc中获取。如果都是空,但是判断后面的data数据区域还有位置(地址差为4和前文为binder_buffer划分的时候呼应),则说明此时没有数据,就没有必要去读取从新进入阻塞。

  • 2.此时再去解析命令,转化命令从BC的驱动命令转化RR的framework命令。此时刚好是BINDER_WORK_TRANSACTION,我们就取出里面的binder_work的binder_transaction 事务项t。注意,当t为空的时候会while向后获取命令,执行命令,直到达到了底部。底部的判断就是binder_buffer申请时候的上一个和下一个的binder_buffer差值为4.

  • 3.如果此时该事务项中的目标进程不为空,则把命令转化为BR_TRANSACTION,并且把里面的数据赋值到binder_transaction_data中。

  • 4.光这些不够,我们还需要把数据从内核空间往用户空间拷贝。也就是使用put_user和copy_to_user方法。由于此时正还是BR_TRANSACTION,所以把目标线程和目标对象设置为当前线程。以此来宣告此时此时处理在事务的是当前进程。

回到service_manager的binder_parse

binder_thread_read 方法返回到用户空间。此时进入的binder_parse是用于解析从驱动传递上来的BR命令的解析。
文件:/frameworks/native/cmds/servicemanager/binder.c

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    int r = 1;
    uintptr_t end = ptr + (uintptr_t) size;

    while (ptr < end) {
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);
#if TRACE
        fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
        switch(cmd) {
        case BR_NOOP:
            break;
        case BR_TRANSACTION_COMPLETE:
            break;
        case BR_INCREFS:
        case BR_ACQUIRE:
        case BR_RELEASE:
        case BR_DECREFS:
...
        case BR_TRANSACTION: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
...
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;

                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn);
                res = func(bs, txn, &msg, &reply);
                if (txn->flags & TF_ONE_WAY) {
                    binder_free_buffer(bs, txn->data.ptr.buffer);
                } else {
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
                }
            }
            ptr += sizeof(*txn);
            break;
        }
        case BR_REPLY: {
...
        case BR_DEAD_BINDER: {
...
        case BR_FAILED_REPLY:
...
        case BR_DEAD_REPLY:
...
        default:
            ALOGE("parse: OOPS %d\n", cmd);
            return -1;
        }
    }

    return r;
}

此时传到service_manager已经转化为BR_TRANSACTION命令。在这个分支中出现了全新的一种结构体

struct binder_io
{
    char *data;            /* pointer to read/write from */
    binder_size_t *offs;   /* array of offsets */
    size_t data_avail;     /* bytes available in data buffer */
    size_t offs_avail;     /* entries available in offsets array */

    char *data0;           /* start of data buffer */
    binder_size_t *offs0;  /* start of offsets buffer */
    uint32_t flags;
    uint32_t unused;
};

这个结构体从名字上可以看得出实际上指代的是一个binder_buffer在ioctl交互时候调用的对象.分为两个区域,四个地址。两个区域分别是binder_buffer的元数据以及有效数据去。并且0为结尾的字段就是指的是元数据以及有效数据区的起始地址。得知这些之后,我们尝试阅读其源码。因此在bio_init中初始化reply,bio_init_from_txn 把从内核中传递上来的数据拷贝的到reply中。

而func是从binder_loop传递下来的方法指针是指方法svcmgr_handler,这个方法负责从驱动从底层转化的code命令的执行。
文件:/frameworks/native/cmds/servicemanager/service_manager.c

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    struct svcinfo *si;
    uint16_t *s;
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int allow_isolated;
    uint32_t dumpsys_priority;

    //ALOGI("target=%p code=%d pid=%d uid=%d\n",
    //      (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);

    if (txn->target.ptr != BINDER_SERVICE_MANAGER)
        return -1;

    if (txn->code == PING_TRANSACTION)
        return 0;
...
    switch(txn->code) {
 ...
    }

    bio_put_uint32(reply, 0);
    return 0;
}
还记得我再一开始要注意到传递下来的code的吗?此时code:PING_TRANSACTION,此时我们不需要任何的处理。因为此时,第一次的transact行为用tcp里面的术语来说,只是为了ping通service_manager确定Android服务系统的核心是否被正确初始化。

此时(txn->flags & TF_ONE_WAY)的结果为0.我们直接看看此时service_manager reply了什么命令回去。

void binder_send_reply(struct binder_state *bs,
                       struct binder_io *reply,
                       binder_uintptr_t buffer_to_free,
                       int status)
{
    struct {
        uint32_t cmd_free;
        binder_uintptr_t buffer;
        uint32_t cmd_reply;
        struct binder_transaction_data txn;
    } __attribute__((packed)) data;

    data.cmd_free = BC_FREE_BUFFER;
    data.buffer = buffer_to_free;
    data.cmd_reply = BC_REPLY;
    data.txn.target.ptr = 0;
    data.txn.cookie = 0;
    data.txn.code = 0;
    if (status) {
...
    } else {
        data.txn.flags = 0;
        data.txn.data_size = reply->data - reply->data0;
        data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
        data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
        data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
    }
    binder_write(bs, &data, sizeof(data));
}

此时我们能看到的是,此时定义了一个新的结构体。该结构体相当于之前的2个binder_read_write结构体。不同的是,第一段的标志位是BC_FREE_BUFFER,数据是之前通过binder_alloc_buf切割下来的结构体地址。第二段是BC_REPLY,后面binder_transaction_data 是需要返回的数据,如binder对象或者引用之类的。此时并有任何数据。接下来将进入binder驱动中

binder 驱动处理service_manager返回的数据

此时的流程和之前的binder_thread_write的解析十分相似,所以这里我只点出不一样的逻辑看看里面做了什么事情.首先是第一段返回的命令是BC_FREE_BUFFER;

case BC_FREE_BUFFER: {
            binder_uintptr_t data_ptr;
            struct binder_buffer *buffer;

            if (get_user(data_ptr, (binder_uintptr_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(binder_uintptr_t);

            buffer = binder_buffer_lookup(proc, data_ptr);

            if (buffer->transaction) {
                buffer->transaction->buffer = NULL;
                buffer->transaction = NULL;
            }
            if (buffer->async_transaction && buffer->target_node) {

                if (list_empty(&buffer->target_node->async_todo))
                    buffer->target_node->has_async_transaction = 0;
                else
                    list_move_tail(buffer->target_node->async_todo.next, &thread->todo);
            }
            binder_transaction_buffer_release(proc, buffer, NULL);
            binder_free_buf(proc, buffer);
            break;
        }

此时我能够发现到此时释放当前的使用的binder_buffer实际上就是把数据清空后,使用内核缓冲区,再回归到空闲内核缓冲区,并且减少对应binder对象的引用计数。

第二段下传下来的数据的命令是BC_REPLY,此时把需要返回的数据添加到事务栈中。

if (reply) {
        in_reply_to = thread->transaction_stack;
...
        binder_set_nice(in_reply_to->saved_priority);
...
        thread->transaction_stack = in_reply_to->to_parent;
        target_thread = in_reply_to->from;
        if (target_thread == NULL) {
            return_error = BR_DEAD_REPLY;
            goto err_dead_binder;
        }
...
        target_proc = target_thread->proc;
    }
.....
    if (!reply && !(tr->flags & TF_ONE_WAY))
....
    else
        t->from = NULL;
....
    if (reply) {
        binder_pop_transaction(target_thread, in_reply_to);
    } else if (!(t->flags & TF_ONE_WAY)) {
...
    } else {
        if (target_node->has_async_transaction) {
            target_list = &target_node->async_todo;
            target_wait = NULL;
        } else
    ...
    }

缩减下来就是这么点逻辑。这里需要和上面BC_TRANSACTION解析的binder_transaction进行联动。还记得在上方,结束BC_TRANSACTION命令时候的添加的行为不?

  • 1.从当前的事务栈拿出来的事务项,就是从之前ServiceManager调用者进程中的生成加在transaction_stack的顶部。
  • 2.此时,我们能够这个事务项获取到目标进程的对象binder_proc。下面的逻辑就就和上面一样,不一样的地方仅仅只是调用的方向逆转过来。
    1. binder_pop_transaction 最后再把这个从之前进程挪移来的事务弹出,并且释放内存。
    1. 最后再添加一个事务工作(binder_transaction)type为BINDER_WORK_TRANSACTION,一个事务工作(binder_work)为BINDER_WORK_TRANSACTION_COMPLETE.唤醒ServiceManager调用者进程。

此时两个进程如上方一样同时进行,service_manager将会直接进入binder_thread_read阻塞休眠。

ServiceManager调用者进程的唤醒

当ServiceManager调用进程复苏之后,将会进入到binder_thread_read的cmd解析循环中。

        case BINDER_WORK_TRANSACTION: {
            t = container_of(w, struct binder_transaction, work);
        } break;
        case BINDER_WORK_TRANSACTION_COMPLETE: {
            cmd = BR_TRANSACTION_COMPLETE;
            if (put_user(cmd, (uint32_t __user *)ptr))
                return -EFAULT;
            ptr += sizeof(uint32_t);

            binder_stat_br(proc, thread, cmd);
            binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE,
                     "%d:%d BR_TRANSACTION_COMPLETE\n",
                     proc->pid, thread->pid);

            list_del(&w->entry);
            kfree(w);
            binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
        } break;

...
        if (t->buffer->target_node) {
....
        } else {
            tr.target.ptr = 0;
            tr.cookie = 0;
            cmd = BR_REPLY;
        }

还记得吗?此时我们在第一次调用BC_TRANSACTION的时候会生成一个BINDER_WORK_TRANSACTION_COMPLETE添加到本进程线程对应的事务栈中,在BC_REPLY的时候又添加了BINDER_WORK_TRANSACTION事务栈。

因此此时做的事情很简单,就是通过循环执行这两个事务。并且回收掉之前binder_work的申请的内存。此时做的事情就是为了告诉binder驱动binder服务已经响应回原来的进程了。

此时你可以看到当service_manager写入BC_REPLY的时候,对应的binder_transaction的reply参数不为0,此时并不设置target_node,因此在这里读取的时候,会把cmd从驱动命令换成framework命令。传回上层。

此时read已经结束.总结一下我们能够发现的时候,在binder驱动中以BINDER开头的命令将会在binder_thread_read中处理,以BC开头的命令会在binder_thread_write中处理,RR开头的命令将会在service_manager/IPCThreadState中处理。

ServiceManager进程调用waitForResponse第二段

此时binder驱动已经完成了所有事务,让我们回到顶层吧。
文件:/frameworks/native/libs/binder/IPCThreadState.cpp
waitForResponse的命令解析:

  switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
...
        case BR_DEAD_REPLY:
...
        case BR_FAILED_REPLY:
...
        case BR_ACQUIRE_RESULT:
....
        case BR_REPLY:
            {
                binder_transaction_data tr;
                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) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        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 {
                        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(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;
                }
            }
            goto finish;

        default:
            err = executeCommand(cmd);
            if (err != NO_ERROR) goto finish;
            break;
        }

此时reply不为空并且(tr.flags & TF_STATUS_CODE)结果为0.此时把Parcel reply字段封装从驱动穿上来的数据。此时我们略过中间的步骤,看到service_manager进程中实际上设置binder数据的时候,实际上因为判断到是PING的命令,已经返回了。此时并没有任何数据在Parcel里面。

回到ProcessState

由于嵌套过深,为了避免逻辑断链,我实际上一直在研究这部分代码:

status_t status = IPCThreadState::self()->transact(
                        0, IBinder::PING_TRANSACTION, data, NULL, 0);

此时,我们可以得到一个结论,当发送code的时候,控制的是service_manager那一块的逻辑。而PING_TRANSACTION什么事情都没做,仅仅只是为了ping 通service_manager进程。但是在这个流程中我们却摸透整个binder在传输时候的设计模型。

总结

整个Android的service Binder架构嵌套十分的深,涉及面十分广。因此本文就停笔总结,让我们归纳一遍Android系统中的Binder事务流程。

经过这一次,我们能够绘制出比上一篇更加详细的binder传输数据的封包,当顶层往下层传输命令时候


binder传输数据的封包.png

特别的,在Android整个协议中BC开头的命令时用来控制binder驱动写的行为,BINDER开头的命令用来控制binder驱动读时候的行为,BR开头的命令用来控制路由器接受端和发送端的解析行为,这里是指service_manager和IPCThreadState。中间的code控制service_manager和IPCThreadState的具体动作行为。

再来总结一份Binder传输时候的时序图


binder数据交互时序图.png

注意这里红色代表着跨越了进程。

光是时序图还是没办法看到细节,进一步的整个进程间通信模型
我们把模型分为三部分去看,第一部分是从ServiceManager的调用端发送数据,service_manager进程获取数据。


发送数据.png

第二部分是返回消息在清除binder_transaction_stack之前:


返回消息在清除binder_transaction_stack.png

第三部分:


binder传输原理.png

这只是大致的原理图,实际上binder_transaction存在的意义就是为了查找依赖栈,以及当每一次加入一个新的事务时候,把当前要处理的事务放在栈顶。当需要恢复回复信息的时候,以此为依据来找到原来发送端。上面那个看起来像是栈,实际上只是一个todo链表。

以上就是整个Binder在进程间通信的流程。这里就是它巧妙的地方,通过两个不同进程的事务栈在传递事务,从而达到数据传递的过程。同时通过tcomplete来结束读取的行为。而上面留下的tcomplete将会在下次读取数据的数据删除。这就是我在上面说的,引用删除可以留到下次读取删除,但是添加引用计数必须立即添加,不然会被智能指针析构掉同一道理。

Binder传输过程中,内存拷贝次数

最后再来计算一次,在整个通信过程中,一共做了几次虚拟内存中的数据拷贝,一次完整的通信过程中必定包含本地端的写入以及远程端的读取,都是在binder_ioctl_write_read方法中进行数据的拷贝,一共12次:

  • 第一次:本地端写入数据到内核,远程端写回数据进行应答:
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
    ret = -EFAULT;
    goto out;
}
  • 第二次远程端读取数据处理,本地段读取远程端写回的数据:
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
    ret = -EFAULT;
    goto out;
}

这里加起来就有四次。

  • 第三次,第四次,第五次:方法binder_thread_write,执行binder_transaction准备写入到事务队列中,读取写入到数据中的binder_transaction_data结构体进行一次拷贝,再对binder_transaction_data中中的元数据大小和偏移量大小数据进行拷贝,最后再转移到目标进程binder缓冲区事务队列:
if (copy_from_user(&tr, ptr, sizeof(tr)))
    return -EFAULT;
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
...
}
if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
...
}
  • 第六次:方法binder_thread_read中解析事务队列中的内容,拷贝到远程端:
if (copy_to_user(ptr, &tr, sizeof(tr)))
    return -EFAULT;

这是一次写入读取,最后远程端一般还有再做一次应答,因此是这个步骤加起来就有8次.

那么一来一回一共就是12次。但是有趣的是,整个过程中binder的进程对应的内核缓冲区都是通过mmap映射到物理页。实际上,这仅仅只是一次从本地物理页直接拷贝到远端物理页的过程。

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

推荐阅读更多精彩内容