Binder驅動和內核其餘模塊同樣,使用C語言實現,爲面向對象的進程間通訊提供底層支持。
複製代碼
3.0 Binder框架四個角色--類比互聯網:java
並非全部進程中的binder都須要註冊給SM廣而告之。例如某個client進程能夠經過已經創建的Binder鏈接,將本身的binder實體引用發送給Server進程。Server進程使用該引用請求Client進程,此過程進程雙方的角色就發生了互換。例如應用啓動過程當中的ApplicationThread
複製代碼
基本格式:命令+數據。使用ioctl(fd,cmd,arg)交互。node
數據格式一樣爲:命令+數據。均存放在write_buffer域指向的內存空間,多條命令可連續存放。數據緊跟着命令後面,數據格式會有所不一樣.
複製代碼
Binder寫操做命令字 | arg | |
---|---|---|
BC_TRANSACTION | 最經常使用命令之一,Client經過驅動向Server發送請求數據, | struct binder_transaction_data 利用binder_transaction_data中的flag域區分同步異步。flag域中的TF_ONE_WAY爲1,則爲異步,Client無需等待BC_REPLY數據包。不然須要等到數據包接收,纔算完成一次交互。 |
BC_REPLY | 最經常使用命令之二Server經過驅動向Client發送應答數據 | |
BC_ACQUIRE_RESULT | ||
BC_ATTEMPT_ACQUIRE | ||
BC_FREE_BUFFER | 釋放一塊映射的內存 | |
BC_INCREFS BC_ACQUIRE BC_RELEASE BC_DECREFS | 管理Binder引用計數 | 32位Binder引用號 |
BC_INCREFS_DONE BC_ACQUIRE_DONE | Binder實體處理引用計數後,給Binder驅動的反饋 | |
BC_REGISTER_LOOPER | 通知binder驅動,Server端建立了一個線程 | Server端線程池管理 |
BC_ENTER_LOOPER | 通知binder驅動,Server端某個線程進入主循環,能夠接收數據 | |
BC_EXIT_LOOPER | 通知binder驅動,Server端某個線程退出主循環,再也不接收數據 | |
BC_REQUEST_DEATH_NOTIFICATION | client要求binder驅動在Binder實體銷燬時,client能獲得通知 | 參數1:uint32 *ptr; 須要獲得死亡通知的Binder引用 參數2:void * *cookie: 與死亡通知相關的信息,驅動會在發出死亡通知時返回給發出請求的進程。 |
BC_DEAD_BINDER_DONE | client收到實體死亡通知,刪除引用,並告知驅動 |
格式與寫操做一致,一樣能連續存放。linux
Binder讀操做命令字 | |
---|---|
BR_ERROR | 內部錯誤? |
BR_OK BR_NOOP |
操做成功,與BR_TRANSACTION_COMPLETE區別在哪? |
BR_SPAWN_LOOPER | 驅動發現接收方線程不夠用,要求接收方建立線程 |
BR_TRANSACTION BR_REPLY |
|
BR_ACQUIRE_RESULT BR_ATTEMPT_ACQUIRE |
|
BRFINISHED | |
BR_DEAD_REPLAY | 交互過程當中若是發現對方進程或線程已經死亡則返回該消息 |
BR_TRANSACTION_COMPLETE | 驅動告知發送方「發送成功」,與接收方是否返回請求數據無關 |
BR_INCREFS BR_ACQUIRE BR_RELEASE BR_DECREFS |
引用與計數管理 |
BR_DEAD_BINDER BR_CLEAR_DEATH_NOTIFICATION_DONE |
告知Client進程,Binder實體死亡通知處理相關 |
BR_FAILED_REPLY | 發送非法引用號,則返回此 |
成員定義 | |
---|---|
union { size_t handle; void *ptr; } target; |
對於發送方 target指向目的地。target.handle(即句柄)存放binder引用 當數據包到達接收方時,target已被驅動賦予了binder實體對象內存的指針,存放在target.ptr |
void *cookie; | 發送方忽略該成員,binder實體建立時,接收方自定義的任意數值,與binder指針相關的額外信息,驅動也基本不關心該成員 |
unsigned int code; | Server端定義的公共接口函數編號(業務碼?) |
unsigned int flags; | 交互相關標誌位,其中最重要的是TF_ONE_WAY位 |
pid_t sender_pid; | 發送方進程ID和用戶ID,由驅動負責填入 |
uid_t sender_euid; | |
size_t data_size; | Server定義的公共接口函數關心的數據相關,傳輸中的Binder,以flat_binder_object的形式包含在buffer中. buffer指向部分會copy到映射區 |
size_t offsets_size; | |
union{ struct { *buffer;*offsets} ptr; uint8_t buf[8]; }data; |
Binder存在於系統的這些部分:設計模式
在不一樣的部分,Binder實現的功能不一樣,表現形式也不同。咱們須要關心其扮演的角色和使用的數據結構。 緩存
Binder本質是底層通訊的方式,與具體服務無關。Server必須提供一套接口函數,以便Client遠程訪問。
這時一般採用Proxy設計模式,將接口函數定義在一個抽象類中安全
如何將Binder和Proxy設計模式結合是應用程序實現面向對象Binder通訊的根本問題 服務器
Binder實體類實現兩個類的全部虛函數:markdown
Client中會有代理類實現這兩個類:cookie
Binder傳輸結構爲flat_binder_object 網絡
屬性 | 含義 |
---|---|
unsigned long type; | binder類型: BINDER_TYPE_BINDER 表示傳遞的是Binder實體,而且指向該實體的都是強引用; BINDER_TYPE_WEAK_BINDER 表示傳遞的是Binder實體,而且指向該實體的都是弱引用; BINDER_TYPE_HANDLE 表示傳遞的是Binder強引用; BINDER_TYPE_WEAK_HANDLE 表示傳遞的是Binder弱引用; BINDER_TYPE_FD 表示傳遞的是文件形式Binder; |
unsigned long flags | 只對第一次傳遞Binder實體時有效,由於是用於驅動在內核中建立Binder實體節點, 內容暫不關心了就。 |
union{ void * binder; signed long handle; }; |
void * binder; 指向Binder實體在Server進程中的內存地址,傳遞Binder實體時使用。 signed long handle; 存放Binder在進程中的引用號,傳遞Binder引用時使用。 |
void * cookie; | 只對Binder實體有效,存放Binder相關附加信息 |
不管是Binder實體仍是引用都從屬某個進程,因此不能透明的在進程間傳遞,必須通過驅動「翻譯」。
binder類型 | 在發送方的操做 | 在接收方的操做 |
---|---|---|
BINDER_TYPE_BINDER BINDER_TYPE_WEAK_BINDER | 只有實體所在的進程才能發送該類型的Binder。第一次發送時,驅動爲其在內核中建立節點,並保存binder,cookie,flag域 | 若是是第一次接收該Binder則建立實體在內核中的引用;將handle域替換爲新建的引用號;將type域替換爲INDER_TYPE_(WEAK_)HANDLE |
BINDER_TYPE_HANDLE BINDER_TYPE_WEAK_HANDLE | 驅動根據handle域提供的引用號查找binder在內核中的引用。若是找不到,則不合法。 | 1.若是收到的binder實體位於接收進程:將ptr域替換爲保存在節點中的binder值,cookie值替換,type替換爲BINDER_TYPE_(WEAK_)BINDER 2.若是收到的Binder實體不在接收進程中:若是第一次接收則建立實體在內核中的引用;將handle域替換爲新建的引用號 |
BINDER_TYPE_FD | 略 |
驅動是Binder通訊的核心:
Binder實體在驅動中,稱爲「節點」,由binder_node 結構表示。根據傳輸數據中的flat_binder_object,構建binder_node節點。
屬性 | 含義 |
---|---|
int debug_id; | 用於調試 |
struct binder_work work; | ? |
union{ struct rb_node rb_node; struct hlist_node dead_node; }; |
內核爲每一個進程維護一顆紅黑樹,存放此進程的全部binder實體的用戶空間指針(ptr).rb_node即爲binder_node在紅黑樹中的形式。 待銷燬節點,待切斷全部引用後,完全銷燬。 |
struct binder_proc *proc; | 該節點所屬進程 |
struct hlist_head refs; | 該節點全部引用隊列 |
int internal_strong_refs; | 強指針計數器 |
int local_strong_refs; | 驅動爲傳輸中的Binder設置的強引用計數 |
int local_weak_refs; | 驅動爲傳輸中的Binder設置的弱引用計數 |
void __user * ptr; | 指向用戶空間內存地址,來自flat_binder_object的binder成員 |
void __user *cookie; | |
unsigned pending_strong_ref; unsigned has_strong_ref; unsigned has_weak_ref; unsigned pending_weak_ref; |
引用計數相關 |
unsigned has_async_transaction; | 表面該節點在to-do隊列中有異步交互還沒有完成。to-do隊列是驅動爲接收進程或線程開闢的,用於暫存發往接收端的數據包。 若是to-do中有還沒有完成的異步交互,則將新到的異步交互,存放在async隊列中。爲同步交互讓路,避免長時間阻塞發送端 |
struct list_head aysnc_todo; | 異步交互等待隊列;分流發往本節點的異步交互包 |
unsigned accept_fds; | 文件binder相關,略 |
int min_priority; | 設置處理請求的線程的最低優先級,值來自於flat_binder_object中的flags成員 區別在哪? |
表述爲binder_ref,根據傳輸數據中的flat_binder_object構建。
屬性 | 含義 |
---|---|
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結構地址,非實體在用戶進程中的內存地址。內核地址是惟一的,而用戶進程地址可能會重合
驅動爲引用分配的32位標識,Binder引用在用戶進程中的句柄,單個進程內是惟一的。經過引用號在紅黑樹中找到binder_ref,經過ref的node域找到Binder實體相關信息。
Binder會預先建立一堆線程,這些線程會阻塞在等待隊列上。一旦有數據請求,驅動會從隊列中喚醒一個線程來處理。
Binder如何管理線程:
1.隊列用以緩解請求與處理的「供需矛盾」:
2.數據包進入進程or線程to-do隊列規則:
3.驅動遞交同步交互和異步交互規則:
爲了減小異步交互對同步發送端的阻塞。異步要爲同步讓步。即一旦to-do隊列中有異步在處理,新提交的異步只能特製的async_todo隊列。而新提交的同步,依然能夠進入to-do隊列
性能:單次拷貝<br /> 安全性:PID/UID可控<br /> 易用性:面向對象設計
複製代碼