Binder驅動中每一次傳輸都表明一個傳輸事件,經過binder_transaction來描述。node
struct binder_thread { struct binder_proc *proc; /* 線程所屬的而進程 */ struct rb_node rb_node; /* 紅黑樹節點,插入到binder_proc->threads中 */ int pid; /* 線程PID */ int looper; /* 線程looper狀態,用上面的枚舉描述 */ struct binder_transaction *transaction_stack; /* Binder傳輸棧 */ struct list_head todo; /* Binder線程todo隊列 */ uint32_t return_error; /* 寫失敗時的返回錯誤碼 */ uint32_t return_error2; /* 寫失敗時的返回錯誤碼2 */ wait_queue_head_t wait; /* Binder線程等待隊列 */ struct binder_stats stats; /* Binder線程統計信息 */ }; struct binder_transaction { int debug_id; /* 全局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; /* 傳輸數據的buffer */ unsigned int code; /* 傳輸的命令碼 */ unsigned int flags; /* 傳輸標識,例如TF_ONE_WAY */ long priority; /* 傳輸優先級 */ long saved_priority; /* 傳輸缺省優先級 */ kuid_t sender_euid; /* 發送端的uid */ }
Binder傳輸時經過Binder線程爲主體進行交互的,因此Binder線程中會保存Binder傳輸事件,在binder_thread中使用transaction_stack作爲一種棧的形式來記錄全部的傳輸事件。transaction_stack保存着當前正在進行的傳輸事件,採起壓棧的方式保存,因此棧頂爲最新的傳輸,棧底爲最先的傳輸。這種方式也表現了線程中傳輸事件的依賴關係,一個傳輸事件必須等待上一個棧的傳輸完成才能進行。函數
經過一個簡單的傳輸來分析傳輸事件的流程。oop
A1發起的傳輸事件(暫且稱爲T1_tr)在BC_TRANSACTION過程當中經過from_parent入站到A1的transaction_stack中。源碼分析
static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply) { ...... // 分配binder transaction t = kzalloc(sizeof(*t), GFP_KERNEL); ...... // 分配binder_work用於處理傳輸完成 tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL); ...... // 同步的非reply傳輸,設置當前線程爲from if (!reply && !(tr->flags & TF_ONE_WAY)) t->from = thread; else t->from = NULL; t->sender_euid = proc->tsk->cred->euid; // 設置傳輸的目標進程和線程 t->to_proc = target_proc; t->to_thread = target_thread; t->code = tr->code; t->flags = tr->flags; t->priority = task_nice(current); ...... if (reply) { ...... } else if (!(t->flags & TF_ONE_WAY)) { // 當前線程的傳輸入棧 t->need_reply = 1; t->from_parent = thread->transaction_stack; thread->transaction_stack = t; } else { ...... // 將傳輸添加到目標隊列中 t->work.type = BINDER_WORK_TRANSACTION; list_add_tail(&t->work.entry, target_list); // 將傳輸完成添加到當前線程todo隊列中 tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; list_add_tail(&tcomplete->entry, &thread->todo); // 喚醒目標線程或進程 if (target_wait) wake_up_interruptible(target_wait); return; ...... }
能夠看到,在傳輸發起時先分配內存給T1_tr,以後對T1_tr的數據進行賦值。而後入棧,將T1_tr的from_parent指向A1的transaction_stack,在將transaction_stack指向T1_tr。若是A1中已有進行中的傳輸,則代表原有的傳輸依賴T1_tr完成才能繼續。在這個簡單的例子中,以前沒有傳輸發生,因此from_parent=null。入棧完成後須要將T1_tr掛到B1線程的todo隊列中,已便B1能夠獲取到T1_tr,這時經過T1_tr的work完成的。ui
在BR_TRANSACTION中,會將T1_tr入棧到B1的transaction_stack中。spa
static int binder_thread_read(struct binder_proc *proc, struct binder_thread *thread, binder_uintptr_t binder_buffer, size_t size, binder_size_t *consumed, int non_block) { ...... 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 { ...... switch (w->type) { // binder_transaction()將工做BINDER_WORK_TRANSACTION加入隊列後喚醒目標進程 case BINDER_WORK_TRANSACTION: { // 經過work中獲取binder_transaction t = container_of(w, struct binder_transaction, work); } break; ...... // 從隊列中移除當前工做事件 list_del(&t->work.entry); t->buffer->allow_user_free = 1; if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) { // 同步傳輸時,命令爲BR_TRANSACTION的狀況下,將工做事件入棧 t->to_parent = thread->transaction_stack; t->to_thread = thread; thread->transaction_stack = t; } else { ...... } ...... }
BR_TRANSACTION處理傳輸事件的大體流程就是:B1獲取todo隊列進行處理,經過工做任務得到到傳輸事件T1_tr,而後將T1_tr經過to_parent入棧到B1的transaction_stack中。線程
T1_tr的出棧是經過BC_REPLY完成的,同時在BC_REPLY過程當中會建立新的傳輸事件(暫且稱爲T1_re)用於Reply。T1_re時沒有入棧動做的,由於Reply時不須要後續處理的。debug
static void binder_pop_transaction(struct binder_thread *target_thread, struct binder_transaction *t) { // 經過from_parent來出棧,用於傳輸發起端出棧 if (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); // 刪除TRANSACTION狀態 binder_stats_deleted(BINDER_STAT_TRANSACTION); } ...... static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply) { ...... 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 (target_thread) { e->to_thread = target_thread->pid; target_list = &target_thread->todo; target_wait = &target_thread->wait; } else { ...... } ...... // reply傳輸的from爲空 if (!reply && !(tr->flags & TF_ONE_WAY)) t->from = thread; else t->from = NULL; ...... if (reply) { // 從目標線程中出棧 binder_pop_transaction(target_thread, in_reply_to); } 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; ...... }
BC_REPLY是由B1發起的,因此當前線程的T1_tr出棧是經過to_parent完成。其目標線程A1是由統一的出棧函數binder_pop_transaction,經過from_parent完成。出棧完成後將T1_tr釋放掉。在BC_REPLY過程當中,新建的傳輸事件t就是以前說的T1_re,用於傳輸Reply事件。能夠看到T1_re並無入棧。
T1_re的釋放是在BR_REPLY中完的,這裏再也不詳細分析。全部傳輸事件完成時都會釋放相應的事件。調試
根據上面的源碼分析,能夠將傳輸事件的變化用下圖表示。code
經過上面對簡單傳輸的分析,能夠清晰的看清傳輸事件的流程。接下分析兩個進程間的反覆調用,看看Binder事件是如何傳輸的。
首先Proc A向Proc B發起IPC傳輸T1,這時跟上面的例子相同,是Thread A1調用到Thread B1。B1收到BR_TRANSACTION後,在處理的過程當中向Proc A發起IPC傳輸T2,這時Porc A是使用哪一個線程來處理T2的?若是Proc A在這個處理過程當中又向Proc B發起IPC傳輸T3,那麼將會發生什麼?看看源碼中是如何處理這種狀況的。
static void binder_transaction(struct binder_proc *proc, struct binder_thread *thread, struct binder_transaction_data *tr, int reply) { ...... if (reply) { ...... } else { ...... if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { 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; } } } ...... }
尋找Binder對端線程的核心代碼就是上面這段。若是當前線程傳輸棧不爲空,則代表當前線程還有未完成的傳輸。而後沿着from_parent尋找是否有來自對端進程的傳輸,若是有就複用這個傳輸的線程來處理這個新發起的傳輸。這中複用線程的方式時合理的,由於from_parent這條鏈表記錄了傳輸調用的流程,鏈表內的傳輸有依賴關係,鏈表中的一個節點上的傳輸依賴上一個節點傳輸的完成。因此鏈表中的節點放在同一線程中處理不會產生影響,而且能夠節約線程。
在咱們的例子中,T1的進程A就是T2的對端進程,因此T2將使用A1作爲目標線程。而後A1向進程B發起T3傳輸時,B1一樣會被選作目標線程。用一個圖來表示可能會更清晰些。
兩個進程間調用的例子給人一個錯覺,好像from_parent與to_parent記錄着同一個傳輸,實際上並非這樣。看一個多進程調用的例子加深理解一下。
這裏的關鍵點時傳輸T3時,C1是如何找到A1的。尋找流程仍是上面那段代碼,沿着from_parent最終找到T1,T1->from=A1,決定了T3會從C1調用到A1。