在Binder驅動程序中有兩種類型的數據結構,其中一種是在內部使用的,另一種是在內部和外部均會使用到的。接下來,我們分別對這些數據結構進行介紹。
**struct binder_work**
**kernel/goldfish/drivers/staging/android/binder.c**
~~~
struct binder_work {
struct list_head entry;
enum {
BINDER_WORK_TRANSACTION = 1,
BINDER_WORK_TRANSACTION_COMPLETE,
BINDER_WORK_NODE,
BINDER_WORK_DEAD_BINDER,
BINDER_WORK_DEAD_BINDER_AND_CLEAR,
BINDER_WORK_CLEAR_DEATH_NOTIFICATION,
} type;
};
~~~
結構體binder_work用來描述待處理的工作項,這些工作項有可能屬于一個進程,也有可能屬于一個進程中的某一個線程。成員變量entry用來將該結構體嵌入到一個宿主結構中,成員變量type用來描述工作項的類型。根據成員變量type的取值,Binder 驅動程序就可以判斷出一個binder_work結構體嵌入到什么類型的宿主結構中。
**struct binder_node**
**kernel/goldfish/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;
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_node用來描述一個Binder實體對象。每一個Service組件在Binder驅動程序中都對應有一個Binder實體對象,用來描述它在內核中的狀態。Binder驅動程序通過強引用計數和弱引用計數技術來維護它們的生命周期。
成員變量proc指向一個Binder實體對象的宿主進程。在Binder驅動程序中,這些宿主進程通過一個binder_proc結構體來描述。宿主進程使用一個紅黑樹來維護它內部所有的Binder實體對象,而每一個Binder實體對象的成員變量rb_node就正好是這個紅黑樹中的一個節點。如果一個Binder實體對象的宿主進程已經死亡了,那么這個Binder實體對象就會通過它的成員變量dead_node保存在一個全局的hash列表中。
由于一個Binder實體對象可能會同時被多個Client組件引用,因此,Binder驅動程序就使用結構體binder_ref來描述這些引用關系,并且將引用了同一個Binder實體對象的所有引用都保存在一個hash列表中。這個hash列表通過Binder實體對象的成員變量refs來描述,而Binder驅動程序通過這個成員變量就可以知道有哪些Client組件引用了同一個Binder實體對象。
成員變量internal_strong_refs和local_strong_refs均是用來描述一個Binder實體對象的強引用計數,而成員變量local_weak_refs用來描述一個Binder實體對象的弱引用計數。當一個Binder實體對象請求一個Service組件來執行某一個操作時,會增加該Service組件的強引用計數或者弱引用計數,相應地,Binder實體對象會將其成員變量has_strong_ref和has_weak_ref的值設置為1。當一個Service組件完成一個Binder實體對象所請求的操作之后,Binder實體對象就會請求減少該Service組件的強用計數或者弱引用計數。Binder實體對象在請求一個Service組件增加或者減少強引用計數或者弱引用計數的過程中,會將其成員變量pending_strong_ref或者pending_weak_ref的值設置為1;而當該Service組件增加或者減少了強引用計數或者弱引用計數之后,Binder實體對象就會將這兩個成員變量的值設置為0。
成員變量ptr和cookie分別指向一個用戶空間地址,它們用來描述用戶空間中的一個Service組件,其中,成員變量cookie指向該Service組件的地址,而成員變量ptr指向該Service組件內部的一個引用計數對象(類型為weakref_impl)的地址。
成員變量has_async_transaction用來描述一個Binder實體對象是否正在處理一個異步事務。如果是,它的值就等于1,否則等于0。一般情況下,Binder驅動程序都是將一個事務保存在一個線程的一個todo隊列中的,表示要由該線程來處理該事務。每一個事務都關聯著一個Binder實體對象,表示該事務的目標處理對象,即要求與該Binder實體對象對應的Service組件在指定的線程中處理該事務。然而,當Binder驅動程序發現一個事務是異步事務時,就會將它保存在目標Binder實體對象的一個異步事務隊列中,這個異步事務隊列就是由該目標Binder實體對象的成員變量async_todo來描述的。異步事務的定義是那些單向的進程間通信請求,即不需要等待應答的進程間通信請求,與此相對的便是同步事務。因為不需要等待應答,Binder驅動程序就認為異步事務的優先級低于同步事務,具體就表現為在同一時刻,一個Binder實體對象的所有異步事務至多只有一個會得到處理,其余的都等待在異步事務隊列中,而同步事務就沒有這個限制。
當一個Binder實體對象的引用計數由0變成1,或者由1變成0時,Binder驅動程序就會請求相應的Service組件增加或者減少其引用計數。這時候Binder驅動程序就會將該引用計數修改操作封裝成一個類型為binder_node的工作項,即將一個Binder實體對象的成員變量work的值設置為BINDER_WORK_NODE,并且將它添加到相應進程的todo隊列中去等待處理。
成員變量min_priority表示一個Binder實體對象在處理一個來自Client進程的請求時,它所要求的處理線程,即Server進程中的一個線程,應該具備的最小線程優先級,這樣就保證了與該Binder實體對象對應的Service組件可以在一個具有一定優先級的線程中處理一個來自Client進程的通信請求。一個Service組件在將自己注冊到Binder驅動程序時,可以指定這個最小線程優先級,而Binder驅動程序會把這個最小線程優先級保存在相應的Binder實體對象的成員變量min_priority中。
成員變量accept_fds用來描述一個Binder實體對象是否可以接收包含有文件描述符的進程間通信數據。如果它的值等于1,就表示可以接收;否則,就表示禁止接收。當一個進程向另外一個進程發送的數據中包含有文件描述符時,Binder驅動程序就會自動在目標進程中打開一個相同的文件。基于安全性考慮,Binder驅動程序就要通過成員變量accept_fds來防止源進程在目標進程中打開文件。
最后,成員變量debug_id用來標志一個Binder實體對象的身份,它是用來幫助調試Binder驅動程序的。
**struct binder_ref_death**
**kernel/goldfish/drivers/staging/android/binder.c**
~~~
struct binder_ref_death {
struct binder_work work;
void __user *cookie;
};
~~~
結構體binder_ref_death用來描述一個Service組件的死亡接收通知。在正常情況下,一個Service組件被其他Client進程引用時,它是不可以銷毀的。然而,Client進程是無法控制它所引用的Service組件的生命周期的,因為Service組件所在的進程可能會意外地崩潰,從而導致它意外地死亡。一個折中的處理辦法是,Client進程能夠在它所引用的Service組件死亡時獲得通知,以便可以做出相應的處理。這時候Client進程就需要將一個用來接收死亡通知的對象的地址注冊到Binder驅動程序中。
成員變量cookie用來保存負責接收死亡通知的對象的地址,成員變量work的取值為BINDER_WORK_DEAD_BINDER、BINDER_WORK_CLEAR_DEATH_NOTIFICATION或者BINDER_WORK_DEAD_BINDER_AND_CLEAR,用來標志一個具體的死亡通知類型。
Binder驅動程序決定要向一個Client進程發送一個Service組件死亡通知時,會將一個binder_ref_death結構體封裝成一個工作項,并且根據實際情況來設置該結構體的成員變量work的值,最后將這個工作項添加到Client進程的todo隊列中去等待處理。
在下面兩種情況下,Binder驅動程序會向一個Client進程發送一個Service組件的死亡通知。
(1)當Binder驅動程序監測到一個Service組件死亡時,它就會找到該Service組件對應的Binder實體對象,然后通過Binder實體對象的成員變量refs就可以找到所有引用了它的Client進程,最后就找到這些Client進程所注冊的死亡接收通知,即一個binder_ref_death結構體。這時候Binder驅動程序就會將該binder_ref_death結構體添加到Client進程的todo隊列中去等待處理。在這種情況下,Binder驅動程序將死亡通知的類型設置為BINDER_WORK_DEAD_BINDER。
(2)當Client進程向Binder驅動程序注冊一個死亡接收通知時,如果它所引用的Service組件已經死亡,那么Binder驅動程序就會馬上發送一個死亡通知給該Client進程。在這種情況下,Binder驅動程序也會將死亡通知的類型設置為BINDER_WORK_DEAD_BINDER。
另外,當Client進程向Binder驅動程序注銷一個死亡接收通知時,Binder驅動程也會向該Client進程的todo隊列發送一個類型為binder_ref_death的工作項,用來表示注銷結果。這時候又需要分兩種情況來考慮。
(1)如果Client進程在注銷一個死亡接收通知時,相應的Service組件還沒有死亡,那么Binder驅動程序就會找到之前所注冊的一個binder_ref_death結構體,并且將它的類型work修改為BINDER_WORK_CLEAR_DEATH_NOTIFICATION,然后再將該binder_ref_death結構體封裝成一個工作項添加到該Client進程的todo隊列中去等待處理。
(2)如果Client進程在注銷一個死亡接收通知時,相應的Service組件已經死亡,那么Binder驅動程序就會找到之前所注冊的一個binder_ref_death結構體,并且將它的類型work修改為BINDER_WORK_DEAD_BINDER_AND_CLEAR,然后再將該binder_ref_death結構體封裝成一個工作項添加到該Client進程的todo隊列中去等待處理。
Client進程在處理這個工作項時,通過對應的binder_ref_death結構體的成員變量work就可以區分注銷結果了,即它所引用的Service組件是否已經死亡。
**struct binder_ref**
**kernel/goldfish/drivers/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;
};
~~~
結構體binder_ref用來描述一個Binder引用對象。每一個Client組件在Binder驅動程序中都對應有一個Binder引用對象,用來描述它在內核中的狀態。Binder驅動程序通過強引用計數和弱引用計數技術來維護它們的生命周期。
成員變量node用來描述一個Binder引用對象所引用的Binder實體對象。前面在介紹結構體binder_node時提到,每一個Binder實體對象都有一個hash列表,用來保存那些引用了它的Binder引用對象,而這些Binder引用對象的成員變量node_entry正好是這個hash列表的節點。
成員變量desc是一個句柄值,或者稱為描述符,它是用來描述一個Binder引用對象的。在Client進程的用戶空間中,一個Binder引用對象是使用一個句柄值來描述的,因此,當Client進程的用戶空間通過Binder驅動程序來訪問一個Service組件時,它只需要指定一個句柄值,Binder驅動程序就可以通過該句柄值找到對應的Binder引用對象,然后再根據該Binder引用對象的成員變量node找到對應的Binder實體對象,最后就可以通過該Binder實體對象找到要訪問的Service組件。
注意:一個Binder引用對象的句柄值在進程范圍內是唯一的,因此,在兩個不同的進程中,同一個句柄值可能代表的是兩個不同的目標Service組件。
成員變量proc指向一個Binder引用對象的宿主進程。一個宿主進程使用兩個紅黑樹來保存它內部所有的Binder引用對象,它們分別以句柄值和對應的Binder實體對象的地址來作為關鍵字保存這些Binder引用對象,而這些Binder引用對象的成員變量rb_node_desc和rb_node_node就正好是這兩個紅黑樹中的節點。
成員變量strong和weak分別用來描述一個Binder引用對象的強引用計數和弱引用計數,Binder驅動程序正是通過它們來維護一個Binder引用對象的生命周期的。
成員變量death指向一個Service組件的死亡接收通知。當Client進程向Binder驅動程序注冊一個它所引用的Service組件的死亡接收通知時,Binder驅動程序就會創建一個binder_ref_death結構體,然后保存在對應的Binder引用對象的成員變量death中。
最后,成員變量debug_id用來標志一個Binder引用對象的身份,它是用來幫助調試Binder驅動程序的。
**struct binder_buffer**
**kernel/goldfish/drivers/staging/android/binder.c**
~~~
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 */
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];
};
~~~
結構體binder_buffer用來描述一個內核緩沖區,它是用來在進程間傳輸數據的。每一個使用Binder進程間通信機制的進程在Binder驅動程序中都有一個內核緩沖區列表,用來保存Binder驅動程序為它所分配的內核緩沖區,而成員變量entry正好是這個內核緩沖區列表的一個節點。同時,進程又使用了兩個紅黑樹來分別保存那些正在使用的內核緩沖區,以及空閑的內核緩沖區。如果一個內核緩沖區是空閑的,即它的成員變量free的值等于1,那么成員變量rb_node就是空閑內核緩沖區紅黑樹中的一個節點;否則,成員變量rb_node就是正在使用內核緩沖區紅黑樹中的一個節點。
成員變量transaction 和target_node用來描述一個內核緩沖區正在交給哪一個事務以及哪一個Binder實體對象使用。Binder驅動程序使用一個binder_transaction結構體來描述一個事務,每一個事務都關聯有一個目標Binder實體對象。Binder驅動程序將事務數據保存在一個內核緩沖區中,然后將它交給目標Binder實體對象處理,而目標Binder實體對象再將該內核緩沖區的內容交給相應的Service組件處理。Service組件處理完成該事務之后,如果發現傳遞給它的內核緩沖區的成員變量allow_user_free的值為1,那么該Service組件就會請求Binder驅動程序釋放該內核緩沖區。
如果與一個內核緩沖區關聯的是一個異步事務,那么Binder驅動程序就會將該內核緩沖區的成員變量async_transaction的值設置為1;否則,就將它的值設置為0。Binder驅動程序限制了分配給異步事務的內核緩沖區的大小,這樣做的目的是為了保證同步事務可以優先得到內核緩沖區,以便可以快速地對該同步事務進行處理。
成員變量data指向一塊大小可變的數據緩沖區,它是真正用來保存通信數據的。數據緩沖區保存的數據劃分為兩種類型,其中一種是普通數據,另一種是Binder對象。Binder驅動程序不關心數據緩沖區中的普通數據,但是必須要知道里面的Binder對象,因為它需要根據它們來維護內核中的Binder實體對象和Binder引用對象的生命周期。例如,如果數據緩沖區中包含了一個Binder引用,并且該數據緩沖區是傳遞給另外一個進程的,那么Binder驅動程序就需要為另外一個進程創建一個Binder引用對象,并且增加相應的Binder實體對象的引用計數,因為它也被另外的這個進程引用了。由于數據緩沖區中的普通數據和Binder對象是混合在一起保存的,它們之間并沒有固定的順序,因此,Binder驅動程序就需要額外的數據來找到里面的Binder對象。在數據緩沖區的后面,有一個偏移數組,它記錄了數據緩沖區中每一個Binder對象在數據緩沖區中的位置。偏移數組的大小保存在成員變量offsets_size中,而數據緩沖區的大小保存在成員變量data_size中。
最后,成員變量debug_id用來標志一個內核緩沖區的身份,它是用來幫助調試Binder驅動程序的。
**struct binder_proc**
**kernel/goldfish/drivers/staging/android/binder.c**
~~~
struct binder_proc {
struct hlist_node proc_node;
struct rb_root threads;
struct rb_root nodes;
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;
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;
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;
};
~~~
結構體binder_proc用來描述一個正在使用Binder進程間通信機制的進程。當一個進程調用函數open來打開設備文件/dev/binder時,Binder驅動程序就會為它創建一個binder_proc結構體,并且將它保存在一個全局的hash列表中,而成員變量proc_node就正好是該hash列表中的一個節點。此外,成員變量pid、tsk和files分別指向了進程的進程組ID、任務控制塊和打開文件結構體數組。
進程打開了設備文件/dev/binder之后,還必須調用函數mmap將它映射到進程的地址空間來,實際上是請求Binder驅動程序為它分配一塊內核緩沖區,以便可以用來在進程間傳輸數據。Binder驅動程序為進程分配的內核緩沖區的大小保存在成員變量buffer_size中。這些內核緩沖區有兩個地址,其中一個是內核空間地址,另外一個是用戶空間地址。內核空間地址是在Binder驅動程序內部使用的,保存在成員變量buffer中,而用戶空間地址是在應用程序進程內部使用的,保存在成員變量vma中。這兩個地址相差一個固定的值,保存在成員變量user_buffer_offset中。這樣,給定一個用戶空間地址或者一個內核空間地址,Binder驅動程序就可以計算出另外一個地址的大小。
注意:這兩個地址指的都是虛擬地址,它們對應的物理頁面保存在成員變量pages中。成員變量pages是類型為struct page*的一個數組,數組中的每一個元素都指向一個物理頁面。Binder驅動程序一開始時只為該內核緩沖區分配一個物理頁面,后面不夠使用時,再繼續分配。
成員變量buffer指向的是一塊大的內核緩沖區,Binder驅動程序為了方便對它進行管理,會將它劃分成若干個小塊。這些小塊的內核緩沖區就是使用前面所介紹的結構體binder_buffer來描述的,它們保存在一個列表中,按照地址值從小到大的順序來排列。成員變量buffers指向的便是該列表的頭部。列表中的小塊內核緩沖區有的是正在使用的,即已經分配了物理頁面;有的是空閑的,即還沒有分配物理頁面,它們分別組織在兩個紅黑樹中,其中,前者保存在成員變量allocated_buffers所描述的紅黑樹中,而后者保存在成員變量free_buffers所描述的紅黑樹中。此外,成員變量buffer_free保存了空閑內核緩沖區的大小,而成員變量free_async_space保存了當前可以用來保存異步事務數據的內核緩沖區的大小。
前面提到,每一個使用了Binder進程間通信機制的進程都有一個Binder線程池,用來處理進程間通信請求,這個Binder線程池是由Binder驅動程序來維護的。結構體binder_proc的成員變量threads是一個紅黑樹的根節點,它以線程ID作為關鍵字來組織一個進程的Binder線程池。進程可以調用函數ioctl將一個線程注冊到Binder驅動程序中,同時,當進程沒有足夠的空閑線程在處理進程間通信請求時,Binder驅動程序也可以主動要求進程注冊更多的線程到Binder線程池中。Binder驅動程序最多可以主動請求進程注冊的線程的數量保存在成員變量max_threads中,而成員變量ready_threads表示進程當前的空閑Binder線程數目。
注意:成員變量max_threads并不是表示Binder線程池中的最大線程數目,進程本身可以主動注冊任意數目的線程到Binder線程池中。Binder驅動程序每一次主動請求進程注冊一個線程時,都會將成員變量requested_threads的值加1;而當進程響應這個請求之后,Binder驅動程序就會將成員變量requested_threads的值減1,而且將成員變量requested_threads_started的值加1,表示Binder驅動程序已經主動請求進程注冊了多少個線程到Binder線程池中。
當進程接收到一個進程間通信請求時,Binder驅動程序就將該請求封裝成一個工作項,并且加入到進程的待處理工作項隊列中,這個隊列使用成員變量todo來描述。Binder線程池中的空閑Binder線程會睡眠在由成員變量wait所描述的一個等待隊列中,當它們的宿主進程的待處理工作項隊列增加了新的工作項之后,Binder驅動程序就會喚醒這些線程,以便它們可以去處理新的工作項。成員變量default_priority的值被初始化為進程的優先級。當一個線程處理一個工作項時,它的線程優先級有可能會被設置為其宿主進程的優先級,即設置為成員變量default_priority的值,這是由于線程是代表其宿主進程來處理一個工作項的。線程在處理一個工作項時的優先級還會受到其他因素的影響,后面我們再詳細描述。
一個進程內部包含了一系列的Binder實體對象和Binder引用對象,進程使用三個紅黑樹來組織它們,其中,成員變量nodes所描述的紅黑樹是用來組織Binder實體對象的,它以Binder實體對象的成員變量ptr作為關鍵字;而成員變量refs_by_desc和refs_by_node所描述的紅黑樹是用來組織Binder引用對象的,前者以Binder引用對象的成員變量desc作為關鍵字,而后者以Binder引用對象的成員變量node作為關鍵字。
成員變量deferred_work_node是一個hash列表,用來保存進程可以延遲執行的工作項。這些延遲工作項有三種類型,如下所示。
**kernel/goldfish/drivers/staging/android/binder.c**
~~~
enum {
BINDER_DEFERRED_PUT_FILES = 0x01,
BINDER_DEFERRED_FLUSH = 0x02,
BINDER_DEFERRED_RELEASE = 0x04,
};
~~~
Binder驅動程序為進程分配內核緩沖區時,會為這個內核緩沖區創建一個文件描述符,進程可以通過這個文件描述符將該內核緩沖區映射到自己的地址空間。當進程不再需要使用Binder進程間通信機制時,它就會通知Binder驅動程序關閉該文件描述符,并且釋放之前所分配的內核緩沖區。由于這不是一個馬上就需要完成的操作,因此,Binder驅動程序就會創建一個BINDER_DEFERRED_PUT_FILES類型的工作項來延遲執行該操作。
前面提到,Binder線程池中的空閑Binder線程是睡眠在一個等待隊列中的,進程可以通過調用函數flush來喚醒這些線程,以便它們可以檢查進程是否有新的工作項需要處理。這時候Binder驅動程序就會創建一個BINDER_DEFERRED_FLUSH類型的工作項,以便可以延遲執行喚醒空閑Binder線程的操作。
當進程不再使用Binder進程間通信機制時,它就會調用函數close來關閉設備文件/dev/binder,這時候Binder驅動程序就會釋放之前為它分配的資源,例如,釋放進程結構體binder_proc、Binder實體對象結構體binder_node以及Binder引用對象結構體binder_ref等。由于資源的釋放操作是一個比較耗時的操作,因此,Binder驅動程序會創建一個BINDER_DEFERRED_RELEASE類型的事務來延遲執行它們。
Binder驅動程序將所有的延遲執行的工作項保存在一個hash列表中。如果一個進程有延遲執行的工作項,那么成員變量deferred_work_node就剛好是該hash列表中的一個節點,并且使用成員變量deferred_work來描述該延遲工作項的具體類型。
當一個進程所引用的Service組件死亡時,Binder驅動程序就會向該進程發送一個死亡通知。這個正在發出的死亡通知被封裝成一個類型為BINDER_WORK_DEAD_BINDER或者BINDER_WORK_DEAD_BINDER_AND_CLEAR的工作項,并且保存在由成員變量delivered_death所描述的一個隊列中,表示Binder驅動程序正在向進程發送的死亡通知。當進程接收到這個死亡通知之后,它便會通知Binder驅動程序,這時候Binder驅動程序就會將對應的工作項從成員變量delivered_death所描述的隊列中刪除。
最后,成員變量stats是用來統計進程數據的,例如,進程接收到的進程間通信請求的次數。
**struct binder_thread**
**kernel/goldfish/drivers/staging/android/binder.c**
~~~
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;
};
~~~
結構體binder_thread用來描述Binder線程池中的一個線程,其中,成員變量proc指向其宿主進程。前面在介紹進程結構體binder_proc時提到,進程結構體binder_proc使用一個紅黑樹來組織其Binder線程池中的線程,其中,結構體binder_thread的成員變量rb_node就是該紅黑樹中的一個節點。
一個Binder線程的ID和狀態是通過成員變量pid和looper來描述的。線程狀態的取值如下所示。
**kernel/goldfish/drivers/staging/android/binder.c**
~~~
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
};
~~~
一個線程注冊到Binder驅動程序時,Binder驅動程序就會為它創建一個binder_thread結構體,并且將它的狀態初始化為BINDER_LOOPER_STATE_NEED_RETURN,表示該線程需要馬上返回到用戶空間。由于一個線程在注冊為Binder線程時可能還沒有準備好去處理進程間通信請求,因此,最好返回到用戶空間去做準備工作。此外,當進程調用函數flush來刷新它的Binder線程池時,Binder線程池中的線程的狀態也會被重置為BINDER_LOOPER_STATE_NEED_RETURN。
一個線程注冊到Binder驅動程序之后,它接著就會通過BC_REGISTER_LOOPER或者BC_ENTER_LOOPER協議來通知Binder驅動程序,它可以處理進程間通信請求了,這時候Binder驅動程序就會將它的狀態設置為BINDER_LOOPER_STATE_REGISTERED或者BINDER_LOOPER_STATE_ENTERED。如果一個線程是應用程序主動注冊的,那么它就通過BC_ENTER_LOOPER協議來通知Binder驅動程序,它已經準備就緒處理進程間通信請求了;如果一個線程是Binder驅動程序請求創建的,那么它就通過BC_REGISTER_LOOPER協議來通知Binder驅動程序,這時候Binder驅動程序就會增加它所請求進程創建的Binder線程的數目。
當一個Binder線程處于空閑狀態時,Binder驅動程序就會把它的狀態設置為BINDER_LOOPER_STATE_WAITING;而當一個Binder線程退出時,它會通過BC_EXIT_LOOPER協議來通知Binder驅動程序,這時候Binder驅動程序就會將它的狀態設置為BINDER_LOOPER_STATE_EXITED。在異常情況下,一個Binder線程的狀態會被設置為BINDER_LOOPER_STATE_INVALID,例如,當該線程已經處于BINDER_LOOPER_STATE_REGISTERED狀態時,如果它又再次通過BC_ENTER_LOOPER協議來通知Binder驅動程序它已經準備就緒了,那么Binder驅動程序就會將它的狀態設置為BINDER_LOOPER_STATE_INVALID。
當一個來自Client進程的請求指定要由某一個Binder線程來處理時,這個請求就會加入到相應的binder_thread結構體的成員變量todo所表示的隊列中,并且會喚醒這個線程來處理,因為這時候這個線程可能處于睡眠狀態。
當Binder驅動程序決定將一個事務交給一個Binder線程處理時,它就會將該事務封裝成一個binder_transaction結構體,并且將它添加到由線程結構體binder_thread的成員變量transaction_stack所描述的一個事務堆棧中。結構體binder_transaction的設計很巧妙,后面我們再詳細介紹它的定義。
當一個Binder線程在處理一個事務T1并需要依賴于其他的Binder線程來處理另外一個事務T2時,它就會睡眠在由成員變量wait所描述的一個等待隊列中,直到事務T2處理完成為止。
一個Binder線程在處理一個事務時,如果出現了異常情況,那么Binder驅動程序就會將相應的錯誤碼保存在其成員變量return_error和reutrn_error2中,這時候線程就會將這些錯誤返回給用戶空間應用程序處理。
最后,成員變量stats是用來統計Binder線程數據的,例如,Binder線程接收到的進程間通信請求的次數。
**struct binder_transaction**
**kernel/goldfish/drivers/staging/android/binder.c**
~~~
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;
uid_t sender_euid;
};
~~~
結構體binder_transaction用來描述進程間通信過程,這個過程又稱為一個事務。成員變量need_reply用來區分一個事務是同步的還是異步的。同步事務需要等待對方回復,這時候它的成員變量need_reply的值就會設置為1;否則就設置為0,表示這是一個異步事務,不需要等待回復。
成員變量from指向發起事務的線程,稱為源線程;成員變量to_proc和to_thread分別指向負責處理該事務的進程和線程,稱為目標進程和目標線程。當Binder驅動程序為目標進程或者目標線程創建一個事務時,就會將該事務的成員變量work的值設置為BINDER_WORK_TRANSACTION,并且將它添加到目標進程或者目標線程的todo隊列中去等待處理。
成員變量priority和sender_euid分別用來描述源線程的優先級和用戶ID。通過這兩個成員變量,目標進程或者目標線程就可以識別事務發起方的身份。
一個線程在處理一個事務時,Binder驅動程序需要修改它的線程優先級,以便滿足源線程和目標Service組件的要求。Binder驅動程序在修改一個線程的優先級之前,會將它原來的線程優先級保存在一個事務結構體的成員變量saved_priority中,以便線程處理完成該事務后可以恢復原來的優先級。前面在介紹結構體binder_node時提到,目標線程在處理一個事務時,它的線程優先級不能低于目標Service組件所要求的線程優先級,而且也不能低于源線程的優先級。這時候Binder驅動程序就會將這二者中的較大值設置為目標線程的優先級。
成員變量buffer指向Binder驅動程序為該事務分配的一塊內核緩沖區,它里面保存了進程間通信數據。成員變量code和flags是直接從進程間通信數據中拷貝過來的,后面在介紹結構體binder_transaction_data時,我們再詳細分析。
成員變量from_parent和to_parent分別描述一個事務所依賴的另外一個事務,以及目標線程下一個需要處理的事務。假設線程A發起了一個事務T1,需要由線程B來處理;線程B在處理事務T1時,又需要線程C先處理事務T2;線程C在處理事務T2時,又需要線程A先處理事務T3。這樣,事務T1就依賴于事務T2,而事務T2又依賴于事務T3,它們的關系如下:
:-: T2->from_parent = T1;
:-: T3->from_parent = T2;
對于線程A來說,它需要處理的事務有兩個,分別是T1和T3,它首先要處理事務T3,然后才能處理事務T1,因此,事務T1和T3的關系如下:
:-: T3->to_parent = T1;
考慮這樣一個情景:如果線程C在發起事務T3給線程A所屬的進程來處理時,Binder驅動程序選擇了該進程的另外一個線程D來處理該事務,這時候會出現什么情況呢?這時候線程A就會處于空閑等待狀態,什么也不能做,因為它必須要等線程D處理完成事務T3后,它才可以繼續執行事務T1。在這種情況下,與其讓線程A閑著,還不如把事務T3交給它來處理,這樣線程D就可以去處理其他事務,提高了進程的并發性。
現在,關鍵的問題又來了——Binder驅動程序在分發事務T3給目標進程處理時,它是如何知道線程A屬于目標進程,并且正在等待事務T3的處理結果的?當線程C在處理事務T2時,就會將事務T2放在其事務堆棧transaction_stack的最前端。這樣當線程C發起事務T3給線程A所屬的進程處理時,Binder驅動程序就可以沿著線程C的事務堆棧transaction_stack向下遍歷,即沿著事務T2的成員變量from_parent向下遍歷,最后就會發現事務T3的目標進程等于事務T1的源進程,并且事務T1是由線程A發起來的,這時候它就知道線程A正在等待事務T3的處理結果了。
> 注意:線程A在處理事務T3時,Binder驅動程序會將事務T3放在其事務堆棧transaction_stack的最前端,而在此之前,該事務堆棧transaction_stack的最前端指向的是事務T1。為了能夠讓線程A處理完成事務T3之后,接著處理事務T1,Binder驅動程序會將事務T1保存在事務T3的成員變量to_parent中。等到線程A處理完成事務T3之后,就可以通過事務T3的成員變量to_parent找到事務T1,再將它放在線程A的事務堆棧transaction_stack的最前端了。
最后,成員變量debug_id用來標志一個事務結構體的身份,它是用來幫助調試Binder驅動程序的。
以上介紹的結構體都是在Binder驅動程序內部使用的。前面提到,應用程序進程在打開了設備文件/dev/binder之后,需要通過IO控制函數ioctl來進一步與Binder驅動程序進行交互,因此,Binder驅動程序就提供了一系列的IO控制命令來和應用程序進程通信。在這些IO控制命令中,最重要的便是BINDER_WRITE_READ命令了,它的定義如下所示。
**kernel/goldfish/drivers/staging/android/binder.h**
~~~
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
~~~
IO控制命令BINDER_WRITE_READ后面所跟的參數是一個binder_write_read結構體,它的定義如下所示。
**kernel/goldfish/drivers/staging/android/binder.h**
~~~
struct binder_write_read {
signed long write_size; /* bytes to write */
signed long write_consumed; /* bytes consumed by driver */
unsigned long write_buffer;
signed long read_size; /* bytes to read */
signed long read_consumed; /* bytes consumed by driver */
unsigned long read_buffer;
};
~~~
結構體binder_write_read用來描述進程間通信過程中所傳輸的數據。這些數據包括輸入數據和輸出數據,其中,成員變量write_size、write_consumed和write_buffer用來描述輸入數據,即從用戶空間傳輸到Binder驅動程序的數據;而成員變量read_size、read_consumed和read_buffer用來描述輸出數據,即從Binder驅動程序返回給用戶空間的數據,它也是進程間通信結果數據。
成員變量write_buffer指向一個用戶空間緩沖區的地址,里面保存的內容即為要傳輸到Binder驅動程序的數據。緩沖區write_buffer的大小由成員變量write_size來指定,單位是字節。成員變量write_consumed用來描述Binder驅動程序從緩沖區write_buffer中處理了多少個字節的數據。
成員變量read_buffer也是指向一個用戶空間緩沖區的地址,里面保存的內容即為Binder驅動程序返回給用戶空間的進程間通信結果數據。緩沖區read_buffer的大小由成員變量read_size來指定,單位是字節。成員變量read_consumed用來描述用戶空間應用程序從緩沖區read_buffer中處理了多少個字節的數據。
緩沖區write_buffer和read_buffer的數據格式如圖5-2所示。

它們都是一個數組,數組的每一個元素都由一個通信協議代碼及其通信數據組成。協議代碼又分為兩種類型,其中一種是在輸入緩沖區write_buffer中使用的,稱為命令協議代碼,另一種是在輸出緩沖區read_buffer中使用的,稱為返回協議代碼。命令協議代碼通過BinderDriverCommandProtocol枚舉值來定義,而返回協議代碼通過BinderDriverReturnProtocol枚舉值來定義。
命令協議代碼的定義如下所示。
**enum BinderDriverCommandProtocol**
**kernel/goldfish/drivers/staging/android/binder.h**
~~~
enum BinderDriverCommandProtocol {
BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data),
BC_REPLY = _IOW('c', 1, struct binder_transaction_data),
/*
* binder_transaction_data: the sent command.
*/
BC_ACQUIRE_RESULT = _IOW('c', 2, int),
/*
* not currently supported
* int: 0 if the last BR_ATTEMPT_ACQUIRE was not successful.
* Else you have acquired a primary reference on the object.
*/
BC_FREE_BUFFER = _IOW('c', 3, int),
/*
* void *: ptr to transaction data received on a read
*/
BC_INCREFS = _IOW('c', 4, int),
BC_ACQUIRE = _IOW('c', 5, int),
BC_RELEASE = _IOW('c', 6, int),
BC_DECREFS = _IOW('c', 7, int),
/*
* int: descriptor
*/
BC_INCREFS_DONE = _IOW('c', 8, struct binder_ptr_cookie),
BC_ACQUIRE_DONE = _IOW('c', 9, struct binder_ptr_cookie),
/*
* void *: ptr to binder
* void *: cookie for binder
*/
BC_ATTEMPT_ACQUIRE = _IOW('c', 10, struct binder_pri_desc),
/*
* not currently supported
* int: priority
* int: descriptor
*/
BC_REGISTER_LOOPER = _IO('c', 11),
/*
* No parameters.
* Register a spawned looper thread with the device.
*/
BC_ENTER_LOOPER = _IO('c', 12),
BC_EXIT_LOOPER = _IO('c', 13),
/*
* No parameters.
* These two commands are sent as an application-level thread
* enters and exits the binder loop, respectively. They are
* used so the binder can have an accurate count of the number
* of looping threads it has available.
*/
BC_REQUEST_DEATH_NOTIFICATION = _IOW('c', 14, struct binder_ptr_cookie),
/*
* void *: ptr to binder
* void *: cookie
*/
BC_CLEAR_DEATH_NOTIFICATION = _IOW('c', 15, struct binder_ptr_cookie),
/*
* void *: ptr to binder
* void *: cookie
*/
BC_DEAD_BINDER_DONE = _IOW('c', 16, void *),
/*
* void *: cookie
*/
};
~~~
命令協議代碼BC_TRANSACTION和BC_REPLY后面跟的通信數據使用一個結構體binder_transaction_data來描述。當一個進程請求另外一個進程執行某一個操作時,源進程就使用命令協議代碼BC_TRANSACTION來請求Binder驅動程序將通信數據傳遞到目標進程;當目標進程處理完成源進程所請求的操作之后,它就使用命令協議代碼BC_REPLY來請求Binder驅動程序將結果數據傳遞給源進程。
命令協議代碼BC_ACQUIRE_RESULT在當前的Binder驅動程序實現中不支持。
命令協議代碼BC_FREE_BUFFER后面跟的通信數據是一個整數,它指向了在Binder驅動程序內部所分配的一塊內核緩沖區。Binder驅動程序就是通過這個內核緩沖區將源進程的通信數據傳遞到目標進程的。當目標進程處理完成源進程的通信請求之后,它就會使用命令協議代碼BC_FREE_BUFFER來通知Binder驅動程序釋放這個內核緩沖區。
命令協議代碼BC_INCREFS、BC_ACQUIRE、BC_RELEASE和BC_DECREFS后面跟的通信數據是一個整數,它描述了一個Binder引用對象的句柄值,其中,命令協議代碼BC_INCREFS和BC_DECREFS分別用來增加和減少一個Binder引用對象的弱引用計數;而命令協議代碼BC_ACQUIRE和BC_RELEASE分別用來增加和減少一個Binder引用對象的強引用計數。
命令協議代碼BC_INCREFS_DONE和BC_ACQUIRE_DONE后面跟的通信數據使用一個結構體binder_ptr_cookie來描述。Binder驅動程序第一次增加一個Binder實體對象的強引用計數或者弱引用計數時,就會使用返回協議代碼BR_ACQUIRE或者BR_INCREFS來請求對應的Server進程增加對應的Service組件的強引用計數或者弱引用計數。當Server進程處理完成這兩個請求之后,就會分別使用命令協議代碼BC_INCREFS_DONE和BC_ACQUIRE_DONE將操作結果返回給Binder驅動程序。
命令協議代碼BC_ATTEMPT_ACQUIRE在當前的Binder驅動程序實現中不支持。
命令協議代碼BC_REGISTER_LOOPER、BC_ENTER_LOOPER和BC_EXIT_LOOPER后面不需要指定通信數據。一方面,當一個線程將自己注冊到Binder驅動程序之后,它接著就會使用命令協議代碼BC_ENTER_LOOPER來通知Binder驅動程序,它已經準備就緒處理進程間通信請求了;另一方面,當Binder驅動程序主動請求進程注冊一個新的線程到它的Binder線程池中來處理進程間通信請求之后,新創建的線程就會使用命令協議代碼BC_REGISTER_LOOPER來通知Binder驅動程序,它準備就緒了。最后,當一個線程要退出時,它就使用命令協議代碼BC_EXIT_LOOPER從Binder驅動程序中注銷,這樣它就不會再接收到進程間通信請求了。
命令協議代碼BC_REQUEST_DEATH_NOTIFICATION和BC_CLEAR_DEATH_NOTIFICATION后面跟的通信數據使用一個結構體binder_ptr_cookie來描述。一方面,如果一個進程希望獲得它所引用的Service組件的死亡接收通知,那么它就需要使用命令協議代碼BC_REQUEST_DEATH_NOTIFICATION來向Binder驅動程序注冊一個死亡接收通知;另一方面,如果一個進程想注銷之前所注冊的一個死亡接收通知,那么它就需要使用命令協議代碼BC_CLEAR_DEATH_NOTIFICATION來向Binder驅動程序發出請求。
命令協議代碼BC_DEAD_BINDER_DONE后面跟的通信數據是一個void類型的指針,指向一個死亡接收通知結構體binder_ref_death的地址。當一個進程獲得一個Service組件的死亡通知時,它就會使用命令協議代碼BC_DEAD_BINDER_DONE來通知Binder驅動程序,它已經處理完成該Service組件的死亡通知了。
返回協議代碼的定義如下所示。
**enum BinderDriverReturnProtocol**
**kernel/goldfish/drivers/staging/android/binder.h**
~~~
enum BinderDriverReturnProtocol {
BR_ERROR = _IOR('r', 0, int),
/*
* int: error code
*/
BR_OK = _IO('r', 1),
/* No parameters! */
BR_TRANSACTION = _IOR('r', 2, struct binder_transaction_data),
BR_REPLY = _IOR('r', 3, struct binder_transaction_data),
/*
* binder_transaction_data: the received command.
*/
BR_ACQUIRE_RESULT = _IOR('r', 4, int),
/*
* not currently supported
* int: 0 if the last bcATTEMPT_ACQUIRE was not successful.
* Else the remote object has acquired a primary reference.
*/
BR_DEAD_REPLY = _IO('r', 5),
/*
* The target of the last transaction (either a bcTRANSACTION or
* a bcATTEMPT_ACQUIRE) is no longer with us. No parameters.
*/
BR_TRANSACTION_COMPLETE = _IO('r', 6),
/*
* No parameters... always refers to the last transaction requested
* (including replies). Note that this will be sent even for
* asynchronous transactions.
*/
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),
/*
* void *: ptr to binder
* void *: cookie for binder
*/
BR_ATTEMPT_ACQUIRE = _IOR('r', 11, struct binder_pri_ptr_cookie),
/*
* not currently supported
* int: priority
* void *: ptr to binder
* void *: cookie for binder
*/
BR_NOOP = _IO('r', 12),
/*
* No parameters. Do nothing and examine the next command. It exists
* primarily so that we can replace it with a BR_SPAWN_LOOPER command.
*/
BR_SPAWN_LOOPER = _IO('r', 13),
/*
* No parameters. The driver has determined that a process has no
* threads waiting to service incomming transactions. When a process
* receives this command, it must spawn a new service thread and
* register it via bcENTER_LOOPER.
*/
BR_FINISHED = _IO('r', 14),
/*
* not currently supported
* stop threadpool thread
*/
BR_DEAD_BINDER = _IOR('r', 15, void *),
/*
* void *: cookie
*/
BR_CLEAR_DEATH_NOTIFICATION_DONE = _IOR('r', 16, void *),
/*
* void *: cookie
*/
BR_FAILED_REPLY = _IO('r', 17),
/*
* The the last transaction (either a bcTRANSACTION or
* a bcATTEMPT_ACQUIRE) failed (e.g. out of memory). No parameters.
*/
};
~~~
返回協議代碼BR_ERROR后面跟的通信數據是一個整數,用來描述一個錯誤代碼。Binder驅動程序在處理應用程序進程發出的某個請求時,如果發生了異常情況,它就會使用返回協議代碼BR_ERROR來通知應用程序進程。
返回協議代碼BR_OK后面不需要指定通信數據。Binder驅動程序成功處理了應用程序進程發出的某一個請求之后,它就會使用返回協議代碼BR_OK來通知應用程序進程。
返回協議代碼BR_TRANSACTION和BR_REPLY后面跟的通信數據使用一個結構體binder_transaction_data來描述。當一個Client進程向一個Server進程發出進程間通信請求時,Binder驅動程序就會使用返回協議代碼BR_TRANSACTION通知該Server進程來處理該進程間通信請求;當Server進程處理完成該進程間通信請求之后,Binder驅動程序就會使用返回協議代碼BR_REPLY將進程間通信請求結果數據返回給Client進程。
返回協議代碼BR_ACQUIRE_RESULT在當前的Binder驅動程序實現中不支持。
返回協議代碼BR_DEAD_REPLY后面不需要指定通信數據。Binder驅動程序在處理進程間通信請求時,如果發現目標進程或者目標線程已經死亡,它就會使用返回協議代碼BR_DEAD_REPLY來通知源進程。
返回協議代碼BR_TRANSACTION_COMPLETE后面不需要指定通信數據。當Binder驅動程序接收到應用程序進程給它發送的一個命令協議代碼BC_TRANSACTION或者BC_REPLY時,它就會使用返回協議代碼BR_TRANSACTION_COMPLETE來通知應用程序進程,該命令協議代碼已經被接收,正在分發給目標進程或者目標線程處理。
返回協議代碼BR_INCREFS、BR_ACQUIRE、BR_RELEASE和BR_DECREFS后面跟的通信數據使用一個結構體binder_ptr_cookie來描述,其中,返回協議代碼BR_INCREFS和BR_DECREFS分別用來增加和減少一個Service組件的弱引用計數;而返回協議代碼BR_ACQUIRE和BR_RELEASE分別用來增加和減少一個Service組件的強引用計數。
返回協議代碼BR_ATTEMPT_ACQUIRE在當前的Binder驅動程序實現中不支持。
返回協議代碼BR_NOOP后面不需要指定通信數據。Binder驅動程序使用返回協議代碼BR_NOOP來通知應用程序進程執行一個空操作,它的存在是為了方便以后可以替換為返回協議代碼BR_SPAWN_LOOPER。
返回協議代碼BR_SPAWN_LOOPER后面不需要指定通信數據。當Binder驅動程序發現一個進程沒有足夠的空閑Binder線程來處理進程間通信請求時,它就會使用返回協議代碼BR_SPAWN_LOOPER來通知該進程增加一個新的線程到Binder線程池中。
返回協議代碼BR_FINISHED在當前的Binder驅動程序實現中不支持。
返回協議代碼BR_DEAD_BINDER和BR_CLEAR_DEATH_NOTIFICATION_DONE后面跟的通信數據是一個void類型的指針,它指向一個用來接收Service組件死亡通知的對象的地址。當Binder驅動程序監測到一個Service組件的死亡事件時,它就會使用返回協議代碼BR_DEAD_BINDER來通知相應的Client進程。當Client進程通知Binder驅動程序注銷它之前所注冊的一個死亡接收通知時,Binder驅動程序執行完成這個注銷操作之后,就會使用返回協議代碼BR_CLEAR_DEATH_NOTIFICATION_DONE來通知Client進程。
返回協議代碼BR_FAILED_REPLY后面不需要指定通信數據。當Binder驅動程序處理一個進程發出的BC_TRANSACTION命令協議時,如果發生了異常情況,它就會使用返回協議代碼BR_FAILED_REPLY來通知源進程。
介紹完Binder驅動程序提供的命令協議代碼和返回協議代碼之后,接下來我們繼續分析這些協議所使用的兩個結構體binder_ptr_cookie和binder_transaction_data的定義。
**struct binder_ptr_cookie**
**kernel/goldfish/drivers/staging/android/binder.h**
~~~
struct binder_ptr_cookie {
void *ptr;
void *cookie;
};
~~~
結構體binder_ptr_cookie用來描述一個Binder實體對象或者一個Service組件的死亡接收通知。當結構體binder_ptr_cookie描述的是一個Binder實體對象時,成員變量ptr和cookie的含義等同于前面所介紹的結構體binder_node的成員變量ptr和cookie;當結構體binder_ptr_cookie描述的是一個Service組件的死亡接收通知時,成員變量ptr指向的是一個Binder引用對象的句柄值,而成員變量cookie指向的是一個用來接收死亡通知的對象的地址。
**struct binder_transaction_data**
**kernel/goldfish/drivers/staging/android/binder.h**
~~~
struct binder_transaction_data {
/* The first two are only used for bcTRANSACTION and brTRANSACTION,
* identifying the target and contents of the transaction.
*/
union {
size_t handle; /* target descriptor of command transaction */
void *ptr; /* target descriptor of return transaction */
} target;
void *cookie; /* target object cookie */
unsigned int code; /* transaction command */
/* General information about the transaction. */
unsigned int flags;
pid_t sender_pid;
uid_t sender_euid;
size_t data_size; /* number of bytes of data */
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 */
const void *buffer;
/* offsets from buffer to flat_binder_object structs */
const void *offsets;
} ptr;
uint8_t buf[8];
} data;
};
~~~
結構體binder_transaction_data用來描述進程間通信過程中所傳輸的數據。
成員變量target是一個聯合體,用來描述一個目標Binder實體對象或者目標Binder引用對象。如果它描述的是一個目標Binder實體對象,那么它的成員變量ptr就指向與該Binder實體對象對應的一個Service組件內部的一個弱引用計數對象(weakref_impl)的地址;如果它描述的是一個目標Binder引用對象,那么它的成員變量handle就指向該Binder引用對象的句柄值。
成員變量cookie是由應用程序進程指定的額外參數。當Binder驅動程序使用返回命令協議BR_TRANSACTION向一個Server進程發出一個進程間通信請求時,這個成員變量才有實際意義,它指向的是目標Service組件的地址。
成員變量code是由執行進程間通信的兩個進程互相約定好的一個通信代碼,Binder驅動程序完全不關心它的含義。
成員變量flags是一個標志值,用來描述進程間通信行為特征,它的取值如下所示。
**kernel/goldfish/drivers/staging/android/binder.h**
~~~
enum transaction_flags {
TF_ONE_WAY = 0x01, /* this is a one-way call: async, no return */
TF_ROOT_OBJECT = 0x04, /* contents are the component's root object */
TF_STATUS_CODE = 0x08, /* contents are a 32-bit status code */
TF_ACCEPT_FDS = 0x10, /* allow replies with file descriptors */
};
~~~
目前,只使用了TF_ONE_WAY、TF_STATUS_CODE和TF_ACCEPT_FDS這三個標志值。如果成員變量flags的TF_ONE_WAY位被設置為1,就表示這是一個異步的進程間通信過程;如果成員變量flags的TF_ACCEPT_FDS位被設置為0,就表示源進程不允許目標進程返回的結果數據中包含有文件描述符;如果成員變量flags的TF_STATUS_CODE位被設置為1,就表示成員變量data所描述的數據緩沖區的內容是一個4字節的狀態碼。
成員變量sender_pid和sender_euid表示發起進程間通信請求的進程的PID和UID。這兩個成員變量的值是由Binder驅動程序來填寫的,因此,目標進程通過這兩個成員變量就可以識別出源進程的身份,以便進行安全檢查。
成員變量data_size和offsets_size分別用來描述一個通信數據緩沖區以及一個偏移數組的大小。成員變量data是一個聯合體,它指向一個通信數據緩沖區。當通信數據較小時,就直接使用聯合體內靜態分配的數組buf來傳輸數據;當通信數據較大時,就需要使用一塊動態分配的緩沖區來傳輸數據了。這塊動態分配的緩沖區通過一個包含兩個指針的結構體來描述,即通過聯合體內的成員變量ptr來描述。結構體ptr的成員變量buffer指向一個數據緩沖區,它是真正用來保存通信數據的,它的大小由前面所描述的成員變量data_size來指定。當數據緩沖區中包含有Binder對象時,那么緊跟在這個數據緩沖區的后面就會有一個偏移數組offsets,用來描述數據緩沖區中每一個Binder對象的位置。有了這個偏移數組之后,Binder驅動程序就可以正確地維護其內部的Binder實體對象和Binder引用對象的引用計數。
數據緩沖區中的每一個Binder對象都使用一個flat_binder_object結構體來描述。下面我們就通過一個例子來描述數據緩沖區的內存布局,如圖5-3所示。

在圖5-3所描述的數據緩沖區中,有兩個Binder對象,相應地,偏移數組的大小就等于2,里面保存的就是這兩個Binder對象在數據緩沖區中的位置n1和n2。
下面我們繼續分析結構體flat_binder_object的定義。
**struct flat_binder_object**
**kernel/goldfish/drivers/staging/android/binder.h**
~~~
struct flat_binder_object {
/* 8 bytes for large_flat_header. */
unsigned long type;
unsigned long flags;
/* 8 bytes of data. */
union {
void *binder; /* local object */
signed long handle; /* remote object */
};
/* extra data associated with local object */
void *cookie;
};
~~~
結構體flat_binder_object除了可以描述一個Binder實體對象和一個Binder引用對象之外,還可以用來描述一個文件描述符,它們是通過成員變量type來加以區別的。
成員變量type的取值范圍如下所示。
**kernel/goldfish/drivers/staging/android/binder.h**
~~~
#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_TYPE_WEAK_BINDER = B_PACK_CHARS('w', 'b', '*', B_TYPE_LARGE),
BINDER_TYPE_HANDLE = B_PACK_CHARS('s', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_WEAK_HANDLE = B_PACK_CHARS('w', 'h', '*', B_TYPE_LARGE),
BINDER_TYPE_FD = B_PACK_CHARS('f', 'd', '*', B_TYPE_LARGE),
};
~~~
其中,BINDER_TYPE_BINDER和BINDER_TYPE_WEAK_BINDER都是用來描述一個Binder實體對象的,前者描述的是一個強類型的Binder實體對象,而后者描述的是一個弱類型的Binder實體對象;BINDER_TYPE_HANDLE和BINDER_TYPE_WEAK_HANDLE用來描述一個Binder引用對象,前者描述的是一個強類型的Binder引用對象,而后者描述的是一個弱類型的Binder引用對象;BINDER_TYPE_FD用來描述一個文件描述符。
成員變量flags是一個標志值,只有當結構體flat_binder_object描述的是一個Binder實體對象時,它才有實際意義。目前只用到該成員變量的第0位到第8位。其中,第0位到第7位描述的是一個Binder實體對象在處理一個進程間通信請求時,它所運行在的線程應當具有的最小線程優先級;第8位用來描述一個Binder實體對象是否可以將一塊包含有文件描述符的數據緩沖區傳輸給目標進程,如果它的值等于1,就表示允許,否則就不允許。
成員變量binder和handle組成了一個聯合體。當結構體flat_binder_object描述的是一個Binder實體對象時,那么就使用成員變量binder來指向與該Binder實體對象對應的一個Service組件內部的一個弱引用計數對象(weakref_impl)的地址,并且使用成員變量cookie來指向該Service組件的地址;當結構體flat_binder_object描述的是一個Binder引用對象時,那么就使用成員變量handle來描述該Binder引用對象的句柄值。
至此,Binder驅動程序的基礎數據結構就介紹完了。接下來,我們開始分析Binder設備的初始化過程。
- 文章概述
- 下載Android源碼以及查看源碼
- win10 平臺通過VMware Workstation安裝Ubuntu
- Linux系統安裝Ubuntu編譯Android源碼
- Eclipse快捷鍵大全
- 前言
- 第一篇 初識Android系統
- 第一章 準備知識
- 1.1 Linux內核參考書籍
- 1.2 Android應用程序參考書籍
- 1.3 下載、編譯和運行Android源代碼
- 1.3.1 下載Android源代碼
- 1.3.2 編譯Android源代碼
- 1.3.3 運行Android模擬器
- 1.4 下載、編譯和運行Android內核源代碼
- 1.4.1 下載Android內核源代碼
- 1.4.2 編譯Android內核源代碼
- 1.4.3 運行Android模擬器
- 1.5 開發第一個Android應用程序
- 1.6 單獨編譯和打包Android應用程序模塊
- 1.6.1 導入單獨編譯模塊的mmm命令
- 1.6.2 單獨編譯Android應用程序模塊
- 1.6.3 重新打包Android系統鏡像文件
- 第二章 硬件抽象層
- 2.1 開發Android硬件驅動程序
- 2.1.1 實現內核驅動程序模塊
- 2.1.2 修改內核Kconfig文件
- 2.1.3 修改內核Makefile文件
- 2.1.4 編譯內核驅動程序模塊
- 2.1.5 驗證內核驅動程序模塊
- 2.2 開發C可執行程序驗證Android硬件驅動程序
- 2.3 開發Android硬件抽象層模塊
- 2.3.1 硬件抽象層模塊編寫規范
- 2.3.1.1 硬件抽象層模塊文件命名規范
- 2.3.1.2 硬件抽象層模塊結構體定義規范
- 2.3.2 編寫硬件抽象層模塊接口
- 2.3.3 硬件抽象層模塊的加載過程
- 2.3.4 處理硬件設備訪問權限問題
- 2.4 開發Android硬件訪問服務
- 2.4.1 定義硬件訪問服務接口
- 2.4.2 實現硬件訪問服務
- 2.4.3 實現硬件訪問服務的JNI方法
- 2.4.4 啟動硬件訪問服務
- 2.5 開發Android應用程序來使用硬件訪問服務
- 第三章 智能指針
- 3.1 輕量級指針
- 3.1.1 實現原理分析
- 3.1.2 使用實例分析
- 3.2 強指針和弱指針
- 3.2.1 強指針的實現原理分析
- 3.2.2 弱指針的實現原理分析
- 3.2.3 應用實例分析
- 第二篇 Android專用驅動系統
- 第四章 Logger日志系統
- 4.1 Logger日志格式
- 4.2 Logger日志驅動程序
- 4.2.1 基礎數據結構
- 4.2.2 日志設備的初始化過程
- 4.2.3 日志設備文件的打開過程
- 4.2.4 日志記錄的讀取過程
- 4.2.5 日志記錄的寫入過程
- 4.3 運行時庫層日志庫
- 4.4 C/C++日志寫入接口
- 4.5 Java日志寫入接口
- 4.6 Logcat工具分析
- 4.6.1 基礎數據結構
- 4.6.2 初始化過程
- 4.6.3 日志記錄的讀取過程
- 4.6.4 日志記錄的輸出過程
- 第五章 Binder進程間通信系統
- 5.1 Binder驅動程序
- 5.1.1 基礎數據結構
- 5.1.2 Binder設備的初始化過程
- 5.1.3 Binder設備文件的打開過程
- 5.1.4 設備文件內存映射過程
- 5.1.5 內核緩沖區管理
- 5.1.5.1 分配內核緩沖區
- 5.1.5.2 釋放內核緩沖區
- 5.1.5.3 查詢內核緩沖區
- 5.2 Binder進程間通信庫
- 5.3 Binder進程間通信應用實例
- 5.4 Binder對象引用計數技術
- 5.4.1 Binder本地對象的生命周期
- 5.4.2 Binder實體對象的生命周期
- 5.4.3 Binder引用對象的生命周期
- 5.4.4 Binder代理對象的生命周期
- 5.5 Binder對象死亡通知機制
- 5.5.1 注冊死亡接收通知
- 5.5.2 發送死亡接收通知
- 5.5.3 注銷死亡接收通知
- 5.6 Service Manager的啟動過程
- 5.6.1 打開和映射Binder設備文件
- 5.6.2 注冊成為Binder上下文管理者
- 5.6.3 循環等待Client進程請求
- 5.7 Service Manager代理對象接口的獲取過程
- 5.8 Service的啟動過程
- 5.8.1 注冊Service組件
- 5.8.1.1 封裝通信數據為Parcel對象
- 5.8.1.2 發送和處理BC_TRANSACTION命令協議
- 5.8.1.3 發送和處理BR_TRANSACTION返回協議
- 5.8.1.4 發送和處理BC_REPLY命令協議
- 5.8.1.5 發送和處理BR_REPLY返回協議
- 5.8.2 循環等待Client進程請求
- 5.9 Service代理對象接口的獲取過程
- 5.10 Binder進程間通信機制的Java實現接口
- 5.10.1 獲取Service Manager的Java代理對象接口
- 5.10.2 AIDL服務接口解析
- 5.10.3 Java服務的啟動過程
- 5.10.4 獲取Java服務的代理對象接口
- 5.10.5 Java服務的調用過程
- 第六章 Ashmem匿名共享內存系統
- 6.1 Ashmem驅動程序
- 6.1.1 相關數據結構
- 6.1.2 設備初始化過程
- 6.1.3 設備文件打開過程
- 6.1.4 設備文件內存映射過程
- 6.1.5 內存塊的鎖定和解鎖過程
- 6.1.6 解鎖狀態內存塊的回收過程
- 6.2 運行時庫cutils的匿名共享內存接口
- 6.3 匿名共享內存的C++訪問接口
- 6.3.1 MemoryHeapBase
- 6.3.1.1 Server端的實現
- 6.3.1.2 Client端的實現
- 6.3.2 MemoryBase
- 6.3.2.1 Server端的實現
- 6.3.2.2 Client端的實現
- 6.3.3 應用實例
- 6.4 匿名共享內存的Java訪問接口
- 6.4.1 MemoryFile
- 6.4.2 應用實例
- 6.5 匿名共享內存的共享原理分析
- 第三篇 Android應用程序框架篇
- 第七章 Activity組件的啟動過程
- 7.1 Activity組件應用實例
- 7.2 根Activity的啟動過程
- 7.3 Activity在進程內的啟動過程
- 7.4 Activity在新進程中的啟動過程
- 第八章 Service組件的啟動過程
- 8.1 Service組件應用實例
- 8.2 Service在新進程中的啟動過程
- 8.3 Service在進程內的綁定過程
- 第九章 Android系統廣播機制
- 9.1 廣播應用實例
- 9.2 廣播接收者的注冊過程
- 9.3 廣播的發送過程
- 第十章 Content Provider組件的實現原理
- 10.1 Content Provider組件應用實例
- 10.1.1 ArticlesProvider
- 10.1.2 Article
- 10.2 Content Provider組件的啟動過程
- 10.3 Content Provider組件的數據共享原理
- 10.4 Content Provider組件的數據更新通知機制
- 10.4.1 內容觀察者的注冊過程
- 10.4.2 數據更新的通知過程
- 第十一章 Zygote和System進程的啟動過程
- 11.1 Zygote進程的啟動腳本
- 11.2 Zygote進程的啟動過程
- 11.3 System進程的啟動過程
- 第十二章 Android應用程序進程的啟動過程
- 12.1 應用程序進程的創建過程
- 12.2 Binder線程池的啟動過程
- 12.3 消息循環的創建過程
- 第十三章 Android應用程序的消息處理機制
- 13.1 創建線程消息隊列
- 13.2 線程消息循環過程
- 13.3 線程消息發送過程
- 13.4 線程消息處理過程
- 第十四章 Android應用程序的鍵盤消息處理機制
- 14.1 InputManager的啟動過程
- 14.1.1 創建InputManager
- 14.1.2 啟動InputManager
- 14.1.3 啟動InputDispatcher
- 14.1.4 啟動InputReader
- 14.2 InputChannel的注冊過程
- 14.2.1 創建InputChannel
- 14.2.2 注冊Server端InputChannel
- 14.2.3 注冊當前激活窗口
- 14.2.4 注冊Client端InputChannel
- 14.3 鍵盤消息的分發過程
- 14.3.1 InputReader處理鍵盤事件
- 14.3.2 InputDispatcher分發鍵盤事件
- 14.3.3 當前激活的窗口獲得鍵盤消息
- 14.3.4 InputDispatcher獲得鍵盤事件處理完成通知
- 14.4 InputChannel的注銷過程
- 14.4.1 銷毀應用程序窗口
- 14.4.2 注銷Client端InputChannel
- 14.4.3 注銷Server端InputChannel
- 第十五章 Android應用程序線程的消息循環模型
- 15.1 應用程序主線程消息循環模型
- 15.2 界面無關的應用程序子線程消息循環模型
- 15.3 界面相關的應用程序子線程消息循環模型
- 第十六章 Android應用程序的安裝和顯示過程
- 16.1 應用程序的安裝過程
- 16.2 應用程序的顯示過程