四問binder

如果帶著問題去學習binder又是怎樣的呢?

1. 第一個binder線程 Binder:PID_1是怎樣創建的

在App進程創建的時候

nativeZygoteInit
  -> com_android_internal_os_RuntimeInit_nativeZygoteInit
    -> onZygoteInit
      -> ProcessState 
        -> startThreadPool()
          -> spawnPooledThread(true)

isMain=true

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName(); //這里是第一個binder 線程,也就是binder主線程
        sp<Thread> t = new PoolThread(isMain);  //生成一個線程
        t->run(name.string());  //線程運行,不停的調用theadLoop
    }   
}
virtual bool threadLoop()
{
    IPCThreadState* ptr = IPCThreadState::self();
    if (ptr) {
        ptr->joinThreadPool(mIsMain);
    }
    return false; //threadLoop返回false表示會退出線程,即線程結束了
}
void IPCThreadState::joinThreadPool(bool isMain)
{
    // mOut是寫入內核的數據,如果是Main Binder線程,則發送 BC_ENTER_LOOPER
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
       
    status_t result;
    do {
        processPendingDerefs(); //處理引用計數
        result = getAndExecuteCommand(); //獲得命令并執行

        //異常退出
        if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
            abort();
        }
        
        //如果timeout了,但如果不是 Main Binder線程也就是非 Binder:xxx_1線程就直接退出
        if(result == TIMED_OUT && !isMain) {
            break;
        }
    } while (result != -ECONNREFUSED && result != -EBADF);
    //通知binder驅動 線程要退出了
    mOut.writeInt32(BC_EXIT_LOOPER);
    talkWithDriver(false);
}

binder_thread_write 中會有對 BC_ENTER_LOOPER的處理, 如下所示,thread是指binder_thread,也就是當前線程的描述符, 將BINDER_LOOPER_STATE_ENTERED保存到 binder_thread的looper變量里.

looper變量主要保存的binder線程的一種狀態如

  • BINDER_LOOPER_STATE_REGISTERED
    標記該binder線程是非主binder線程
  • BINDER_LOOPER_STATE_ENTERED
    標記該binder線程是主binder線程
  • BINDER_LOOPER_STATE_EXITED
    標記該binder線程馬上就要退出了
  • BINDER_LOOPER_STATE_INVALID
    標記是無效的,但是這個并沒有實際用處,一般是 原來是主線程,然后用戶態又通過主線程發送了BC_REGISTER_LOOPER,就會標志 INVALID, 同理與REGISTER一樣
  • BINDER_LOOPER_STATE_WAITING
    標記當前binder線程正在等著client的請求
  • BINDER_LOOPER_STATE_NEED_RETURN
    這個標志,是當前新線程下來在生成binder_thread里置位的,表示該binder線程在處理完transaction后需要返回到用戶態。
case BC_ENTER_LOOPER:
    if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
        thread->looper |= BINDER_LOOPER_STATE_INVALID;
        binder_user_error("%d:%d ERROR: BC_ENTER_LOOPER called after BC_REGISTER_LOOPER\n",
            proc->pid, thread->pid);
    }
    thread->looper |= BINDER_LOOPER_STATE_ENTERED;
    break;

從上面可知,startThreadPool就是創建APP進程的binder線程池模型, 它也會創建第一個binder主線程,該線程不會退出(非 critcal錯誤),而spawnPooledThread函數用于創建一個普通的binder線程,這些線程都有可能會退出。

二、 一個APP里最多能有多少個binder線程呢? 其它binder線程是怎么創建的,由誰創建的呢?

從第一節可知,只要調用 spawnPooledThread()函數就創建了一個binder線程,因此從代碼上來看除了第一個主binder線程由APP用戶態主動創建外,其它的都是由Binder驅動主動向APP申請創建,也就是說binder線程的創建是看binder驅動是否很繁忙(這里的繁忙是與本進程相關的),來決定是否需要向APP進程申請創建,也就是APP進程被動創建。

2.1 Server端

其實也很好理解,binder最終的通信包括client和server雙方。server方并不知道什么時候會有client的請求,client的有請求,它就直接丟給binder驅動,然后由binder驅動根據當前server的能力(是否有多余線程去處理)去看下是否需要新的線程來處理client的請求。

那試問一個 Server端 App 最多同時能跑多少個binder線程呢?

這個問題,可以簡單的測試一下,寫一個Service, 然后實現一個API里死循環sleep, 反正就是不返回, 另一個client在創建多個非UI線程去請求API, 然后再dump出來,就知道最多支持多少個binder線程了。

我們從 spawnPooledThread的調用函數來看就知道能有多少個binder線程了。

IPCThreadState::executeCommand(int32_t cmd) {
    case BR_SPAWN_LOOPER:
        mProcess->spawnPooledThread(false); 
        break;
}

executeCommand函數執行從Binder 驅動傳遞到 BR_SPAWN_LOOPER, 這時就得繼續從binder驅動中去找 BR_SPAWN_LOOPER了。

static int binder_thread_read(...)
    ...
    if (proc->requested_threads + proc->ready_threads == 0 &&
        proc->requested_threads_started < proc->max_threads &&
        (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
         BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
         /*spawn a new thread if we leave this out */) {
        proc->requested_threads++;
        if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
            return -EFAULT;
        binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
    }
}

在binder_thread_read調用的最后,就會有可能去spawn一個線程, 但是也是有條件的, 且要同時滿足才行

  1. binder_proc -> ready_threads = 0
    ready_threads表明當前進程中還沒有準備好的線程, 換句話說,當前binder線程都有各自的任務在處理, 沒空接受新的任務了。

  2. binder_proc -> requested_threads = 0
    如果 reqeusted_threads >0 表明 binder_driver已經通過其它binder線程請求APP進程去創建binder線程來處理任務的路上了, 所以啊,binder驅動就不會再通知一次了,因為如果再通知一次,那么就會創建兩個binder線程,資源浪費嘛。
    所以如果 requested_threads = 0,如果其它條件滿足,那就由我這個binder線程就請求APP去創建另一個binder線程

  3. proc->requested_threads_started < proc->max_threads
    max_threads: 這個是APP進程通知binder驅動我這個APP進程當前最多只能創建max_threads個線程,如果我已經創建這么多了,不要再通知我去創建了。至于別人(其它APP)想要獲得我的服務或其它,別急,取號等著,等著我的binder線程不忙了再去處理。

  4. thread->looper & (BINDER_LOOPER_STATE_REGISTERED |BINDER_LOOPER_STATE_ENTERED
    looper保存著當前線程的一些狀態,BINDER_LOOPER_STATE_REGISTERED(非主Binder線程)|BINDER_LOOPER_STATE_ENTERED(主binder線程) 這兩個是binder線程的標志. 即: Binder驅動只有通過我的APP創建的(有效的)binder線程才有資格向我(APP)提出創建其它binder線程.

好了,上面4個條件同時滿足了,就通知APP創建一個新的 binder線程了。

但還有幾個問題

  1. binder_proc->max_threads最大是多少呢?
    在ProcessState.cpp的 open_driver 函數中, 會設置一個默認的最大binder線程數 DEFAULT_MAX_BINDER_THREADS = 15;
    但是BinderInternal.java也提供了setMaxThreads去修改Binder驅動中的max_threads值. 比如system_server就設置了max_threads為31
    注意 : BinderInternal是internal的類,APP不能直接調用。

  2. ready_threads 這個是什么意思呢?
    binder_thread_read 函數中

static int binder_thread_read(..) {
    //wait_for_proc_work表示是否需要去等著proc的任務,當然前提是這個binder線程沒有任務可做了,
    //那這個binder線程就可以向進程申請去處理進程的其它任務了
    wait_for_proc_work = thread->transaction_stack == NULL &&
                list_empty(&thread->todo);
    if (wait_for_proc_work)
        proc->ready_threads++;  //表明當前進程又多了一個可用的binder線程去接受任務了

    if (wait_for_proc_work) {
            //等著整個進程的任務
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    } else {
            //等著binder線程本身的任務
            ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
    }
    ...
    //代碼運行到這里就表明有任務了,那么我這個binder線程就要去接受任務去做了,
    if (wait_for_proc_work)
        //既然我已經開始去處理任務了,那么整個進程當前就少了我一個可用的線程啦
        proc->ready_threads--;

   ...
done:
    //決定是否再創建一個新的binder線程
    *consumed = ptr - buffer;
    if (proc->requested_threads + proc->ready_threads == 0 &&
        proc->requested_threads_started < proc->max_threads &&
        (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
         BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
        proc->requested_threads++; //請求創建線程+1
        if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
            return -EFAULT;
        binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
    }
}
  1. requested_threads與requested_threads_started
    requested_threads表示要請求創建的線程數,在上面決定創建binder線程后,該值會+1
    而requested_threads_started表示當前進程已經啟動吧這么多個線程,
    APP接收到創建新的binder線程的請求后就會調用 spawnPooledThread(false) 去創建一個非Main的binder線程, 而最后又會通過joinThreadPool觸發binder驅動的BC_REGISTER_LOOPER
static int binder_thread_write(...) {
        case BC_REGISTER_LOOPER:
            if (thread->looper & BINDER_LOOPER_STATE_ENTERED) {
                thread->looper |= BINDER_LOOPER_STATE_INVALID;
            } else if (proc->requested_threads == 0) {
                thread->looper |= BINDER_LOOPER_STATE_INVALID;
            } else {
                //正常情況會進入該分支
                proc->requested_threads--;   //要請求創建的binder線程數減1
                proc->requested_threads_started++; //啟動過的線程數+1
            }
            thread->looper |= BINDER_LOOPER_STATE_REGISTERED;
            break;
binder線程創建

2.2 client

client端作為請求端,是從用戶態下發起請求的,所以client一般是自己申請創建線程,然后在線程里去請求 server端的服務,這里不會涉及到binder線程。

那么client就只有一個默認的主binder線程了么?當然不是,從2.1可知,binder驅動會不管是server,還是client, 都會默認有一個空閑線程,所以client的主binder線程在binder_thread_read的最后發現當前沒有可ready的線程,這時就通知client端創建一個binder線程。所以一般client有兩個binder線程。

三、Server的binder線程阻塞在哪里了?

人第二節可知道,Server端的binder線程會一直等著client的請求,即如果沒有客戶端請求時,binder線程應該是一直阻塞著的,那么Server端等在什么地方呢?

來看下binder線程的運行過程便可知, 第一小節已經貼出相關代碼了, 再帖一次,

void IPCThreadState::joinThreadPool(bool isMain)
{
    // mOut是寫入內核的數據,如果是Main Binder線程,則發送 BC_ENTER_LOOPER
    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
       
    status_t result;
    do {
        processPendingDerefs(); //處理引用計數
        result = getAndExecuteCommand(); //獲得命令并執行

        //異常退出
        if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
            abort();
        }
        
        //如果timeout了,但如果不是 Main Binder線程也就是非 Binder:xxx_1線程就直接退出
        if(result == TIMED_OUT && !isMain) {
            break;
        }
    } while (result != -ECONNREFUSED && result != -EBADF);
    //通知binder驅動 線程要退出了
    mOut.writeInt32(BC_EXIT_LOOPER);
    talkWithDriver(false);
}

從joinThreadPool里,有一個getAndExecuteCommand, 來看下這個函數干了些什么吧

status_t IPCThreadState::getAndExecuteCommand()
{
    status_t result;
    int32_t cmd;

    result = talkWithDriver();
    if (result >= NO_ERROR) {
        size_t IN = mIn.dataAvail();
        if (IN < sizeof(int32_t)) return result; //異常處理
        cmd = mIn.readInt32(); //獲得從Binder驅動返回來的BR_ 號
         ...
        result = executeCommand(cmd); //開始執行相關命令
        ...
    }

    return result;
}

該函數很簡單,從binder驅動里拿到client要操作的CMD, 然后調用executeCommand執行CMD。

在看 talkWithDriver 之前,先來看下 binder_write_read

binder_write_read

binder_write_read是一個結構體,它主要作用是告訴binder驅動我這次請求的目的是什么,一是read, 另一個就是write,

  • write_size > 0 表明需要往binder驅動寫write_buffer的數據
  • read_size >0 表明需要往binder驅動讀數據放到write_buffer的數據
  • write_consumed 表明已經寫入了多少字節
  • read_consumed 表明讀入了多少字節
  • write_buffer 指向用戶態下的數據地址,其實也就是IPCThreadState里的mOut (Parcel)里的mData地址
  • read_buffer 指向用戶態下的數據地址,其實也就是IPCThreadState里的mIn (Parcel)里的mData地址
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
    binder_write_read bwr; 
    
    //mIn.dataPosition()是當前mIn中mData的指針,
    const bool needRead = mIn.dataPosition() >= mIn.dataSize();
    //doReceive默認為true, needRead為true表明還需要從Binder驅動中讀取數據,這時候就不能往binder驅動中寫數據
    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__)
        //阻塞式的調用 binder_ioctl
        if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
            err = NO_ERROR;
        else
            err = -errno;
#else
        err = INVALID_OPERATION;
#endif
        if (mProcess->mDriverFD <= 0) {
            err = -EBADF;
        }
    } while (err == -EINTR);

    if (err >= NO_ERROR) {
        if (bwr.write_consumed > 0) {
            //根據寫了多少數據,來更新mOut的相關數據
            if (bwr.write_consumed < mOut.dataSize())
                mOut.remove(0, bwr.write_consumed);
            else
                mOut.setDataSize(0);
        }
        if (bwr.read_consumed > 0) {
            //根據讀了多少數據,來更新mIn的相關數據
            mIn.setDataSize(bwr.read_consumed);
            mIn.setDataPosition(0);
        }
        return NO_ERROR;
    }
        
    return err;
}

3.1 binder線程第一次調用 binder_ioctl

我們來看下binder線程創建后第一次調用 binder_ioctl 的情況
從joinThreadPool可以看出, 會寫入一個 BC_REGISTER_LOOPER

mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

因此此時binder_write_read的結構如下所示

binder_write_read

來看下binder驅動是怎么處理的呢

static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
    // binder_stop_on_user_error默認為0,只有條件為false的時候才會一直block在這,所以這里并不會block
    ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
    binder_lock(__func__);
    //創建一個與binder線程相關的binder_thread,  第一次創建會設置thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN
    thread = binder_get_thread(proc);

    switch (cmd) {
    case BINDER_WRITE_READ: //執行BINDER_WRITE_READ命令
        ret = binder_ioctl_write_read(filp, cmd, arg, thread);
        break;
        ...
    }
    
    ret = 0;
err:
    if (thread) //這里取消BINDER_LOOPER_STATE_NEED_RETURN
        thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
    binder_unlock(__func__);
    wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
err_unlocked:
    return ret;
}

其中BINDER_LOOPER_STATE_NEED_RETURN這個非常重要,就是這個標志,決定了binder線程第一次的走向,如果是第一次進來, binder_thread->looper都設置了該標志,而該binder_ioctl返回時,會將該標志取消

static int binder_ioctl_write_read(struct file *filp,
                unsigned int cmd, unsigned long arg,
                struct binder_thread *thread)
{
    int ret = 0;
    struct binder_proc *proc = filp->private_data;
    unsigned int size = _IOC_SIZE(cmd);
    void __user *ubuf = (void __user *)arg;
    struct binder_write_read bwr;
    //獲得用戶態的 binder_write_read
    if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
        ret = -EFAULT;
        goto out;
    }

    if (bwr.write_size > 0) {
        //有數據要寫入,這里就是 BC_REGISTER_LOOPER
        ret = binder_thread_write(proc, thread,  bwr.write_buffer,
                      bwr.write_size, &bwr.write_consumed);
    }
    if (bwr.read_size > 0) {
        //有數據要讀取
        ret = binder_thread_read(proc, thread, bwr.read_buffer,
                     bwr.read_size,  &bwr.read_consumed,  filp->f_flags & O_NONBLOCK);
        //如果進程描述結構體中有任務要做時,喚醒等在binder_proc->wait的列表
        if (!list_empty(&proc->todo))
            wake_up_interruptible(&proc->wait);
    }
    //將數據拷貝到用戶態
    if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
    }
out:
    return ret;
}

binder線程第一次調用binder_ioctl也就是寫入一個BC_REGISTER_LOOPER, 所以 binder_thread_write只是將binder_thread->looper設置一個BINDER_LOOPER_STATE_REGISTERED

現在來看下binder_thread_read

static int binder_thread_read(...)
{
    void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
    void __user *ptr = buffer + *consumed;
    void __user *end = buffer + size;

    int ret = 0;
    int wait_for_proc_work;

    //第一次進來 consumed為0,所以進入該分支, 會返回一個 BR_NOOP
    if (*consumed == 0) {
        if (put_user(BR_NOOP, (uint32_t __user *)ptr)) return -EFAULT;
        ptr += sizeof(uint32_t);
    }

retry:
    //binder線程第一次進來,沒有transaction任務,也沒有其它任務,所以waite_for_proc_work為true
    wait_for_proc_work = thread->transaction_stack == NULL &&
                list_empty(&thread->todo);
    thread->looper |= BINDER_LOOPER_STATE_WAITING;
    if (wait_for_proc_work)
        proc->ready_threads++;  //線程可用

    binder_unlock(__func__);

    if (wait_for_proc_work) { //進入該分支
        if (non_block) {
            if (!binder_has_proc_work(proc, thread))
                ret = -EAGAIN;
        } else
            //如果binder_has_proc_work為false,將會一直等在這里,直到被喚醒
            ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
    } else {
        if (non_block) {
            if (!binder_has_thread_work(thread))
                ret = -EAGAIN;
        } else
            ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
    }

    binder_lock(__func__);

    if (wait_for_proc_work)
        proc->ready_threads--; //線程接收到任務,準備執行了
    thread->looper &= ~BINDER_LOOPER_STATE_WAITING;

    if (ret)
        return ret;

    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 {
            /* no data added */
            if (ptr - buffer == 4 &&
                !(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
                goto retry;
            break; //直接break到
        }

        if (end - ptr < sizeof(tr) + 4)
            break;
        
        switch(...) {}
    }

done:
       
    //決定是否要向用戶態申請創建一個新的線程
    *consumed = ptr - buffer;
    if (proc->requested_threads + proc->ready_threads == 0 &&
        proc->requested_threads_started < proc->max_threads &&
        (thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
         BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
         /*spawn a new thread if we leave this out */) {
        proc->requested_threads++;
        if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
            return -EFAULT;
        binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
    }
    return 0;
}

ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
現在來看下wait_event_freezable_exclusive, 該函數的條件如果為true,即表明binder有任務時,就不會阻塞,而是直接執行,binder線程第一次進來的時候(因為binder線程的創建是防止后續有請求到來,所以當前是沒有任務的), 當前沒有任務的話,binder線程應該一直等著呀,為什么這里沒有阻塞式的等呢?

static int binder_has_proc_work(struct binder_proc *proc,   struct binder_thread *thread)
{
    return !list_empty(&proc->todo) ||
        (thread->looper & BINDER_LOOPER_STATE_NEED_RETURN);
}

哦,原來binder_has_proc_work會判斷如果binder線程是第一次進來的話,也就是設置了BINDER_LOOPER_STATE_NEED_RETURN,也就是表明該線程不管你有沒有任務,我都需要返回到用戶態,所以這里為true, 也就不會等待了。

所以第一次返回給用戶態時,只返回一個 BR_NOOP, 表示無操作

3.2 binder線程第二次調用 binder_ioctl

3.1 節,mOut已經寫完數據了,

binder_write_buf

這里write_size=0, read_size 依然是 256,

只不過第二次進入binder_ioctl后, binder_thread->looper已經沒有了BINDER_LOOPER_STATE_NEED_RETURN
所以binder_has_proc_work就會返回false, 然后binder線程就這樣一直阻塞式的阻塞了。

整個過程如下

binder線程阻塞

四、Server端的binder線程是怎么被喚醒的呢?

由第三節知道 Server端的binder線程一直阻塞在 binder_thread_read 這個函數的中的, 也就是

ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));

注意wait_event_freezable_exclusive是一個排他的等待隊列,當喚醒該等待隊列時,只會喚醒第一個。

對應的binder線程要被喚醒,那么就找下哪里 wake up了 proc->wait就知道了。

下面來看幾種正常的情況

4.1 被其它進程(binder_proc)喚醒

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
{
    ...
    if (target_thread) { //一般這里為false
        e->to_thread = target_thread->pid;
        target_list = &target_thread->todo;
        target_wait = &target_thread->wait;
    } else { //取得的是進程上的todo等待列表
        target_list = &target_proc->todo;
        target_wait = &target_proc->wait;
    }

    ...

    list_add_tail(&t->work.entry, target_list); 
    if (target_wait) //喚醒
        wake_up_interruptible(target_wait);  
    ...
}

當client進程需要獲得server端的服務時,如調用一個"server端的API函數", 最終binder驅動會找到server的binder_proc,也就是代碼中的target_proc, 接著client將要請求server的相關數據加入到server的等待隊列中,也就是target_list, 最后通過wake_up_interruptible喚醒server線程來處理client的請求。

binder線程的喚醒

注意,這里只會喚醒一個binder線程(因為睡眠時是wait_event_freezable_exclusive)

4.2 被自己喚醒

4.1節會喚醒一個binder線程去執行客戶端的請求,來看下server端客戶線程會喚醒后做了什么操作

static int binder_ioctl_write_read(struct file *filp,
                unsigned int cmd, unsigned long arg, struct binder_thread *thread)
{
    ...
    if (bwr.write_size > 0) {
        ...
    }
    if (bwr.read_size > 0) {
        ret = binder_thread_read(proc, thread, bwr.read_buffer,
                     bwr.read_size,&bwr.read_consumed,filp->f_flags & O_NONBLOCK);
        if (!list_empty(&proc->todo)) 
            wake_up_interruptible(&proc->wait);
    }
    ...
}

當server有一個binder線程被喚醒了,這時binder線程就會返回到用戶態去執行相關操作,在返回之前, binder線程又會檢查是否現在binder驅動比較繁忙,也就是是否還有其它的客戶端請求,如果還有,那么當前binder線程觸發喚醒server進程上的等待的其它binder線程。
也就是說,當有binder線程被喚醒時,binder線程就可能會喚醒其它的binder線程盡量將當前等待著的請求執行完。

4.3 binder死亡通知

如linkToDeath, 這里不討論這種情況。

五、client是怎么找到server端在binder驅動中的binder_proc的呢?

在4.1節binder_transaction,client通過找到target_proc,然后就喚醒了server端的binder線程,那么這個target_proc是怎么來的呢?

static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
{
    struct binder_proc *target_proc;

    if (reply) { //reply在 BC_TRANACTION時為false
    } else { //進入else分支
        if (tr->target.handle) { //如果handle不為0,則進入下面的分支
            struct binder_ref *ref;
            ref = binder_get_ref(proc, tr->target.handle, true);
            //通過handle在當前binder_proc中獲得target的binder_ref, 也就是binder_node的引用
            target_node = ref->node; //通過引用獲得target binder node
        } else { //如果handle為0的話進入else分支
            target_node = binder_context_mgr_node; //這個是固定的ServiceManager
        }
        target_proc = target_node->proc;
    }
    if (target_thread) {
    } else {
        target_list = &target_proc->todo;
        target_wait = &target_proc->wait;
    }
    ...
}

哦,原來是通過 handle 這個整形數來獲得的target binder proc. 那這個handle又是什么呢? 從上面看出是binder_transaction_data里面的handle,

binder_transaction_data

而整個transact傳遞流程如下

BpBinder transact

從代碼上來看

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

這個handle是BpBinder里的mHandle,

那么問題來了,BpBinder里的mHandle又是怎么初始化的呢

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容