Android 重學(xué)系列 Binder 服務(wù)的初始化以及交互原理(上)

前言

經(jīng)過前面三篇binder驅(qū)動的初始化闡述,我大致上稍微復(fù)習(xí)一邊linux內(nèi)核的基礎(chǔ)知識,也對binder的理解更加深刻。接下來我們來看看binder 的服務(wù)是怎么注冊到service_manager。
如果遇到問題請到:http://m.itdecent.cn/p/04e53fd86ca2

正文

很多文章一開始直接放出了binder在framework中類的之間的uml圖。我先不放出,因為一開始就直接上這種圖的確不怎么好理解。等我們探索完binder 服務(wù)初始化架構(gòu)之后再放出來。

ServiceManager 注冊service

還記得我之前寫的SystemServiceManager那一章節(jié)。我稍微的提及到了服務(wù)的注冊,但是我并沒有深入源碼。這一次我連同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);

在這里為系統(tǒng)添加了windowManagerService的服務(wù)。這個服務(wù)Android應(yīng)該十分熟悉,用來控制窗口的服務(wù)。我之后會專門取出一節(jié)來詳細(xì)解析其中的代碼。

讓我們看看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);
        }
    }

看這個代表著遠(yuǎn)程的異常,我們猜測這里是不是通過某種手段進(jìn)行進(jìn)程間通信,把IBinder添加到遠(yuǎn)程端中。

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

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

首先判斷IServiceManager 這個接口對象是否存在。不存在則從從遠(yuǎn)程端獲取這個實例。

妙啊妙,我在Binder第一節(jié)就探討過進(jìn)程間通信,無一不是通過兩端通過協(xié)議進(jìn)行交流,而在這里的IPC通行居然就能抽象著兩個遠(yuǎn)程端直接繼承一個接口,進(jìn)行通信。但是還記得嗎?進(jìn)程之間的地址都有自己保護(hù),我們不可能真的抽離一個接口讓兩個進(jìn)程之間通信,只是在這個過程中把進(jìn)程間通信透明化了。

這么棒的設(shè)計,讓我們看看Google工程師是怎么設(shè)計的。
文件:/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);
}

這里可以看到一個很關(guān)鍵的類。我們可以見到在Binder 服務(wù)中十分重要的一個類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;
}

從這里我們可以的得知這是一個單例,如果在這個進(jìn)程的ProcessState沒有實例化則會實例化ProcessState。

接著我們看看這個構(gòu)造函數(shù)。

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十分相似,先調(diào)用open打開binder驅(qū)動,再進(jìn)行映射。

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 設(shè)置最大線程數(shù)。

經(jīng)過ProcessState構(gòu)造函數(shù)之后,我們也把當(dāng)前這個類已經(jīng)映射到了binder驅(qū)動中。實際上這個類我在第一篇啟動系統(tǒng)中已經(jīng)驚鴻一現(xiàn),系統(tǒng)啟動的時候,zygote進(jìn)程孵化的SystemServer進(jìn)程也會映射到binder驅(qū)動中。

細(xì)節(jié)不繼續(xù)贅述了,我們繼續(xù)看看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對象還是會自己添加一份進(jìn)去。所以一般情況下不用擔(dān)心找不到引用,至于為什么說是引用,放到之后再說。
因此,此時我們會走到b==null的分支當(dāng)中,調(diào)用了BpBinder::create(handle)的方法生成一個遠(yuǎn)程端的binder代理對象。

IPCThreadState 初始化

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

此時handle為0,會走到下面的方法:

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

此時出現(xiàn)了另一個十分關(guān)鍵的類IPCThreadState??吹竭@個調(diào)用,我們也可以知道這是一個單例設(shè)計。

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相關(guān)的操作,不妨可以代入思考。

IPCThreadState 和binder驅(qū)動交互

我們接下來看看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驅(qū)動傳遞數(shù)據(jù)最為核心的的機制。
這里我們分為兩段來討論。注意此時flags & TF_ONE_WAY (0x10)將用來控制整個流程是否是等待遠(yuǎn)程端的回應(yīng)標(biāo)志位。此時flag為而0,因此會等待。當(dāng)flag為1的時候則不等待。在這里的表現(xiàn)就是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;
//是這遠(yuǎn)程端的目標(biāo)
    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;
//設(shè)置通信的數(shù)據(jù)
    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代表著元數(shù)據(jù)的偏移量以及data_size代表著有效數(shù)據(jù)的大小。其實是在Parcel中已經(jīng)做好了管理了。在Parcel中mData對象代表著普通數(shù)據(jù),而mObjects是一個偏移數(shù)組,它保存了在數(shù)據(jù)緩沖區(qū)mData中所有Binder對象的位置。Binder會通過這個對象來尋找Binder對象。

此時需要進(jìn)去的數(shù)據(jù)是:

  • cmd:BC_TRANSACTION
  • tr : binder_transaction_data
  • code: IBinder::PING_TRANSACTION
    BC_TRANSACTION 代表此時有個事務(wù)處理,binder_transaction_data代表事務(wù)數(shù)據(jù)。

這里為mOut Parcel設(shè)置了第一段數(shù)據(jù)。

waitForResponse(reply)

我們接著看循環(huán)第二段。waitForResponse 這里把這個循環(huán)拆成兩段。

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

在這個循環(huán)里的talkWithDriver函數(shù),看到名字就知道是和binder驅(qū)動進(jìn)行溝通。

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這個結(jié)構(gòu)體。
這個結(jié)構(gòu)體的讀取部分設(shè)置了Parcel全局對象mIn,寫入部分設(shè)置Parcel全局對象mOut。當(dāng)童星結(jié)束之后將會清空全局的Parcel變量mOut以及mIn中的寫入/讀取標(biāo)記位置。

binder_transaction

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

binder_transaction 雖然之前已經(jīng)提過,但是這個方法過于長,這里我們繼續(xù)只關(guān)注我們需要的。
此時我們通過ioctl通信到Binder驅(qū)動中。我們這邊直接進(jìn)入binder驅(qū)動的對應(yīng)方法看看。
方法 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查找對應(yīng)的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;
...
//尋找事務(wù)依賴
        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.設(shè)置當(dāng)前的目標(biāo)等待隊列以及目標(biāo)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);
//為事務(wù)分配一個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查找對應(yīng)的binder引用。在這里我們的cmd是BC_TRANSCATION。因此會直接拿binder_context_mgr_node。而這個就是我在上一篇說過的service_manager的binder實體。此時我們也相當(dāng)于拿到了service_manager進(jìn)程映射的地址了。

  • 2.找到我們需要的binder實體之后,我們看看該進(jìn)程的binder_thread對應(yīng)的事務(wù)棧transaction_stack。我們需要通過這個棧去尋找,當(dāng)前分發(fā)下來的事務(wù)是否有依賴其他事務(wù)。

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

因此,binder驅(qū)動會通過一個循環(huán),直接從棧頂開始查找依賴的事務(wù)的線程(binder_transaction->from)。會去查找依賴的事務(wù)所對應(yīng)的binder_proc是不是當(dāng)前的目標(biāo)進(jìn)程。是則說明有線程也在使用這個目標(biāo)進(jìn)程,我們需要把把這個binder_thread取出來,先把依賴的事務(wù)給處理了。

  • 3.判斷此時有沒有找到目標(biāo)的binder_thread。找到則設(shè)置目標(biāo)binder_thread的todo list和等待隊列。不然則是binder_proc的todo list和等待隊列。

  • 4.申請內(nèi)存 t 和 tcomplete 事務(wù)對象binder_transaction,工作項 binder_work。并且把相關(guān)的數(shù)據(jù)如目標(biāo)進(jìn)程,目標(biāo)binder_thread,code,flag等寫入binder_transaction。并且通過binder_alloc_buf 為這一次事務(wù)在目標(biāo)進(jìn)程中申請一段binder_buffer(內(nèi)核緩沖區(qū)).

  • 5.通過copy_from_user把binder中普通數(shù)據(jù)拷貝到binder_transaction_data的data中,元數(shù)據(jù)拷貝到offsets中。

為了弄清楚這一點,我們看看binder_transaction_data數(shù)據(jù)結(jié)構(gòu)

struct binder_transaction_data {
  union {
//引用
    __u32 handle;
//對應(yīng)用戶空間binder_transaction_data 的ptr
    binder_uintptr_t ptr;
  } target;
//對應(yīng)用戶空間binder_transaction_data的cookie
  binder_uintptr_t cookie;
//對應(yīng)用戶空間binder_transaction_data的code
  __u32 code;
//對應(yīng)用戶空間binder_transaction_data的flags
  __u32 flags;
  pid_t sender_pid;
  uid_t sender_euid;
//數(shù)據(jù)大小
  binder_size_t data_size;
//偏移量大小
  binder_size_t offsets_size;
//元數(shù)據(jù)
  union {
//binder_buffer
    struct {
      binder_uintptr_t buffer;
//binder數(shù)組
      binder_uintptr_t offsets;
    } ptr;
//數(shù)據(jù)
    __u8 buf[8];
  } data;
};

此時拷貝tr->data.ptr.buffer到了binder_transaction的內(nèi)核緩沖區(qū)的數(shù)據(jù)區(qū)。tr->data.ptr.offsets拷貝到offp 地址中。而這個offp地址是這么計算的

offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
這么計算的結(jié)果是把binder_transaction的binder_buffer的末尾地址計算出來,相當(dāng)于定位了mData中Binder偏移數(shù)組的起始位置。

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

...
//獲取flat_binder_object 結(jié)構(gòu)體
        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;
//找到對應(yīng)的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;
            }
//如果引用對象剛好是目標(biāo)進(jìn)程,轉(zhuǎn)化為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;
//不是則獲取目標(biāo)進(jìn)程中的引用
                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ài),添加到目標(biāo)進(jìn)程的工作隊列
    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;

...

}

增加引用計數(shù)

在這個循環(huán)中有一個關(guān)鍵的結(jié)構(gòu)體,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中也有一個一模一樣的數(shù)據(jù)結(jié)構(gòu),這個結(jié)構(gòu)體實際上就是Binder偏移數(shù)組中的數(shù)據(jù)。很有意思,當(dāng)我們嘗試著寫內(nèi)核和用戶空間交互數(shù)據(jù)的時候,我們往往可以在兩側(cè)構(gòu)造一樣的的數(shù)據(jù)結(jié)構(gòu)再進(jìn)行copy。

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

但是這是怎么回事?為什么這樣子能夠強轉(zhuǎn)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經(jīng)常借助Parcel傳遞消息,而Binder的傳遞恰好就是調(diào)用這個方法。而在framework層換算flat_binder_object對象,也是通過普通數(shù)據(jù)+偏移量轉(zhuǎn)型為flat_binder_object并且賦值。實際上這里的內(nèi)核算法只是和用戶空間的算法保持一致。

在這個循環(huán)中,主要處理了三種flat_binder_object 對應(yīng)的5種type。首先看看flat_binder_object 是怎么獲得的。這些type

  • 1.BINDER_TYPE_BINDER/BINDER_TYPE_WEAK_BINDER
    該標(biāo)志位是在Parcel中設(shè)置,判斷如果是本地binder對象則,加入此標(biāo)志位。此時binder驅(qū)動接受到之后,說明此時是本地端發(fā)起的交互,因此此時需要獲取目標(biāo)進(jìn)程的引用,并且增加引用計數(shù),同時把狀態(tài)轉(zhuǎn)化為BINDER_TYPE_HANDLE / BINDER_TYPE_WEAK_HANDLE。

  • 2.BINDER_TYPE_HANDLE / BINDER_TYPE_WEAK_HANDLE
    該標(biāo)志位也是在Parcel中設(shè)置。判斷到此時不是binder本地而是遠(yuǎn)程端,則會加入此標(biāo)記。當(dāng)binder驅(qū)動接受到,說明此時是遠(yuǎn)程端發(fā)起的交互。需要通過handle獲取本地端的引用。當(dāng)handle獲取到的引用剛好事目標(biāo)進(jìn)程,則把標(biāo)志位設(shè)置為BINDER_TYPE_BINDER/BINDER_TYPE_WEAK_BINDER 。不是則直接從目標(biāo)進(jìn)程查找引用。

  • 3.BINDER_TYPE_FD
    該標(biāo)志控制著一個文件描述符,并且讀寫進(jìn)數(shù)據(jù),做跨進(jìn)程的數(shù)據(jù)透傳。

細(xì)節(jié):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紅黑樹查找查找,當(dāng)前的node是否有binder的引用,因為此時要做的行為是要把binder往對端的用戶空間傳遞。此時不可能做到把binder實體交給另外的進(jìn)程,因此會傳遞一個引用。
  • 2.沒有找到binder引用說明此時這個binder是新加入的,需要需要把當(dāng)前生成的新的binder_node(binder實體)添加到binder_proc的refs_by_node。
  • 3.把當(dāng)前的binder_ref的描述設(shè)置為1(如果當(dāng)前的binder的實體是service_manager則設(shè)置為0)。循環(huán)后面的引用描述(句柄),當(dāng)遇到引用(句柄)比當(dāng)前新的大則跳出,否則則新的引用是老引用+1.實際上就是就是調(diào)整整個算法。因為在整個過程是獲取refs_by_desc第二個管理binder引用的紅黑樹(一個以引用描述為key的)的后繼,比較句柄大小,找到當(dāng)前最大的那個往后加1。這個break的存在就是為了對付加入一直加的普通的binder對象,突然添加了service_manager的對象,此時我們必須要保證這個binder引用描述為0才設(shè)計。

  • 4.把引用以desc 為key添加到refs_by_desc中。這個紅黑樹更加重要,因為我們在上層的handle就是對應(yīng)這個描述符,通過這個描述符去查找對應(yīng)的引用,從而傳輸binder對象引用。

對于binder來說,是不會直接使用binder實體類,因為binder實體里包含這個其他進(jìn)程的地址,會導(dǎo)致無法訪問的情況。因此,需要一層binder引用包裹。

小總結(jié)

為什么要做這一步?對于智能指針來講,每一次減少意味這個這個binder引用可能有被析構(gòu)的可能,反過來當(dāng)我們不需要這個引用的時候,我們也可以把這個計數(shù)減少的步驟向后挪移也沒問題。所以對于這種情況來說,我們在離開本次作用域之前需,把需要傳遞的binder引用對象新增加一個引用計數(shù),來防止其被析構(gòu)。
更為關(guān)鍵的的是,在Parcel讀取的時候,會通過這個type,BINDER_TYPE_BINDER轉(zhuǎn)型為BBinder(代表本地端對象),還是BINDER_TYPE_HANDLE轉(zhuǎn)型為BpBinder(代表遠(yuǎn)程端對象)。

喚醒目標(biāo)進(jìn)程

    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ài),添加到目標(biāo)進(jìn)程的工作隊列
    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;

第一段是判斷當(dāng)前是否是BC_REPLY,這里先不去研究。當(dāng)時t->flags & TF_ONE_WAY為0的時候,則設(shè)置當(dāng)前的binder_transaction的need_reply為1,設(shè)置依賴事務(wù)為當(dāng)前的binder_thread的transaction_stack。不然則是選擇todo list為目標(biāo)binder實體的異步隊列。

最后把工作加入到目標(biāo)工作(todo)隊列。最后調(diào)用wake_up_interruptible喚醒目標(biāo)進(jìn)程。tcomplete添加到當(dāng)前線程的todo中。

回到當(dāng)前場景

當(dāng)前場景,我們發(fā)送了的mOut中實際上沒有設(shè)置任何的data數(shù)據(jù)。因此在整個binder_transaction(事務(wù)交易)中,循環(huán)offsets的那部分直接跳過,將會直接尋找目標(biāo)對象,設(shè)置好目標(biāo)binder_thread以及工作隊列還有等待隊列。事務(wù)項的type設(shè)置為BINDER_WORK_TRANSACTION。tcomplete設(shè)置BINDER_WORK_TRANSACTION_COMPLETE。

還記得,此時我們喚醒的target_wait是誰嗎?就是指service_manager。此時service_manager在binder_thread_read方法被阻塞了。我們看看binder_thread_read方法吧。喚醒的同時,ServiceManager調(diào)用者本身的進(jìn)程也在繼續(xù)進(jìn)行。我們在這里記住,我們繼續(xù)回去看看IPCThreadState的waitForResponse方法。實際上這是兩個進(jìn)程同時進(jìn)行的事務(wù)工作。

ServiceManager調(diào)用者進(jìn)程的talkWithDriver的情景分析

還記得嗎。此時talkwithDriver的doneed默認(rèn)為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;
//判斷當(dāng)前工作項的type
        switch (w->type) {
        case BINDER_WORK_TRANSACTION: {
            t = container_of(w, struct binder_transaction, work);
        } break;
...
        }

        if (!t)
            continue;
//如果對應(yīng)的目標(biāo)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 {
//為空,則設(shè)置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);
//設(shè)置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;
        }
//設(shè)置參數(shù)
        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 *));
//拷貝數(shù)據(jù)到用戶空間
        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;
    }

當(dāng)ServiceManager對應(yīng)的進(jìn)程喚醒當(dāng)前service_manager的時候,函數(shù)將會繼續(xù)往下走,此時就會進(jìn)入到這個循環(huán)。

  • 1.可以看到的是,此時會從service_manager進(jìn)程中獲取出todo列表的頭部,優(yōu)先獲取binder_thread的todo雙向隊列。這樣就能拿到我們就能拿到從ServiceManager加入進(jìn)去的工作項。
    如果binder_thread沒有則去binder_proc中獲取。如果都是空,但是判斷后面的data數(shù)據(jù)區(qū)域還有位置(地址差為4和前文為binder_buffer劃分的時候呼應(yīng)),則說明此時沒有數(shù)據(jù),就沒有必要去讀取從新進(jìn)入阻塞。

  • 2.此時再去解析命令,轉(zhuǎn)化命令從BC的驅(qū)動命令轉(zhuǎn)化RR的framework命令。此時剛好是BINDER_WORK_TRANSACTION,我們就取出里面的binder_work的binder_transaction 事務(wù)項t。注意,當(dāng)t為空的時候會while向后獲取命令,執(zhí)行命令,直到達(dá)到了底部。底部的判斷就是binder_buffer申請時候的上一個和下一個的binder_buffer差值為4.

  • 3.如果此時該事務(wù)項中的目標(biāo)進(jìn)程不為空,則把命令轉(zhuǎn)化為BR_TRANSACTION,并且把里面的數(shù)據(jù)賦值到binder_transaction_data中。

  • 4.光這些不夠,我們還需要把數(shù)據(jù)從內(nèi)核空間往用戶空間拷貝。也就是使用put_user和copy_to_user方法。由于此時正還是BR_TRANSACTION,所以把目標(biāo)線程和目標(biāo)對象設(shè)置為當(dāng)前線程。以此來宣告此時此時處理在事務(wù)的是當(dāng)前進(jìn)程。

回到service_manager的binder_parse

binder_thread_read 方法返回到用戶空間。此時進(jìn)入的binder_parse是用于解析從驅(qū)動傳遞上來的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已經(jīng)轉(zhuǎn)化為BR_TRANSACTION命令。在這個分支中出現(xiàn)了全新的一種結(jié)構(gòu)體

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

這個結(jié)構(gòu)體從名字上可以看得出實際上指代的是一個binder_buffer在ioctl交互時候調(diào)用的對象.分為兩個區(qū)域,四個地址。兩個區(qū)域分別是binder_buffer的元數(shù)據(jù)以及有效數(shù)據(jù)去。并且0為結(jié)尾的字段就是指的是元數(shù)據(jù)以及有效數(shù)據(jù)區(qū)的起始地址。得知這些之后,我們嘗試閱讀其源碼。因此在bio_init中初始化reply,bio_init_from_txn 把從內(nèi)核中傳遞上來的數(shù)據(jù)拷貝的到reply中。

而func是從binder_loop傳遞下來的方法指針是指方法svcmgr_handler,這個方法負(fù)責(zé)從驅(qū)動從底層轉(zhuǎn)化的code命令的執(zhí)行。
文件:/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里面的術(shù)語來說,只是為了ping通service_manager確定Android服務(wù)系統(tǒng)的核心是否被正確初始化。

此時(txn->flags & TF_ONE_WAY)的結(jié)果為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));
}

此時我們能看到的是,此時定義了一個新的結(jié)構(gòu)體。該結(jié)構(gòu)體相當(dāng)于之前的2個binder_read_write結(jié)構(gòu)體。不同的是,第一段的標(biāo)志位是BC_FREE_BUFFER,數(shù)據(jù)是之前通過binder_alloc_buf切割下來的結(jié)構(gòu)體地址。第二段是BC_REPLY,后面binder_transaction_data 是需要返回的數(shù)據(jù),如binder對象或者引用之類的。此時并有任何數(shù)據(jù)。接下來將進(jìn)入binder驅(qū)動中

binder 驅(qū)動處理service_manager返回的數(shù)據(jù)

此時的流程和之前的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;
        }

此時我能夠發(fā)現(xiàn)到此時釋放當(dāng)前的使用的binder_buffer實際上就是把數(shù)據(jù)清空后,使用內(nèi)核緩沖區(qū),再回歸到空閑內(nèi)核緩沖區(qū),并且減少對應(yīng)binder對象的引用計數(shù)。

第二段下傳下來的數(shù)據(jù)的命令是BC_REPLY,此時把需要返回的數(shù)據(jù)添加到事務(wù)棧中。

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進(jìn)行聯(lián)動。還記得在上方,結(jié)束BC_TRANSACTION命令時候的添加的行為不?

  • 1.從當(dāng)前的事務(wù)棧拿出來的事務(wù)項,就是從之前ServiceManager調(diào)用者進(jìn)程中的生成加在transaction_stack的頂部。
  • 2.此時,我們能夠這個事務(wù)項獲取到目標(biāo)進(jìn)程的對象binder_proc。下面的邏輯就就和上面一樣,不一樣的地方僅僅只是調(diào)用的方向逆轉(zhuǎn)過來。
    1. binder_pop_transaction 最后再把這個從之前進(jìn)程挪移來的事務(wù)彈出,并且釋放內(nèi)存。
    1. 最后再添加一個事務(wù)工作(binder_transaction)type為BINDER_WORK_TRANSACTION,一個事務(wù)工作(binder_work)為BINDER_WORK_TRANSACTION_COMPLETE.喚醒ServiceManager調(diào)用者進(jìn)程。

此時兩個進(jìn)程如上方一樣同時進(jìn)行,service_manager將會直接進(jìn)入binder_thread_read阻塞休眠。

ServiceManager調(diào)用者進(jìn)程的喚醒

當(dāng)ServiceManager調(diào)用進(jìn)程復(fù)蘇之后,將會進(jìn)入到binder_thread_read的cmd解析循環(huán)中。

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

還記得嗎?此時我們在第一次調(diào)用BC_TRANSACTION的時候會生成一個BINDER_WORK_TRANSACTION_COMPLETE添加到本進(jìn)程線程對應(yīng)的事務(wù)棧中,在BC_REPLY的時候又添加了BINDER_WORK_TRANSACTION事務(wù)棧。

因此此時做的事情很簡單,就是通過循環(huán)執(zhí)行這兩個事務(wù)。并且回收掉之前binder_work的申請的內(nèi)存。此時做的事情就是為了告訴binder驅(qū)動binder服務(wù)已經(jīng)響應(yīng)回原來的進(jìn)程了。

此時你可以看到當(dāng)service_manager寫入BC_REPLY的時候,對應(yīng)的binder_transaction的reply參數(shù)不為0,此時并不設(shè)置target_node,因此在這里讀取的時候,會把cmd從驅(qū)動命令換成framework命令。傳回上層。

此時read已經(jīng)結(jié)束.總結(jié)一下我們能夠發(fā)現(xiàn)的時候,在binder驅(qū)動中以BINDER開頭的命令將會在binder_thread_read中處理,以BC開頭的命令會在binder_thread_write中處理,RR開頭的命令將會在service_manager/IPCThreadState中處理。

ServiceManager進(jìn)程調(diào)用waitForResponse第二段

此時binder驅(qū)動已經(jīng)完成了所有事務(wù),讓我們回到頂層吧。
文件:/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)結(jié)果為0.此時把Parcel reply字段封裝從驅(qū)動穿上來的數(shù)據(jù)。此時我們略過中間的步驟,看到service_manager進(jìn)程中實際上設(shè)置binder數(shù)據(jù)的時候,實際上因為判斷到是PING的命令,已經(jīng)返回了。此時并沒有任何數(shù)據(jù)在Parcel里面。

回到ProcessState

由于嵌套過深,為了避免邏輯斷鏈,我實際上一直在研究這部分代碼:

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

此時,我們可以得到一個結(jié)論,當(dāng)發(fā)送code的時候,控制的是service_manager那一塊的邏輯。而PING_TRANSACTION什么事情都沒做,僅僅只是為了ping 通service_manager進(jìn)程。但是在這個流程中我們卻摸透整個binder在傳輸時候的設(shè)計模型。

總結(jié)

整個Android的service Binder架構(gòu)嵌套十分的深,涉及面十分廣。因此本文就停筆總結(jié),讓我們歸納一遍Android系統(tǒng)中的Binder事務(wù)流程。

經(jīng)過這一次,我們能夠繪制出比上一篇更加詳細(xì)的binder傳輸數(shù)據(jù)的封包,當(dāng)頂層往下層傳輸命令時候


binder傳輸數(shù)據(jù)的封包.png

特別的,在Android整個協(xié)議中BC開頭的命令時用來控制binder驅(qū)動寫的行為,BINDER開頭的命令用來控制binder驅(qū)動讀時候的行為,BR開頭的命令用來控制路由器接受端和發(fā)送端的解析行為,這里是指service_manager和IPCThreadState。中間的code控制service_manager和IPCThreadState的具體動作行為。

再來總結(jié)一份Binder傳輸時候的時序圖


binder數(shù)據(jù)交互時序圖.png

注意這里紅色代表著跨越了進(jìn)程。

光是時序圖還是沒辦法看到細(xì)節(jié),進(jìn)一步的整個進(jìn)程間通信模型
我們把模型分為三部分去看,第一部分是從ServiceManager的調(diào)用端發(fā)送數(shù)據(jù),service_manager進(jìn)程獲取數(shù)據(jù)。


發(fā)送數(shù)據(jù).png

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


返回消息在清除binder_transaction_stack.png

第三部分:


binder傳輸原理.png

這只是大致的原理圖,實際上binder_transaction存在的意義就是為了查找依賴棧,以及當(dāng)每一次加入一個新的事務(wù)時候,把當(dāng)前要處理的事務(wù)放在棧頂。當(dāng)需要恢復(fù)回復(fù)信息的時候,以此為依據(jù)來找到原來發(fā)送端。上面那個看起來像是棧,實際上只是一個todo鏈表。

以上就是整個Binder在進(jìn)程間通信的流程。這里就是它巧妙的地方,通過兩個不同進(jìn)程的事務(wù)棧在傳遞事務(wù),從而達(dá)到數(shù)據(jù)傳遞的過程。同時通過tcomplete來結(jié)束讀取的行為。而上面留下的tcomplete將會在下次讀取數(shù)據(jù)的數(shù)據(jù)刪除。這就是我在上面說的,引用刪除可以留到下次讀取刪除,但是添加引用計數(shù)必須立即添加,不然會被智能指針析構(gòu)掉同一道理。

Binder傳輸過程中,內(nèi)存拷貝次數(shù)

最后再來計算一次,在整個通信過程中,一共做了幾次虛擬內(nèi)存中的數(shù)據(jù)拷貝,一次完整的通信過程中必定包含本地端的寫入以及遠(yuǎn)程端的讀取,都是在binder_ioctl_write_read方法中進(jìn)行數(shù)據(jù)的拷貝,一共12次:

  • 第一次:本地端寫入數(shù)據(jù)到內(nèi)核,遠(yuǎn)程端寫回數(shù)據(jù)進(jìn)行應(yīng)答:
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
    ret = -EFAULT;
    goto out;
}
  • 第二次遠(yuǎn)程端讀取數(shù)據(jù)處理,本地段讀取遠(yuǎn)程端寫回的數(shù)據(jù):
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
    ret = -EFAULT;
    goto out;
}

這里加起來就有四次。

  • 第三次,第四次,第五次:方法binder_thread_write,執(zhí)行binder_transaction準(zhǔn)備寫入到事務(wù)隊列中,讀取寫入到數(shù)據(jù)中的binder_transaction_data結(jié)構(gòu)體進(jìn)行一次拷貝,再對binder_transaction_data中中的元數(shù)據(jù)大小和偏移量大小數(shù)據(jù)進(jìn)行拷貝,最后再轉(zhuǎn)移到目標(biāo)進(jìn)程binder緩沖區(qū)事務(wù)隊列:
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中解析事務(wù)隊列中的內(nèi)容,拷貝到遠(yuǎn)程端:
if (copy_to_user(ptr, &tr, sizeof(tr)))
    return -EFAULT;

這是一次寫入讀取,最后遠(yuǎn)程端一般還有再做一次應(yīng)答,因此是這個步驟加起來就有8次.

那么一來一回一共就是12次。但是有趣的是,整個過程中binder的進(jìn)程對應(yīng)的內(nèi)核緩沖區(qū)都是通過mmap映射到物理頁。實際上,這僅僅只是一次從本地物理頁直接拷貝到遠(yuǎn)端物理頁的過程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容