編輯:關於Android編程
最近突然看到一個博客,是講Binder原理的,以前看深入理解Android I的時候看過,那時候就看不懂。感覺這個還有點意思,就看了好幾天,發現越看越不懂,然後看老羅的博客,發現也不是太懂,現在想根據書上的東西好好梳理下Binder。
感覺裡面應該重點掌握的應是bind_node是注冊的服務在內核中的代表,bind_ref是客戶端連接在驅動的代表。bind_buffer內核緩沖區,通過mmap之後可以在用戶空間操作,然後在內核也可以訪問,bind_proc是進程的代表,客戶端和服務端都有,對上面的進行管理,未完,待續,等看完了藝術探索過來更新。
進程間通信根據Client和Server的狀態設置該值
struct binder_work { struct list_head entry; // enum { BINDER_WORK_TRANSACTION = 1, 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; 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; // struct rb_root nodes; //binder實體對象的集合 以ptr作為關鍵字 struct rb_root refs_by_desc; struct rb_root refs_by_node; int 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小空間 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 page **pages; //對於的物理頁面 數組中每個元素指向一個物理頁面 size_t buffer_size; //mmap後內核緩沖區的大小 uint32_t buffer_free; //空閒內核緩沖區的大小 struct list_head todo; //把待處理請求封裝成一個工作項,加如到待處理工作項隊列 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),// };
需求我們知道,Android系統本身有自帶的日歷控件,網絡上也有很多開源的日歷控件資源,但是這些日歷控件往往樣式較單一,API較多,不易於在實際項目中擴展並實現出符合具體
前言 心好疼:昨晚寫完了這篇博客一半,今天編輯的時候網絡突然斷了,我的文章就這樣沒了,但是為了Developer的使用AS這款IDE可以快速上手,我還是繼續進行詳解
為什麼要使用Android StudioAndroid Studio是谷歌推出了新的Android開發環境,其重要性可想而知!1. 集成了Gradle 打包工具2. 所見
這方面的知識不是孤立的,其中有關於,Socket編程,多線程的操作,以及I/O流的操作。當然,實現方法不止一種,這只是其中一種,給同是新手一點點思路。如果有什麼推薦的話,