聽說你Binder機制學的不錯懒震,來面試下這幾個問題(二)

本篇是第二篇,主要是涉及線程與進程的喚醒岂贩,數(shù)據(jù)傳輸?shù)姆庋b與解析茫经,

  • Binder線程的睡眠與喚醒(請求線程睡在哪個等待隊列上,喚醒目標端哪個隊列上的線程)
  • Binder協(xié)議中BC與BR的區(qū)別
  • Binder在傳輸數(shù)據(jù)的時候是如何層層封裝的--不同層次使用的數(shù)據(jù)結(jié)構(gòu)(命令的封裝)
  • Binder驅(qū)動傳遞數(shù)據(jù)的釋放(釋放時機)
  • 一個簡單的Binder通信C/S模型

聽說你Binder機制學的不錯河闰,來解決下這幾個問題(一)
聽說你Binder機制學的不錯科平,來看看這幾個問題(二)
聽說你Binder機制學的不錯,來看看這幾個問題(三)

Client端線程睡眠在哪個隊列上姜性,喚醒Server端哪個等待隊列上的線程

先看第一部分:發(fā)送端線程睡眠在哪個隊列上瞪慧?

發(fā)送端線程一定睡眠在自己binder_thread的等待隊列上,并且部念,該隊列上有且只有自己一個睡眠線程

再看第二部分:在Binder驅(qū)動去喚醒線程的時候弃酌,喚醒的是哪個等待隊列上的線程?

理解這個問題需要理解binder_thread中的 struct binder_transaction * transaction_stack棧儡炼,這個棧規(guī)定了transaction的執(zhí)行順序:棧頂?shù)囊欢ㄏ扔跅?nèi)執(zhí)行妓湘。

如果本地操作是BC_REPLY,一定是喚醒之前發(fā)送等待的線程,這個是100%的乌询,但是如果是BC_TRANSACTION榜贴,那就不一定了,尤其是當兩端互為服務(wù)相互請求的時候,場景如下:

  • 進程A的普通線程AT1請求B進程的B1服務(wù)妹田,喚醒B進程的Binder線程唬党,AT1睡眠等待服務(wù)結(jié)束
  • B進程的B1服務(wù)在執(zhí)行的的時候鹃共,需要請求進程A的A1服務(wù),則B進程的Binder線程BT1睡眠驶拱,等待服務(wù)結(jié)束霜浴。

這個時候就會遇到一個問題:喚醒哪個線程比較合適?是睡眠在進程隊列上的線程蓝纲,還是之前睡眠的線程AT1阴孟?答案是:之前睡眠等待B服務(wù)返回的線程AT1,具體看下面的圖解分析

首先第一步A普通線程去請求B進程的B1服務(wù)税迷,這個時候在A進程的AT1線程的binder_ref中會將binder_transaction1入棧永丝,而同樣B的Binder線程在讀取binder_work之后,也會將binder_transaction1加入自己的堆棧翁狐,如下圖:

binder_transaction堆棧及喚醒那個隊列1.jpg

而當B的Binder線程被喚醒后类溢,執(zhí)行Binder實體中的服務(wù)時,發(fā)現(xiàn)服務(wù)函數(shù)需要反過來去請求A端的A1服務(wù)露懒,那就需要通過Binder向A進程發(fā)送請求闯冷,并新建binder_transaction2壓入自己的binder_transaction堆棧,這個沒有任何問題懈词。但是蛇耀,在A端入棧的時候,會面臨一個抉擇坎弯,寫入那個隊列纺涤?是binder_proc上的隊列,還是正在等候B1服務(wù)返回的AT1線程的隊列抠忘?

binder_transaction堆棧及喚醒那個隊列2.jpg

結(jié)果已經(jīng)說過撩炊,是AT1的隊列,為什么呢崎脉?因為AT1隊列上的之前的binder_transaction1在等待B進程執(zhí)行完拧咳,但是B端執(zhí)行binder_transaction1時候,需要等待binder_transaction2執(zhí)行完囚灼,也就是說骆膝,在binder_transaction2執(zhí)行完畢前,A端的binder_transaction1一定是不會被執(zhí)行的灶体,也就是線程AT1在B執(zhí)行binder_transaction2的時候阅签,一定是空閑的,那么蝎抽,不妨喚醒AT1線程政钟,讓它幫忙執(zhí)行完binder_transaction2,執(zhí)行完之后,AT1又會睡眠等待B端返回养交,這樣衷戈,既不妨礙binder_transaction1的執(zhí)行,同樣也能提高AT1線程利用率层坠,出棧的過程其實就簡單了,

  • AT1 執(zhí)行binder_transaction2刁笙,喚醒B端BT1 Binder線程破花,并且AT1繼續(xù)睡眠(因為還有等待的transaction)
  • BT1 處理binder_transaction2結(jié)果,并執(zhí)行完binder_transaction1疲吸,喚醒AT1
  • AT1處理binder_transaction1返回結(jié)果 執(zhí)行結(jié)束

不妨再深入一點座每,如果A端binder_transaction2又需要B進程B2服務(wù),這個時候是什么效果喚醒誰摘悴,答案是BT1峭梳,這就杜絕了兩端循環(huán)請求的,不斷增加線程池容量蹂喻。

binder_transaction堆棧及喚醒那個隊列3.jpg

從這里可以看出葱椭,Binder其實設(shè)計的還是很巧妙的,讓線程復(fù)用口四,提高了效率孵运,還避免了新建不必要的Binder線程,這段優(yōu)化在binder驅(qū)動實現(xiàn)代碼如下:其實就是根據(jù)binder_transaction記錄蔓彩,處理入棧喚醒問題

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
    {..
        while (tmp) {
                    // 找到對方正在等待自己進程的線程治笨,如果線程沒有在等待自己進程的返回,就不要找了
        
                    // 判斷是不target_proc中赤嚼,是不是有線程旷赖,等待當前線程
                    // thread->transaction_stack,這個時候更卒,
                    // 是binder線程的等孵,不是普通線程 B去請求A服務(wù),
                    // 在A服務(wù)的時候逞壁,又請求了B流济,這個時候,A的服務(wù)一定要等B處理完腌闯,才能再返回B绳瘟,可以放心用B
                        if (tmp->from && tmp->from->proc == target_proc)
                            target_thread = tmp->from;
                        tmp = tmp->from_parent;
          ...           }
        } }

Binder協(xié)議中BC與BR的區(qū)別

BC與BR主要是標志數(shù)據(jù)及Transaction流向,其中BC是從用戶空間流向內(nèi)核姿骏,而BR是從內(nèi)核流線用戶空間糖声,比如Client向Server發(fā)送請求的時候,用的是BC_TRANSACTION,當數(shù)據(jù)被寫入到目標進程后蘸泻,target_proc所在的進程被喚醒琉苇,在內(nèi)核空間中,會將BC轉(zhuǎn)換為BR悦施,并將數(shù)據(jù)與操作傳遞該用戶空間并扇。

BR與BC區(qū)別

Binder在傳輸數(shù)據(jù)的時候是如何層層封裝的--不同層次使用的數(shù)據(jù)結(jié)構(gòu)(命令的封裝)

內(nèi)核中,與用戶空間對應(yīng)的結(jié)構(gòu)體對象都需要新建抡诞,但傳輸數(shù)據(jù)的數(shù)據(jù)只拷貝一次穷蛹,就是一次拷貝的時候。

從Client端請求開始分析昼汗,暫不考慮java層肴熏,只考慮Native,以ServiceManager的addService為例顷窒,具體看一下

MediaPlayerService::instantiate();

MediaPlayerService會新建Binder實體蛙吏,并將其注冊到ServiceManager中:

void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
            String16("media.player"), new MediaPlayerService());
}   

這里defaultServiceManager其實就是獲取ServiceManager的遠程代理:

sp<IServiceManager> defaultServiceManager()
{
    if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
    
    {
        AutoMutex _l(gDefaultServiceManagerLock);
        if (gDefaultServiceManager == NULL) {
            gDefaultServiceManager = interface_cast<IServiceManager>(
                ProcessState::self()->getContextObject(NULL));
        }
    }
    
    return gDefaultServiceManager;
}

如果將代碼簡化其實就是

return gDefaultServiceManager = BpServiceManager (new BpBinder(0));

addService就是調(diào)用BpServiceManager的addService,

virtual status_t addService(const String16& name, const sp<IBinder>& service,
        bool allowIsolated)
{
    Parcel data, reply;
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
    data.writeString16(name);
    data.writeStrongBinder(service);
    data.writeInt32(allowIsolated ? 1 : 0);
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
    return err == NO_ERROR ? reply.readExceptionCode() : err;
}

這里會開始第一步的封裝鞋吉,數(shù)據(jù)封裝鸦做,其實就是講具體的傳輸數(shù)據(jù)寫入到Parcel對象中,與Parcel對應(yīng)是ADD_SERVICE_TRANSACTION等具體操作坯辩。比較需要注意的就是data.writeStrongBinder馁龟,這里其實就是把Binder實體壓扁:

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

具體做法就是轉(zhuǎn)換成flat_binder_object,以傳遞Binder的類型漆魔、指針之類的信息:

status_t flatten_binder(const sp<ProcessState>& proc,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;
    
    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.handle = handle;
            obj.cookie = NULL;
        } else {
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = local->getWeakRefs();
            obj.cookie = local;
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = NULL;
        obj.cookie = NULL;
    }
    
    return finish_flatten_binder(binder, obj, out);
}

接下來看 remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); 在上面的環(huán)境中坷檩,remote()函數(shù)返回的就是BpBinder(0),

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

    return DEAD_OBJECT;
}

之后通過 IPCThreadState::self()->transact( mHandle, code, data, reply, flags)進行進一步封裝:

status_t IPCThreadState::transact(int32_t handle,
                uint32_t code, const Parcel& data,
                Parcel* reply, uint32_t flags){
    if ((flags & TF_ONE_WAY) == 0) {
        if (err == NO_ERROR) {
            err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
        }
        if (reply) {
            err = waitForResponse(reply);
        } 
        ..
    return err;
    }

writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);是進一步封裝的入口改抡,在這個函數(shù)中Parcel& data矢炼、handle、code阿纤、被進一步封裝成binder_transaction_data對象句灌,并拷貝到mOut的data中去,同時也會將BC_TRANSACTION命令也寫入mOut欠拾,這里與binder_transaction_data對應(yīng)的CMD是BC_TRANSACTION胰锌,binder_transaction_data也存儲了數(shù)據(jù)的指引新信息:

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.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(size_t);
        tr.data.ptr.offsets = data.ipcObjects();
    } ..
    mOut.writeInt32(cmd);
    mOut.write(&tr, sizeof(tr));
    return NO_ERROR;
}

mOut封裝結(jié)束后,會通過waitForResponse調(diào)用talkWithDriver繼續(xù)封裝:

status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    binder_write_read bwr;
    // Is the read buffer empty? 這里會有同時返回兩個命令的情況 BR_NOOP藐窄、BR_COMPLETE
    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 = (long unsigned int)mOut.data();      // This is what we'll read.
    if (doReceive && needRead) {
        bwr.read_size = mIn.dataCapacity();
        bwr.read_buffer = (long unsigned int)mIn.data();
    } else {
        bwr.read_size = 0;
        bwr.read_buffer = 0;
    }
    // Return immediately if there is nothing to do.
    if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
    bwr.write_consumed = 0;
    bwr.read_consumed = 0;
    status_t err;
    do {
        资昧。。
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR荆忍;
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
    } while (err == -EINTR);

    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            if (bwr.write_consumed < (ssize_t)mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        return NO_ERROR;
    }
    return err;
}

talkWithDriver會將mOut中的數(shù)據(jù)與命令繼續(xù)封裝成binder_write_read對象格带,其中bwr.write_buffer就是mOut中的data(binder_transaction_data+BC_TRRANSACTION)撤缴,之后就會通過ioctl與binder驅(qū)動交互,進入內(nèi)核叽唱,這里與binder_write_read對象對應(yīng)的CMD是BINDER_WRITE_READ屈呕,進入驅(qū)動后,是先寫后讀的順序棺亭,所以才叫BINDER_WRITE_READ命令虎眨,與BINDER_WRITE_READ層級對應(yīng)的幾個命令碼一般都是跟線程、進程镶摘、數(shù)據(jù)整體傳輸相關(guān)的操作专甩,不涉及具體的業(yè)務(wù)處理,比如BINDER_SET_CONTEXT_MGR是將線程編程ServiceManager線程,并創(chuàng)建0號Handle對應(yīng)的binder_node钉稍、BINDER_SET_MAX_THREADS是設(shè)置最大的非主Binder線程數(shù),而BINDER_WRITE_READ就是表示這是一次讀寫操作:

#define BINDER_CURRENT_PROTOCOL_VERSION 7
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int)
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)
#define BINDER_THREAD_EXIT _IOW('b', 8, int)
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)

詳細看一下binder_ioctl對于BINDER_WRITE_READ的處理棺耍,

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    switch (cmd) {
    case BINDER_WRITE_READ: {
        struct binder_write_read bwr;
        ..
        <!--拷貝binder_write_read對象到內(nèi)核空間-->
        if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
            ret = -EFAULT;
            goto err;
        }
        <!--根據(jù)是否需要寫數(shù)據(jù)處理是不是要寫到目標進程中去-->
        if (bwr.write_size > 0) {
            ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
        }
      <!--根據(jù)是否需要寫數(shù)據(jù)處理是不是要讀贡未,往自己進程里讀數(shù)據(jù)-->
        if (bwr.read_size > 0) {
            ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
            <!--是不是要同時喚醒進程上的阻塞隊列-->
            if (!list_empty(&proc->todo))
                wake_up_interruptible(&proc->wait);
        }
        break;
    }
    case BINDER_SET_MAX_THREADS:
        if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
        }
        break;
    case BINDER_SET_CONTEXT_MGR:
       .. break;
    case BINDER_THREAD_EXIT:
        binder_free_thread(proc, thread);
        thread = NULL;
        break;
    case BINDER_VERSION:
    ..
}

binder_thread_write(proc, thread, (void __user )bwr.write_buffer, bwr.write_size, &bwr.write_consumed)這里其實就是把解析的binder_write_read對象再剝離,bwr.write_buffer* 就是上面的(BC_TRANSACTION+ binder_transaction_data)蒙袍,

int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
            void __user *buffer, int size, signed long *consumed)
{
    uint32_t cmd;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;
    while (ptr < end && thread->return_error == BR_OK) {

        // binder_transaction_data  BC_XXX+binder_transaction_data
        if (get_user(cmd, (uint32_t __user *)ptr))  (BC_TRANSACTION)
            return -EFAULT;
        ptr += sizeof(uint32_t);
        switch (cmd) {
        ..
        case BC_FREE_BUFFER: {
            ...
        }
        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;
        }
        case BC_REGISTER_LOOPER:
            ..
        case BC_ENTER_LOOPER:
            ...
            thread->looper |= BINDER_LOOPER_STATE_ENTERED;
            break;
        case BC_EXIT_LOOPER:
        // 這里會修改讀取的數(shù)據(jù)俊卤,
        *consumed = ptr - buffer;
    }
    return 0;
}

binder_thread_write會進一步根據(jù)CMD剝離出binder_transaction_data tr,交給binder_transaction處理害幅,其實到binder_transaction數(shù)據(jù)幾乎已經(jīng)剝離極限消恍,剩下的都是業(yè)務(wù)相關(guān)的,但是這里牽扯到一個Binder實體與Handle的轉(zhuǎn)換過程以现,同城也牽扯兩個進程在內(nèi)核空間共享一些數(shù)據(jù)的問題狠怨,因此這里又進行了一次進一步的封裝與拆封裝,這里新封裝了連個對象 binder_transaction與binder_work邑遏,有所區(qū)別的是binder_work可以看做是進程私有佣赖,但是binder_transaction是兩個交互的進程共享的:binder_work是插入到線程或者進程的work todo隊列上去的:

struct binder_thread {
    struct binder_proc *proc;
    struct rb_node rb_node;
    int pid;
    int looper;
    struct binder_transaction *transaction_stack;
    struct list_head todo;
    uint32_t return_error; /* Write failed, return error code in read buf */
    uint32_t return_error2; /* Write failed, return error code in read */
    wait_queue_head_t wait;
    struct binder_stats stats;
};

這里主要關(guān)心一下binder_transaction:binder_transaction主要記錄了當前transaction的來源,去向记盒,同時也為了返回做準備憎蛤,buffer字段是一次拷貝后數(shù)據(jù)在Binder的內(nèi)存地址。

struct binder_transaction {
    int debug_id;
    struct binder_work work;
    struct binder_thread *from; 
    struct binder_transaction *from_parent;
    struct binder_proc *to_proc;
    struct binder_thread *to_thread;
    struct binder_transaction *to_parent;
    unsigned need_reply:1;
    /* unsigned is_dead:1; */   /* not used at the moment */
    struct binder_buffer *buffer;
    unsigned int    code;
    unsigned int    flags;
    long    priority;
    long    saved_priority;
    uid_t   sender_euid;
};

binder_transaction函數(shù)主要負責的工作:

  • 新建binder_transaction對象纪吮,并插入到自己的binder_transaction堆棧中

  • 新建binder_work對象俩檬,插入到目標隊列

  • Binder與Handle的轉(zhuǎn)換 (flat_binder_object)

      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;
          size_t *offp, *off_end;
          struct binder_proc *target_proc;
          struct binder_thread *target_thread = NULL;
          struct binder_node *target_node = NULL;
       **關(guān)鍵點1** 
      if (reply) {
          in_reply_to = thread->transaction_stack;
          thread->transaction_stack = in_reply_to->to_parent;
          target_thread = in_reply_to->from;
          target_proc = target_thread->proc;
          }else {
          if (tr->target.handle) {
              struct binder_ref * ref;
                  ref = binder_get_ref(proc, tr->target.handle);
                  target_node = ref->node;
              } else {
                  target_node = binder_context_mgr_node;
              }
            ..。
      **關(guān)鍵點2**
       t = kzalloc(sizeof( * t), GFP_KERNEL); 
       ...
       tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
       
     **關(guān)鍵點3 **
      off_end = (void *)offp + tr->offsets_size;
      
      for (; offp < off_end; offp++) {
          struct flat_binder_object *fp;
          fp = (struct flat_binder_object *)(t->buffer->data + *offp);
          switch (fp->type) {
          case BINDER_TYPE_BINDER:
          case BINDER_TYPE_WEAK_BINDER: {
              struct binder_ref *ref;
              struct binder_node *node = binder_get_node(proc, fp->binder);
              if (node == NULL) {
                  node = binder_new_node(proc, fp->binder, fp->cookie);
              }..
              ref = (target_proc, node);                 if (fp->type == BINDER_TYPE_BINDER)
                  fp->type = BINDER_TYPE_HANDLE;
              else
                  fp->type = BINDER_TYPE_WEAK_HANDLE;
              fp->handle = ref->desc;
          } break;
          case BINDER_TYPE_HANDLE:
          case BINDER_TYPE_WEAK_HANDLE: {
              struct binder_ref *ref = binder_get_ref(proc, fp->handle);
              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;
              } else {
                  struct binder_ref *new_ref;
                  new_ref = binder_get_ref_for_node(target_proc, ref->node);
                  fp->handle = new_ref->desc;
              }
          } break;
          
     **關(guān)鍵點4** 將binder_work 插入到目標隊列
     
      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;
    

    }

關(guān)鍵點1碾盟,找到目標進程棚辽,關(guān)鍵點2 創(chuàng)建binder_transaction與binder_work,關(guān)鍵點3 處理Binder實體與Handle轉(zhuǎn)化巷疼,關(guān)鍵點4晚胡,將binder_work插入目標隊列灵奖,并喚醒相應(yīng)的等待隊列,在處理Binder實體與Handle轉(zhuǎn)化的時候估盘,有下面幾點注意的:

  • 第一次注冊Binder實體的時候瓷患,是向別的進程注冊的,ServiceManager遣妥,或者SystemServer中的AMS服務(wù)
  • Client請求服務(wù)的時候擅编,一定是由Binder驅(qū)動為Client分配binder_ref,如果本進程的線程請求箫踩,fp->type = BINDER_TYPE_BINDER爱态,否則就是fp->type = BINDER_TYPE_HANDLE。
  • Android中的Parcel里面的對象一定是flat_binder_object

如此下來境钟,寫數(shù)據(jù)的流程所經(jīng)歷的數(shù)據(jù)結(jié)構(gòu)就完了锦担。再簡單看一下被喚醒一方的讀取流程,讀取從阻塞在內(nèi)核態(tài)的binder_thread_read開始慨削,以傳遞而來的BC_TRANSACTION為例洞渔,binder_thread_read會根據(jù)一些場景添加BRXXX參數(shù),標識驅(qū)動傳給用戶空間的數(shù)據(jù)流向:

enum BinderDriverReturnProtocol {

 BR_ERROR = _IOR_BAD('r', 0, int),
 BR_OK = _IO('r', 1),
 BR_TRANSACTION = _IOR_BAD('r', 2, struct binder_transaction_data),
 BR_REPLY = _IOR_BAD('r', 3, struct binder_transaction_data),

 BR_ACQUIRE_RESULT = _IOR_BAD('r', 4, int),
 BR_DEAD_REPLY = _IO('r', 5),
 BR_TRANSACTION_COMPLETE = _IO('r', 6),
 BR_INCREFS = _IOR_BAD('r', 7, struct binder_ptr_cookie),

 BR_ACQUIRE = _IOR_BAD('r', 8, struct binder_ptr_cookie),
 BR_RELEASE = _IOR_BAD('r', 9, struct binder_ptr_cookie),
 BR_DECREFS = _IOR_BAD('r', 10, struct binder_ptr_cookie),
 BR_ATTEMPT_ACQUIRE = _IOR_BAD('r', 11, struct binder_pri_ptr_cookie),

 BR_NOOP = _IO('r', 12),
 BR_SPAWN_LOOPER = _IO('r', 13),
 BR_FINISHED = _IO('r', 14),
 BR_DEAD_BINDER = _IOR_BAD('r', 15, void *),

 BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR_BAD('r', 16, void *),
 BR_FAILED_REPLY = _IO('r', 17),
};

之后缚态,read線程根據(jù)binder_transaction新建binder_transaction_data對象磁椒,再通過copy_to_user,傳遞給用戶空間玫芦,

static int
binder_thread_read(struct binder_proc *proc, struct binder_thread *thread,
    void  __user *buffer, int size, signed long *consumed, int non_block)
{
    while (1) {
            uint32_t cmd;
         struct binder_transaction_data tr ;
            struct binder_work *w;
            struct binder_transaction *t = NULL;

        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 {
            if (ptr - buffer == 4 && !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN)) /* no data added */
                goto retry;
            break;
        }
        
    // 數(shù)據(jù)大小
        tr.data_size = t->buffer->data_size;
        tr.offsets_size = t->buffer->offsets_size;
    // 偏移地址要加上
        tr.data.ptr.buffer = (void *)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;
        // 寫數(shù)據(jù)結(jié)構(gòu)體到用戶空間浆熔,
        ptr += sizeof(uint32_t);
        if (copy_to_user(ptr, &tr, sizeof(tr)))
            return -EFAULT;
        ptr += sizeof(tr);
}

上層通過ioctrl等待的函數(shù)被喚醒,假設(shè)現(xiàn)在被喚醒的是服務(wù)端,一般會執(zhí)行請求,這里首先通過Parcel的ipcSetDataReference函數(shù)將數(shù)據(jù)將數(shù)據(jù)映射到Parcel對象中桥帆,之后再通過BBinder的transact函數(shù)處理具體需求医增;

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    ...
    // read到了數(shù)據(jù)請求,這里是需要處理的邏輯 老虫,處理完畢调窍,
    case BR_TRANSACTION:
        {
            binder_transaction_data tr;
            Parcel buffer;
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(size_t), freeBuffer, this);
     ...
 // 這里是處理 如果非空,就是數(shù)據(jù)有效张遭,
    if (tr.target.ptr) {
        // 這里什么是tr.cookie
        sp<BBinder> b((BBinder*)tr.cookie);
        const status_t error = b->transact(tr.code, buffer, &reply, tr.flags);
        if (error < NO_ERROR) reply.setError(error);

    }   

這里的 b->transact(tr.code, buffer, &reply, tr.flags);就同一開始Client調(diào)用transact( mHandle, code, data, reply, flags)函數(shù)對應(yīng)的處理類似邓萨,進入相對應(yīng)的業(yè)務(wù)邏輯。

Binder在傳輸數(shù)據(jù)的時候是如何層層封裝的--不同層次使用的數(shù)據(jù)結(jié)構(gòu)(命令的封裝.jpg

Binder驅(qū)動傳遞數(shù)據(jù)的釋放(釋放時機)

在Binder通信的過程中菊卷,數(shù)據(jù)是從發(fā)起通信進程的用戶空間直接寫到目標進程內(nèi)核空間缔恳,而這部分數(shù)據(jù)是直接映射到用戶空間,必須等用戶空間使用完數(shù)據(jù)才能釋放洁闰,也就是說Binder通信中內(nèi)核數(shù)據(jù)的釋放時機應(yīng)該是用戶空間控制的歉甚,內(nèi)種中釋放內(nèi)存空間的函數(shù)是binder_free_buf,其他的數(shù)據(jù)結(jié)構(gòu)其實可以直接釋放掉扑眉,執(zhí)行這個函數(shù)的命令是BC_FREE_BUFFER纸泄。上層用戶空間常用的入口是IPCThreadState::freeBuffer:

void IPCThreadState::freeBuffer(Parcel* parcel, const uint8_t* data, size_t dataSize,
                                const size_t* objects, size_t objectsSize,
                                void* cookie)
{
    if (parcel != NULL) parcel->closeFileDescriptors();
    IPCThreadState* state = self();
    state->mOut.writeInt32(BC_FREE_BUFFER);
    state->mOut.writeInt32((int32_t)data);
}

那什么時候會調(diào)用這個函數(shù)呢赖钞?在之前分析數(shù)據(jù)傳遞的時候,有一步是將binder_transaction_data中的數(shù)據(jù)映射到Parcel中去聘裁,其實這里是關(guān)鍵

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

    while (1) {
    ...
        case BR_REPLY:
            {
            binder_transaction_data tr;
            // 注意這里是沒有傳輸數(shù)據(jù)拷貝的雪营,只有一個指針跟數(shù)據(jù)結(jié)構(gòu)的拷貝,
            err = mIn.read(&tr, sizeof(tr));
            ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
            if (err != NO_ERROR) goto finish;
            // free buffer衡便,先設(shè)置數(shù)據(jù)献起,直接
            if (reply) {
                if ((tr.flags & TF_STATUS_CODE) == 0) {
                    // 牽扯到數(shù)據(jù)利用,與內(nèi)存釋放
                    reply->ipcSetDataReference(
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(size_t),
                        freeBuffer, this);

Parcel 的ipcSetDataReference函數(shù)不僅僅能講數(shù)據(jù)映射到Parcel對象镣陕,同時還能將數(shù)據(jù)的清理函數(shù)映射進來

void Parcel::ipcSetDataReference(const uint8_t* data, size_t dataSize,
    const size_t* objects, size_t objectsCount, release_func relFunc, void* relCookie)

看函數(shù)定義中的release_func relFunc參數(shù)谴餐,這里就是指定內(nèi)存釋放函數(shù),這里指定了IPCThreadState::freeBuffer函數(shù)呆抑,在Native層岂嗓,Parcel在使用完,并走完自己的生命周期后鹊碍,就會調(diào)用自己的析構(gòu)函數(shù)摄闸,在其析構(gòu)函數(shù)中調(diào)用了freeDataNoInit(),這個函數(shù)會間接調(diào)用上面設(shè)置的內(nèi)存釋放函數(shù):

Parcel::~Parcel()
{
    freeDataNoInit();
}

這就是數(shù)據(jù)釋放的入口妹萨,進入內(nèi)核空間后,執(zhí)行binder_free_buf炫欺,將這次分配的內(nèi)存釋放乎完,同時更新binder_proc的binder_buffer表,重新標記那些內(nèi)存塊被使用了品洛,哪些沒被使用树姨。

static void binder_free_buf(struct binder_proc *proc,
                struct binder_buffer *buffer)
{
    size_t size, buffer_size;
    buffer_size = binder_buffer_size(proc, buffer);
    size = ALIGN(buffer->data_size, sizeof(void *)) +
        ALIGN(buffer->offsets_size, sizeof(void *));
    binder_debug(BINDER_DEBUG_BUFFER_ALLOC,
             "binder: %d: binder_free_buf %p size %zd buffer"
             "_size %zd\n", proc->pid, buffer, size, buffer_size);

    if (buffer->async_transaction) {
        proc->free_async_space += size + sizeof(struct binder_buffer);
        binder_debug(BINDER_DEBUG_BUFFER_ALLOC_ASYNC,
                 "binder: %d: binder_free_buf size %zd "
                 "async free %zd\n", proc->pid, size,
                 proc->free_async_space);
    }
    binder_update_page_range(proc, 0,
        (void *)PAGE_ALIGN((uintptr_t)buffer->data),
        (void *)(((uintptr_t)buffer->data + buffer_size) & PAGE_MASK),
        NULL);
    rb_erase(&buffer->rb_node, &proc->allocated_buffers);
    buffer->free = 1;
    if (!list_is_last(&buffer->entry, &proc->buffers)) {
        struct binder_buffer *next = list_entry(buffer->entry.next,
                        struct binder_buffer, entry);
        if (next->free) {
            rb_erase(&next->rb_node, &proc->free_buffers);
            binder_delete_free_buffer(proc, next);
        }
    }
    if (proc->buffers.next != &buffer->entry) {
        struct binder_buffer *prev = list_entry(buffer->entry.prev,
                        struct binder_buffer, entry);
        if (prev->free) {
            binder_delete_free_buffer(proc, buffer);
            rb_erase(&prev->rb_node, &proc->free_buffers);
            buffer = prev;
        }
    }
    binder_insert_free_buffer(proc, buffer);
}

Java層類似,通過JNI調(diào)用Parcel的freeData()函數(shù)釋放內(nèi)存桥状,在用戶空間帽揪,每次執(zhí)行BR_TRANSACTION或者BR_REPLY,都會利用freeBuffer發(fā)送請求辅斟,去釋放內(nèi)核中的內(nèi)存

簡單的Binder通信C/S模型

簡單的Binder通信模型

聽說你Binder機制學的不錯转晰,來解決下這幾個問題(一)
聽說你Binder機制學的不錯,來看看這幾個問題(二)
聽說你Binder機制學的不錯士飒,來看看這幾個問題(三)

作者:看書的小蝸牛
原文鏈接: 聽說你Binder機制學的不錯查邢,來面試下這幾個問題(二)

僅供參考,歡迎指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末酵幕,一起剝皮案震驚了整個濱河市扰藕,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌芳撒,老刑警劉巖邓深,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件未桥,死亡現(xiàn)場離奇詭異,居然都是意外死亡芥备,警方通過查閱死者的電腦和手機冬耿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來门躯,“玉大人淆党,你說我怎么就攤上這事⊙攘梗” “怎么了染乌?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我草戈,道長旗国,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任勒庄,我火速辦了婚禮,結(jié)果婚禮上瘫里,老公的妹妹穿的比我還像新娘实蔽。我一直安慰自己,他們只是感情好谨读,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布局装。 她就那樣靜靜地躺著,像睡著了一般劳殖。 火紅的嫁衣襯著肌膚如雪铐尚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天哆姻,我揣著相機與錄音宣增,去河邊找鬼。 笑死矛缨,一個胖子當著我的面吹牛爹脾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播箕昭,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼誉简,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了盟广?” 一聲冷哼從身側(cè)響起闷串,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎筋量,沒想到半個月后烹吵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碉熄,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年肋拔,在試婚紗的時候發(fā)現(xiàn)自己被綠了锈津。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡凉蜂,死狀恐怖琼梆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情窿吩,我是刑警寧澤茎杂,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站纫雁,受9級特大地震影響煌往,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜轧邪,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一刽脖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧忌愚,春花似錦曲管、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至癌幕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昧穿,已是汗流浹背勺远。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留时鸵,地道東北人胶逢。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像饰潜,于是被迫代替她去往敵國和親初坠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內(nèi)容