红茶一杯话Binder(传输机制篇_下)
1 事務(wù)的傳遞和處理
??????? 從IPCThreadState的角度看,它的transact()函數(shù)是通過(guò)向binder驅(qū)動(dòng)發(fā)出BC_TRANSACTION語(yǔ)義,來(lái)表達(dá)其傳輸意圖的,而后如有必要,它會(huì)等待從binder發(fā)回的回饋,這些回饋語(yǔ)義常常以“BR_”開(kāi)頭。另一方面,當(dāng)IPCThreadState作為處理命令的一方需要向發(fā)起方反饋信息的話,它會(huì)調(diào)用sendReply()函數(shù),向binder驅(qū)動(dòng)發(fā)出BC_REPLY語(yǔ)義。當(dāng)BC_語(yǔ)義經(jīng)由binder驅(qū)動(dòng)遞送到目標(biāo)端時(shí),會(huì)被binder驅(qū)動(dòng)自動(dòng)修改為相應(yīng)的BR_語(yǔ)義,這個(gè)我們?cè)诤笪脑偌?xì)說(shuō)。
??????? 當(dāng)語(yǔ)義傳遞到binder驅(qū)動(dòng)后,會(huì)走到binder_ioctl()函數(shù),該函數(shù)又會(huì)調(diào)用到binder_thread_write()和binder_thread_read():
??????? 在上一篇文章中,我們大體闡述了一下binder_thread_write()和binder_thread_read()的喚醒與被喚醒關(guān)系,而且還順帶在“傳輸機(jī)制的大體運(yùn)作”小節(jié)中提到了todo隊(duì)列的概念。本文將在此基礎(chǔ)上再補(bǔ)充一些知識(shí)。需要強(qiáng)調(diào)的是,我們必須重視binder_thread_write()和binder_thread_read(),因?yàn)槭聞?wù)的傳遞和處理就位于這兩個(gè)函數(shù)中,它們的調(diào)用示意圖如下:
??????? binder_thread_write()的代碼截選如下。因?yàn)楸疚闹饕P(guān)心傳輸方面的問(wèn)題,所以只摘取了case BC_TRANSACTION、case BC_REPLY部分的代碼:
int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,void __user *buffer, int size, signed long *consumed) {. . . . . .while (ptr < end && thread->return_error == BR_OK){. . . . . .switch (cmd){. . . . . .. . . . . .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;}. . . . . .. . . . . .}*consumed = ptr - buffer;}return 0; }這部分代碼比較簡(jiǎn)單,主要是從用戶態(tài)拷貝來(lái)binder_transaction_data數(shù)據(jù),并傳給binder_transaction()函數(shù)進(jìn)行實(shí)際的傳輸。而binder_transaction()可是需要我們費(fèi)一點(diǎn)兒力氣去分析的,大家深吸一口氣,準(zhǔn)備開(kāi)始。
?
1.1 BC_TRANSACTION事務(wù)(攜帶TF_ONE_WAY標(biāo)記)的處理
??????? 首先我們要認(rèn)識(shí)到,同樣是BC_TRANSACTION事務(wù),帶不帶TF_ONE_WAY標(biāo)記還是有所不同的。我們先看相對(duì)簡(jiǎn)單的攜帶TF_ONE_WAY標(biāo)記的BC_TRANSACTION事務(wù),這種事務(wù)是不需要回復(fù)的。
1.1.1 binder_transaction()
??????? 此時(shí),binder_transaction()所做的工作大概有:
??????? binder_transaction()代碼截選如下:
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_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;. . . . . .. . . . . .{// 先從tr->target.handle句柄值,找到對(duì)應(yīng)的binder_ref節(jié)點(diǎn),及binder_node節(jié)點(diǎn)if (tr->target.handle){struct binder_ref *ref;ref = binder_get_ref(proc, tr->target.handle);. . . . . .target_node = ref->node;}else{// 如果句柄值為0,則獲取特殊的binder_context_mgr_node節(jié)點(diǎn),// 即Service Manager Service對(duì)應(yīng)的節(jié)點(diǎn)target_node = binder_context_mgr_node;. . . . . .}// 得到目標(biāo)進(jìn)程的binder_proctarget_proc = target_node->proc;. . . . . .}// 對(duì)于帶TF_ONE_WAY標(biāo)記的BC_TRANSACTION來(lái)說(shuō),此時(shí)target_thread為NULL,// 所以準(zhǔn)備向binder_proc的todo中加節(jié)點(diǎn). . . . . .target_list = &target_proc->todo;target_wait = &target_proc->wait;. . . . . .// 創(chuàng)建新的binder_transaction節(jié)點(diǎn)。t = kzalloc(sizeof(*t), GFP_KERNEL);. . . . . .t->from = NULL;t->sender_euid = proc->tsk->cred->euid;t->to_proc = target_proc;t->to_thread = target_thread;// 將binder_transaction_data的code、flags域記入binder_transaction節(jié)點(diǎn)。t->code = tr->code;t->flags = tr->flags;t->priority = task_nice(current);t->buffer = binder_alloc_buf(target_proc, tr->data_size, tr->offsets_size,!reply && (t->flags & TF_ONE_WAY));. . . . . .t->buffer->transaction = t;t->buffer->target_node = target_node;. . . . . .// 下面的代碼分析所傳數(shù)據(jù)中的所有binder對(duì)象,如果是binder實(shí)體的話,要在紅黑樹(shù)中添加相應(yīng)的節(jié)點(diǎn)。// 首先,從用戶態(tài)獲取所傳輸?shù)臄?shù)據(jù),以及數(shù)據(jù)里的binder對(duì)象的偏移信息offp = (size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)). . . . . .if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)). . . . . .. . . . . .// 遍歷每個(gè)flat_binder_object信息,創(chuàng)建必要的紅黑樹(shù)節(jié)點(diǎn) ....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:{// 如果是binder實(shí)體struct binder_ref *ref;struct binder_node *node = binder_get_node(proc, fp->binder);if (node == NULL){// 又是“沒(méi)有則創(chuàng)建”的做法,創(chuàng)建新的binder_node節(jié)點(diǎn)node = binder_new_node(proc, fp->binder, fp->cookie);. . . . . .}. . . . . .// 必要時(shí),會(huì)在目標(biāo)進(jìn)程的binder_proc中創(chuàng)建對(duì)應(yīng)的binder_ref紅黑樹(shù)節(jié)點(diǎn)ref = binder_get_ref_for_node(target_proc, node);. . . . . .// 修改所傳數(shù)據(jù)中的flat_binder_object信息,因?yàn)檫h(yuǎn)端的binder實(shí)體到了目標(biāo)// 端,就變?yōu)閎inder代理了,所以要記錄下binder句柄了。fp->handle = ref->desc;. . . . . .} break;case BINDER_TYPE_HANDLE:case BINDER_TYPE_WEAK_HANDLE: {struct binder_ref *ref = binder_get_ref(proc, fp->handle);// 有時(shí)候需要對(duì)flat_binder_object做必要的修改,比如將BINDER_TYPE_HANDLE// 改為BINDER_TYPE_BINDER. . . . . .} break;case BINDER_TYPE_FD: {. . . . . .} break;. . . . . .}. . . . . .{. . . . . .if (target_node->has_async_transaction){target_list = &target_node->async_todo;target_wait = NULL;}elsetarget_node->has_async_transaction = 1;}t->work.type = BINDER_WORK_TRANSACTION;// 終于把binder_transaction節(jié)點(diǎn)插入target_list(即目標(biāo)todo隊(duì)列)了。list_add_tail(&t->work.entry, target_list);. . . . . .list_add_tail(&tcomplete->entry, &thread->todo);// 傳輸動(dòng)作完畢,現(xiàn)在可以喚醒系統(tǒng)中其他相關(guān)線程了,wake up!if (target_wait)wake_up_interruptible(target_wait);return;. . . . . .. . . . . . }?
??????? 雖然已經(jīng)是截選,代碼卻仍然顯得冗長(zhǎng)。這也沒(méi)辦法,Android frameworks里的很多代碼都是這個(gè)樣子,又臭又長(zhǎng),大家湊合著看吧。我常常覺(jué)得google的工程師多少應(yīng)該因這樣的代碼而感到臉紅,不過(guò),哎,這有點(diǎn)兒說(shuō)遠(yuǎn)了。
我們畫(huà)一張示意圖,如下:
上圖體現(xiàn)了從binder_ref找到“目標(biāo)binder_node”以及“目標(biāo)binder_proc”的意思,其中“A端”表示發(fā)起方,“B端”表示目標(biāo)方。可以看到,攜帶TF_ONE_WAY標(biāo)記的事務(wù),其實(shí)是比較簡(jiǎn)單的,驅(qū)動(dòng)甚至不必費(fèi)心去找目標(biāo)線程,只需要?jiǎng)?chuàng)建一個(gè)binder_transaction節(jié)點(diǎn),并插入目標(biāo)binder_proc的todo鏈表即可。
??????? 另外,在將binder_transaction節(jié)點(diǎn)插入目標(biāo)todo鏈表之前,binder_transaction()函數(shù)用一個(gè)for循環(huán)分析了需要傳輸?shù)臄?shù)據(jù),并為其中包含的binder對(duì)象生成了相應(yīng)的紅黑樹(shù)節(jié)點(diǎn)。
??????? 再后來(lái),binder_transaction節(jié)點(diǎn)成功插入目標(biāo)todo鏈表,此時(shí)說(shuō)明目標(biāo)進(jìn)程有事情可做了,于是binder_transaction()函數(shù)會(huì)調(diào)用wake_up_interruptible()喚醒目標(biāo)進(jìn)程。
?
1.1.2 binder_thread_read()
??????? 當(dāng)目標(biāo)進(jìn)程被喚醒時(shí),會(huì)接著執(zhí)行自己的binder_thread_read(),嘗試解析并執(zhí)行那些剛收來(lái)的工作。無(wú)論收來(lái)的工作來(lái)自于“binder_proc的todo鏈表”,還是來(lái)自于某“binder_thread的todo鏈表”,現(xiàn)在要開(kāi)始從todo鏈表中摘節(jié)點(diǎn)了,而且在完成工作之后,會(huì)徹底刪除binder_transaction節(jié)點(diǎn)。
???????? binder_thread_read()的代碼截選如下:
static int binder_thread_read(struct binder_proc *proc,struct binder_thread *thread,void __user *buffer, int size,signed long *consumed, int non_block) {. . . . . . retry:// 優(yōu)先考慮thread節(jié)點(diǎn)的todo鏈表中有沒(méi)有工作需要完成wait_for_proc_work = thread->transaction_stack == NULL&& list_empty(&thread->todo);. . . . . .. . . . . .if (wait_for_proc_work){. . . . . .ret = wait_event_interruptible_exclusive(proc->wait,binder_has_proc_work(proc, thread));}else{. . . . . .ret = wait_event_interruptible(thread->wait, binder_has_thread_work(thread));}. . . . . .thread->looper &= ~BINDER_LOOPER_STATE_WAITING;// 如果是非阻塞的情況,ret值非0表示出了問(wèn)題,所以return。 // 如果是阻塞(non_block)情況,ret值非0表示等到的結(jié)果出了問(wèn)題,所以也return。if (ret)return ret; while (1){. . . . . .// 讀取binder_thread或binder_proc中todo列表的第一個(gè)節(jié)點(diǎn)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);. . . . . . switch (w->type){case BINDER_WORK_TRANSACTION: {t = container_of(w, struct binder_transaction, work);} break;case BINDER_WORK_TRANSACTION_COMPLETE: {cmd = BR_TRANSACTION_COMPLETE;. . . . . .// 將binder_transaction節(jié)點(diǎn)從todo隊(duì)列摘下來(lái)list_del(&w->entry);kfree(w);binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);} break;. . . . . .. . . . . . }if (!t)continue;. . . . . .if (t->buffer->target_node) {struct binder_node *target_node = t->buffer->target_node;tr.target.ptr = target_node->ptr;// 用目標(biāo)binder_node中記錄的cookie值給binder_transaction_data的cookie域賦值,// 這個(gè)值就是目標(biāo)binder實(shí)體的地址tr.cookie = target_node->cookie; t->saved_priority = task_nice(current);. . . . . .cmd = BR_TRANSACTION;}. . . . . .tr.code = t->code;tr.flags = t->flags;tr.sender_euid = t->sender_euid;. . . . . .tr.data_size = t->buffer->data_size;tr.offsets_size = t->buffer->offsets_size;// binder_transaction_data中的data只是記錄了binder緩沖區(qū)中的地址信息,并再做copy動(dòng)作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 *));// 將cmd命令寫(xiě)入用戶態(tài),此時(shí)應(yīng)該是BR_TRANSACTIONif (put_user(cmd, (uint32_t __user *)ptr))return -EFAULT;ptr += sizeof(uint32_t);// 當(dāng)然,binder_transaction_data本身也是要copy到用戶態(tài)的if (copy_to_user(ptr, &tr, sizeof(tr)))return -EFAULT;. . . . . .. . . . . . // 將binder_transaction節(jié)點(diǎn)從todo隊(duì)列摘下來(lái)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;// TF_ONE_WAY情況,此時(shí)會(huì)刪除binder_transaction節(jié)點(diǎn)kfree(t);binder_stats_deleted(BINDER_STAT_TRANSACTION);}break;}. . . . . .. . . . . .return 0; }簡(jiǎn)單說(shuō)來(lái)就是,如果沒(méi)有工作需要做,binder_thread_read()函數(shù)就進(jìn)入睡眠或返回,否則binder_thread_read()函數(shù)會(huì)從todo隊(duì)列摘下了一個(gè)節(jié)點(diǎn),并把節(jié)點(diǎn)里的數(shù)據(jù)整理成一個(gè)binder_transaction_data結(jié)構(gòu),然后通過(guò)copy_to_user()把該結(jié)構(gòu)傳到用戶態(tài)。因?yàn)檫@次傳輸帶有TF_ONE_WAY標(biāo)記,所以copy完后,只是簡(jiǎn)單地調(diào)用kfree(t)把這個(gè)binder_transaction節(jié)點(diǎn)干掉了。
???????? binder_thread_read()嘗試調(diào)用wait_event_interruptible()或wait_event_interruptible_exclusive()來(lái)等待待處理的工作。wait_event_interruptible()是個(gè)宏定義,和wait_event()類(lèi)似,不同之處在于前者不但會(huì)判斷“蘇醒條件”,還會(huì)判斷當(dāng)前進(jìn)程是否帶有掛起的系統(tǒng)信號(hào),當(dāng)“蘇醒條件”滿足時(shí)(比如binder_has_thread_work(thread)返回非0值),或者有掛起的系統(tǒng)信號(hào)時(shí),表示進(jìn)程有工作要做了,此時(shí)wait_event_interruptible()將跳出內(nèi)部的for循環(huán)。如果的確不滿足跳出條件的話,wait_event_interruptible()會(huì)進(jìn)入掛起狀態(tài)。
??????? 請(qǐng)注意給binder_transaction_data的cookie賦值的那句:
tr.cookie = target_node->cookie;binder_node節(jié)點(diǎn)里儲(chǔ)存的cookie值終于發(fā)揮作用了,這個(gè)值反饋到用戶態(tài)就是目標(biāo)binder實(shí)體的BBinder指針了。
??????? 另外,在調(diào)用copy_to_user()之前,binder_thread_read()先通過(guò)put_user()向上層拷貝了一個(gè)命令碼,在當(dāng)前的情況下,這個(gè)命令碼是BR_TRANSACTION。想當(dāng)初,內(nèi)核態(tài)剛剛從用戶態(tài)拷貝來(lái)的命令碼是BC_TRANSACTION,現(xiàn)在要發(fā)給目標(biāo)端了,就變成了BR_TRANSACTION。
?
1.2 BC_TRANSACTION事務(wù)(不帶TF_ONE_WAY標(biāo)記)
1.2.1 再說(shuō)binder_transaction()
??????? 然而,對(duì)于不帶TF_ONE_WAY標(biāo)記的BC_TRANSACTION事務(wù)來(lái)說(shuō),情況就沒(méi)那么簡(jiǎn)單了。因?yàn)閎inder驅(qū)動(dòng)不僅要找到目標(biāo)進(jìn)程,而且還必須努力找到一個(gè)明確的目標(biāo)線程。正如我們前文所說(shuō),binder驅(qū)動(dòng)希望可以充分復(fù)用目標(biāo)進(jìn)程中的binder工作線程。
??????? 那么,哪些線程(節(jié)點(diǎn))是可以被復(fù)用的呢?我們?cè)僬硪幌耣inder_transaction()代碼,本次主要截選不帶TF_ONE_WAY標(biāo)記的代碼部分:
static void binder_transaction(struct binder_proc *proc,struct binder_thread *thread,struct binder_transaction_data *tr, int reply) {struct binder_transaction *t;. . . . . .. . . . . . if (tr->target.handle){. . . . . .target_node = ref->node;}else{target_node = binder_context_mgr_node;. . . . . .}. . . . . .// 先確定target_proctarget_proc = target_node->proc;. . . . . . if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack){ struct binder_transaction *tmp;tmp = thread->transaction_stack;. . . . . .// 找到from_parent這條鏈表中,最后一個(gè)可以和target_proc匹配 // 的binder_transaction節(jié)點(diǎn),// 這個(gè)節(jié)點(diǎn)的from就是我們要找的“目標(biāo)線程” while (tmp){ if (tmp->from && tmp->from->proc == target_proc)target_thread = tmp->from;tmp = tmp->from_parent;}}. . . . . .// 要確定target_list和target_wait了,如果能找到“目標(biāo)線程”,它們就來(lái)自目標(biāo)線程,否則// 就只能來(lái)自目標(biāo)進(jìn)程了。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;}. . . . . .// 創(chuàng)建新的binder_transaction節(jié)點(diǎn)。t = kzalloc(sizeof(*t), GFP_KERNEL);. . . . . .. . . . . .t->from = thread; // 新節(jié)點(diǎn)的from域記錄事務(wù)的發(fā)起線程t->sender_euid = proc->tsk->cred->euid;t->to_proc = target_proc;t->to_thread = target_thread; // 新節(jié)點(diǎn)的to_thread域記錄事務(wù)的目標(biāo)線程t->code = tr->code;t->flags = tr->flags;t->priority = task_nice(current);// 從binder buffer中申請(qǐng)一個(gè)區(qū)域,用于存儲(chǔ)待傳輸?shù)臄?shù)據(jù)t->buffer = binder_alloc_buf(target_proc, tr->data_size,tr->offsets_size,!reply && (t->flags & TF_ONE_WAY));. . . . . .t->buffer->transaction = t;t->buffer->target_node = target_node;. . . . . .// 從用戶態(tài)拷貝來(lái)待傳輸?shù)臄?shù)據(jù)if (copy_from_user(t->buffer->data, tr->data.ptr.buffer, tr->data_size)) {. . . . . .}if (copy_from_user(offp, tr->data.ptr.offsets, tr->offsets_size)) {. . . . . .}// 遍歷每個(gè)flat_binder_object信息,創(chuàng)建必要的紅黑樹(shù)節(jié)點(diǎn) ....for (; offp < off_end; offp++){struct flat_binder_object *fp;. . . . . .. . . . . .}. . . . . .t->need_reply = 1;// 新binder_transaction節(jié)點(diǎn)成為發(fā)起端transaction_stack棧的新棧頂t->from_parent = thread->transaction_stack;thread->transaction_stack = t;. . . . . .t->work.type = BINDER_WORK_TRANSACTION;// 終于把binder_transaction節(jié)點(diǎn)插入target_list(即目標(biāo)todo隊(duì)列)了。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;. . . . . .. . . . . . }其中,獲取目標(biāo)binder_proc的部分和前一小節(jié)沒(méi)什么不同,但是因?yàn)楸敬蝹鬏敳辉贁y帶TF_ONE_WAY標(biāo)記了,所以函數(shù)中會(huì)盡力去查一個(gè)合適的“目標(biāo)binder_thread”,此時(shí)會(huì)用到binder_thread里的“事務(wù)棧”(transaction_stack)概念。
???????? 那么,怎么找“目標(biāo)binder_thread”呢?首先,我們很清楚“發(fā)起端”的binder_thread節(jié)點(diǎn)是哪個(gè),而且也可以找到“目標(biāo)端”的binder_proc,這就具有了搜索的基礎(chǔ)。在binder_thread節(jié)點(diǎn)的transaction_stack域里,記錄了和它相關(guān)的若干binder_transaction,這些binder_transaction事務(wù)在邏輯上具有類(lèi)似堆棧的屬性,也就是說(shuō)“最后入棧的事務(wù)”會(huì)最先處理。
從邏輯上說(shuō),線程節(jié)點(diǎn)的transaction_stack域體現(xiàn)了兩個(gè)方面的意義:
因此,一個(gè)工作節(jié)點(diǎn)(即binder_transaction節(jié)點(diǎn))往往會(huì)插入兩個(gè)transaction_stack堆棧,示意圖如下:
當(dāng)binder_transaction節(jié)點(diǎn)插入“發(fā)起端”的transaction_stack棧時(shí),它是用from_parent域來(lái)連接堆棧中其他節(jié)點(diǎn)的。而當(dāng)該節(jié)點(diǎn)插入“目標(biāo)端”的transaction_stack棧時(shí),卻是用to_parent域來(lái)連接其他節(jié)點(diǎn)的。關(guān)于插入目標(biāo)端堆棧的動(dòng)作,位于binder_thread_read()中,我們?cè)诤笪臅?huì)看到。
??????? 這么看來(lái),from_parent域其實(shí)將一系列邏輯上有先后關(guān)系的若干binder_transaction節(jié)點(diǎn)串接起來(lái)了,而且這些binder_transaction節(jié)點(diǎn)可能是由不同進(jìn)程、線程發(fā)起的。那么我們只需遍歷一下這個(gè)堆棧里的事務(wù),看哪個(gè)事務(wù)的“from線程所屬的進(jìn)程”和“目標(biāo)端的binder_proc”一致,就說(shuō)明這個(gè)from線程正是我們要找的目標(biāo)線程。為什么這么說(shuō)呢?這是因?yàn)槲覀兊男率聞?wù)將成為binder_transaction的新棧頂,而這個(gè)堆棧里其他事務(wù)一定是在新棧頂事務(wù)處理完后才會(huì)處理的,因此堆棧里某個(gè)事務(wù)的發(fā)起端線程可以理解為正處于等待狀態(tài),如果這個(gè)發(fā)起端線程所從屬的進(jìn)程恰恰又是我們新事務(wù)的目標(biāo)進(jìn)程的話,那就算合拍了,這樣就找到“目標(biāo)binder_thread”了。我把相關(guān)的代碼再抄一遍:
struct binder_transaction *tmp; tmp = thread->transaction_stack;while (tmp) {if (tmp->from && tmp->from->proc == target_proc)target_thread = tmp->from;tmp = tmp->from_parent; }代碼用while循環(huán)來(lái)遍歷thread->transaction_stack,發(fā)現(xiàn)tmp->from->proc == target_proc,就算找到了。
??????? 如果能夠找到“目標(biāo)binder_thread”的話,binder_transaction事務(wù)就會(huì)插到它的todo隊(duì)列去。不過(guò)有時(shí)候找不到“目標(biāo)binder_thread”,那么就只好退而求其次,插入binder_proc的todo隊(duì)列了。再接下來(lái)的動(dòng)作沒(méi)有什么新花樣,大體上會(huì)嘗試喚醒目標(biāo)進(jìn)程。
?
1.2.2 再說(shuō)binder_thread_read()
??????? 目標(biāo)進(jìn)程在喚醒后,會(huì)接著當(dāng)初阻塞的地方繼續(xù)執(zhí)行,這個(gè)已在前一小節(jié)闡述過(guò),我們不再贅述。值得一提的是binder_thread_read()中的以下句子:
// 將binder_transaction節(jié)點(diǎn)從todo隊(duì)列摘下來(lái)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;// TF_ONE_WAY情況,此時(shí)會(huì)刪除binder_transaction節(jié)點(diǎn)kfree(t);binder_stats_deleted(BINDER_STAT_TRANSACTION);}因?yàn)闆](méi)有攜帶TF_ONE_WAY標(biāo)記,所以此處會(huì)有一個(gè)入棧操作,binder_transaction節(jié)點(diǎn)插入了目標(biāo)線程的transaction_stack堆棧,而且是以to_thread域來(lái)連接堆棧中的其他節(jié)點(diǎn)的。
?
??????? 總體說(shuō)來(lái),binder_thread_read()的動(dòng)作大體也就是:
1) 利用wait_event_xxxx()讓自己掛起,等待下一次被喚醒;?
2) 喚醒后找到合適的待處理的工作節(jié)點(diǎn),即binder_transaction節(jié)點(diǎn);?
3) 把binder_transaction中的信息整理到一個(gè)binder_transaction_data中;?
4) 整理一個(gè)cmd整數(shù)值,具體數(shù)值或者為BR_TRANSACTION,或者為BR_REPLY;?
5) 將cmd數(shù)值和binder_transaction_data拷貝到用戶態(tài);?
6) 如有必要,將得到的binder_transaction節(jié)點(diǎn)插入目標(biāo)端線程的transaction_stack堆棧中。
?
1.2.3 目標(biāo)端如何處理傳來(lái)的事務(wù)
??????? binder_thread_read()本身只負(fù)責(zé)讀取數(shù)據(jù),它并不解析得到的語(yǔ)義。具體解析語(yǔ)義的動(dòng)作并不在內(nèi)核態(tài),而是在用戶態(tài)。
??????? 我們?cè)倩氐接脩魬B(tài)的IPCThreadState::waitForResponse()函數(shù)。
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult) {while (1){// talkWithDriver()內(nèi)部會(huì)完成跨進(jìn)程事務(wù)if ((err = talkWithDriver()) < NO_ERROR)break;// 事務(wù)的回復(fù)信息被記錄在mIn中,所以需要進(jìn)一步分析這個(gè)回復(fù). . . . . .cmd = mIn.readInt32();. . . . . .err = executeCommand(cmd);. . . . . .}. . . . . . }當(dāng)發(fā)起端調(diào)用binder_thread_write()喚醒目標(biāo)端的進(jìn)程時(shí),目標(biāo)進(jìn)程會(huì)從其上次調(diào)用binder_thread_read()的地方蘇醒過(guò)來(lái)。輾轉(zhuǎn)跳出上面的talkWithDriver()函數(shù),并走到executeCommand()一句。
??????? 因?yàn)閎inder_thread_read()中已經(jīng)把BR_命令整理好了,所以executeCommand()當(dāng)然會(huì)走到case BR_TRANSACTION分支:
status_t IPCThreadState::executeCommand(int32_t cmd) {BBinder* obj;RefBase::weakref_type* refs;. . . . . .. . . . . .case BR_TRANSACTION:{binder_transaction_data tr;result = mIn.read(&tr, sizeof(tr));. . . . . .mCallingPid = tr.sender_pid;mCallingUid = tr.sender_euid;mOrigCallingUid = tr.sender_euid;. . . . . .Parcel reply;. . . . . .if (tr.target.ptr) {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);} else {const status_t error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);if (error < NO_ERROR) reply.setError(error);}. . . . . .if ((tr.flags & TF_ONE_WAY) == 0){LOG_ONEWAY("Sending reply to %d!", mCallingPid);sendReply(reply, 0);}. . . . . .. . . . . .}break;. . . . . .. . . . . .return result; }??????? 最關(guān)鍵的一句當(dāng)然是b->transact()啦,此時(shí)b的值來(lái)自于binder_transaction_data的cookie域,本質(zhì)上等于驅(qū)動(dòng)層所記錄的binder_node節(jié)點(diǎn)的cookie域值,這個(gè)值在用戶態(tài)就是BBinder指針。
??????? 在調(diào)用完transact()動(dòng)作后,executeCommand()會(huì)判斷tr.flags有沒(méi)有攜帶TF_ONE_WAY標(biāo)記,如果沒(méi)有攜帶,說(shuō)明這次傳輸是需要回復(fù)的,于是調(diào)用sendReply()進(jìn)行回復(fù)。
?
2 小結(jié)
??????? 至此,《紅茶一杯話Binder(傳輸機(jī)制篇)》的上、中、下三篇文章總算寫(xiě)完了。限于個(gè)人水平,文中難免有很多細(xì)節(jié)交代不清,還請(qǐng)各位看官海涵。作為我個(gè)人而言,只是盡力嘗試把一些底層機(jī)制說(shuō)得清楚一點(diǎn)兒,奈何Android內(nèi)部的代碼細(xì)節(jié)繁雜,邏輯交疊,往往搞得人頭昏腦漲,所以我也只是針對(duì)其中很小的一部分進(jìn)行闡述而已。因?yàn)楸救四壳暗闹饕d趣已經(jīng)不在binder了,所以這篇文章耽誤了好久才寫(xiě)完,呵呵,見(jiàn)諒見(jiàn)諒。
原文地址:?https://my.oschina.net/youranhongcha/blog/167314
與50位技術(shù)專(zhuān)家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的红茶一杯话Binder(传输机制篇_下)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 红茶一杯话Binder(传输机制篇_中)
- 下一篇: 品茗论道说广播(Broadcast内部机