最近突然看到一個博客,是講Binder原理的,以前看深入理解Android I的時候看過,那時候就看不懂。感覺這個還有點意思,就看了好幾天,發現越看越不懂,然後看老羅的博客,發現也不是太懂,現在想根據書上的東西好好梳理下Binder。
感覺裏面應該重點掌握的應是bind_node是註冊的服務在內核中的代表,bind_ref是客戶端連接在驅動的代表。bind_buffer內核緩衝區,通過mmap之後可以在用戶空間操作,然後在內核也可以訪問,bind_proc是進程的代表,客戶端和服務端都有,對上面的進行管理,未完,待續,等看完了藝術探索過來更新。
handle, RPC代碼和RPC數據保存在binder_transaction_data的結構體中,屬於RPC範疇.
binder_write_read 等於binder_transaction_data(用戶空間數據)加上binder協議,是IPC數據範疇.
binder_write_read中的write_size是發送ipc數據時使用,read_size是接收數據時使用.
flat_binder_object在內核中傳輸的時候,會判斷type的類型,來判斷是否創建binder節點(服務註冊的時候存在)
進程間通信根據Client和Server的狀態設置該值
struct binder_work {//用戶查找binder_transaction結構體
struct list_head entry; //
enum {
BINDER_WORK_TRANSACTION = 1, //獲取ipc數據
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER, //Binder驅動檢測到Service組件死亡時,找到Binder實體對象中的refs,找到引用它的client進程和Client進程主動註冊死亡通知發現Service組件已死亡兩種情況
BINDER_WORK_DEAD_BINDER_AND_CLEAR, //Client進程註銷死亡通知時,相應的Service組件已死亡,binder驅動找到之前註冊的binder_ref_death結構體,並修改它work
BINDER_WORK_CLEAR_DEATH_NOTIFICATION, //Client進程註銷一個死亡通知,相應的Service組件沒有死亡,Binder驅動程序會找到之前註冊的,一個binder_ref_death結構體,並且將它的work修改爲此, 然後將該結構體封裝成一個工作項添加到Client進程的todo隊列中
} type; //工作項的類型
};
binder實體對象 Service中的Binder在內核中的代表
struct binder_node {
int debug_id;
struct binder_work work;//引用計數發生變化時 BINDER_WORK_NODE,並且將它添加到響應進程的todo隊列中
union {
struct rb_node rb_node;
struct hlist_node dead_node;
};
struct binder_proc *proc; //指向service進程
struct hlist_head refs; //所有的Client的binder引用binder_ref->node
int internal_strong_refs;
int local_weak_refs;
int local_strong_refs;
void __user *ptr; //指向Service組件內部的一個引用計數 weakref_impl 弱引用計數
void __user *cookie; //指向Service組件的地址
unsigned has_strong_ref:1; //請求Service組件時 1 結束 0
unsigned pending_strong_ref:1; // 請求的時候爲1,service增加後爲0
unsigned has_weak_ref:1; //請求Service組件時 1 結束 0
unsigned pending_weak_ref:1;
unsigned has_async_transaction:1; //每一個事務都關聯一個binder實體對象
unsigned accept_fds:1; //是否接收含有文件描述符的進程間通信數據 防止源進程在目標進程中打開
unsigned min_priority:8; //線程優先級
struct list_head async_todo; //異步事務隊列
};
Service組件,在驅動中的binder_node,binder_ref都維護引用計數
描述Client組件的死亡通知在驅動中的代表,
struct binder_ref_death {
struct binder_work work; //見第一個數據結構
void __user *cookie; //保存Client負責接收死亡通知對象的地址
};
Binder驅動程序決定向客戶端進程發送一個Service組件死亡通知時。會將binder_ref_death結構體封裝成一個工作項。加到Client進程的todo隊列中 ,Client進程在處理這個工作項,會通過binder_ref_death結構體成員變量的work來區是哪一種情況,見第一個結構體中的枚舉值
描述一個Binder引用對象 Client進程中的Binder引用在驅動中的代表
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; //hash列表中的節點 對應與binder_node->refs
struct binder_proc *proc; //Binder引用對象的宿主進程
struct binder_node *node; //Binder引用對象所引用的Binder實體對象
uint32_t desc; //在Client進程的用戶空間,Binder引用對象是使用一個句柄值來描述的,BInder驅動程序就可以通過該句柄值找到對於的,Binder引用對象即是binder_ref
int strong;
int weak;
struct binder_ref_death *death; //Client進程註冊的死亡通知保存的地方
};
進程對應內核緩衝區 緩衝區中的事務會隨着請求發送變化
struct binder_buffer {
struct list_head entry; /* free and allocated entries by addesss */ //內核緩衝區列表的一個節點
struct rb_node rb_node; /* free entry by size or allocated entry */
/* by address */ //free 爲1 表示爲空閒內核緩衝區中的一個節點
unsigned free:1; //1表示內核緩衝區是空閒的 不會分配物理頁面的
unsigned allow_user_free:1;//Service組件處理完後發現爲1,Service組件請求Binder驅動釋放該內核緩衝區
unsigned async_transaction:1; //爲1表示異步事務
unsigned debug_id:29;
struct binder_transaction *transaction; //每一個事務都關聯一個目標Binder實體對象
struct binder_node *target_node; //目標的binder節點
size_t data_size; //數據緩衝區的大小
size_t offsets_size; //偏移數組 記錄了每一個Binder對象再數據緩衝區中的位置
uint8_t data[0]; //指向大小可變的數據緩衝區,用來保存通信數據的 可保存普通數據和Binder對象 Binder驅動程序只關心Binder對象
};
進程調用open /dev/binder的時候創建 將它保存在全局的hash 進程在驅動中的代表
struct binder_proc {
struct hlist_node proc_node; //是hash列表中的節點
struct rb_root threads; // 擁有binder_thread結構體的紅黑樹的根
struct rb_root nodes; //binder實體對象的紅黑樹的根 以ptr作爲關鍵字
struct rb_root refs_by_desc;
struct rb_root refs_by_node; //帶有binder_ref 結構體紅黑樹的根,使用binder_node結構體區分binder_ref
int pid; //創建binder_proc結構體的進程的pid
struct vm_area_struct *vma; //內核緩衝區地址 用戶空間地址 在應用程序內部使用
struct task_struct *tsk; //任務控制塊 進程相關
struct files_struct *files; //文件結構體數組
struct hlist_node deferred_work_node; //進程延遲執行的工作項
int deferred_work; //延遲工作項的具體內容
void *buffer; //內核緩衝區的地址 內核空間地址 大塊空間 劃分爲Binder_buffer小空間 接收ipc數據的結構體指針
ptrdiff_t user_buffer_offset; //內核緩衝區中 用戶空間地址和內核空間地址的差值 接收的ipc數據的內核和用戶的地址偏移量,將接收到的ipc數據傳遞到用戶空間
struct list_head buffers; //指向指向該列表的頭部 爲接收ipc數據而分配的binder_buffer結構體列表
struct rb_root free_buffers; //已分配物理頁面 接收ipc數據後,要釋放的binder_buffer結構體列表 根據RPC數據的大小分配binder_buffer結構體的data
struct rb_root allocated_buffers;
size_t free_async_space; //保存異步事務數據緩衝區的大小
struct page **pages; //對於的物理頁面 數組中每個元素指向一個物理頁面
size_t buffer_size; //mmap後內核緩衝區的大小 內核中開闢的buffer大小
uint32_t buffer_free; //空閒內核緩衝區的大小
struct list_head todo; //把待處理請求封裝成一個工作項,加如到待處理工作項隊列 進程從待機狀態喚醒後要做的事情 和binder_work聯繫一塊
wait_queue_head_t wait; //空閒binder線程會睡眠在等待隊列裏面 讓進程進入待機狀態
struct binder_stats stats; //統計進程數據的,進程見請求的次數
struct list_head delivered_death; //死亡通知封裝成一個工作項保存在所描述的一個隊列中,進程收到後會刪除
int max_threads;
int requested_threads; //驅動主動請求進程註冊一個線程時加1,進程響應後減1
int requested_threads_started;//驅動程序主動請求進程註冊的數目,進程響應後加1
int ready_threads; //當前空閒的Binder線程數目
long default_priority; //宿主進程的優先級
struct dentry *debugfs_entry;
};
deferred_work的可能取值enum binder_deferred_state {
BINDER_DEFERRED_PUT_FILES = 0x01, //進程關閉文件描述符
BINDER_DEFERRED_FLUSH = 0x02, //喚醒線程檢查進程是否有新的工作項需要處理
BINDER_DEFERRED_RELEASE = 0x04, //不再使用binder進程間通信機制,驅動釋放它分配的資源,釋放進程結構體,binder實體對象
};
binder驅動會爲內核緩衝區分配文件描述符,進程可以通過文件描述符把內核緩衝區映射到自己的地址空間binder線程池中的一個線程,線程註冊到binder驅動時,驅動會創建該結構體
struct binder_thread {
struct binder_proc *proc; //指向宿主進程
struct rb_node rb_node; //一個節點
int pid; //線程ID
int looper; //狀態
struct binder_transaction *transaction_stack; //一個事務交給一個線程處理時,事務封裝成結構體 處理誰,誰放在最前端 查找另一端接收進程
struct list_head todo; //Client進程的請求
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; //接收到進程間通信請求的次數
};
線程的狀態
enum {
BINDER_LOOPER_STATE_REGISTERED = 0x01, //收到用戶線程發送BC_register_looper
BINDER_LOOPER_STATE_ENTERED = 0x02, //表明準備就緒BC_ENTER_looper
BINDER_LOOPER_STATE_EXITED = 0x04,
BINDER_LOOPER_STATE_INVALID = 0x08,
BINDER_LOOPER_STATE_WAITING = 0x10, //binder線程處於空閒狀態
BINDER_LOOPER_STATE_NEED_RETURN = 0x20 //初始化狀態
};
線程是應用程序主動註冊的,那麼它通過BC_ENTER_looper來通知binder驅動描述進程間通信過程
struct binder_transaction {
int debug_id;
struct binder_work work; //binder驅動爲目標線程創建一個事務後(由驅動負責)設置BINDER_WORK_TRANSACTION,並添加到目標線程的todo隊列中
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; //爲1 表示是同步事務
/* unsigned is_dead:1; */ /* not used at the moment */
struct binder_buffer *buffer; //binder驅動程序爲該事務分配的一塊內存緩衝區
unsigned int code;
unsigned int flags;
long priority; //線程優先級
long saved_priority; //保存原來的線程優先級
uid_t sender_euid; //線程用戶ID
};
應用程序通過IO命令和驅動交互#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
進程間通信
struct binder_write_read {
signed long write_size; /* bytes to write */ 緩衝區大小
signed long write_consumed; /* bytes consumed by driver */ 驅動從緩衝區處理的字節
unsigned long write_buffer; //描述輸入數據 從用戶空間傳輸到binder驅動程序的數據 指向一個用戶緩衝區地址
signed long read_size; /* bytes to read */
signed long read_consumed; /* bytes consumed by driver */
unsigned long read_buffer; //指向一個用戶空間緩衝區的地址
};
全部是進程發送給binder驅動
enum BinderDriverCommandProtocol {
BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
BC_FREE_BUFFER = _IOW('c', 3, int), //指向內核緩衝區
BC_INCREFS = _IOW('c', 4, int), //binder引用對象的句柄值 弱引用
BC_ACQUIRE = _IOW('c', 5, int), //增加 強引用計數
BC_RELEASE = _IOW('c', 6, int),
BC_DECREFS = _IOW('c', 7, int), //弱引用
BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie),
BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie),
BC_REGISTER_LOOPER = _IO('c', 11), //驅動請求進程註冊線程到驅動
BC_ENTER_LOOPER = _IO('c', 12), //線程主動註冊
BC_EXIT_LOOPER = _IO('c', 13), //線程要退出時
BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_ptr_cookie), //註冊
BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_ptr_cookie), //清空
BC_DEAD_BINDER_DONE = _IOW('c', 16, void *), //指向一個死亡通知結構體binder_ref_death的地址
//Client用命令協議碼通知binder驅動程序處理完Service組件的死亡通知了
};
源進程使用命令協議代碼BC_TRANSACTION請求binder驅動將通信數據傳遞到目標進程目標進程處理完源進程的請求操作之後使用命令協議碼BC_REPLY請求驅動將結果傳遞給源進程
返回協議代碼
enum BinderDriverReturnProtocol {
BR_ERROR = _IOR('r', 0, int), //驅動處理請求時出錯
BR_OK = _IO('r', 1), //成功處理
BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data), //通知Server進程來處理該進程間通信請求
BR_REPLY = _IOR('r', 3, struct binder_transaction_data),//server處理完請求之後,驅動以此協議返回Client進程
BR_DEAD_REPLY = _IO('r', 5), //發現目標進程或線程已經死亡 驅動會返回這個協議代碼
BR_TRANSACTION_COMPLETE = _IO('r', 6),//當binder驅動收到進程發來的BC_TRANSACTION或是BC_REPLY,驅動返回此碼,告知進程已收到
BR_INCREFS = _IOR('r', 7, struct binder_ptr_cookie),
BR_ACQUIRE = _IOR('r', 8, struct binder_ptr_cookie),
BR_RELEASE = _IOR('r', 9, struct binder_ptr_cookie),
BR_DECREFS = _IOR('r', 10, struct binder_ptr_cookie),
BR_NOOP = _IO('r', 12), //通知應用進程執行了一個空操作
BR_SPAWN_LOOPER = _IO('r', 13),//發現進程沒有足夠的空閒Binder線程來處理進程間通信請求,通知該進程增加一個線程到Binder線程池中
BR_DEAD_BINDER = _IOR('r', 15, void *),//當驅動檢測到Service組件死亡事件時,通知Client進程
BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, void *),//binder驅動執行完註銷後返回此碼通知Client進程
BR_FAILED_REPLY = _IO('r', 17), // 處理進程發送的BC_TRANSACTION異常時,返回此碼
};
描述一個Binder實體對象或是引用
struct binder_ptr_cookie {
void *ptr; //Binder引用的句柄 或是
void *cookie;//接收死亡通知對象的地址
};
進程中線程的通信傳輸的數據 用戶空間數據struct binder_transaction_data {
union {
size_t handle; /* target descriptor of command transaction */ //引用對象的句柄值
void *ptr; /* target descriptor of return transaction */ //指向Service組件內部弱引用計數的地址
} target;
void *cookie; /* target object cookie */ //目標Service組件的地址
unsigned int code; /* transaction command */ //雙方約定好的通信代碼
/* General information about the transaction. */
unsigned int flags; //描述進程間通信行爲的特徵
pid_t sender_pid; //進程的pid
uid_t sender_euid; //
size_t data_size; /* 通信數據的大小 */
size_t offsets_size; /* 偏移數組的大小 */
union {
struct {
/* transaction data */
const void *buffer; //指向數據緩衝區,真正保存通信數據的 大小由data_size決定
/* offsets from buffer to flat_binder_object structs */
const void *offsets; //保存每一個binder對象的位置,分別指向偏平結構的首地址
} ptr;//數據量大的時候
uint8_t buf[8]; //數據量小的時候
} data; //數據緩衝區是 來傳輸數據
};
上面的flags取值如下enum transaction_flags {
TF_ONE_WAY = 0x01, /* 1表示異步的進程間通信過程 */
TF_ROOT_OBJECT = 0x04, /* contents are the component's root object */
TF_STATUS_CODE = 0x08, /* 1表示data數據緩衝區的內容是一個4字節的狀態碼 */
TF_ACCEPT_FDS = 0x10, /* 0表示源進程不允許結果護具中含有文件描述符 */
};
數據緩衝區中每一個Binder對象都使用一個flat_binder_object來描述struct flat_binder_object {
/* 8 bytes for large_flat_header. */
unsigned long type; //區分是Binder實體對象還是引用對象,亦或是文件描述符
unsigned long flags; //只有描述的是Binder實體,它纔有意義
/* 8 bytes of data. */
union {
void *binder; /* local Binder實體對象 指向一個內部弱引用對象的地址 */
signed long handle; /* remote Binder引用對象的句柄值 */
};
/* extra data associated with local object */
void *cookie; //指向該Service組件的地址
};
扁平結構中的type的取值如下#define B_PACK_CHARS(c1, c2, c3, c4) \
((((c1)<<24)) | (((c2)<<16)) | (((c3)<<8)) | (c4))
#define B_TYPE_LARGE 0x85
enum {
BINDER_TYPE_BINDER = B_PACK_CHARS('s', 'b', '*', B_TYPE_LARGE),//強類型的Binder實體對象
BINDER_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),//弱類型的實體對象
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),//描述的是強類型Binder引用對象
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),//弱類型引用
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),//
};