本文主要對Binder驅動下的數據結構和設備的初始化過程進行簡要的分析,以理清數據結構對象之間的關係。基於Linux-4.15node
Binider進程的通訊機制以下圖所示:算法
Client、Service以及ServiceManager都運行在用戶空間中,而Binder Driver運行在內核空間中,其中ServiceManager和Binder驅動由系統負責提供,Clien和Services由應用程序實現。而Binder驅動與Service、Client、Servicemanager的交互是經過open、mmap、ioctl等接口實現通訊的。爲了更好地理解上層的Binder通訊,所以有必要對下層驅動進行簡單的分析。segmentfault
binder_work
處理的工做項struct binder_work{ //用於嵌入宿主,包括進程,線程 struct list_head entry; //描述工做項類型 enum { BINDER_WORK_TRANSACTION = 1, BINDER_WORK_TRANSACTION_COMPLETE, BINDER_WORK_RETURN_ERROR, BINDER_WORK_NODE, //下面三項爲死亡通知類型 BINDER_WORK_DEAD_BINDER, BINDER_WORK_DEAD_BINDER_AND_CLEAR, BINDER_WORK_CLEAR_DEATH_NOTIFICATION, } type; }
binder_work
主要是爲了描述待處理的工做項,因爲工做項有可能屬於進程或線程,利用entry能夠將該結構體嵌入到宿主的結構當中,而list_head
的數據結構是雙向鏈表的節點,而list_head
自己是不帶有數據閾的,其經常使用的用法即嵌入到其餘數據結構如binder_work
造成鏈表,雙向鏈表的結構如圖所示:有關kernel雙向鏈表的實現,可參考Kernel list數據結構學習數組
type描述的是工做項的類型,能夠分爲cookie
BINDER_WORK_TRANSACTION
BINDER_WORK_TRANSACTION_COMPLETE
BINDER_WORK_TRANSACTION_NODE
BINDER_WORK_DEAD_BINDER
BINDER_WORK_DEAD_BINDER_AND_CLEAR
BINDER_WORK_CLEAR_DEATH_NOTIFICATION
binder_node
用於描述Binder實體對象,每一個Service組件都在Binder驅動中對應一個binder_node
實體,以描述其在內核的狀態。其數據結構以下:數據結構
struct binder_node { int debug_id; spinlock_t lock; struct binder_work work;//Binder實體須要處理的工做項 union { struct rb_node rb_node; //紅黑樹節點,宿主進程有一棵紅黑樹維護全部Binder實體 struct hlist_node dead_node;//hash節點,用於保存在全局hash列表中 }; struct binder_proc *proc; //用於指向Binder實體對象宿主進程 struct hlist_head refs; //全部Client引用了該Binder實體都會保存在該hash變量中 int internal_strong_refs;//強引用計數 int local_weak_refs; //弱引用計數 int local_strong_refs; //強引用計數 int tmp_refs; binder_uintptr_t ptr;//指向Service組件地址 binder_uintptr_t cookie;//指向Service組件內部的引用計數 struct { /* * bitfield elements protected by * proc inner_lock */ u8 has_strong_ref:1; u8 pending_strong_ref:1; u8 has_weak_ref:1; u8 pending_weak_ref:1; }; struct { /* * invariant after initialization */ u8 accept_fds:1; u8 min_priority; }; bool has_async_transaction;//Binder實體是否正在處理一個異步事務 struct list_head async_todo;//用於保存異步事務隊列 };
debug_id
用於在debug中標誌Binder實體的ID身份binder_node
包含一個binder工做項Binder_node
實體,而binder_node
實體中將會有一個rb_node
(紅黑樹)節點結構,做爲宿主紅黑樹中的一員。在這裏能夠稍微補充下關於紅黑樹的基本特性:異步
紅黑樹的優點在於可以以O(log2 n)的時間複雜度進行搜索、插入、刪除操做。此外,因爲它的設計,任何不平衡都會在三次旋轉以內解決。固然,還有一些更好的,但實現起來更復雜的數據結構可以作到一步旋轉以內達到平衡,但紅黑樹可以給咱們一個比較「便宜」的解決方案。紅黑樹的算法時間複雜度和AVL相同,但統計性能比AVL樹更高。
binder_ref
來進行描述(後面會介紹),所以將全部引用了該binder實體對象的全部引用都放在了哈希表當中,即hlist_head refs
。internal_strong_refs,local_strong_refs
均描述binder實體對象的強引用數,local_weak_refs
描述了其弱引用數。has_strong_ref,has_weak_ref,pending_strong_ref,pending_weak_ref
則是相關的標誌位。其中又有以下規則:async
has_strong_ref,has_weak_ref
均置1。has_async_transaction
描述是否正在處理異步事務。通常而言Binder驅動程序都將一個事務保存在線程的todo隊列中,代表線程將要處理該事務。而每一個事務和一個binder實體相關聯,表示該事務的處理目標對象。而經過binder_node
是能夠找到對應的Service組件,那麼與該Binder對應的Service組件將會處理該事務。注:異步的概念就是單向的進程間通訊請求,即不須要等待應答的進程間通訊請求。同步則須要等待應答纔會作下一步的動做。因爲不須要應答,Binder驅動認爲異步事務優先級比同步優先級要低。
min_priority
表示Binder實體在處理來自Client的請求時,處理線程(Server進程中的線程)所應該具有的最小線程優先級accept_fds
描述Binder實體對象是否能夠接收包含有文件描述符的進程通訊數據。爲1時能夠接收。binder_ref_death
死亡接收通知struct binder_ref_death { /** * @work: worklist element for death notifications * (protected by inner_lock of the proc that * this ref belongs to) */ struct binder_work work; binder_uintptr_t cookie; };
binder_ref_death
用來描述Binder的死亡接收通知。正常而言,當Service組件被其餘Client組件所引用時,是不能被銷燬的,可是存在Service之外崩潰的情景,此時,Client可以經過Binder驅動得知Service Dead的消息。所以client會將一個可以接收死亡通知的地址註冊在Binder驅動程序中。ide
Client接收Binder驅動死亡通知情景:函數
binder_ref_death
結構體,並將這些結構體添加到Client進程的todo隊列中等待處理,死亡通知類型設置爲BINDER_WORK_DEAD_BINDER
BINDER_WORK_DEAD_BINDER
。Client註銷Binder驅動死亡通知情景
binder_ref_death
結構體,並將類型work修改爲爲BINDER_WORK_CLEAR_DEATH_NOTIFICATION
,而後將該結構體封裝成一個工做項添加到client進程的todo隊列等待處理。BINDER_WORK_DEAD_BINDER_AND_CLEAR
。與Service組件在Binder驅動中存在一個Binder實體相似,Client在Binder驅動中也對應一個Binder引用對象以描述其在內核的狀態。一樣也是使用強弱計數來控制生命週期。
binder_ref_data
將debug_id,desc,strong,weak
等屬性封裝起來。
struct binder_ref_data { int debug_id; uint32_t desc; int strong; int weak; };
debug_id
標誌Binder引用對象,幫助Binder驅動程序進行調試。struct binder_ref { /* Lookups needed: */ /* node + proc => ref (transaction) */ /* desc + proc => ref (transaction, inc/dec ref) */ /* node => refs + procs (proc exit) */ struct binder_ref_data data; struct rb_node rb_node_desc;//以desc爲紅黑樹節點 struct rb_node rb_node_node;//以binder實體爲紅黑樹節點 struct hlist_node node_entry;//哈希節點,放置在Binder實體哈希列表中 struct binder_proc *proc;//binder引用對象的宿主 struct binder_node *node;//指向binder實體 struct binder_ref_death *death; };
binder_proc
爲結構的宿主中(進程或者線程)會有兩棵紅黑樹(事實上宿主有多棵紅黑樹進行管理數據結構)保存Binder引用對象,而rb_node_desc
和rb_node_node
分別是紅黑樹中的節點,分別是以句柄值(desc)和對應的Binder實體對象的地址來做爲保存的關鍵字。binder_node *node
是引用指向的Binder實體對象。binder_ref_death
結構體保存在death中。binder_buffer
內核緩衝區binder_buffer
用來描述內核緩衝區,它是用於進程通訊中傳輸數據的。每一個參與Binder進程間通訊的進程在Binder驅動進程中都有一個內核緩衝區列表,用於保存爲進程分配的內核緩衝區,其中binder_buffer
中的entry就是鏈表的節點。
struct binder_buffer { struct list_head entry; /* free and allocated entries by address */ struct rb_node rb_node; /* free entry by size or allocated entry */ /* by address */ unsigned free:1; //判斷該內核緩衝區是由空閒內核緩衝區紅黑樹仍是正在使用的空閒內核緩衝區紅黑樹 unsigned allow_user_free:1; //置1時,Service組件會請求Binder驅動程序釋放內核緩衝區 unsigned async_transaction:1; //關聯的事務是否爲異步 unsigned free_in_progress:1;//判斷是否正在釋放內核緩衝區 unsigned debug_id:28; struct binder_transaction *transaction; struct binder_node *target_node;//內核緩衝區正在使用的binde實體對象 size_t data_size; size_t offsets_size;//數據緩衝區的偏移量,偏移數組保存Binde對象 size_t extra_buffers_size; void *data;//指向大小可變的數據緩衝區 };
rb_node
就是在指紅黑樹的節點,而區別是那棵樹是經過變量free來區別,假如free爲1,那麼是空閒內核緩衝區。
注:Binder驅動中常常看到unsigned 變量:n這樣的寫法,那是由於C中並無bool類型,C Programmer會利用unsigned(這裏的unsigned實際是unsigned int)並指定須要用的bits數目來實現,但令我疑惑的是雖然只指定使用其中的n bits,可是實際上仍是以unsigned int的大小來存儲數據的,但當struct程序是以
__attribute((packed))
來規定數據規格來編譯時,那麼其數據結構所佔的大小就爲1
binder_transaction
爲事務結構體,用以描述該內核緩衝區正在處理哪個事務,值得留意的是binder_transaction
中也存在者指向內核緩衝區的指針,即它們的關係是雙向的。前面說到過事務與對應的Binder對象是相關聯的,這是由於binder_buffer
中存在着指向binder實體的指針,那麼transaction經過找到內核緩衝區便可找到相關聯的binder實體。從而將內核緩衝區內容交給Service處理。allow_user_free
爲1時,則Service處理完事務後將會請求Binder驅動程序釋放內核緩衝區。data_size
、offset_size
分別用來描述數據緩衝區,其中data指向的是實際的數據內容。數據緩衝區保存的數據分爲兩種類型,一種是普通數據,另外的是Binder對象,Binder驅動不關心數據的內容,可是要知道其中的Binder對象,從而根據它們來維護內核中實體對象以及引用對象的生命週期。offset_size
中。struct binder_proc { struct hlist_node proc_node; struct rb_root threads;//Binder線程池根節點,以線程ID做爲關鍵字 struct rb_root nodes;//Binder實體對象紅黑樹根節點 struct rb_root refs_by_desc;//binder引用對象紅黑樹,以desc爲關鍵字 struct rb_root refs_by_node;//binder引用對象紅黑樹,以binder_node地址爲關鍵字 struct list_head waiting_threads; int pid; struct task_struct *tsk; struct files_struct *files; struct mutex files_lock; struct hlist_node deferred_work_node;//保存進程能夠延遲執行的工做項,爲哈希列表 int deferred_work; bool is_dead; struct list_head todo;//待處理工做項,當進程接收到進程間通訊請求時,Binder驅動封裝工做項加入todo隊列 wait_queue_head_t wait;//等待隊列 struct binder_stats stats;//統計進程數據 struct list_head delivered_death; int max_threads; int requested_threads; int requested_threads_started; int tmp_ref; long default_priority; struct dentry *debugfs_entry; struct binder_alloc alloc; struct binder_context *context; spinlock_t inner_lock; spinlock_t outer_lock; };
binder_proc
用來描繪使用Binder通訊機制進行通訊的進程,當進程調用函數open時打開/dev/binder時,Binder驅動將會爲其建立一個binder_proc
結構體,並將其保存在一個全局的哈希表當中,其中proc_node
即爲該進程在哈希表的節點。struct binder_alloc { struct mutex mutex; struct vm_area_struct *vma;//內核緩衝區的用戶空間地址 struct mm_struct *vma_vm_mm; void *buffer;//內核緩衝區的內核空間地址 ptrdiff_t user_buffer_offset;//內核緩衝區中用戶空間地址與內核空間地址差值 struct list_head buffers;//指向小塊內核緩衝區鏈表的頭部 struct rb_root free_buffers;//空閒內核緩衝區紅黑樹根節點 struct rb_root allocated_buffers;//已分配內核緩衝區紅黑樹根節點 size_t free_async_space;//當前能夠保存異步事務數據的內核緩衝區大小 struct binder_lru_page *pages; size_t buffer_size;//binde驅動爲進程分配的內核緩衝區大小 uint32_t buffer_free; int pid; };
refs_by_desc
,refs_by_node
,管理內核緩衝區的free_buffers
,allocated_buffers
,管理Binder實體對象的nodes。user_buffer_offset
保存,所以只要知道其中一個地址即可以經過該相差值得出另一個空間的地址。另外因爲用戶空間的地址高於內核空間地址,因此計算user_buffer_offset
的差值時,都是使用用戶空間地址減去內核空間地址。進程收到進程間通訊的請求時,將會把請求封裝成工做項加入到進程的待處理工做項todo隊列中。線程池中的wait的等待隊列維護着空閒的Binder線程,當線程的宿主進程增長了新工做項後,Binder驅動就會喚醒這些線程以便它們可以處理新工做項。
deffered_work_node
正是哈希表的節點,並使用deffered_work
維護延遲工做項的類型。struct binder_thread { struct binder_proc *proc; struct rb_node rb_node; struct list_head waiting_thread_node; int pid; int looper; /* only modified by this thread */ bool looper_need_return; /* can be written by other thread */ struct binder_transaction *transaction_stack; struct list_head todo; struct binder_error return_error; struct binder_error reply_error; wait_queue_head_t wait; struct binder_stats stats; atomic_t tmp_ref; bool is_dead; };
enum { BINDER_LOOPER_STATE_REGISTERED = 0x01, BINDER_LOOPER_STATE_ENTERED = 0x02, BINDER_LOOPER_STATE_EXITED = 0x04, BINDER_LOOPER_STATE_INVALID = 0x08, BINDER_LOOPER_STATE_WAITING = 0x10, BINDER_LOOPER_STATE_POLL = 0x20, };
參考文獻: 《Android系統源代碼情景分析》