android Binder設計與實現五

5.3 Binder 在驅動中的表述

驅動是Binder通信的核心,系統中所有的Binder實體以及每個實體在各個進程中的引用都登記在驅動中;驅動需要記錄Binder引用 ->實體之間多對一的關系;為引用找到對應的實體;在某個進程中為實體創建或查找到對應的引用;記錄Binder的歸屬地(位於哪個進程中);通過 管理Binder的強/弱引用創建/銷毀Binder實體等等。

驅動裡的Binder是什麼時候創建的呢?前面提到過,為瞭實現實名Binder的註冊,系統必須創建第一隻雞 – 為SMgr創建的,註冊實名Binder專用的Binder實體,負責實名Binder註冊過程中的進程間通信。既然創建瞭實體也要有對應的引用:驅動將所有進程中的0號引用都預留給該Binder實體,即一開始所有進程的0號引用都指註冊實名Binder專用的Binder,無須特殊操作任何進程通過0 號引用都可以註冊實名Binder。接下來隨著應用程序通過不斷地註冊實名Binder,不斷向SMgr索要Binder的引用,不斷將Binder從一 個進程傳遞給另一個進程,越來越多的Binder以傳輸結構 – flat_binder_object的形式穿越驅動做跨進程的遷徙。由於binder_transaction_data中data.offset數組 的存在,所有流經驅動的Binder都逃不過驅動的眼睛。Binder將對每個穿越進程邊界的Binder做如下操作:檢查傳輸結構的type域,如果是 BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER則創建Binder的實體;如果是 BINDER_TYPE_HANDLE或BINDER_TYPE_WEAK_HANDLE則創建Binder的引用;如果是 BINDER_TYPE_HANDLE則為進程打開文件,無須創建任何數據結構。詳細過程可參考表7。隨著越來越多的Binder實體或引用穿過驅動在進 程間傳遞,驅動會在內核裡創建越來越多的節點或引用,當然這個過程對用戶來說是透明的。

5.3.1 Binder 實體在驅動中的表述

驅動中的Binder實體也叫‘節點’,隸屬於提供實體的進程,由struct binder_node結構來表示:

表 8 Binder節點描述結構:binder_node

  

成員 含義
int debug_id; 用於調試
struct binder_work work; 當本節點引用計數發生改變,需要通知所屬進程時,通過該成員掛入所屬進程的to-do隊列裡,喚醒所屬進程執行Binder實體引用計數的修改。
union {
struct rb_node rb_node;
struct hlist_node dead_node;
};
每個進程都維護一棵程式設計樹,以Binder實體在用戶空間的指針,即本結構的ptr成員為索引存放該進程所有的Binder實體。這樣驅動可以根據Binder實體在用戶空間的指針很快找到其位於內核的節點。rb_node用於將本節點鏈入該程式設計樹中。
銷毀節點時須將rb_node從程式設計樹中摘除,但如果本節點還有引用沒有切斷,就用dead_node將節點隔離到另一個鏈表中,直到通知所有進程切斷與該節點的引用後,該節點才可能被銷毀。
struct binder_proc *proc; 本成員指向節點所屬的進程,即提供該節點的進程。
struct hlist_head refs; 本成員是隊列頭,所有指向本節點的引用都鏈接在該隊列裡。這些引用可能隸屬於不同的進程。通過該隊列可以遍歷指向該節點的所有引用。
int internal_strong_refs; 用以實現強指針的計數器:產生一個指向本節點的強引用該計數就會加1。
int local_weak_refs; 驅動為傳輸中的Binder設置的弱引用計數。如果一個Binder打包在數據包中從一個進程發送到另一個進程,驅動會為該Binder增加引用計數,直到接收進程通過BC_FREE_BUFFER通知驅動釋放該數據包的數據區為止。
int local_strong_refs; 驅動為傳輸中的Binder設置的強引用計數。同上。
void __user *ptr; 指向用戶空間Binder實體的指針,來自於flat_binder_object的binder成員
void __user *cookie; 指向用戶空間的附加指針,來自於flat_binder_object的cookie成員
unsigned has_strong_ref;
unsigned pending_strong_ref;
unsigned has_weak_ref;
unsigned pending_weak_ref
這一組標志用於控制驅動與Binder實體所在進程交互式修改引用計數
unsigned has_async_transaction; 該成員表明該節點在to-do隊列中有異步交互尚未完成。驅動將所有發送往接收端的數據包暫存在接收進程或線程開辟的to-do隊列裡。對於異步交互,驅動做瞭適當流控:如果to-do隊列裡有異步交互尚待處理則該成員置1,這將導致新到的異步交互存放在本結構成員– asynch_todo隊列中,而不直接送到to-do隊列裡。目的是為同步交互讓路,避免長時間阻塞發送端。
unsigned accept_fds 表明節點是否同意接受文件方式的Binder,來自flat_binder_object中flags成員的FLAT_BINDER_FLAG_ACCEPTS_FDS位。由於接收文件Binder會為進程自動打開一個文件,占用有限的文件描述符,節點可以設置該位拒絕這種行為。
int min_priority 設置處理Binder請求的線程的最低優先級。發送線程將數據提交給接收線程處理時,驅動會將發送線程的優先級也賦予接收線程,使得數據即使跨瞭進程也能以同樣優先級得到處理。不過如果發送線程優先級過低,接收線程將以預設的最小值運行。
該域的值來自於flat_binder_object中flags成員。
struct list_head async_todo 異步交互等待隊列;用於分流發往本節點的異步交互包

 

每個進程都有一棵程式設計樹用於存放創建好的節點,以Binder在用戶空間的指針作為索引。每當在傳輸數據中偵測到一個代表Binder實體的 flat_binder_object,先以該結構的binder指針為索引搜索程式設計樹;如果沒找到就創建一個新節點添加到樹中。由於對於同一個進程來說內存地址是唯一的,所以不會重復建設造成混亂。

5.3.2 Binder 引用在驅動中的表述

和實體一樣,Binder的引用也是驅動根據傳輸數據中的flat_binder_object創建的,隸屬於獲得該引用的進程,用struct binder_ref結構體表示:

表 9 Binder引用描述結構:binder_ref

  

成員 含義
int debug_id; 調試用
struct rb_node rb_node_desc; 每個進程有一棵程式設計樹,進程所有引用以引用號(即本結構的desc域)為索引添入該樹中。本成員用做鏈接到該樹的一個節點。
struct rb_node rb_node_node; 每個進程又有一棵程式設計樹,進程所有引用以節點實體在驅動中的內存地址(即本結構的node域)為所引添入該樹中。本成員用做鏈接到該樹的一個節點。
struct hlist_node node_entry; 該域將本引用做為節點鏈入所指向的Binder實體結構binder_node中的refs隊列
struct binder_proc *proc; 本引用所屬的進程
struct binder_node *node; 本引用所指向的節點(Binder實體)
uint32_t desc; 本結構的引用號
int strong; 強引用計數
int weak; 弱引用計數
struct binder_ref_death *death; 應用程序向驅動發送BC_REQUEST_DEATH_NOTIFICATION或BC_CLEAR_DEATH_NOTIFICATION命令從而當Binder實體銷毀時能夠收到來自驅動的提醒。該域不為空表明用戶訂閱瞭對應實體銷毀的‘噩耗’。

 

就象一個對象有很多指針一樣,同一個Binder實體可能有很多引用,不同的是這些引用可能分佈在不同的進程中。和實體一樣,每個進程使用程式設計樹存 放所有該進程正在使用的引用。但Binder的引用可以通過兩個鍵值索引:

· 對應實體在內核中的地址。註意這裡指的是驅動創建於內核中的binder_node結構的地址,而不是Binder實體在用戶進程中的地址。實體在內核中的地址是唯一的,用做索引不會產生二義性;但實體可能來自不同用戶進程,而實體在不同用戶進程中的地址可能重合,不能用來做索引。驅動利用該程式設計樹在一個 進程中快速查找某個Binder實體所對應的引用(一個實體在一個進程中隻建立一個引用)。

· 引用號。引用號是驅動為引用分配的一個32位標識,在一個進程內是唯一的,而在不同進程中可能會有同樣的值,這和進程的打開文件號很類似。引用號將返回給應用程序,可以看作Binder引用在用戶進程中的句柄。除瞭0號引用在所有進程裡都保留給SMgr,其它值由驅動在創建引用時動態分配。向Binder 發送數據包時,應用程序通過將引用號填入binder_transaction_data結構的target.handle域中表明該數據包的目的 Binder。驅動根據該引用號在程式設計樹中找到引用的binder_ref結構,進而通過其node域知道目標Binder實體所在的進程及其它相關信 息,實現數據包的路由。

摘自 LuoXianXiong,您的夥伴

發佈留言