3.5 receive async requet 接收異步方式發(fā)送過來的請求和接收同步方式發(fā)來的請求基本一樣,不同的是,在將binder_transaction的數(shù)據(jù)轉(zhuǎn)換到binder_transaction_data之后,將會釋放掉binder_transaction數(shù)據(jù)結(jié)構(gòu)的空間。 由于binder驅(qū)動對異步通信做了分流的處理,如果當(dāng)前目標(biāo)進程已經(jīng)有一個異步通信正在處理,那么為了保證同步通信的實時性,所以會將后來發(fā)給該進程的異步通信任務(wù)放在一個等待隊列async_todo中,直到前面那個異步通信任務(wù)完成后才會從異步等待隊列中取出一個任務(wù)放進前次處理異步任務(wù)的task的todo隊列中去。 (從這里可以看出,如果某個線程正在處理異步任務(wù),當(dāng)完成的時候發(fā)現(xiàn)異步等待隊列中還有異步任務(wù)需要處理,那么這個等待的異步任務(wù)也會被當(dāng)前這個線程處理,直到這個時間段內(nèi)的異步任務(wù)處理完。隔了段時間之后,如果再有異步任務(wù)到來的話,此時驅(qū)動可能會分配其他的線程來處理接下來時間段內(nèi)的異步任務(wù)。簡單點說,在某線程執(zhí)行任何一個異步任務(wù)未完成之前就已經(jīng)排到異步等待隊列中來的異步任務(wù),都將會由這個線程來執(zhí)行。)。 不過,我們在binder_thread_read()函數(shù)的最后沒有看到將異步任務(wù)移入線程的todo隊列中的動作,這個函數(shù)和異步請求接收相關(guān)的只有如下地方: 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) { if (!t) // 非 BINDER_WORK_TRANSACTION 的情況,放棄執(zhí)行后面的重新循環(huán) continue; … if (t->from) { // 記錄發(fā)送線程的binder_thread … // 同步傳輸時 } else { // reply 或者異步傳輸時 tr.sender_pid = 0; } … 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 { /* 如果收到的是回復(fù)數(shù)據(jù)或者而是異步請求,這里將會釋放掉這次單邊 傳輸?shù)腷inder_transaction結(jié)構(gòu)體,另外所有的傳輸?shù)腷inder_buffer結(jié)構(gòu)體空間都是通過上層發(fā)送命令BC_FREE_BUFFER來通知binder驅(qū)動釋放的,因為這部分空間是驅(qū)動在管理。*/ t->buffer->transaction = NULL; kfree(t); binder_stats_deleted(BINDER_STAT_TRANSACTION); } break; }// while(1) } 那究竟是在哪里移入下一個異步等待任務(wù)的呢?其實我們可以想一下,這個binder_thread_read()函數(shù)執(zhí)行完的時候,異步任務(wù)還沒開始執(zhí)行,驅(qū)動還會將binder_transaction_data結(jié)構(gòu)體傳回上層程序,上層程序才真正開始執(zhí)行異步任務(wù),不過通常上層應(yīng)用程序在執(zhí)行完異步任務(wù)(其實不只是異步任務(wù),應(yīng)該是所有類型的任務(wù))被執(zhí)行完,都應(yīng)該發(fā)送BC_FREE_BUFFER這個命令到binder驅(qū)動,通知驅(qū)動釋放掉一次單邊傳輸時的binder_buffer內(nèi)存空間。到這里之后,這個異步任務(wù)才算得上真正完成。所以我們的前面提到的移入異步任務(wù)的事情就是在這個時候做的,請看源碼: 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) { … switch (cmd) { … case BC_FREE_BUFFER: { // BC_FREE_BUFFER = _IOW('c', 3, int), // cmd | data_ptr (data_ptr是指binder_buffer.data開始地址)
void __user *data_ptr; struct binder_buffer *buffer;
if (get_user(data_ptr, (void * __user *)ptr)) return -EFAULT; ptr += sizeof(void *);
buffer = binder_buffer_lookup(proc, data_ptr); // 取出binder_buffer結(jié)構(gòu)體指針 … /* 當(dāng)前釋放的binder_buffer如果是異步傳輸所用,并且目標(biāo)binder_node存在 (非回復(fù)的情況下)。*/ if (buffer->async_transaction && buffer->target_node) { BUG_ON(!buffer->target_node->has_async_transaction);// 異常檢查 if (list_empty(&buffer->target_node->async_todo)) // 為NULL,表明沒有異步任務(wù)在等待執(zhí)行了。 buffer->target_node->has_async_transaction = 0; else // 不為NULL,將最早等待的異步任務(wù)加入當(dāng)前task的todo隊列中 list_move_tail(buffer->target_node->async_todo.next, &thread->todo); } … }// switch(cmd) … } // while(…) return 0; } 這樣的話,當(dāng)這個task再次調(diào)用ioctl讀或者調(diào)用poll(sslect)的時候,都會發(fā)現(xiàn)私有todo隊列中有異步任務(wù)需要執(zhí)行的。
3.6 transaction reply 當(dāng)接收者task處理完請求之后,也會在上層的用戶空間組織一個binder_transaction_data的數(shù)據(jù)結(jié)構(gòu)體用ioctl傳遞進binder驅(qū)動,這個時候用到的命令字就是BC_REPLY(前面發(fā)送請求的命令字是BC_TRANSACTION)。 同樣ioctl()調(diào)用binder_thread_write(),最后調(diào)用到binder_transaction()函數(shù)。 binder_transaction(proc, thread, &tr, cmd == BC_REPLY);這里最后傳遞進去的參數(shù)為1。 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; … 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; … if (reply) { // 發(fā)送回復(fù)數(shù)據(jù) in_reply_to = thread->transaction_stack; /* 一次單邊傳輸過程,不管是同步還是異步,都只存在一個binder_transacti on結(jié)構(gòu)體,同步傳輸返回信息也是使用的發(fā)送請求時創(chuàng)建的binder_transaction結(jié)構(gòu)體。*/ if (in_reply_to == NULL) { …/* 只要是同步傳輸,都會有發(fā)送回復(fù)數(shù)據(jù)的過程。got reply transaction wi th no transaction stack。*/ } binder_set_nice(in_reply_to->saved_priority); // 將當(dāng)前task的nice優(yōu)先級還原成處理接收數(shù)據(jù)之前的本來擁有的優(yōu)先級 … /* 當(dāng)前線程在之前接收數(shù)據(jù)的時候保存了自己的線程地址到binder_transactio n.to_thread中,這里用來做校驗。*/ if (in_reply_to->to_thread != thread) { …// 驗證不成功的錯誤處理 } thread->transaction_stack = in_reply_to->to_parent; /* 在當(dāng)期這次同步通信中,接收方將發(fā)送請求時的binder_transaction結(jié)構(gòu)體從 傳輸鏈表上摘下。這個時候這個結(jié)構(gòu)體還掛在發(fā)送請求的發(fā)送方的transaction_stack上。這個結(jié)構(gòu)體在什么時候從發(fā)送請求方的transaction_stack鏈表上摘下呢?請往下看。*/ target_thread = in_reply_to->from; // 將之前的請求發(fā)送方變成回復(fù)數(shù)據(jù)接收方。 if (target_thread == NULL){//唯有BC_REPLY和異步傳輸時這個from才為NULL …// 錯誤處理 } if (target_thread->transaction_stack != in_reply_to) { /* 如前所述,一次單邊傳輸只有一個binder_transaction,所以發(fā)送方和接 收方線程的transaction_stack指向同一個binder_transaction結(jié)構(gòu)體。如果這里不相等,那就說明前面發(fā)送請求就出了問題,不過,這種錯誤幾乎不會發(fā)生,但是必須得留這么一手。*/ … goto err_dead_binder; } target_proc = target_thread->proc; /* 好像沒有看到設(shè)置target_node這個指針呢?對,在發(fā)送回復(fù)數(shù)據(jù)的時候確實 不用這個binder_node了。為什么?一般的接收方都是server,是具有binder實體的,而發(fā)送方一般是client,是沒有binder實體,所以這個不用設(shè)置。除非發(fā)送方和接收方都具有binder實體,才有可能。 */ }else { // 發(fā)送的是請求數(shù)據(jù) … ref = binder_get_ref(proc, tr->target.handle); target_node = ref->node; 或者 target_node = binder_context_mgr_node; … target_proc = target_node->proc; … } // else
if (target_thread) {// 發(fā)送回復(fù)數(shù)據(jù)時,這個由binder驅(qū)動記錄,非NULL target_list = &target_thread->todo; target_wait = &target_thread->wait; } else { target_list = &target_proc->todo; target_wait = &target_proc->wait; } … // 申請binder_transaction和binder_work的內(nèi)存空間 if (!reply && !(tr->flags & TF_ONE_WAY)) t->from = thread; else // 發(fā)送回復(fù)或者是異步發(fā)送請求 t->from = NULL; … t->to_proc = target_proc; t->to_thread = target_thread; … /* 申請binder_buffer的內(nèi)存空間,binder_buffer.async_transaction = 1,從 binder_alloc_buf()函數(shù)的最后一個參數(shù)可以看出,如果是發(fā)送回復(fù)數(shù)據(jù)的時候,binder_transaction_data.flags的TF_ONE_WAY需要為1才行,因為既然是發(fā)送的回復(fù)數(shù)據(jù),那肯定就不需要再讓對方回信息了,除非沒完沒了。*/ t->buffer->allow_user_free = 0; t->buffer->debug_id = t->debug_id; t->buffer->transaction = t; t->buffer->target_node = target_node; // BC_REPLY時應(yīng)該為NULL
…// 完成數(shù)據(jù)在進程間的拷貝,同時處理包含在數(shù)據(jù)中的falt_binder_object結(jié)構(gòu)體
if (reply) { BUG_ON(t->buffer->async_transaction != 0); // 等于1才ok,否則就是異常 binder_pop_transaction(target_thread, in_reply_to);// note3.6_1 /* 這里表示一次同步通訊過程中,接收方已經(jīng)將回復(fù)數(shù)據(jù)發(fā)送給發(fā)送方,這 里就可以pop出之前,發(fā)送方給接收方發(fā)送時創(chuàng)建的binder_transaction數(shù)據(jù)結(jié)構(gòu),釋放其占用的內(nèi)存空間。*/ /* 這次同步通信中,發(fā)送請求時候產(chǎn)生的binder_transaction數(shù)據(jù)結(jié)構(gòu)在該函數(shù)前面已經(jīng)從請求接收方的stack鏈表摘下,這里就將其從發(fā)送請求方的stack鏈表上摘下。 */ } else if (!(t->flags & TF_ONE_WAY)) { // 同步請求 … }else { // 異步請求 … } 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; …// 錯誤處理 } /************************** note3.6_1 **********************/ static void binder_pop_transaction(struct binder_thread *target_thread, struct binder_transaction *t) { if (target_thread) { BUG_ON(target_thread->transaction_stack != t); BUG_ON(target_thread->transaction_stack->from != target_thread); // 異常檢查 target_thread->transaction_stack = target_thread->transaction_stack->from_parent; t->from = NULL; } t->need_reply = 0; if (t->buffer) t->buffer->transaction = NULL; kfree(t); // 釋放binder_transaction所占的空間。 binder_stats_deleted(BINDER_STAT_TRANSACTION); } /************************** note3.6_1 **********************/
3.7 receive reply 接收回復(fù)數(shù)據(jù)和接收請求數(shù)據(jù)大同小異,如下: static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, void __user *buffer, int size, signed long *consumed, int non_block) { void __user *ptr = buffer + *consumed; void __user *end = buffer + size; … while (1) { uint32_t cmd; struct binder_transaction_data tr; struct binder_work *w; struct binder_transaction *t = NULL; … if (t->buffer->target_node) { // 收到的是請求數(shù)據(jù) … } else { // 收到的是BC_REPLY tr.target.ptr = NULL; tr.cookie = NULL; cmd = BR_REPLY; } … if (t->from) { // 收到的是同步請求數(shù)據(jù) … } else { // 收到的是異步請求數(shù)據(jù)或者是REPLY數(shù)據(jù) tr.sender_pid = 0; } … t->buffer->allow_user_free = 1; if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {// 同步傳輸 … } else { /* 如果收到的是回復(fù)數(shù)據(jù)或者是異步請求,這里所做的是一樣,釋放掉 這個單邊傳輸是產(chǎn)生的binder_transaction數(shù)據(jù)結(jié)構(gòu)所占內(nèi)存。*/ t->buffer->transaction = NULL; kfree(t); binder_stats_deleted(BINDER_STAT_TRANSACTION); } break; }// while(1) done: *consumed = ptr - buffer; // 實際接收到的字節(jié)數(shù) … //檢查空閑線程是否夠用,不夠的話,申請再孵化。 return 0; } // binder_thread_read()
3.8 關(guān)于發(fā)送請求時的一點優(yōu)化 (引用universus的原話)當(dāng)進程P1的線程T1向進程P2發(fā)送請求時,驅(qū)動會先查看一下線程T1是否也正在處理來自P2某個線程請求但尚未完成(沒有發(fā)送回復(fù))。這種情況通常發(fā)生在兩個進程都有Binder實體并互相對發(fā)時請求時。假如驅(qū)動在進程P2中發(fā)現(xiàn)了這樣的線程,比如說T2,就會要求T2來處理T1的這次請求。因為T2既然向T1發(fā)送了請求尚未得到返回包,說明T2肯定(或?qū)┳枞谧x取返回包的狀態(tài)。 這時候可以讓T2順便做點事情,總比等在那里閑著好。 這個場景之下,binder驅(qū)動查看T1是否正在處理來自P2某線程的請求但尚未完成的工作是在binder_transaction()函數(shù)中完成的,也就是前文2.3節(jié)中提到過,但是沒有分析,現(xiàn)在又了前面這些分析,應(yīng)該對binder_transaction、binder_buffer結(jié)構(gòu)體,特別是binder_transaction.transaction_stack這個鏈表有所理解,這個時候來分析這個優(yōu)化才是最佳時機。 代碼量很小,先把代碼貼上來吧! static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply) { … if (reply) { … }else { … /* 這個優(yōu)化是針對同步通訊的,所以對于異步通訊就不存在這個優(yōu)化了。 如上面的情景:本來是T1向P2發(fā)送的同步請求(交互1),ioctl運行到這里,而如果在這之前,T1也正在處理來自P2進程T2的請求還沒結(jié)束(交互2),那么交互2中接收方T1也就是交互1的發(fā)送方了。其實這里完全可以不用優(yōu)化,直接將交互1的請求投進P2進程的全局todo隊列進行處理,但是為了提高效率和節(jié)省資源,反正交互2的發(fā)送方T2也在等待接收方的回復(fù),也沒有做事,這個時候讓其做點事情豈不是更好,所以出于這樣的考慮,才有了如下的優(yōu)化過程。 交互2中發(fā)送方T2和接收方T1的binder_thread.transaction_stack都應(yīng)該是指向同一個binder_transaction結(jié)構(gòu)體,而交互1中的發(fā)送方T1也就是交互2中的接收方。 */ if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { struct binder_transaction *tmp; tmp = thread->transaction_stack; if (tmp->to_thread != thread) { … // 需要當(dāng)前task是前次同步傳輸中的接收方才能進行發(fā)送優(yōu)化 return_error = BR_FAILED_REPLY; goto err_bad_call_stack; } while (tmp) { /* 如果此時T1在這之前,不僅僅只是接收到T2的請求,而 且還接收到了其他很多線程的請求均沒有完成的話,那么這個transaction_stack鏈表中就有多個binder_transaction結(jié)構(gòu)體存在,所以需要查找。查找的時候第一個條件都滿足,只是第二個條件就不好滿足了,就要一個一個比較,需要交互1的目標(biāo)binder_proc匹配。 */ if (tmp->from && tmp->from->proc == target_proc) target_thread = tmp->from; // 找到之后,直接將交互2的發(fā)送者設(shè)置成交互1的接收者。 tmp = tmp->from_parent; // 否則,繼續(xù)查找鏈表 } } // if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) } // else if (target_thread) { // 如果這個優(yōu)化有結(jié)果,找到了對應(yīng)的線程 e->to_thread = target_thread->pid; target_list = &target_thread->todo; // 目標(biāo)任務(wù)列表 target_wait = &target_thread->wait; // 目標(biāo)線程等待隊列 } else {// 優(yōu)化不成功的話,將任務(wù)送到進程對于的全局任務(wù)隊列 target_list = &target_proc->todo; target_wait = &target_proc->wait; // 同上 } … } // binder_transaction()
四、BINDER_WRITE_READ其他功能 BC_FREE_BUFFER和binder實體死亡通知相關(guān)的功能在后面的文章中單獨討論。其余的 命令都比較簡單,看源碼就可以明白。
五、其他ioctl功能實現(xiàn) ioctl的其余命令也沒有幾個,BINDER_SET_MAX_THREADS,BINDER_SET_CONTEXT_MGR, BINDER_THREAD_EXIT,BINDER_VERSION,也都是比較簡單的,看源碼即可明白。
參考資料: http://blog.csdn.net/universus/archive/2011/02/27/6211589.aspx Android Binder設(shè)計與實現(xiàn) – 設(shè)計篇 |
|