Android Binder學習(一)之Binder中的資料結構
備註:雙向箭頭表示雙向連結串列,各成員是串聯起來的。
在分析Android framework程式碼時,遇到最多的就是binder程序間通訊了。如果只知道怎麼用,也不影響我們日常的工作。但如果你想閱讀binder原始碼,就需要花點時間了。相對與linux核心來說,Android可以理解成一個“應用”了,所以分析起來大家一定要有信心。在寫部落格之前,我也看過老羅講的binder部分,老羅大神寫的太詳細了。本文以及後面的幾篇博文算是自己學習binder的學習筆記吧(自己敲出來的,理解時間會長些)。本文屬個人觀點,如有問題,歡迎指正。
注意:本文是基於android5.1分析的
一、Binder模型
和學習其它技術一樣,首先我們一起看看Binder粗略的線條。一看就知道是經典的CS模型,但是圖中表面上看,我們直接和對應的服務交流。其實中間還加了一個"總司令“servicemanager,需要註冊的服務都掛在它名下,具體的怎麼掛的,後面的博文會描述的。這裡簡稱servicemanager為sm。
粗略的線條理解:
1.service都是由server註冊到sm中的。在註冊時先得到一個sm的代理物件,將當前new出來的service物件的引用技術和物件引用打包傳給kernel。並以binder_node的形式保留在binder驅動中的binder_context_mgr_node全域性連結串列中。可以理解成在sm中有一個服務列表,需要什麼樣的服務,就點什麼服務。
2.client在請求相應的服務時,會在kernel中由建立一個binder_ref引用物件(關鍵是給了一個desc描述符),並寄掛在當前程序binder_proc的下面,而這個binder_ref物件會返回到使用者空間,根據這個desc描述符生成對應的代理物件,其中這個desc就儲存在Bpbinder類物件的私有成員mHandle中。
3.當client通過代理物件使用服務功能時,就會將上面的desc描述符傳給binder kernel驅動,進而就可以找到對應的binder_node,接著就可以找到對應的service 使用者空間物件了。最後我們就可以使用對應的服務了。
二、binder資料結構
1.binder_node
在drivers/staging/android/binder.c中
struct binder_node {
int debug_id;
struct binder_work work;
union {
struct rb_node rb_node;
struct hlist_node dead_node;
};
struct binder_proc *proc; //binder實體對應的程序,可以理解成service是在哪個server程序中
struct hlist_head refs; //binder引用物件的連結串列,可以理解成使用該服務的“客戶端”構成的連結串列
int internal_strong_refs;
int local_weak_refs;
int local_strong_refs;
void __user *ptr; //服務在使用者空間引用技術物件的地址
void __user *cookie; //服務物件在使用者空間對應的地址。
unsigned has_strong_ref:1;
unsigned pending_strong_ref:1;
unsigned has_weak_ref:1;
unsigned pending_weak_ref:1;
unsigned has_async_transaction:1;
unsigned accept_fds:1;
unsigned min_priority:8;
struct list_head async_todo;
};
上面這個結構體是binder實體,它是每一個servie元件在核心中的存在形式。核心可以通過這個物件找到使用者空間service元件物件。
(01) rb_node和dead_node屬於一個union。如果該Binder實體還在使用,則通過rb_node將該節點連結到proc->nodes紅黑樹中;否則,則將該Binder實體通過dead_node連結到全域性雜湊表binder_dead_nodes中。
(02) proc,它是binder_proc(程序上下文資訊)結構體物件。目的是儲存該Binder實體的程序。
(03) refs,它是該Binder實體所有引用組成的連結串列(這裡就是程序使用的服務引用物件)。
2.binder_ref
binder_ref是一個代理物件在核心中的表現形式,每一個代理物件在核心中都有一個binder_ref中,並掛在客戶端所屬的binder_proc下面。它定義在driver/staging/android/binder.c中。
struct binder_ref {
/* Lookups needed: */
/* node + proc => ref (transaction) */
/* desc + proc => ref (transaction, inc/dec ref) */
/* node => refs + procs (proc exit) */
int debug_id;
struct rb_node rb_node_desc;
struct rb_node rb_node_node;
struct hlist_node node_entry;
struct binder_proc *proc;
struct binder_node *node;
uint32_t desc;
int strong;
int weak;
struct binder_ref_death *death;
};
<1>rb_node_desc:該指標是要連結到客戶端程序的binder_proc->refs_by_desc紅黑樹中,該紅黑樹按desc來排序。
<2>rb_node_node:該指標是要連結到客戶端程序的binder_proc->refs_by_node紅黑樹中,該紅黑樹按node來排序的。
<3>node_entry:該指標要連結到binder_node->refs中,這樣服務才知道誰來看過它
<4>proc:指向當前使用程序間通訊的客戶端程序的binder_proc物件
<5>node:當前引用物件使用的服務物件(binder_node)的地址
<6>desc:這個在前面講過了表示該引用物件的控制代碼。
3.binder_buffer
binder_buffer是用來程序間傳遞資料的核心緩衝區。
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;
unsigned async_transaction:1;
unsigned debug_id:29;
struct binder_transaction *transaction;
struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
uint8_t data[0];
};
<1>entry:下面我們瞭解binder_proc結構時,就能瞭解到通過該域可連結當前binder_buffer到對應程序的free或allocated binder_buffer連結串列中。<2>free:表示該binder_buffer是否是空閒的。如果為1,則說明該binder_buffer是空閒的,就會連結到binder_proc->free_buffers連結串列中。反之就連結到binder_proc->allocated_buffers。
<3>async_transaction:如果為1,說明和該binder_buffer關聯的是一個非同步事務。
<4>transaction:該核心緩衝區屬於哪個事務所用
<5>target_node:表示是哪一個binder實體在使用該binder_buffer.
<6>data:這裡才是真正的資料。
4.binder_proc
每一個使用binder程序間通訊的程序在核心中都對應有一個binder_proc物件,它描述了該程序的上下文資訊。當程序開啟“/dev/binder”檔案節點時,binder驅動就會建立一個binder_proc物件,並通過proc_node連線到全域性的binder_procs雜湊表中,這一過程我們會在後續的博文中介紹。
struct binder_proc {
struct hlist_node proc_node; //通過這個物件可將當前binder_proc物件連結進全域性binder_procs物件雜湊表中
struct rb_root threads; //當前程序處理程序通訊的執行緒池紅黑樹
struct rb_root nodes; //當前程序內部binder實體組成的紅黑樹,只有server才有binder實體物件。
struct rb_root refs_by_desc; //binder引用物件組成的紅黑樹,且已描述符來排序
struct rb_root refs_by_node; //binder引用物件組成的紅黑樹,切以binder實體的地址來排序
int pid; //當前程序的pid
struct vm_area_struct *vma; //使用者空間傳來的虛擬地址
struct mm_struct *vma_vm_mm;
struct task_struct *tsk; //當前程序任務控制塊
struct files_struct *files;
struct hlist_node deferred_work_node;
int deferred_work;
void *buffer; //當前程序的buffer首地址,
ptrdiff_t user_buffer_offset; //buffer user層虛擬地址和kernel 虛擬地址的差值。
struct list_head buffers;//可以理解成buffer連結串列,便於查詢適合的buffer
struct rb_root free_buffers; //空閒buffer連結串列
struct rb_root allocated_buffers; //已分配buffer的連結串列
size_t free_async_space;
struct page **pages; //申請buffer時,描述實體記憶體的page頁陣列
size_t buffer_size; //申請buffer大小,
uint32_t buffer_free;
struct list_head 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 ready_threads;
long default_priority;
struct dentry *debugfs_entry;
};
我就拿一些我認為比較重要的,來介紹一下。
<1>proc_node:將該物件連結到全域性binder_procs物件中。
<2>threads:該程序內用於處理使用者請求的所有執行緒組成的紅黑樹。後面介紹binder_thread結構體時,你就知道binder_thread->rb_node就是連結到這個地方的。
<3>nodes:該程序內的所有Binder實體所組成的紅黑樹,binder實體對應的就是服務了,一般只有server程序才會用到。
<4>refs_by_desc:該程序內的所有按著desc描述符排列的Binder引用組成的紅黑樹。之前我們介紹binder引用物件時它的binder_ref->rb_node_desc就是連結到這裡的。
<5>refs_by_node:該程序內的所有按著binder_node地址排列的Binder引用組成的紅黑樹。之前我們介紹binder引用物件時它的binder_ref->rb_node_node就是連結到這裡的。
<6>vma:user空間傳下來的虛擬地址
<7>buffer:整個buffer的地址,後面會根據需要分割成相應的binder_buffer.
<8>buffers:binder_buffer構成的連結串列
<9>user_buffer_offset:是該核心虛擬地址和程序虛擬地址之間的差值。為了減少memcpy操作,binder驅動將程序的核心虛擬地址和程序虛擬地址對映到同一物理頁面,這樣的話使用者空間直接寫的話,核心去對應地址就可以看到,不需要傳值,傳址。只要其中一個地址,我們就能根據這個值計算處另外一個。其實這些我們在寫程式碼是不用關心的。
<10>free_buffers:沒有使用的binder_buffer構成的連結串列。
<11>allocated_buffers:已分配出去的binder_buffer構成的連結串列
<12>pages:描述實體記憶體的page頁陣列
<13>todo:該程序的待處理事務佇列。
<14>wait:程序事務等待佇列。它和todo佇列是相輔相成的,實現程序的等待和喚醒。(引用老羅的:比如說,當程序的wait佇列為空時,程序就沒事可做了,即進入等待狀態。當它們的作用是實現程序的等待/喚醒。例如,當Server程序的wait等待佇列為空時,Server就進入中斷等待狀態;當某Client向Server傳送請求時,就將該請求新增到Server的todo待處理事務佇列中,並嘗試喚醒Server等待佇列上的執行緒。如果,此時Server的待處理事務佇列不為空,則Server被喚醒後;喚醒後,則取出待處理事務進行處理,處理完畢,則將結果返回給Client)。
一個binder_proc對應一個程序,程序可能包含多個執行緒(binder_thread),也可能註冊(server程序)了多個服務(binder_node),也可能引用了(客戶端程序)多個服務(bider_ref),有時也使用多個buffer.反映出來就像下圖這樣。
5.binder_thread
struct binder_thread {
struct binder_proc *proc;
struct rb_node rb_node;
int pid;
int looper;
struct binder_transaction *transaction_stack;
struct list_head todo;
uint32_t return_error; /* Write failed, return error code in read buf */
uint32_t return_error2; /* Write failed, return error code in read */
/* buffer. Used when sending a reply to a dead process that */
/* we are also waiting on */
wait_queue_head_t wait;
struct binder_stats stats;
};
<1>proc:指向其宿主程序binder_proc
<2>rb_node:連結到binder_proc->threads中
<3>transaction_stack:事務堆疊,
<4>
todo:需要執行緒處理的請求連結串列。例如當client程序請求指定binder執行緒來處理,那麼就會將請求新增到相應binder執行緒todo佇列中
<5>wait:等待佇列
<6>stats:binder執行緒的一些統計資料
6.binder_transaction
如資料庫事務一樣,該結構描述了一個程序間通訊過程。
struct binder_transaction {
int debug_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;
unsigned int code;
unsigned int flags;
long priority; /*源執行緒的優先順序*/
long saved_priority; /*binder驅動在修改一個執行緒優先順序之前,會將原來執行緒優先順序儲存在這裡,以備後面還原*/
uid_t sender_euid;/*使用者id*/
};
<1>work:當binder驅動為目標進或目標執行緒建立一個事務時,就會將將work->type設定為BINDER_WORK_TRANSACTION,並將該事務物件連結到目標程序binder_proc->todo佇列,或者binder_thread->todo佇列中。
<2>from:發起事務的執行緒
<3>from_parent:當前事務依賴的另外一個事務,需要等前一個事務做完,才能處理當前事務。
<4>to_parent:目標執行緒需要處理的下一個事務。
<5>to_proc:處理該事務的程序結構體
<6>to_thread:處理該事務的執行緒結構體
<7>buffer:就是事務所需要的binder_buffer了。
3.binder_buffer
binder_buffer就是binder驅動通訊資料緩衝區了。
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;
unsigned async_transaction:1;
unsigned debug_id:29;
struct binder_transaction *transaction;
struct binder_node *target_node;
size_t data_size;
size_t offsets_size;
uint8_t data[0];
};
(1)transaction:該binder_buffer是被哪一個事務所使用。
(2)target_node:表示該binder_buffer交給哪一個binder實體使用。
(3)data_size:該binder_buffer資料緩衝區的大小。
(4)offsets_size:這個是偏移陣列,用來表示binder物件的個數,當buffer中含有binder物件時,我們可以根據這個標誌找出其中的binder物件。
(5)data[0]:真正的資料緩衝區,使用時拿到data的地址,就是有效buffer的首地址。
7.binder_write_read
struct binder_write_read {
binder_size_t write_size; /* bytes to write */
binder_size_t write_consumed; /* bytes consumed by driver */
binder_uintptr_t write_buffer;
binder_size_t read_size; /* bytes to read */
binder_size_t read_consumed; /* bytes consumed by driver */
binder_uintptr_t read_buffer;
};
該結構體就是用來和kernel通訊的結構體
(1)write_size:寫給kernel binder驅動資料的長度。
(2)write_consumed:kernel binder驅動已經寫入的資料長度。
(3)write_buffer:write buffer資料緩衝區。
(4)read_size:將要讀取的資料長度。
(5)read_consumed:binder kernel驅動已經讀取的資料長度。
(6)read_buffer:讀取資料的buffer。
8.flat_binder_object
struct flat_binder_object {
/* 8 bytes for large_flat_header. */
__u32 type;
__u32 flags;
/* 8 bytes of data. */
union {
binder_uintptr_t binder; /* local object */
__u32 handle; /* remote object */
};
/* extra data associated with local object */
binder_uintptr_t cookie;
};
binder物件結構體:該結構體可以表示代理物件和本地物件。
(1)type:表示代理物件還是本地物件的型別。
(2)binder&handle:這個兩個成員變數構成一個聯合體,當該flat_binder_object物件表示一個代理物件時,這裡會使用handle,用來表示一個binder引用物件的控制代碼值(這個值就儲存在binder代理物件中,後續通訊時會先根據該控制代碼找到和哪個binder實體物件通訊)。當該flat_binder_object物件表示一個本地物件是,binder物件有效,表示一個本地物件內部弱引用計數物件的地址。同時cookie時真正的服務物件的使用者空間地址,返回到server程序時,會根據找個地址找到對應的服務物件,進而就可以進行相應的操作。
9.binder_transaction_data
struct binder_transaction_data {
/* The first two are only used for bcTRANSACTION and brTRANSACTION,
* identifying the target and contents of the transaction.
*/
union {
__u32 handle; /* target descriptor of command transaction */
binder_uintptr_t ptr; /* target descriptor of return transaction */
} target;
binder_uintptr_t cookie; /* target object cookie */
__u32 code; /* transaction command */
/* General information about the transaction. */
__u32 flags;
pid_t sender_pid;
uid_t sender_euid;
binder_size_t data_size; /* number of bytes of data */
binder_size_t offsets_size; /* number of bytes of offsets */
/* If this transaction is inline, the data immediately
* follows here; otherwise, it ends with a pointer to
* the data buffer.
*/
union {
struct {
/* transaction data */
binder_uintptr_t buffer;
/* offsets from buffer to flat_binder_object structs */
binder_uintptr_t offsets;
} ptr;
__u8 buf[8];
} data;
};
該結構體在binder通訊的核心資料結構:
(1)handle & ptr : 這兩個資料包含在共同體target中,當該target描述一個binder實體物件時,ptr有效,表示該服務物件一個弱引用計數物件的地址。當target描述一個binder引用物件時,handle資料有效,表示一個binder引用物件控制代碼值。
(2)cookie:當target描述一個本地物件時,cookie就是該服務物件在server中的地址。
(3)code:通訊碼
(4)sender_pid & sender_euid:表示事務發起者的pid和uid。
(5)data_size:資料緩衝區大小。
(6)offset_size:偏移陣列大小,用來表示有多少個binder物件,包含在資料緩衝區中。
(7)data:data是一個共用體,當通訊資料很小的時,可以直接使用buf[8]來儲存資料。當夠大時,只能用指標buffer來描述一個申請的資料緩衝區。
三、總結
通過檢視原始碼,我發現每一個使用了binder通訊的程序,其binder_proc物件都會連結到全域性的proc_node中,就如下圖所看到的那樣,手拉手。同時,每一個使用服務的程序都會在其binder_proc->refs_by_desc維護一個引用物件的紅黑樹,該紅黑樹描述了它使用的服務,用了多少服務,就有多少引用物件。如下圖所示,同一個服務被多個程序使用時的景象,每一個程序都用一個該binder物件的引用物件。如果拿到該引用物件後,就可以直接與對應的服務通訊了,不需要service_manage來參與了。
隨著學習的深入,我們會碰到一個叫匿名binder的傢伙,該傢伙一開始使用系統中已經註冊的服務得到一個代理物件,然後通過該代理物件,直接到對應的本地服務中申請其他服務物件,然後以binder物件的形式進行傳遞,這樣kernel中就會生成一個binder_node,進而會生成一個binder_ref返回給相應的代理物件。後面匿名binder拿著這個代理物件,就直接可以和對應的服務通訊了,不需要service_manage的參與(後面在簡單結合從surfaceflinger申請buffer時的案例分析一下)。
相關推薦
Android Binder學習(一)之Binder中的資料結構
備註:雙向箭頭表示雙向連結串列,各成員是串聯起來的。 在分析Android framework程式碼時,遇到最多的就是binder程序間通訊了。如果只知道怎麼用,也不影響我們日常的工作。但如果你想閱讀binder原始碼,就需要花點時間了。相對與linux核心來說,A
Android Framework學習(九)之Binder概述
Android系統中,每個應用程式是由Android的Activity,Service,Broadcast,ContentProvider這四大元件的中一個或多個組合而成,這四大元件所涉及的多程序間的通訊底層都是依賴於Binder IPC機制。不僅於此,整個An
Android O 學習(一)之HAL型別
備註:這裡已Camera模組為例,如問題,歡迎討論。 以往的hal和framwork的程式碼緊密聯絡起來的,為此google為了framework 升級的方便在Android 8.0 上對 Android 作業系統底層進行了重新架構。新的架構已經瞭解一部分,但仍需要不斷學習,加上
Android 動畫學習(一)之View Animation
Android動畫初步 動畫(Animation)在我們日常的Android開發工作當中使用得較為頻繁,尤其對於Android遊戲這個動畫的集合體,掌握動畫開發的重要性毋庸置疑。同
Redis學習筆記之Redis基本資料結構
Redis基礎資料結構 Redis有5種基本資料結構:String(字串)、list(列表)、set(集合)、hash(雜湊)、zset(有序集合) 字串string 字串型別是Redis的value最簡單的資料結構,類似與Java語言中的ArrayList(數字列表),不過在Redis裡String是一種動
Android開發藝術探索學習-IPC之Binder(一)
1. Binder簡介 1.1 What is Binder? Android Dev Doc:Base class for a remotable object, the core part of a lightweight remote procedure
Android NDK學習起航之路之先買一艘船
感覺Android寫了這麼久了, 也該學點裝逼的技能了。所以我把目光放在了NDK和C++上,只要玩轉了這兩個, 我就能去玩OpenCV和TensorFlow了,想想就美滋滋。今天先來基礎配置一個最簡單的NDK環境。基於AndroidStudio 3.2,使用Cma
Android開發學習資源之(一)
1.Android效能優化(包括記憶體優化)一系列文章 http://androidperformance.com/categories/Android/http://www.csdn.net/article/2015-04-29/2824583-android-perfo
Android程式設計學習筆記 之 File資料儲存
File檔案可用來存放大量資料,如文字、圖片、音訊、視訊等。 在Android的資料儲存操作和Java中的IO流差不多的用法。 進行File資料儲存的步驟如下: ①開啟一個File物件 ②開啟一個FileOutputStream檔案輸出流,寫入資料 ③開啟一個FileIn
Android程式設計學習筆記 之 Fragment與Activity的資料傳遞
傳遞方向的不同: ①Activity----->Fragment: 在Activity中建立Bundle資料包,並呼叫Fragment的setArguments(Bundle bundle)方法 ②Fragment----->Activity: 需要在Frag
Android多媒體學習一:Android中Image的簡單例項。
在多媒體應用中,Image是最基礎的功能模組,接下來我們將看看在Android中是如何獲取和儲存Image的。Android內嵌的Image獲取和儲存功能,可以讓我們對於整個媒體框架有個比較全面的瞭解,同時為audio和video的學習打下基礎。 一、Image的獲取可以通過
JavaWeb學習篇之----Tomcat中配置數字證書以及網路傳輸資料中的密碼學知識
今天是學習JavaWeb的第二天,我們來了解什麼呢?就瞭解一下Tomcat中配置數字證書的相關內容,但是在說這部分內容的時候,我們貌似得先說一下數字證書的相關概念,那說到數字證書的時候我們還得了解一些
Android程式設計學習筆記 之 SQLite資料儲存
SQLite是一個輕量級的嵌入式的資料庫,我們在Android開發中不需要安裝任何外掛即可使用, 如果是root過的手機,可以在data/data/包名/databases裡面找到db資料庫檔案,推薦
Android學習筆記之MVVM----DataBinding(資料雙向繫結)
要把大象裝冰箱,總共三步! 第一步:在app.gradle中新增一下程式碼 第二步:建立一個普通bean類和XML檔案 注意:xml檔案的命名將影響生成的binding類名,如需要自定義b
機器學習入門-載入sklearn中資料並用matplotlib進行視覺化
from sklearn import datasets import matplotlib.pyplot as plt def get_data(): """ 從sklearn中獲取鳶尾花的資料 :return: 鳶尾花資料的字典,字典中包括的key有:【'data'
Metasploit框架的學習一之目錄結構
首先是介紹整個框架的結構: 0x01:最重要的目錄是data,modules,scripts,tools,plugins 1:我們先進去data目錄,該目錄包含大量有用的模組,如meterpreter,exploits,wordlists,templates等等 我們重點注意met
Android JNI 學習(一):JNI 簡介
將在 編程語言 ase 環境 公開 javase 全局 face let JNI 即 Java Native Interface 是 native 編程接口,它允許在Java虛擬機(VM)內運行Java代碼與其他編程語言(主要是C和C++)編寫的應用程序和庫進行交互操作。
Maven學習五之Nexus中各repository介紹
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
LinuxC基礎學習一之各類軟體環境介紹
LinuxC基礎學習一之各類軟體環境介紹 一、常用軟體 1、VM12+Ubuntu14.04 2、Everything: 本地搜尋工具 3、Mindmanager:思維導圖工具 4、Xshell:
Java程式設計思想學習(一)----物件導論中多型的理解
1.1抽象過程 1)萬物皆物件。 2)程式是物件的集合,他們通過傳送訊息來告知彼此所要求做的。 3)每個物件都有自己的由其他物件所構成的儲存。 4)每個物件都擁有其型別。 5)某一特定型別的所有物件都可以接收同樣的訊息。 上面是書上總結的內容,具體程式碼如下: //每個物件都有一個介面,介