通過前面的學習我們知道,binder通信涉及搭配兩個程序,A,B:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TPR1kMjpXT4FkeNBDOsJGcohVYsR2MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLyAjN0AzN0kDMzADNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
A發送一個BC_TRANSACTION消息給驅動,驅動把其轉化為BR_TRANSACTIONB轉發給B。
B接收到一個類型為BR_TRANSACTIONB的資料,然後發送一個BC_REPLY的消息給驅動,驅動把其轉化為BR_REPLY,然後回複給A程序。
簡單概述
在前面的分析中,我們沒有詳細的分析,驅動程式中,如何查找到消息的目的程序。該小節我們進行詳細的分析。主要解決以下兩個問題即可:
1.發給誰?handle隻表明了程序,是發送給程序,還是發送給線程?
2.回複給誰?回複的時候沒有了handle表明目的程序,必定在某個地方記錄了發送者(即後面要講解的transaction_stack)
在擷取服務的時候,我們獲得的handle代表一個程序,但是需要發送的目标可能是某個線程。
如test_client要發送資料給test_sever。
test_sever中存在多個線程(binder_thread),其在binder驅動中,對應一個binder_proc結構體,并且還有一個todo連結清單。但是binder_thread中也存在一個todo連結清單。那麼問題來了,發送資料的時候,我們是把資料放入binder_proc的todo連結清單還是放入binder_thread的todo連結清單呢?
一般來說,test_client會把資料放入到binder_proc的todo連結清單中,喚醒等待于binder_proc.wait的空閑線程。但是也有特殊情況,對于雙向傳輸,則放在binder_thread.todo中,然後喚醒該線程。那怎麼分辨是否為雙向傳輸呢?其也是通過該小節要講解的transaction_stack機制來判斷。
下面我們進行情景分析。
情景分析
下面是一個test_client與test_server執行的流程圖:
test_client發送一個BC_TRANSACTION,test_server接收到一個BR_TRANSACTION。test_server處理完成之後發送BC_REPLY給test_client,test_client接收到一個BR_REPLY。
根據這四個過程,我們看看transaction_stack是如何其作用的,前面分析過,binder_ioctl最終會調用到binder_transaction()函數,
static void binder_transaction(struct binder_proc *proc,struct binder_thread *thread,struct binder_transaction_data *tr, int reply,binder_size_t extra_buffers_size)
if (reply) {
......
} else {
struct binder_ref *ref;
/*根據tr->target.handle獲得一個struct binder_ref *ref*/
ref = binder_get_ref_olocked(proc, tr->target.handle,true);
/*根據ref獲得struct binder_node *target_node,即目的節點*/
target_node = binder_get_node_refs_for_txn(ref->node, &target_proc,&return_error);
......
......
下面是一個詳細的框圖,我們圍繞該框圖進行講解:
BC_TRANSACTION過程
1.在一看是的時候,test_client與test_server是非雙向傳輸的,是以資料将放在test_server的binder_proc.todo的連結清單中,喚醒test_server.binder_proc.wait上等待到得線程
static void binder_transaction(struct binder_proc *proc,struct binder_thread *thread,struct binder_transaction_data *tr, int reply,binder_size_t extra_buffers_size)
/*發送之後還想得到回複*/
} else if (!(t->flags & TF_ONE_WAY)) {
/*一個連結清單的操作,入棧。t中包含了甚多資訊,如發送給誰,從哪裡開始發送等等*/
t->from_parent = thread->transaction_stack;
/*程序在沒有建立線線程的時候,也存在一個主線程*/
binder_enqueue_work(proc, tcomplete, &thread->todo);
2.test_client中的binder_thread存在.transaction_stack指向一個傳輸結構。
BC_TRANSACTION過程
test_server接收到BR_TRANSACTION之後: