Binder學習(一)

目錄:
1、 IPC通信
2、 Binder通信
3、 從Binder driver看Binder通信

一、IPC通信
Inter Process Communication(IPC),進程間通信。Linux系統不允許進程間直接訪問彼此的內存空間,進程間需要通信則必須藉助IPC相關的技術,比如:socket 、 pipe 、message queue等。而Android基於Linux系統,進程間的通信採用了更加有效的技術Binder。Binder建立在Linux中共享內核空間這一基礎上,通過抽象驅動Binder Driver來達到進程間的通信。

二、Binder通信
每個進程有自己獨立的用戶內存空間,其他的進程無法直接訪問,從而保證的進程中數據的安全。但是很多時候我們需要跨進程操作,比如:應用服務A需要調用音頻播放,那麼必須在A這個進程中去調用系統的AudioFlinger服務,而AudioFlinger服務在自己獨立的進程中。
2.1 Binder通信原理
Linux進程除了用戶內存空間外還有公共的內核空間,內核空間的數據所有進程都可以訪問。基於這點,Binder可以大顯身手了。通信原理如圖1所示:
 
圖1 Binder通信原理
客戶端想調用Service server中的方法foo()需要以下步驟:
(1) 將請求封裝成IPC數據併發送給Binder Driver
(2) Binder Driver接收客戶端發來的IPC數據,解析得到想要調用的目標服務
(3) Binder Driver將客戶端的請求封裝成IPC數據發給目標服務
(4) 目標服務接收到IPC數據,解析後調用服務函數foo()併發送回應信息

2.2 IPC數據
IPC數據結構如圖2所示:
 
圖2 IPC數據
其中handle對應的就是我們的目標服務的索引信息,RPC代碼和數據分別對應調用的方法名和參數。
2.3 binder相關的結構體
此處只列出部分結構體包括:
(1)struct binder_state 

int fd; //打開的文件描述符
void *mapped; //通過mmap把"/dev/binder"設備文件映射到進程虛擬空間的地址(用戶空間)
unsigned mapsize; 
}; 
作用:一般用來記錄open("/dev/binder")打開binder設備的句柄以及通過mmap把該設備文件映射到進程的用戶空間的起始地址。

(2) struct binder_proc {
struct hlist_node proc_node;//連入總鏈表的點.
struct rb_root threads; // 紅黑樹的節點,所有的服務都有一個note節點)
struct rb_root nodes;
struct rb_root refs_by_desc;
struct rb_root refs_by_node;
int pid; //進程的id.
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;//表示要映射的物理內存在內核空間中的起始位置
//內核使用的虛擬地址與進程使用的虛擬地址之間的差值,即如果某個物理頁面在內核空間中對應的 虛擬地址是addr的話, 
//那麼這個物理頁面在進程空間對應的虛擬地址就爲addr + user_buffer_offset 
ptrdiff_t user_buffer_offset; 
struct list_head buffers;//通過mmap映射的內存空間.
struct rb_root free_buffers;//空閒的binder_buffer通過成員變量rb_node連入到struct binder_proc中的 free_buffers表示的紅黑樹中去,
struct rb_root allocated_buffers;//正在使用的binder_buffer通過成員變量rb_node連入到struct binder_proc中的allocated_buffers表 示的紅黑樹中去。
size_t free_async_space;
struct page **pages;// struct page 用來描述物理頁面的數據結構
size_t buffer_size; //表示要映射的內存的大小.
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;
};
作用:用來保存進程的相關信息,每個進程只能打開一個binder設備,所以對於一個進程來說在Binder driver中會創建唯一的一個binder_proc,這是一個非常重要的結構體,IPC數據的傳遞與迴應都與之有密切的關係。

(3)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; 
struct hlist_head refs; 
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; 
int min_priority : 8; 
struct list_head async_todo; 
};
作用:描述一個Binder實體,注意這個binder實體是在驅動也就是內核中表示一個Binder。

(4)struct binder_ref {//就是 refs_by_desc、refs_by_node 這兩個引用樹種的數據結構.
int debug_id;
struct rb_node rb_node_desc;//連接des的引用樹.
struct rb_node rb_node_node;//連接node的引用樹.
struct hlist_node node_entry;
struct binder_proc *proc; //該binder引用所屬的進程,
struct binder_node *node;//和遠程的binder實體binder_node關聯的地方.相對應.
uint32_t desc;
int strong;
int weak;
struct binder_ref_death *death;
};
作用: 描述一個Binder實體的引用。(底層用binder實體和binder引用來相對的稱呼,相當於用戶空間binder代理)

(5) struct binder_buffer {
struct list_head entry; //連入 binder_proc的buffers
//空閒的binder_buffer通過成員變量rb_node連入到binder_proc中的free_buffers表示的紅黑樹中.
//正在使用的binder_buffer通過成員變量rb_node連入到binder_proc中的allocated_buffers表示的紅黑 //樹中去。
struct rb_node rb_node; 
unsigned free:1; //每一個binder_buffer又分爲正在使用的和空閒的,通過free成員變量來區分.
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];
};
作用:描述一段通過mmap映射的內存空間,定義在binder驅動binder.c當中.  每一個binder_buffer通過其成員entry按從低址到高地址連入到struct binder_proc中的buffers表示的鏈表中去.

(6) binder_thread
線程狀態如下:
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_NEED_RETURN = 0x20  
};
struct binder_thread {
struct binder_proc *proc; //當前線程所屬的進程。
struct rb_node rb_node; //來連入binder_proc的threads紅黑樹.
int pid;
int looper;//表示線程的狀態  就是上面enum的類型。
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; //用來保存一些統計信息
};
作用:描述執行當前binder通信事物的線程,在驅動中定義。

(7) struct binder_transaction {
int debug_id;
struct binder_work work;//當前在進行什麼類型的工作項的處理
struct binder_thread *from; //來自於哪一個binder線程
struct binder_transaction *from_parent;//來自於哪一個binder事務
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;
kuid_t sender_euid;
};
作用:client端和server端通信時,用來記錄通信信息,表示一次通信.

(8)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;
};
作用:傳輸數據的封裝

(9)struct binder_transaction_data {
/* The first two are only used for bcTRANSACTION and brTRANSACTION,
* identifying the target and contents of the transaction.
*/
union {
/* target descriptor of command transaction */
__u32 handle;//當通信命令的目標對象不是本地Binder實體時,使用handle來表示這個Binder實體的 引用
/* target descriptor of return transaction */
binder_uintptr_t ptr;//當通信命令的目標對象是本地Binder實體時,就使用ptr來表示這個對象在本進 程中的地址.
} target;
//只有目標對象是Binder實體時,cookie成員變量纔有意義,表示一些附加數據,由Binder實體來解 釋這個個附加數據.
binder_uintptr_t cookie; 
__u32 code;//通信的命令碼如:BC_XXXX、BR_XXXXX等等.
/* General information about the transaction. */
__u32 flags; //前面列舉的flags
pid_t sender_pid;
uid_t sender_euid;
binder_size_t data_size; //data.buffer緩衝區的大小
binder_size_t offsets_size; //表示data.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通信事務,封裝了RPC數據和服務的編號handle

(10) struct flat_binder_object {
/* 8 bytes for large_flat_header. */
__u32 type; //handle 標識檢索已有的binder_note ;binder表示新建一個binder_note
__u32 flags; 
/* 8 bytes of data. */
union {
binder_uintptr_t binder; //binder表示這是一個Binder實體
__u32 handle; //handle表示這是一個Binder引用.
};
/* extra data associated with local object */
binder_uintptr_t cookie;//當這是一個Binder實體時,cookie纔有意義,表示附加數據,由進程自己解
};
作用:傳輸過程中的每一個Binder實體或者引用

2.4 Binder Driver
Binder Driver是運行在Linux內存空間中的抽象驅動程序,在進程間通信中充當中轉的角色。服務程序的註冊、檢索、調用都需要與Binder Driver打交道,接下來將站在Binder Driver的角度上來分析程序的註冊、檢索、調用三個過程中Binder機制是如何運行的。
2.4.1 服務註冊
Linux內部通過一個Context Manager進程來統一管理系統的所有服務,所有的服務都要註冊到Context Manager的服務列表中。圖3展示了從Binder Driver分析服務註冊的過程。
 
圖3 註冊服務是Binder Driver的行爲
(1)~(3) Context Manager在所有服務啓動前最先運行,它會通過調用binder_open()函數在Binder Driver中初始化一個binder_proc的結構體,並在內核空間中開闢一塊buffer用於接收IPC數據,然後進入等待狀態,等待buffer中有IPC數據過來再喚醒讀取IPC數據

(4) ~(5)Context Manager處於等待狀態後,Service server進程也通過binder_open()函數來初始該進程對應的binder_proc結構體和開闢一塊buffer用於接收IPC應答。Service server生成IPC數據,用來調用Context Manager進程的註冊函數

(6) Service server將IPC數據發送給Binder Driver,Context Manager對應的handle=0,通過解析IPC中的handle可以獲得Context Manager對應的binder_note和binder_proc。每一個服務都會在內核控件中創建一個binder_note節點,該節點包含了改服務的相關信息,此處不詳細介紹。

(7) Binder Driver爲需要註冊的服務創建binder_note節點,並將該節點分別註冊到服務進程的binder_proc和Context Manager的binder_proc中。

(8)~(9) Binder Driver將IPC數據傳入Context Manager開闢的read_buffer中,等待Context Manager讀取,然後將Service server的binder_proc記錄下來,以便發送Context Manager的應答信息。

(10) Binder Driver讓Service server進入等待狀態,並將Context Manager從等待狀態喚醒,接收來自Service server的IPC數據,Context Manager解析IPC數據後將Service server註冊到服務列表後併發送回應信息

(11) Binder Driver查找記錄下來的Service server的binder_proc結構體中的buffer,並將來自Context Manager的迴應IPC數據傳入其中,然後喚醒Service server讀取回應信息。Service server被喚醒後讀取回應信息並處理,整個註冊過程就完成了。


文章大部分內容來自《Android框架揭祕》

參考文獻:http://blog.csdn.net/zy00000000001/article/details/53443151


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章