銜接上文Binder驅動之設備控制 – 概述&數據結構 。這篇文章將深入Binder驅動實現進程間通信的核心,揭開Binder通信的神祕面紗, 😃
1. binder_ioctl
Binder驅動沒有提供read/write接口用於讀寫操作,所有數據傳輸、控制都是通過binder_ioctl
進行,因此該部分是Binder通信的核心內容,承載了Binder數據傳輸部分的主要業務。這一節我們就一起來看看Binder進程間通信驅動層總入口 ---- binder_ioctl
的實現。
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int ret;
struct binder_proc *proc = filp->private_data;
struct binder_thread *thread;
/*讀取命令的大小*/
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
trace_binder_ioctl(cmd, arg);
/* 如果binder_stop_on_user_error < 2 則直接返回0;
* 否則,調用_wait_event_interruptible進入可中斷的掛起狀態,接着讓出處理器,
* 直到被wake_up且條件(binder_stop_on_user_error < 2)爲真時才返回
*/
ret = wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret)
goto err_unlocked;
/*獲取binder_main_lock鎖*/
binder_lock(__func__);
/*在proc->threads紅黑樹中查找thread,該紅黑樹以pid爲序,具體詳見1.1*/
thread = **binder_get_thread**(proc);
if (thread == NULL) {
ret = -ENOMEM;
goto err;
}
/*根據不同的命令,調用不同的處理函數進行處理,後文將對命令依次分析*/
switch (cmd) {
case BINDER_WRITE_READ:
/*讀寫命令,數據傳輸,binder IPC通信的核心邏輯,詳見2.1 */
ret = binder_ioctl_write_read(filp, cmd, arg, thread);
if (ret)
goto err;
break;
case BINDER_SET_MAX_THREADS:
/*設置最大線程數,直接將值設置到proc結構的max_threads域中。*/
if (copy_from_user(&**proc->max_threads**, ubuf, sizeof(proc->max_threads))) {
ret = -EINVAL;
goto err;
}
break;
case BINDER_SET_CONTEXT_MGR:
/*設置Context manager,即將自己設置爲ServiceManager,詳見2.2 */
ret = binder_ioctl_set_ctx_mgr(filp);
if (ret)
goto err;
break;
case BINDER_THREAD_EXIT:
/*binder線程退出命令,釋放相關資源,詳見2.3*/
binder_debug(BINDER_DEBUG_THREADS, "%d:%d exit\n",
proc->pid, thread->pid);
binder_free_thread(proc, thread);
thread = NULL;
break;
case BINDER_VERSION: {
/*獲取binder驅動版本號,在kernel4.4版本中,32位該值爲7,64位版本該值爲8*/
struct binder_version __user *ver = ubuf;
if (size != sizeof(struct binder_version)) {
ret = -EINVAL;
goto err;
}
/*將版本號信息寫入用戶態地址空間struct binder_version的protocol_version中*/
if (put_user(BINDER_CURRENT_PROTOCOL_VERSION,
&ver->protocol_version)) {
ret = -EINVAL;
goto err;
}
break;
}
default:
ret = -EINVAL;
goto err;
}
ret = 0;
err:
if (thread)
thread->looper &= ~BINDER_LOOPER_STATE_NEED_RETURN;
binder_unlock(__func__); /*釋放binder_main_lock鎖*/
wait_event_interruptible(binder_user_error_wait, binder_stop_on_user_error < 2);
if (ret && ret != -ERESTARTSYS)
pr_info("%d:%d ioctl %x %lx returned %d\n", proc->pid, current->pid, cmd, arg, ret);
err_unlocked:
trace_binder_ioctl_done(ret);
return ret;
}
-
_IOC_SIZE讀取參數命令的大小。在32位的體系架構中,參數
cmd
由四個域組成:- 讀寫屬性域(
direction: read/write
), 區分是讀取命令,還是寫入命令。bit30~bit31,佔2bit。可用宏_IOC_DIR
讀取。 - 數據大小域(
size : size of argument
), 表示ioctl
中arg
變量所指內存區域佔用內存大小。bit16~bit29,佔14bit。可用宏_IOC_SIZE
讀取 。 - 魔數域 (
type:usually related to the major number
),又叫“幻數”區,用來區分不同的設備驅動,當傳入的值與自身的值不一致時則不進行進一步處理,是用於防止誤使用的一種狀態標識位。一般用字母A~Z
或者a~z
表示。bit8~bit15,佔8bit。可用宏_IOC_TYPE
讀取。 - 序號數或者基數(
command
):用於區分各種命令。bit0~bit7,佔8bit。可用宏_IOC_NR
讀取。
- 讀寫屬性域(
-
binder_stop_on_user_error
, 該變量是一個全局靜態變量, 它的值通過模塊參數stop_on_user_error
控制,當系統出現問題時,可以通過將該值設置爲一個大於或等於2的值,來暫停binder,來進行debug。模塊參數的設置可以在模塊插入時以參數傳遞的形式設定,如insmod xxx.ko arg=xxxx
形式;如果權限設置允許的話,也可以通過sysfs來動態設置(如echo 3 > /sys/module/binder/parameters/stop_no_user_error
)。相關代碼如下:
static int binder_stop_on_user_error;
/*定義模塊參數`stop_on_user_error`的設置函數*/
static int binder_set_stop_on_user_error(const char *val,
struct kernel_param *kp)
{
int ret;
ret = param_set_int(val, kp);/*將sysfs中/sys/module/binder/parameters/stop_on_user_error讀入binder_stop_on_user_error*/
if (binder_stop_on_user_error < 2)
wake_up(&binder_user_error_wait);
return ret;
}
module_param_call(stop_on_user_error/*模塊參數名字,所在路徑爲:/sys/module/binder/parameters/stop_on_user_error*/,
binder_set_stop_on_user_error /*模塊參數`stop_on_user_error`的set函數*/,
param_get_int/*模塊參數`stop_on_user_error`的讀取函數*/,
&binder_stop_on_user_error/*模塊參數對應的變量地址*/,
S_IWUSR | S_IRUGO /*在sysfs中的權限設置*/);
module_param_call
該宏用於定義一個內核模塊參數,它的定義爲module_param_call(name, set, get, arg, perm)
,其中:name
:內核模塊參數的名字,也是在sysfs中顯示的名字;set
:是該內核模塊參數設定的回調函數,當在插入模式時傳遞參數或者通過sysfs設定內核模塊參數時,該函數會被調用;get
: 是該內核模塊參數讀取的回調函數;arg
:內核模塊參數的地址;perm
:該內核模塊參數的權限設置,可以在sysfs中看到。
對於基本的數據類型的讀取和設定回調函數,內核已經預先做了定義,一般形式爲:param_get_xxx
和param_set_xxx
,xxx
是int
, short
等。可以參考一下這篇博客,Linux內核模塊的編寫方法和技巧。
- 額外提一下sysfs,它是Linux2.6開始提供的一種虛擬文件系統,設計該文件系統的目的是把原本在procfs關於設備的部分獨立出來,以“設備層次結構架構(device tree)”的形式呈現。它可以把設備和驅動程序的信息從內核輸出到用戶空間,也可以對設備和驅動程序做設置。具體詳見sysfs簡介。
1.1 查找thread — binder_get_thread
static struct binder_thread *binder_get_thread(struct binder_proc *proc)
{
struct binder_thread *thread = NULL;
struct rb_node *parent = NULL;
struct rb_node **p = &proc->threads.rb_node; /*獲取紅黑樹根節點*/
/*查找pid等於當前線程id的thread,該紅黑樹以pid大小爲序組織*/
while (*p) {
parent = *p;
thread = rb_entry(parent, struct binder_thread, rb_node);
/*current->pid 是當前運行線程的id,不是進程的id*/
if (current->pid < thread->pid)
p = &(*p)->rb_left;
else if (current->pid > thread->pid)
p = &(*p)->rb_right;
else
break;
}
if (*p == NULL) {
/*如果沒有找到,則新創建一個*/
thread = kzalloc(sizeof(*thread), GFP_KERNEL);
if (thread == NULL)
return NULL;
/*更新thread創建統計計數*/
binder_stats_created(BINDER_STAT_THREAD);
/*初始化相關數據成員*/
thread->proc = proc;
thread->pid = current->pid; /*獲取線程id*/
init_waitqueue_head(&thread->wait); /*初始化等待隊列*/
INIT_LIST_HEAD(&thread->todo); /*初始化待處理隊列*/
rb_link_node(&thread->rb_node, parent, p); /*加入到proc的threads紅黑樹中*/
rb_insert_color(&thread->rb_node, &proc->threads);
thread->looper |= BINDER_LOOPER_STATE_NEED_RETURN;
thread->return_error = BR_OK;
thread->return_error2 = BR_OK;
}
return thread;
}
2. binder_ioctl
命令處理邏輯
2.1 讀寫命令 – BINDER_WRITE_READ
首先我們來看一下BINDER_WRITE_READ
的定義如下,它是通過調用內核提供的_IOWR
宏來構造。關於ioctl
命令的構造方法,有興趣可以看看這篇文章, 基本講清楚了。 —— 構造IOCTL學習心得.
#define BINDER_WRITE_READ _IOWR(‘b’/*type 魔數域*/, 1/*command 序號數*/, struct binder_write_read/*size:用來求數據大小域*/)
如果傳入的命令是BINDER_WRITE_READ
,則直接調用binder_ioctl_write_read
進行處理。
2.1.1 命令處理函數binder_ioctl_write_read
static int binder_ioctl_write_read(struct file *filp,
unsigned int cmd, unsigned long arg,
struct binder_thread *thread)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
/*讀取arg的大小,通過2.1節它可知它大小應爲 sizeof(struct binder_write_read) */
unsigned int size = _IOC_SIZE(cmd);
void __user *ubuf = (void __user *)arg;
struct binder_write_read bwr;
if (size != sizeof(struct binder_write_read)) {
ret = -EINVAL;
goto out;
}
/* 從用戶態地址讀取struct binder_write_read結構體 */
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d write %lld at %016llx, read %lld at %016llx\n",
proc->pid, thread->pid,
(u64)bwr.write_size, (u64)bwr.write_buffer,
(u64)bwr.read_size, (u64)bwr.read_buffer);
/* write_size大於0,表示用戶進程有數據發送到驅動,則調用binder_thread_write發送數據 詳見:2.1.2*/
if (bwr.write_size > 0) {
ret = **binder_thread_write**(proc, thread,
bwr.write_buffer,
bwr.write_size,
&bwr.write_consumed);
trace_binder_write_done(ret);
if (ret < 0) {
/*binder_thread_write中有錯誤發生,則read_consumed設爲0,表示kernel沒有數據返回給進程*/
bwr.read_consumed = 0;
/*將bwr返回給用戶態調用者,bwr在binder_thread_write中會被修改*/
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
/*read_size大於0, 表示進程用戶態地址空間希望有數據返回給它,則調用binder_thread_read進行處理*/
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, bwr.read_buffer,
bwr.read_size,
&bwr.read_consumed,
**filp->f_flags & O_NONBLOCK**);
trace_binder_read_done(ret);
/*讀取完後,如果proc->todo鏈表不爲空,則喚醒在proc->wait等待隊列上的進程*/
if (!list_empty(&proc->todo))
wake_up_interruptible(&proc->wait);
if (ret < 0) {
/*如果binder_thread_read返回小於0,可能處理一半就中斷了,需要將bwr拷貝回進程的用戶態地址*/
if (copy_to_user(ubuf, &bwr, sizeof(bwr)))
ret = -EFAULT;
goto out;
}
}
binder_debug(BINDER_DEBUG_READ_WRITE,
"%d:%d wrote %lld of %lld, read return %lld of %lld\n",
proc->pid, thread->pid,
(u64)bwr.write_consumed, (u64)bwr.write_size,
(u64)bwr.read_consumed, (u64)bwr.read_size);
/* 處理成功的情況,也需要將bwr拷貝回進程的用戶態地址空間*/
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
out:
return ret;
}
-
binder_ioctl_write_read
整個處理邏輯相對簡單,它首先從arg
中讀取用戶態傳進來的struct binder_write_read
結構體,然後根據其成員變量write_size
和read_size
是否大於0,判斷本次調用是讀操作還是寫操作。- 如果
write_size
大於0,則爲寫操作,調用binder_thread_write
處理髮送請求。 - 否則爲讀操作,調用
binder_thread_read
處理接收請求。
- 如果
-
這裏要注意的一點是:在
binder_thread_write
和binder_thread_read
的調用中,有兩個參數bwr.write_consumed
和bwr.read_consumed
是**傳址*參數,這意味着這兩個成員變量是會在被調用函數中修改的。如果在binder_thread_write
處理過程中出錯(返回值小於0),則不再處理read_size
大於0的情況。最後不管是成功還是失敗,都會將bwr
通過copy_to_user
返回給進程,進程可以通過write_consumed
和read_consumed
字段得知驅動讀取和寫入多少字節的數據。其實上述三處的copy_to_user
調用,其實可以統一合併到最後一處,然後將out
跳轉標籤移到最後一處之前,其他兩處的copy_to_user
直接移除即可,以減少重複代碼。
2.1.2 數據發送 — binder_thread_write
這個函數代碼量比較大,我們需要分段來看,從前往後,我們先看函數的開始部分:
static int binder_thread_write(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed)
{
uint32_t cmd;
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
while (ptr < end && thread->return_error == BR_OK) {
/*從用戶態地址空間bwr的write_buffer中讀取一個32位無符號整型到cmd*/
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
/*指針後移4個字節*/
ptr += sizeof(uint32_t);
trace_binder_command(cmd);
/*更新該cmd相關的統計信息*/
if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
binder_stats.bc[_IOC_NR(cmd)]++;
proc->stats.bc[_IOC_NR(cmd)]++;
thread->stats.bc[_IOC_NR(cmd)]++;
}
switch (cmd) {
- 函數開始先確定了寫緩衝區中開始(
ptr
)和結束的位置(end
)的位置,接着就開始進入循環,讀取命令,更新相關統計信息。然後進入switch
分支根據不同的命令類型處理執行相應的處理。
下面我們就來看看,具體的命令處理流程。
2.1.2.1 增減binder_ref
強弱引用計數的四個命令
/*增加或者減少強(BC_ACQUIRE,BC_RELEASE),弱(BC_INCREFS, BC_DECREFS)引用計數*/
case BC_INCREFS:
case BC_ACQUIRE:
case BC_RELEASE:
case BC_DECREFS: {
uint32_t target;
struct binder_ref *ref;
const char *debug_string;
/*從傳入參數的用戶態地址中讀取想要修改引用計數的struct binder_ref的目標handle*/
if (get_user(target, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
if (target == 0 && binder_context_mgr_node &&
(cmd == BC_INCREFS || cmd == BC_ACQUIRE)) {
/* - 如果是想請求增加ServiceManager的強或弱的binder_ref引用,**binder_get_ref_for_node**會先在proc的refs_by_node紅黑樹中查找,
* desc域等於target的binder_ref。如果有找到,就返回找到的binfer_buf;如果沒有找到,就新創建一個並插入到`proc->ref_by_node`紅黑樹中。
* 還要爲新創建的節點通過`rb_node_desc`域加入到`proc->refs_by_desc`紅黑樹中。
*
*- `refs_by_desc`紅黑樹是以`binder_buf`中的desc爲序組織的,新創建節點的`desc`的值是該`proc`的`refs_by_desc`紅黑樹中最小的且還未被使用值,
* 即如果引用的`binder_node`是`binder_context_mgr_node`則是0,其他的就是1開始最小的還沒被其他節點使用的值。最後還要將新創建的節點
* 通過其`node_entry`域,鏈入`binder_context_mgr_node`的`refs`哈希鏈表中。
*/
ref = binder_get_ref_for_node(proc,
binder_context_mgr_node);
if (ref->desc != target) {
binder_user_error("%d:%d tried to acquire reference to desc 0, got %d instead\n",
proc->pid, thread->pid,
ref->desc);
}
} else
/* 與binder_get_ref_for_node類似,也是在proc->refs_by_node紅黑樹中查找desc域等於target的binder_ref
* 但是如果沒找到,不會創建新的binder_ref節點,而是直接返回NULL
*/
ref = binder_get_ref(proc, target);
if (ref == NULL) {
binder_user_error("%d:%d refcount change on invalid ref %d\n",
proc->pid, thread->pid, target);
break;
}
switch (cmd) { /* 進一步區分增/減 強弱引用命令 */
case BC_INCREFS:
debug_string = "IncRefs";
binder_inc_ref(ref, 0, NULL );/* 增加弱引用計數(ref->weak--) */
break;
case BC_ACQUIRE:
debug_string = "Acquire";
/* 增加強引用計數(ref->strong++)。如果增加前strong的值爲0,則還需要增加其所對應(引用)
* binder_node節點的internal_strong_refs的值
*/
binder_inc_ref(ref, 1, NULL);
break;
case BC_RELEASE:
debug_string = "Release";
/* 減少強引用計數(ref->strong--)。如果減少後strong的值爲0,則還需要減少其所對應(引用)
* binder_node節點的internal_strong_refs的值。
* strong減完後,如果發現此時strong和weak都爲0,還要刪除該binder_ref節點
*/
binder_dec_ref(ref, 1);
break;
case BC_DECREFS:
default:
debug_string = "DecRefs";
/* 減少弱引用計數(ref->weak—)。減完後,如果發現此時strong和weak都爲0,還要刪除該binder_ref節點*/
binder_dec_ref(ref, 0);
break;
}
binder_debug(BINDER_DEBUG_USER_REFS,
"%d:%d %s ref %d desc %d s %d w %d for node %d\n",
proc->pid, thread->pid, debug_string, ref->debug_id,
ref->desc, ref->strong, ref->weak, ref->node->debug_id);
break;
} /*到這裏BC_INCREFS, BC_ACQUIRE, BC_RELEASE, BC_DECREFS四個命令處理結束*/
....../*其他命令的處理*/
- 第一個處理的是以下四個用於增加或者減少client端的驅動層表示
binder_ref
的強弱引用計數的命令BC_INCREFS
: 增加binder_ref的弱引用計數。如果是第一次增加(即,ref->weak == 0),還會去增加對應的binder_node的弱引用計數。它是在用戶態進程的BpBinder
的構造函數中,通過調用IPCThreadState
的incWeakHandle
發出。BC_DECREFS
:減少binder_ref的弱引用計數,但不會去減少對應binder_node的弱引用計數,即使ref->weak == 0,這與強引用計數的處理是不同的。在BpBinder
的析構函數中,通過調用IPCThreadState
的decWeakHandle
發出。BC_ACQUIRE
:增加binder_ref的強引用計數。如果是第一次增加(即,ref->strong == 0),還會去增加對應的binder_node的強引用計數。在BpBinder
的onFirstRef
函數中,通過調用IPCThreadState
的incStrongHandle
發出。BC_RELEASE
:減少binder_ref的強引用計數。如果減少後強引用計數爲0(即,ref->strong == 0),還會去減少對應的binder_node的強引用計數。在BpBinder
的onLastStrongRef
函數中,通過調用IPCThreadState
的decStrongHandle
發出。
如果減少強或弱引用計數後,發現強弱引用計數都變爲0,則會調用binder_delete_ref
刪除對應的binder_ref
。
2.1.2.2 BC_INCREFS_DONE
和BC_ACQUIRE_DONE
BC_INCREFS_DONE
和BC_ACQUIRE_DONE
兩個命令分別是進程用戶態在處理完對應的BR_INCREFS
和BR_ACQUIRE
回覆Binder驅動的兩個命令。關於後面兩個命令BR_INCREFS
和BR_ACQUIRE
分別用於Binder驅動請求進程用戶態增加IPCThreadState
中的mProcess
成員的(類型爲:ProcessState
)弱引用和強引用計數。
case BC_INCREFS_DONE:
case BC_ACQUIRE_DONE: {
/*說明此時傳入的是一個flat_binder_object*/
binder_uintptr_t node_ptr;
binder_uintptr_t cookie;
struct binder_node *node;
/* 從進程用戶態地址空間中讀取BBinder對象的弱引用計數器成員mRefs的地址
* BBinder繼承自IBinder,後者繼承自RefBase,mRefs爲RefBase的類型爲weakref_impl的對象
*/
if (get_user(node_ptr, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
/*從進程用戶態地址空間中讀取`BBinder`對象的地址,放到cookie變量中*/
if (get_user(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
/*根據之前讀取的node_ptr,在proc中的nodes紅黑樹中查找對應的binder_node*/
node = binder_get_node(proc, node_ptr);
if (node == NULL) {
binder_user_error("%d:%d %s u%016llx no match\n",
proc->pid, thread->pid,
cmd == BC_INCREFS_DONE ?
"BC_INCREFS_DONE" :
"BC_ACQUIRE_DONE",
(u64)node_ptr);
break;
}
if (cookie != node->cookie) {
binder_user_error("%d:%d %s u%016llx node %d cookie mismatch %016llx != %016llx\n",
proc->pid, thread->pid,
cmd == BC_INCREFS_DONE ?
"BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
(u64)node_ptr, node->debug_id,
(u64)cookie, (u64)node->cookie);
break;
}
if (cmd == BC_ACQUIRE_DONE) {
if (node->pending_strong_ref == 0) {
binder_user_error("%d:%d BC_ACQUIRE_DONE node %d has no pending acquire request\n",
proc->pid, thread->pid,
node->debug_id);
break;
}
/*將pending_strong_ref重新置爲0,表示增加強引用計數操作已完成,在發出`BR_ACQUIRE`命令前,該值會被設成1*/
node->pending_strong_ref = 0;
} else {/*cmd == BC_INCREFS_DONE*/
if (node->pending_weak_ref == 0) {
binder_user_error("%d:%d BC_INCREFS_DONE node %d has no pending increfs request\n",
proc->pid, thread->pid,
node->debug_id);
break;
}
/*將pending_weak_ref重新置爲0,表示增加弱引用計數操作已完成,在發出`BR_INCREFS`命令前,該值會被設成1*/
node->pending_weak_ref = 0;
}
binder_dec_node(node, cmd == BC_ACQUIRE_DONE, 0);
binder_debug(BINDER_DEBUG_USER_REFS,
"%d:%d %s node %d ls %d lw %d\n",
proc->pid, thread->pid,
cmd == BC_INCREFS_DONE ? "BC_INCREFS_DONE" : "BC_ACQUIRE_DONE",
node->debug_id, node->local_strong_refs, node->local_weak_refs);
break;
}
2.1.2.3 BC_ATTEMPT_ACQUIRE
, BC_ACQUIRE_RESULT
這兩個命令,還不支持。
case BC_ATTEMPT_ACQUIRE:
pr_err("BC_ATTEMPT_ACQUIRE not supported\n");
return -EINVAL;
case BC_ACQUIRE_RESULT:
pr_err("BC_ACQUIRE_RESULT not supported\n");
return -EINVAL;
2.1.2.4 BC_FREE_BUFFER
該命令是binder_bufffer
緩衝區釋放命令,來看一下具體的處理邏輯:
case BC_FREE_BUFFER: {
binder_uintptr_t data_ptr;
struct binder_buffer *buffer;
/*讀取binder_buffer的**data域**,在用戶態的地址*/
if (get_user(data_ptr, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
/* 先把進程用戶態的地址data_ptr,換算成內核態binder_buffer節點的地址
* (data_ptr - proc->user_buffer_offset - **offset(struct binder_buffer, data****)**;需要
* 然後再在proc->allocated_buffers紅黑樹中查找,該紅黑樹是以binder_buffer內核態地址大小爲序組織的
*/
buffer = binder_buffer_lookup(proc, data_ptr);
if (buffer == NULL) {
binder_user_error("%d:%d BC_FREE_BUFFER u%016llx no match\n",
proc->pid, thread->pid, (u64)data_ptr);
break;
}
if (!buffer->allow_user_free) {
/* 如果該節點地址空間不允許用戶釋放,則輸出出錯信息,並跳過該命令的處理 */
binder_user_error("%d:%d BC_FREE_BUFFER u%016llx matched unreturned buffer\n",
proc->pid, thread->pid, (u64)data_ptr);
break;
}
binder_debug(BINDER_DEBUG_FREE_BUFFER,
"%d:%d BC_FREE_BUFFER u%016llx found buffer %d for %s transaction\n",
proc->pid, thread->pid, (u64)data_ptr,
buffer->debug_id,
buffer->transaction ? "active" : "finished");
/*刪除binder_buffer與transaction的關聯*/
if (buffer->transaction) {
/*移除transaction中的buffer域對該binder_buffer的引用*/
buffer->transaction->buffer = NULL;
/*移除該binder_buffer對該transaction的引用*/
buffer->transaction = NULL;
}
/*處理該binder_buffer相關的異步事務*/
if (buffer->async_transaction && buffer->target_node) {
BUG_ON(!buffer->target_node->has_async_transaction);
if (list_empty(&buffer->target_node->async_todo))
/*target_node的異步待處理事務隊列爲空,則將是否有異步事物標誌位重置爲0*/
buffer->target_node->has_async_transaction = 0;
else
/*將該binder_buffer對應的target_node中所有待處理的異步事務(async_todo),移到當前線程的todo隊列中*/
list_move_tail(buffer->target_node->async_todo.next, &thread->todo);
}
trace_binder_transaction_buffer_release(buffer);
binder_transaction_buffer_release(proc, buffer, NULL);
/*釋放binder_buffer管理的空間*/
binder_free_buf(proc, buffer);
break;
}
BC_FREE_BUFFER
命令是在IPCThreadState
的free_buffer
函數中發出的。而freeBuffer
一般在兩個地方被調用,一個是waitForResponse
處理BR_REPLY
命令時;另一個時處理BR_TRANSACTION
和BR_REPLY
命令時,通過Parcel
的ipcSetDataReference
註冊後到mOwner
中,然後在之後間接調用。binder_transaction_buffer_release
會先看一下buffer->target_node
是否爲空,不是話就先減少對這個binder_node
的引用計數,因爲在binder_transaction
(該函數在下一節有詳細的介紹)中對它遞增了引用計數,因防止改binder_node
被釋放。接着循環處理在該binder_buffer
的offsets
區域存放的flat_binder_object
,根據其類型分別減少對應binder_node
或者binder_ref
的引用計數,具體是:BINDER_TYPE_BINDER
減少對應binder_node
的強引用計數,BINDER_TYPE_WEAK_BINDER
減少對應binder_node
的弱引用計數。BINDER_TYPE_HANDLE
減少對應binder_ref
的強引用計數,BINDER_TYPE_WEAK_HANLE
減少對應binder_ref
的弱引用計數。
binder_free_buf
釋放binder_buffer
管理空間,如果可以還會合並相鄰前後空閒的節點,具體可參考Binder驅動之 binder_buffer的分配與回收的3.1小節。
2.1.2.5 BC_TRANSACTION
& BC_REPLY
這兩個命令是Binder事務處理命令,是Binder進程間數據通信的關鍵命令。
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
/*從將binder_transaction_data從進程用戶態地址拷到內核地址空間*/
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
/*直接調用binder_transaction進行事務處理*/
**binder_transaction**(proc, thread, &tr, cmd == BC_REPLY);
break;
}
對於這兩個命令處理,binder_thread_write
只是將binder_transaction_data從進程用戶態地址拷到內核地址空間,接着就調用binder_transaction
進行具體的事務處理了。這裏我們就順着處理邏輯繼續跟進binder_transaction
,看看它是如何實現的。
2.1.2.5.1 binder_transaction
-
binder_transaction
函數雖然代碼挺長的,但邏輯上並不算複雜,大體可以分爲四個步驟:-
第一步:確定
target_proc
及target_thread
,並根據target_thread
和target_proc
確定target_list
和target_wait
; -
第二步:分別創建
binder_transaction -->t
,binder_buffer --> t->buffer
,binder_work --> tcomplete
幾個對象並做相關初始化,binder_transaction
中的data.prt.buffer
和data.prt.offsets
的內容從用戶態拷貝到內核態的binder_buffer
中,這裏就是binder通信中僅有的一次數據拷貝發生的地方; -
第三步:依次處理從
binder_transaction_data
中拷貝進來的的flat_binder_object
:- a. 如果類型是
BINDER_TYPE_BINDER
或者BINDER_TYPE_WEAK_BINDER
,說明這個flat_binder_object
是一個binder service,或者說BBinder
。flat_binder_object
中存儲的binder對象地址(binder
和cookie兩個域
)在不同進程的客戶端中無法直接使用,需將其轉換成對應代理或者說句柄(handle
)。這個代理的handle
即是其內核態表示binder_ref
的desc
域的值。轉換過程如下,先在proc->nodes
紅黑樹中查找該binder service在內核態表示binder_node
,如果沒有找到就創建一個;接着根據這個binder_node
在proc->refs_by_node
紅黑樹中查找這個binder service在內核態的表示binder_ref
, 同樣的如果未在該紅黑樹找到,也創建一個插入到樹中;得到binder_ref
後,其desc
域就是轉換flat_binder_object
的handle
值,即客戶端代理的句柄,並將flat_binder_object
的類型改爲對應的BINDER_TYPE_HANDLE
或者BINDER_TYPE_WEAK_HANLE
。 - b. 如果類型是
BINDER_TYPE_HANDLE
或者BINDER_TYPE_WEAK_BINDER
,則說明這個flat_binder_object
是一個binder service的客戶端代理,或者說BpBinder
。 此時先從proc->refs_by_desc
根據flat_binder_object
的handle
找到相應的binder_ref
。然後分兩種情況處理:如果此次transaction的目標進程是該binder service所在的進程,就需要將flat_binder_object
轉換成對應的服務端表示,即flat_binder_object
的binder
和cookie
域設置爲binder_node
中的ptr
和cookie
, 這兩個域前一個是BBinder
基類RefBase
中引用計數計數器成員mRef
, 後一個是BBinder
的地址,這樣用戶態讀到到flat_binder_object
後就可以很容易的轉換得到BBinder
對象,還需將類型type
設置成BINDER_TYPE_BINDER
或者BINDER_TYPE_WEAK_BINDER
,最後增加binder_node
的強或者弱引用計數;如果此次transaction的目標進程不是該binder service所在的進程,則需先在目標進程中的proc->refs_by_node
查找對應的binder_ref
,如果沒有找到的話就會創建一個,然後將flat_binder_object
中的handle
域,修改爲找到binder_ref
的desc
值,因爲handle
的作用域是一個進程中,不同進程中相同的handle
表示的不同binder_ref
,因此它對應的binder_node
也可能是不一樣的。最後增加這個binder_ref
的強或者弱引用計數。 - c. 如果類型是
BINDER_TYPE_FD
,說明這個flat_binder_object
傳遞的是一個文件描述符,這時flat_binder_object
的handle
域存放的就是要傳遞的文件描述符。首先根據handle
即fd
的值用fget
獲取到相應的struct file
,接着調用task_get_unused_fd_flags
在目標進程獲取一個還沒被使用的fd,然後使用task_fd_install
將目標中的fd和第一個步驟獲取到的struct file
建立關聯,最後將flat_binder_object
中的handle
修改爲目標進程的fd,這樣目標進程在用戶態操作這個fd就作用在了與發送進程相同的struct file
上。所以文件描述符傳遞本質上是共享struct file
。
- a. 如果類型是
-
第四步:事務處理,根據本次處理的事務命令,進行不同的處理。
- 如果本次處理的是
BC_REPLY
命令,說明前一次的BC_TRANSACTION
命令已經處理完畢了,因此將目標線程的事務棧頂元素出棧,表示依次事務 處理完成。 - 如果本次處理的是一個
BC_TRANSACTION
命令,且是不帶有ONE_WAY
標識(需要對方reply),則將第二步創建的事務t
入棧;如果帶有ONE_WAY
標識(不需要對方reply),則根據對方線程是否已經有異步的binder_transaction
決定是否將target_list
修改爲目標線程的async_todo
隊列,還是僅僅設立has_async_transaction
標誌位,target_list
依舊爲目標線程的todo
隊列。然後將事務t
的binder_work
類型設置爲BINDER_WORK_TRANSACTION
,通過work.entry
插入到target_list
(目標線程/進程的todo
或者async_todo
)中。同時也把第二步創建的tcomplete
的work類型設置爲BINDER_WORK_TRANSACTION_COMPLETE
, 鏈入到本地線程的todo
隊列的隊尾,用於通知進程本次TRANSACTION
請求或REPLY
已發出。最後,如果根據target_wait
是否爲空,決定是否需要喚醒等待在target_thread
/target_proc
的wait
隊列上的進程。
- 如果本次處理的是
-
-
注意:
binder_transaction_data
中的offset
區(即data.ptr.offset
域指向的緩衝區),存儲的是每一個flat_binder_object
在data
區(即data.ptr.buffer
域指向的緩衝區)的偏移量。
下面是具體的代碼實現:
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply)
{
struct binder_transaction *t;
struct binder_work *tcomplete;
binder_size_t *offp, *off_end;
binder_size_t off_min;
struct binder_proc *target_proc;
struct binder_thread *target_thread = NULL;**
struct binder_node *target_node = NULL;
struct list_head *target_list;
wait_queue_head_t *target_wait;
struct binder_transaction *in_reply_to = NULL;
struct binder_transaction_log_entry *e;
uint32_t return_error;
/*填充日誌信息*/
e = binder_transaction_log_add(&binder_transaction_log);
e->call_type = reply ? 2 : !!(tr->flags & TF_ONE_WAY);
e->from_proc = proc->pid;
e->from_thread = thread->pid;
e->target_handle = tr->target.handle;
e->data_size = tr->data_size;
e->offsets_size = tr->offsets_size;
/* 第一步: 先確定目標線程(target_thread)和目標進程(target_proc)*/
if (reply) { /*BC_REPLY命令,說明這是一個Server發給Client的事務處理回覆。在server端的線程上。*/
/*從取出棧頂reply對應的transaction*/
in_reply_to = thread->transaction_stack;
if (in_reply_to == NULL) {
binder_user_error("%d:%d got reply transaction with no transaction stack\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_empty_call_stack;
}
binder_set_nice(in_reply_to->saved_priority);
/*transaction的目標線程需是當前線程*/
if (in_reply_to->to_thread != thread) {
binder_user_error("%d:%d got reply transaction with bad transaction stack, transaction %d has target %d:%d\n",
proc->pid, thread->pid, in_reply_to->debug_id,
in_reply_to->to_proc ?
in_reply_to->to_proc->pid : 0,
in_reply_to->to_thread ?
in_reply_to->to_thread->pid : 0);
return_error = BR_FAILED_REPLY;
in_reply_to = NULL;
goto err_bad_call_stack;
}
/*從棧頂移除該transaction*/
thread->transaction_stack = in_reply_to->to_parent;
/*本次reply的目標線程是對應transaction的發起線程*/
target_thread = in_reply_to->from;
if (target_thread == NULL) {
return_error = BR_DEAD_REPLY;
goto err_dead_binder;
}
/*目標線程(client)的棧頂事務需是本次reply對應的transaction的*/
if (target_thread->transaction_stack != in_reply_to) {
binder_user_error("%d:%d got reply transaction with bad target transaction stack %d, expected %d\n",
proc->pid, thread->pid,
target_thread->transaction_stack ?
target_thread->transaction_stack->debug_id : 0,
in_reply_to->debug_id);
return_error = BR_FAILED_REPLY;
in_reply_to = NULL;
target_thread = NULL;
goto err_dead_binder;
}
/*通過目標線程找到目標進程*/
target_proc = target_thread->proc;
} else {/*BC_TRANSACTION 命令,說明這是一個Client發給Server的請求事務。在Client端線程上。*/
/*第一步:先確定target_node*/
if (tr->target.handle) {/*目標service是普通service,handle > 0 */
struct binder_ref *ref;
ref = binder_get_ref(proc, tr->target.handle);
if (ref == NULL) {
binder_user_error("%d:%d got transaction to invalid handle\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_invalid_target_handle;
}
target_node = ref->node;
} else {/*目標service是ServiceManager*/
target_node = binder_context_mgr_node;
if (target_node == NULL) {
return_error = BR_DEAD_REPLY;
goto err_no_context_mgr_node;
}
}
e->to_node = target_node->debug_id;
/*第二步:根據target_node找到目標進程target_proc*/
target_proc = target_node->proc;
if (target_proc == NULL) {
return_error = BR_DEAD_REPLY;
goto err_dead_binder;
}
if (security_binder_transaction(proc->tsk,
target_proc->tsk) < 0) {
return_error = BR_FAILED_REPLY;
goto err_invalid_target_handle;
}
/*第三步:根據目標進程target_proc查找目標線程target_thread*/
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) { /*非one_way, 需要replay,且transaction棧不爲空*/
struct binder_transaction *tmp;
tmp = thread->transaction_stack;
if (tmp->to_thread != thread) {
binder_user_error("%d:%d got new transaction with bad transaction stack, transaction %d has target %d:%d\n",
proc->pid, thread->pid, tmp->debug_id,
tmp->to_proc ? tmp->to_proc->pid : 0,
tmp->to_thread ?
tmp->to_thread->pid : 0);
return_error = BR_FAILED_REPLY;
goto err_bad_call_stack;
}
/* 從事務棧(transaction_stack)的棧頂向下搜索,
* 找到最後(最早)一個目標進程中向當前進程發起事務請求的線程爲本次請求的目標線程。
*/
while (tmp) {
if (tmp->from && tmp->from->proc == target_proc)
target_thread = tmp->from;
tmp = tmp->from_parent;
}
}
}
if (target_thread) {
/*找到target_thread, 則target_list和target_wait分別初始化爲目標線程的todo和wait隊列*/
e->to_thread = target_thread->pid;
target_list = &target_thread->todo;
target_wait = &target_thread->wait;
} else {
/* 沒有找到target_thread, target_list和target_wait分別初始化爲目標進程的todo和wait隊列
* 這個情況只有BC_TRANSACTION命令纔有可能發生
*/
target_list = &target_proc->todo;
target_wait = &target_proc->wait;
}
e->to_proc = target_proc->pid;
以上就是第一步內容的實現。
/* TODO: reuse incoming transaction for reply */
/*分配一個binder_transaction*/
t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_t_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION);
/*分配一個binder_work*/
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);**
if (tcomplete == NULL) {
return_error = BR_FAILED_REPLY;
goto err_alloc_tcomplete_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
t->debug_id = ++binder_last_id;
e->debug_id = t->debug_id;
if (reply)
binder_debug(BINDER_DEBUG_TRANSACTION,
"%d:%d BC_REPLY %d -> %d:%d, data %016llx-%016llx size %lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_thread->pid,
(u64)tr->data.ptr.buffer,
(u64)tr->data.ptr.offsets,
(u64)tr->data_size, (u64)tr->offsets_size);
else
binder_debug(BINDER_DEBUG_TRANSACTION,
"%d:%d BC_TRANSACTION %d -> %d - node %d, data %016llx-%016llx size %lld-%lld\n",
proc->pid, thread->pid, t->debug_id,
target_proc->pid, target_node->debug_id,
(u64)tr->data.ptr.buffer,
(u64)tr->data.ptr.offsets,
(u64)tr->data_size, (u64)tr->offsets_size);
if (!reply && !(tr->flags & TF_ONE_WAY)) /*BC_TRANSACTION,且不是one way,即需要replay,則發起線程(from)設爲當前線程*/
t->from = thread;
else/*BC_REPLY,from置爲空*/
t->from = NULL;
/*初始化binder_transaction各域*/
t->sender_euid = task_euid(proc->tsk);
t->to_proc = target_proc;
t->to_thread = target_thread;
t->code = tr->code;
t->flags = tr->flags;
t->priority = task_nice(current);
trace_binder_transaction(reply, t, target_node);
t->buffer = **binder_alloc_buf**(target_proc, tr->data_size,
tr->offsets_size, !reply && (t->flags & TF_ONE_WAY));
if (t->buffer == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_alloc_buf_failed;
}
t->buffer->allow_user_free = 0;
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t;
t->buffer->target_node = target_node;
trace_binder_transaction_alloc_buf(t->buffer);
if (target_node) /*該target_node被binder_buffer引用,所以增加引用計數*/
binder_inc_node(target_node, 1, 0, NULL);
/*計算offset區的起始地址*/
offp = (binder_size_t *)(t->buffer->data +
ALIGN(tr->data_size, sizeof(void *)));
/*將用戶態binder_transaction_data中的數據拷貝到內核驅動的binder_buffer中,binder通信的一次拷貝就是發生在這裏*/
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
**tr->data.ptr.buffer**, tr->data_size)) {
binder_user_error("%d:%d got transaction with invalid data ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
/* 拷貝binder_transaction_data的offset區到內核驅動
*/
if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
binder_user_error("%d:%d got transaction with invalid offsets ptr\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
goto err_copy_data_failed;
}
if (!IS_ALIGNED(tr->offsets_size, sizeof(binder_size_t))) {
binder_user_error("%d:%d got transaction with invalid offsets size, %lld\n",
proc->pid, thread->pid, (u64)tr->offsets_size);
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
off_end = (void *)offp + tr->offsets_size; /*offset區的結束地址*/
off_min = 0;
以上就是第二步內容的實現。接下來開始進入第三步,進行事務處理。
/*接下來是循環處理在前一步從binder_transaction_data中拷貝進來所有flat_binder_object*/
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
if (*offp > t->buffer->data_size - sizeof(*fp) ||
*offp < off_min ||
t->buffer->data_size < sizeof(*fp) ||
!IS_ALIGNED(*offp, sizeof(u32))) {
binder_user_error("%d:%d got transaction with invalid offset, %lld (min %lld, max %lld)\n",
proc->pid, thread->pid, (u64)*offp,
(u64)off_min,
(u64)(t->buffer->data_size -
sizeof(*fp)));
return_error = BR_FAILED_REPLY;
goto err_bad_offset;
}
/*獲取flat_binder_object的地址*/
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
off_min = *offp + sizeof(struct flat_binder_object);
switch (fp->type) {
/*BBinder的flat_binder_object*/
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct binder_ref *ref;
struct binder_node *node = binder_get_node(proc, fp->binder);
if (node == NULL) {
/*內核態驅動還沒有相應的binder_node來表示該binder service,則新建一個*/
node = binder_new_node(proc, fp->binder, fp->cookie);
if (node == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_new_node_failed;
}
node->min_priority = fp->flags & FLAT_BINDER_FLAG_PRIORITY_MASK;
node->accept_fds = !!(fp->flags & FLAT_BINDER_FLAG_ACCEPTS_FDS);
}
if (fp->cookie != node->cookie) {
binder_user_error("%d:%d sending u%016llx node %d, cookie mismatch %016llx != %016llx\n",
proc->pid, thread->pid,
(u64)fp->binder, node->debug_id,
(u64)fp->cookie, (u64)node->cookie);
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
if (security_binder_transfer_binder(proc->tsk,
target_proc->tsk)) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
/*在proc的`refs_by_node`紅黑樹中查找該binder_node對應的binder_ref, 如果沒有找到的話,會新建一個插入到該紅黑樹中*/
ref = binder_get_ref_for_node(target_proc, node);
if (ref == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
/* 轉換成客戶端表示,分別修改type和handle */
if (fp->type == BINDER_TYPE_BINDER**)
fp->type = BINDER_TYPE_HANDLE;
else
fp->type = BINDER_TYPE_WEAK_HANDLE;
fp->handle = ref->desc;**
/*增加引用計數,防止該binder_ref被釋放*/
binder_inc_ref(ref, fp->type == BINDER_TYPE_HANDLE,
&thread->todo);
trace_binder_transaction_node_to_ref(t, node, ref);
binder_debug(BINDER_DEBUG_TRANSACTION,
" node %d u%016llx -> ref %d desc %d\n",
node->debug_id, (u64)node->ptr,
ref->debug_id, ref->desc);
} break;
/*BpBinder的flat_binder_object*/
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
struct binder_ref *ref = binder_get_ref(proc, fp->handle);
if (ref == NULL) {
binder_user_error("%d:%d got transaction with invalid handle, %d\n",
proc->pid,
thread->pid, fp->handle);
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_failed;
}
if (security_binder_transfer_binder(proc->tsk,
target_proc->tsk)) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_failed;
}
if (ref->node->proc == target_proc) {
/*該flat_binder_object的目標進程是該binder service所在進程*/
if (**fp->type == BINDER_TYPE_HANDLE**)
**fp->type = BINDER_TYPE_BINDER;**
else
**fp->type = BINDER_TYPE_WEAK_BINDER;**
fp->binder = ref->node->ptr;
fp->cookie = ref->node->cookie;
binder_inc_node**(ref->node, fp->type == BINDER_TYPE_BINDER, 0, NULL);
trace_binder_transaction_ref_to_node(t, ref);
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d -> node %d u%016llx\n",
ref->debug_id, ref->desc, ref->node->debug_id,
(u64)ref->node->ptr);
} else {
/*該flat_binder_object的目標進程不是該binder service所在進程*/
struct binder_ref *new_ref;
/*在目標進程中binder_node找到對應的binder_ref。每個進程都有自己的binder_ref來對應binder_node*/
new_ref = **binder_get_ref_for_node**(target_proc, ref->node);
if (new_ref == NULL) {
return_error = BR_FAILED_REPLY;
goto err_binder_get_ref_for_node_failed;
}
fp->handle = new_ref->desc;/*更新handle的值,設置成目標進程binder_ref的desc*/
binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL); /*增加目標進程binde_ref的引用計數*/
trace_binder_transaction_ref_to_ref(t, ref,
new_ref);
binder_debug(BINDER_DEBUG_TRANSACTION,
" ref %d desc %d -> ref %d desc %d (node %d)\n",
ref->debug_id, ref->desc, new_ref->debug_id,
new_ref->desc, ref->node->debug_id);
}
} break;
case BINDER_TYPE_FD: {
int target_fd;
struct file *file;
if (reply) {
if (!(in_reply_to->flags & TF_ACCEPT_FDS)) {/*客戶端進程不接受fd*/
binder_user_error("%d:%d got reply with fd, %d, but target does not allow fds\n",
proc->pid, thread->pid, fp->handle);
return_error = BR_FAILED_REPLY;
goto err_fd_not_allowed;
}
} else if (!target_node->accept_fds) {/*目標進程不接受fd*/
binder_user_error("%d:%d got transaction with fd, %d, but target does not allow fds\n",
proc->pid, thread->pid, fp->handle);
return_error = BR_FAILED_REPLY;
goto err_fd_not_allowed;
}
file = fget(fp->handle); /*根據文件描述符找到對應的struct file結構體*/
if (file == NULL) {
binder_user_error("%d:%d got transaction with invalid fd, %d\n",
proc->pid, thread->pid, fp->handle);
return_error = BR_FAILED_REPLY;
goto err_fget_failed;
}
if (security_binder_transfer_file(proc->tsk,
target_proc->tsk,
file) < 0) {
fput(file);
return_error = BR_FAILED_REPLY;
goto err_get_unused_fd_failed;
}
target_fd = task_get_unused_fd_flags(target_proc, O_CLOEXEC); /*從目標進程獲取一個可用的文件描述符*/
if (target_fd < 0) {
fput(file);
return_error = BR_FAILED_REPLY;
goto err_get_unused_fd_failed;
}
task_fd_install(target_proc, target_fd, file); /*在目標進程中,將file與剛獲取到文件描述符target_fd對應起來。這樣兩個進程中雖然fd不同,但其實都是對應同一個struct file結構體*/**
trace_binder_transaction_fd(t, fp->handle, target_fd);
binder_debug(BINDER_DEBUG_TRANSACTION,
" fd %d -> %d\n", fp->handle, target_fd);
/* TODO: fput? */
fp->handle = target_fd;
} break;
default:
binder_user_error("%d:%d got transaction with invalid object type, %x\n",
proc->pid, thread->pid, fp->type);
return_error = BR_FAILED_REPLY;
goto err_bad_object_type;
}
}
以上就是第三步,該函數最核心內容的實現,接下來便是第四步,對事務棧進行處理:
if (reply) {
BUG_ON(t->buffer->async_transaction != 0);
/*事務處理完成,將本次reply對應的transaction從目標線程(Client)事務棧中移除,並釋放其所佔用的地址空間*/
binder_pop_transaction(target_thread, in_reply_to);
} else if (!(t->flags & TF_ONE_WAY)) {/*一個client到server的transaction,且需要reply*/
/*將本次事務的binder_transaction加入到本線程事務棧中*/
BUG_ON(t->buffer->async_transaction != 0);
t->need_reply = 1;
t->from_parent = thread->transaction_stack;
**thread->transaction_stack = t;**
} else {
BUG_ON(target_node == NULL);
BUG_ON(t->buffer->async_transaction != 1);
if (target_node->has_async_transaction) {
target_list = &target_node->async_todo;
*target_wait = NULL;
} else
target_node->has_async_transaction = 1;
}
t->work.type = BINDER_WORK_TRANSACTION;
list_add_tail(&t->work.entry, target_list);
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
list_add_tail(&tcomplete->entry, &thread->todo); /添加一個本線程的todo隊列中,稍後在線程處理todo隊列的該binder_work時,會發送個BR_WORK_TRANSCAION_COMPLETE給進程,告知請求/回覆已發送出去。*/
if (target_wait)
wake_up_interruptible(target_wait);
return;
到這裏函數主要邏輯就都結束了,下面是出錯處理:
/*接下來是出錯處理*/
err_get_unused_fd_failed:
err_fget_failed:
err_fd_not_allowed:
err_binder_get_ref_for_node_failed:
err_binder_get_ref_failed:
err_binder_new_node_failed:
err_bad_object_type:
err_bad_offset:
err_copy_data_failed:
trace_binder_transaction_failed_buffer_release(t->buffer);
/* 遞減transaction相關的binder_node, binder_ref及data中的flat_binder_object有關的binder_ref,binder_node的引用計數
* 如果傳遞的文件描述符,還要關閉該文件描述符
*/
binder_transaction_buffer_release(target_proc, t->buffer, offp);
t->buffer->transaction = NULL;
/*釋放之前分配的binder_buffer*/
binder_free_buf(target_proc, t->buffer);
err_binder_alloc_buf_failed:
/*釋放binder_work*/
kfree(tcomplete);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
err_alloc_tcomplete_failed:
/*釋放binder_transaction*/
kfree(t);
binder_stats_deleted(BINDER_STAT_TRANSACTION);
err_alloc_t_failed:
err_bad_call_stack:
err_empty_call_stack:
err_dead_binder:
err_invalid_target_handle:
err_no_context_mgr_node:
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
"%d:%d transaction failed %d, size %lld-%lld\n",
proc->pid, thread->pid, return_error,
(u64)tr->data_size, (u64)tr->offsets_size);
{
struct binder_transaction_log_entry *fe;
fe = binder_transaction_log_add(&binder_transaction_log_failed);
*fe = *e;
}
BUG_ON(thread->return_error != BR_OK);
if (in_reply_to) {
thread->return_error = BR_TRANSACTION_COMPLETE;
binder_send_failed_reply(in_reply_to, return_error);
} else
/*將錯誤碼存儲起來,以便在之後的binder_thread_read中處理*/
thread->return_error = return_error;
}
分析完binder_thread_write
中最核心的兩個命令BC_TRANSACTION
, BC_REPLY
我們接着來看剩餘的命令
2.1.2.6 BC_REGISTER_LOOPER
, BC_ENTER_LOOPER
BC_EXIT_LOOPER
這是Binder線程狀態相關的三個命令。
BC_REGISTER_LOOPER
: 進程的非主線程調用IPCThreadState::joinThreadPool
時向Binder驅動發送的命令,表示該線程將進入的binder循環狀態,即不斷讀取並執行binder的請求。IPCThreadState::joinThreadPool
的一個經典的調用地方就是BinderServer
(framework/native/include/binder/BinderService.h
)的publishAndJoinThreadPool
, 用於一個binder service向ServiceManager註冊後進入Binder循環狀態,不斷的處理客戶端發過來的請求。它與BC_ENTER_LOOPER
的主要區別是:- 從進程用戶態角度看,它是非主線程調用
IPCThreadState::joinThreadPool
發出的命令,而BC_ENTER_LOOPER
則是主線程發出的; - 從內核驅動的角度看,則是收到該命令時,
proc->request_threads
需不爲於0,而proc->request_threads
的++
操作是發生在binder_thread_read
向進程用戶態發出BR_SPAWN_LOOPER
時發生的。因此BC_REGISTER_LOOPER
是進程用戶態在處理完驅動發給它的BR_SPAWN_LOOPER
命令後發給內核Binder驅動的 。處理BR_SPAWN_LOOPER
用戶進程會創建新線程,調用IPCThreadState::joinThreadPool
。至於內核Binder驅動的binder_thread_read
何時會發出BR_SPAWN_LOOPER
命令,我們等到本篇後面章節中分析binder_thread_read
的時候再說明。
- 從進程用戶態角度看,它是非主線程調用
BC_ENTER_LOOPER
: 該命令在進程用戶態的兩個接口中會發出,一個是主線程調用IPCThreadState::joinThreadPool
;另一個是IPCThreadState::setupPolling
(如system/core/healthd/healthd_mode_android.cpp
)。BC_EXIT_LOOPER
: 退出binder循環狀態。在進程用戶態的IPCThreadState::joinThreadPool
函數中退出while
循環處理binder請求時發出。
所以這三個命令就是跟Binder驅動同步線程狀態的命令,Binder驅動獲知相應狀態後,將其更新到thread->looper
中。
代碼實現如下:
case BC_REGISTER_LOOPER:
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BC_REGISTER_LOOPER\n",
proc->pid, thread->pid);
if (thread->looper & BINDER_LOOPER_STATE_ENTERED) {
thread->looper |= BINDER_LOOPER_STATE_INVALID;
binder_user_error("%d:%d ERROR: BC_REGISTER_LOOPER called after BC_ENTER_LOOPER\n",
proc->pid, thread->pid);
} else if (proc->requested_threads == 0) {
thread->looper |= BINDER_LOOPER_STATE_INVALID;
binder_user_error("%d:%d ERROR: BC_REGISTER_LOOPER called without request\n",
proc->pid, thread->pid);
} else {
proc->requested_threads--;
proc->requested_threads_started++;
}
thread->looper |= BINDER_LOOPER_STATE_REGISTERED;
break;
case BC_ENTER_LOOPER:
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BC_ENTER_LOOPER\n",
proc->pid, thread->pid);
if (thread->looper & BINDER_LOOPER_STATE_REGISTERED) {
thread->looper |= BINDER_LOOPER_STATE_INVALID;
binder_user_error("%d:%d ERROR: BC_ENTER_LOOPER called after BC_REGISTER_LOOPER\n",
proc->pid, thread->pid);
}
thread->looper |= BINDER_LOOPER_STATE_ENTERED;
break;
case BC_EXIT_LOOPER:
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BC_EXIT_LOOPER\n",
proc->pid, thread->pid);
thread->looper |= BINDER_LOOPER_STATE_EXITED;
break;
2.1.2.7 BC_REQUEST_DEATH_NOTIFICATION
,BC_CLEAR_DEATH_NOTIFICATION
,BC_DEAD_BINDER_DONE
這是Binder"死亡"相關的幾個命令。
case BC_REQUEST_DEATH_NOTIFICATION:
case BC_CLEAR_DEATH_NOTIFICATION: {
/*這兩個命令是Binder客戶端(BpBinder)纔會發出的*/
uint32_t target;
binder_uintptr_t cookie;
struct binder_ref *ref;
struct binder_ref_death *death;
/*讀取客戶端的句柄(handle),以便查找到對應的binder_ref*/
if (get_user(**target**, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
/*讀取BpBinder對象的地址*/
if (get_user(**cookie**, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
/*根據讀取到handle在proc->refs_by_desc紅黑樹中查找對應的binder_ref*/
**ref = binder_get_ref(proc, target);**
if (ref == NULL) {
binder_user_error("%d:%d %s invalid ref %d\n",
proc->pid, thread->pid,
cmd == BC_REQUEST_DEATH_NOTIFICATION ?
"BC_REQUEST_DEATH_NOTIFICATION" :
"BC_CLEAR_DEATH_NOTIFICATION",
target);
break;
}
binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
"%d:%d %s %016llx ref %d desc %d s %d w %d for node %d\n",
proc->pid, thread->pid,
cmd == BC_REQUEST_DEATH_NOTIFICATION ?
"BC_REQUEST_DEATH_NOTIFICATION" :
"BC_CLEAR_DEATH_NOTIFICATION",
(u64)cookie, ref->debug_id, ref->desc,
ref->strong, ref->weak, ref->node->debug_id);
if (cmd == BC_REQUEST_DEATH_NOTIFICATION) {
/*之前已經註冊過了*/
if (ref->death) {
binder_user_error("%d:%d BC_REQUEST_DEATH_NOTIFICATION death notification already set\n",
proc->pid, thread->pid);
break;
}
/*創建一個binder_ref_death對象*/
death = kzalloc(sizeof(*death), GFP_KERNEL);
if (death == NULL) {
thread->return_error = BR_ERROR;
binder_debug(BINDER_DEBUG_FAILED_TRANSACTION,
"%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n",
proc->pid, thread->pid);
break;
}
binder_stats_created(BINDER_STAT_DEATH);
INIT_LIST_HEAD(&death->work.entry);
death->cookie = cookie; /*保存要通知的BpBinder*/
ref->death = death;
/* binder_deferred_func —> binder_deferred_release->binder_node_release函數中會將proc設爲NULL*/
if (ref->node->proc == NULL) {
/* 如果binder service所在的進程已經不在了,說明對應的binder service已經掛啦
*/
ref->death->work.type = **BINDER_WORK_DEAD_BINDER**;
if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
/*線程已經進入binder循環,則將binder_ref_death通過work域加入線程的todo隊列*/
list_add_tail(&ref->death->work.entry, &thread->todo);
} else {
/*否則,將binder_ref_death通過work域加入進程的todo隊列*/
list_add_tail(&ref->death->work.entry, &proc->todo);
/*喚醒等待在這個wait隊列上的進程*/
wake_up_interruptible(&proc->wait);
}
}
} else {/* BC_CLEAR_DEATH_NOTIFICATION */
if (ref->death == NULL) {/*該binder_ref沒有註冊binder service死亡通知*/
binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification not active\n",
proc->pid, thread->pid);
break;
}
death = ref->death;
/*每個BpBinder只能清除自己註冊的死亡通知,不能清除別人註冊的*/
if (death->cookie != cookie) {
binder_user_error("%d:%d BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch %016llx != %016llx\n",
proc->pid, thread->pid,
(u64)death->cookie,
(u64)cookie);
break;
}
ref->death = NULL;
if (list_empty(&death->work.entry)) {/*對應的binder service沒有死亡*/
death->work.type = **BINDER_WORK_CLEAR_DEATH_NOTIFICATION**;
if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
**list_add_tail(&death->work.entry, &thread->todo);**
} else {
** list_add_tail(&death->work.entry, &proc->todo);**
**wake_up_interruptible(&proc->wait);**
}
} else {/*對應的binder service已經死亡了,此時death通過work鏈接在proc->delivered_death隊列*/
BUG_ON(death->work.type != BINDER_WORK_DEAD_BINDER);/*確保對應的binder service已經掛了*/
death->work.type = **BINDER_WORK_DEAD_BINDER_AND_CLEAR**;
}
}
} break;
case BC_DEAD_BINDER_DONE: {
struct binder_work *w;
binder_uintptr_t cookie;
struct binder_ref_death *death = NULL;
if (get_user(cookie, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
list_for_each_entry(w, &proc->delivered_death, entry) {
struct binder_ref_death *tmp_death = container_of(w, struct binder_ref_death, work);
if (tmp_death->cookie == cookie) {
death = tmp_death;
break;
}
}
binder_debug(BINDER_DEBUG_DEAD_BINDER,
"%d:%d BC_DEAD_BINDER_DONE %016llx found %p\n",
proc->pid, thread->pid, (u64)cookie,
death);
if (death == NULL) {
binder_user_error("%d:%d BC_DEAD_BINDER_DONE %016llx not found\n",
proc->pid, thread->pid, (u64)cookie);
break;
}
list_del_init(&death->work.entry);/*從proc->delivered_death鏈表中取出,然後重新初始化即pre,next都指向自己*/
/*之前客戶端給驅動發送了BC_CLEAR_DEATH_NOTIFICATION命令清除死亡通知*/
if (death->work.type == BINDER_WORK_DEAD_BINDER_AND_CLEAR) {
death->work.type = BINDER_WORK_CLEAR_DEATH_NOTIFICATION;/*修改type*/
/*將death從proc->delivered_death隊列移動到thread->todo或者proc->todo隊列*/
if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) {
list_add_tail(&death->work.entry, &thread->todo);
} else {
list_add_tail(&death->work.entry, &proc->todo);
wake_up_interruptible(&proc->wait);
}
}
} break;
/*其他命令,直接打印一條錯誤消息,返回EINVAL*/
default:
pr_err("%d:%d unknown command %d\n",
proc->pid, thread->pid, cmd);
return -EINVAL;
}//end of switch
*consumed = ptr - buffer;
}// end of while
return 0;
}//end of binder_thread_write
- 前兩個命令分別是客戶端(BpBinder)用於註冊和取消服務端(BBinder)死亡通知的。當一個服務端(BBinder或binder service)因正常或者異常情況下退出時,Binder驅動發送
BR_DEAD_BINDER
會告知客戶端,以便客戶端發“訃告”(sendObituary),執行一些資源清理相關的工作。在準備發送“訃告”前,客戶端會調用clearDeathNotification
,發送BC_CLEAR_DEATH_NOTIFICATION
命令給驅動,以註銷死亡通知。具體過程代碼基本已經註明了,就不再贅述了。有兩點需要注意的這裏簡單說明一下:- 在註冊死亡通知,即處理
BC_REQUEST_DEATH_NOTIFICATION
命令時,要先判斷服務端是否就已經死亡(通過ref->node->proc == NULL
來判斷)。如果服務端已經死亡了,就將death->work.type
設爲BINDER_WORK_DEAD_BINDER
,然後看當前線程是否已經進入binder循環,來決定是將該work加入到線程的todo
隊列還是進程的todo
隊列。 - 在取消死亡通知,即處理
BC_CLEAR_DEATH_NOTIFICATION
命令時,需判斷服務端是否已經死亡,這時的判斷方法是查看death->work.entry
隊列是否爲空,因爲如果服務端已經死亡,death->work
會加入線程的todo
隊列。如果death->work.entry
爲空,則將work
的類型設爲BINDER_WORK_CLEAR_DEATH_NOTIFICATION
,然後加入線程或者進程的todo
隊列;如果如果death->work.entry
不爲空,則說明服務端已經死亡,work
已經在todo
隊列,不需要加入隊列的操作了,但類型需修改爲BINDER_WORK_DEAD_BINDER_AND_CLEAR
。
- 在註冊死亡通知,即處理
BC_DEAD_BINDER_DONE
命令是binder_thread_write
中處理的最後一個命令,它是客戶端(BpBinder)在收到Binder驅動發的BR_DEAD_BINDER
命令之後,回覆給Binder驅動的命令,用於告知驅動客戶端已經處理完服務端死亡相關操作(如:發送“訃告”)。如前所述:在發送“訃告”前,客戶端還會先調用clearDeathNotification
發送一個BC_CLEAR_DEATH_NOTIFICATION
命令給Binder驅動,用於清除服務端(binder service)的死亡通知。BC_DEAD_BINDER_DONE
命令在驅動中主要操作就是就death.work.type
修改爲BINDER_WORK_CLEAR_DEATH_NOTIFICATION
,並將death從proc->delivered_death
隊列移動到thread->todo
或者proc->todo
隊列,以便在之後的binder_thread_read
處理todo
隊列時,釋放death
、回覆客戶端等操作。
下面這個時序圖是binder service死亡時,death.work類型及所在隊列的流轉圖:
圖中proc
和thread
都是客戶端的proc
和thread
。但binder_deferred_func->binder_defrred_release->binder_node_release
,這個調用時發生服務端的內核線程中的。
到這裏binder_thread_write
處理涉及的所有命令我們就分析完啦,其他無法識別的命令全都走default
分支,輸出一條錯誤消息後,直接返回-EINVAL
。
到這裏我們就對binder_thread_write
的處理邏輯分析完啦。
2.1.3 數據接收及任務處理 — binder_thread_read
簡單回顧一下前面binder_ioctl_write_read
的邏輯,如果bwr.read_size
大於0,即進程用戶態進程希望從Binder驅動中收到數據,就調用binder_thread_read
進行處理。binder_thread_read
的代碼量相較於binder_thread_write
會容易一些,主體處理邏輯如下:
-
首先根據
*consumed
,即binder_write_read
結構體的read_consumed
域,是否爲0判斷當前的可寫入指針是不是在bwr.read_buffer
的起始位置,是的話就先寫入一個BR_NOOP
命令。由此可見,在bwr.read_buffer
中,它總是以一個BR_NOOP
命令開頭的; -
接着檢查之前的處理是否有錯誤發生(
return_error
,return_error2
),因爲寫操作是先於讀操作處理。之前發生了錯誤,將錯誤碼寫入用戶態地址空間的read_buffer
中,然後跳過中間的處理邏輯,直接到達最後判斷是否需要創建線程操作; -
如果之前沒有錯誤發生,就接着看當前線程任務棧及
todo
隊列是否還有任務未處理,以決定是等待在當前線程還是在進程的wait
隊列上。如果沒有任務要處理 ——todo
隊列爲空,且用戶態不需要內核態返回值,則根據進程是否設置非阻塞標誌位,設置了就返回-EAGAIN
,表示稍後重試;未設置且沒有任務要處理就等待在進程/線程的wait
隊列上,等到有任務要處理時再被wake_up
。這裏有三個小點需要注意:- 線程進入睡眠前要先釋放之前拿到的binder鎖,這個鎖是一個大鎖,binder驅動的全局數據都靠其保護,如果線程睡眠前不釋放鎖,其他binder線程很可能都要阻塞在等待這個鎖上。當線程再次等被喚醒後,會重新獲取鎖。
- 線程在進入睡眠前和喚醒後要分別設置和取消
BINDER_LOOPER_STATE_WAITING
狀態標誌位。 - 如果線程是等待進程的
wait
隊列上,睡眠前和喚醒後要分別增減空閒線程數——proc->ready_threads
。
-
開始循環處理線程/進程的
todo
隊列上的任務,這是讀操作的核心邏輯。處理進程todo
隊列的條件是線程的todo
隊列已經處理完了,進程的todo
隊列不爲空,且之前等待在進程的wait
隊列上,這其實相當線程的todo隊列是各自線程的家務事,而進程的todo隊列則是各線程的公共事務,線程先處理自己的家務事,再去處理公共事務。對於每一個binder_work
的處理流程,則根據其類型,走各自的處理邏輯:BINDER_WORK_TRANSACTION
, 根據binder_work
在binder_transaction
結構體的偏移計算出binder_transaction
對象的地址。然後根據是Client->Server的binder請求還是Server->Client的回覆(t->buffer->target_node是否爲NULL來區分),確定發送給用戶態進程的cmd
是BR_TRANSACTION
還是BR_REPLY
。如果是Client->Server的請求,還要根據target_node
,得到binder server對象在server所在進程的用戶態地址(cookie
)及其相關的引用計數(ptr
),填入binder_transaction_data
中。再就是將binder請求的相關信息,如發送進程的pid,有效用戶id,code
,flags
及數據相關信息。這裏需要單獨提出來講一下的是transaction相關data區及offsets區的內容,並不需要從內核態拷貝到用戶態的操作,只需將binder_transaction_data
的data.ptr.buffer
和data.ptr.offsets
兩個指針修改爲相應用戶態地址即可。可以這樣做的原因是binder_buffer
的data
所指的緩衝區的物理頁框同時映射到了用戶態和內核態的虛擬機地址空間,即binder_buffer.data
(內核態)和binder_buffer.data + user_buffer_offset
(用戶態)兩段虛擬地址空間映射的是同一組物理頁框。這裏就是Binder進程間通信高效的精髓所在,只需要一次發送端的用戶態到內核態拷貝即可,接收端只需簡單修改指針就好了。這部分內容的不太清楚的可以參考一下之前整理的Binder驅動之binder_buffer的分配與回收第二節內容。剩下的工作就是將cmd
和binder_transaction_data
發送到用戶態(binder_write_read.read_buffer
),從todo
移除該binder_work
,加入事務棧(BR_TRANSACTION)或者釋放binder_transaction
(BR_REPLY)等。還有一個要說明的是對於BINDER_WORK_TRANSACTION
的binder_work
,一次binder_thread_read
操作只會執行一個,處理完了就跳出循環,以便線程可以回到用戶態處理本次Binder Transaction。BINDER_WORK_TRANSACTION_COMPLETED
,這個的主要用途是用來告知進程binder請求或者回復已經發出去,整體邏輯比較簡單,代碼中已添加相關注釋,就不再贅述了。BINDER_WORK_NODE
,這是一個處理binder_node
與binder service(BBinder)
強弱引用計數相關的命令。當binder_node
有強/弱引用時,確保其對應服務端進程用戶態地址空間中binder service對象,不會被釋放;當binder_node
有強/弱引用歸0時,遞減其對應服務端進程用戶態地址空間中binder service對象引用計數,以確保用戶地址空間的對象會被正確釋放。BINDER_WORK_DEAD_BINDER
,BINDER_WORK_DEAD_BINDER_AND_CLEAR
,BINDER_WORK_CLEAR_DEATH_NOTIFICATION
,這三種binder_work
是binder service死亡相關幾個work
。從前文的binder_thread_write
中的死亡通知的幾個命令中,已經基本講清楚了這幾個類型及隊列的轉移過程,可以回頭重新看一下。
下面是代碼的具體實現,可以結合上面的分析,一起看。
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
int ret = 0;
int wait_for_proc_work;
if (*consumed == 0) {/*當前的寫入位置爲bwr.read_buffer的起始位置,先寫入一個BR_NOOP命令到read_buffer中,該命令在用戶態是一個空操作,什麼也不做,主要意義應該是在輸出日誌等*/
if (put_user(BR_NOOP, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
}
retry:
/*如果線程事務棧和todo隊列都爲空,說明此時沒有要當前線程處理的任務,將增加空閒線程的計數器(即將wait_for_proc_work設爲1),讓線程等待在**進程**的wait隊列上*/
wait_for_proc_work = thread->transaction_stack == NULL &&
list_empty(&thread->todo);
if (thread->return_error != BR_OK && ptr < end) {/*之前在binder_transaction或者binder death時發生了錯誤*/
if (thread->return_error2 != BR_OK) { /*發送reply時發生了錯誤,將錯誤返回給進程用戶態*/
if (put_user(thread->return_error2, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
binder_stat_br(proc, thread, thread->return_error2);
if (ptr == end)
goto done;
thread->return_error2 = BR_OK;
}
if (put_user(thread->return_error, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
binder_stat_br(proc, thread, thread->return_error);
thread->return_error = BR_OK;
goto done;
}
/*即將進入睡眠等待區,這會導致進程/線程進入阻塞狀態,先將線程狀態改爲BINDER_LOOPER_STATE_WAITING*/
thread->looper |= **BINDER_LOOPER_STATE_WAITING**;
if (wait_for_proc_work)/*進程/線程沒事需要處理*/
proc->ready_threads++;/*空閒線程數+1*/
/*線程/進程將可能進入阻塞等待狀態,先釋放鎖,這個鎖是在binder_ioctl開始執行就拿了*/
binder_unlock(__func__);
trace_binder_wait_for_work(wait_for_proc_work,
!!thread->transaction_stack,
!list_empty(&thread->todo));
if (wait_for_proc_work) {/*線程暫時沒有工作要處理,進程/線程需要等待*/
if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))) {
/* 線程還未進入binder循環,輸出錯誤信息,並阻塞直到binder_stop_on_user_error小於2*/
binder_user_error("%d:%d ERROR: Thread waiting for process work before calling BC_REGISTER_LOOPER or BC_ENTER_LOOPER (state %x)\n",
proc->pid, thread->pid, thread->looper);
wait_event_interruptible(binder_user_error_wait,
binder_stop_on_user_error < 2);
}
binder_set_nice(proc->default_priority);
if (non_block) {/*設置了非阻塞標識*/
if (!binder_has_proc_work(proc, thread)) /*檢查當前進程是否有工作待處理,如果沒有就將返回值設爲-EAGAIN,以便用戶進程稍後重試*/
ret = -**EAGAIN**;
} else/*如果是阻塞的讀操作,則讓進程阻塞在proc的wait隊列上,直到binder_has_proc_work(thread)爲true,即進程有工作待處理*/
ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread));
} else {/**/
if (non_block) {/*讀操作設置了非阻塞標識*/
if (!binder_has_thread_work(thread)) /*檢查當前線程是否有工作待處理,如果沒有就將返回值設爲-EAGAIN,以便用戶進程稍後重試*/
ret = -**EAGAIN**;
} else/*如果是阻塞的讀操作,則讓線程阻塞在thread的wait隊列上,直到binder_has_thread_work(thread)爲true,即線程有工作待處理*/
ret = wait_event_freezable(thread->wait, binder_has_thread_work(thread));
}
/*運行到這裏,要麼是線程/進程沒有工作待處理,但是講返回值ret設置成了-EAGAIN;要麼是線程/進程已經有工作待處理了*/
binder_lock(__func__); /*重新獲取鎖*/
if (wait_for_proc_work)/*之前進入了等待操作,線程被掛起了*/
proc->ready_threads--;/*空閒線程數減1*/
thread->looper &= **~BINDER_LOOPER_STATE_WAITING**;/*移除線程等待標誌位*/
/* We cannot return -ERESTARTSYS here. This code is called
* after binder_thread_write() has already interpreted the
* input buffer. A restart will result in a doubled set of
* commands. Just return success, having consumed zero
* bytes. */
if (ret)
return ret == -ERESTARTSYS ? 0 : ret;
/*開始循環處理thread/proc的todo隊列上的每一個binder_work*/
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
struct binder_work *w;
struct binder_transaction *t = NULL;
/*取出一個binder work來處理*/
if (!list_empty(&thread->todo)) {/*線程的待處理列表不爲空*/
w = list_first_entry(&thread->todo, struct binder_work, /*從線程的待處理列表隊頭中取出一項工作處理*/
entry);
} else if (!list_empty(&proc->todo) && wait_for_proc_work) {/*進程的待處理列表不爲空,且睡眠等待前線程的`todo`隊列和事務棧都爲空*/
w = list_first_entry(&proc->todo, struct binder_work, /*從進程的待處理列表的隊頭中取出一項工作處理*/
entry);
} else {
/* no data added */
if (ptr - buffer == 4 &&
!(thread->looper & BINDER_LOOPER_STATE_NEED_RETURN))
goto retry;
break;
}
if (end - ptr < sizeof(tr) + 4)
break;
switch (w->type) {
case **BINDER_WORK_TRANSACTION**: {/*要處理的是一個事務(Binder請求)*/
/*根據binder_work在binder_transaction的偏移計算出binder_transaction的地址*/
t = container_of(w, struct binder_transaction, work);
} break;
case **BINDER_WORK_TRANSACTION_COMPLETE**: {
/*TRANSACTION或者REPLY發送完成消息,通過給進程發送BR_TRANSACTION_COMPLETE告知*/
cmd = BR_TRANSACTION_COMPLETE;
if (put_user(cmd, (uint32_t __user *)ptr))/*發送給用戶進程*/
return -EFAULT;
ptr += sizeof(uint32_t);
binder_stat_br(proc, thread, cmd);/*更新統計數據*/
binder_debug(BINDER_DEBUG_TRANSACTION_COMPLETE,
"%d:%d BR_TRANSACTION_COMPLETE\n",
proc->pid, thread->pid);
list_del(&w->entry);/*從todo隊列中移除*/
kfree(w); /*釋放在binder_thread_write在處理BC_TRANSACTION命令時在binder_transaction中申請的binder_work*/
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE); /*更新BINDER_STAT_TRANSACTION_COMPLETE統計數據*/
} break;
case **BINDER_WORK_NODE**: {/*取出的binder_work是一個binder_node*/
struct binder_node *node = container_of(w, struct binder_node, work); /*根據偏移計算出binder_node的地址*/
uint32_t cmd = BR_NOOP;
const char *cmd_name;
int strong = node->internal_strong_refs || node->local_strong_refs;
int weak = !hlist_empty(&node->refs) || node->local_weak_refs || strong;
if (weak && !node->has_weak_ref) {/*弱引用計數不爲0,但是弱引用標誌位爲0*/
cmd = BR_INCREFS; /*發送BR_INCREFS命令給進程用戶態,讓其增加弱引用計數*/
cmd_name = "BR_INCREFS";
node->has_weak_ref = 1; /*設置弱引用標誌位*/
node->pending_weak_ref = 1; /*設置pengding標誌位,表示(進程用戶態)有未處理的弱引用增加命令*/
node->local_weak_refs++; /*增加本地弱引用計數器*/
} else if (strong && !node->has_strong_ref) {/*強引用計數不爲0,但是強引用標誌位爲0*/
cmd = BR_ACQUIRE; /*發送BR_ACQUIRE命令給進程,讓其增加強引用計數*/
cmd_name = "BR_ACQUIRE";
node->has_strong_ref = 1; /*設置強引用標誌位*/
node->pending_strong_ref = 1; /*設置pengding標誌位,表示(進程用戶態)有未處理的強引用增加命令*/
node->local_strong_refs++; /*增加本地強引用計數器*/
} else if (!strong && node->has_strong_ref) {/*強引用計數爲0,但是強引用標誌位不爲0*/
cmd = BR_RELEASE;
cmd_name = "BR_RELEASE";
node->has_strong_ref = 0;
} else if (!weak && node->has_weak_ref) {/*弱引用計數爲0,但是弱引用標誌位不爲0*/
cmd = BR_DECREFS;
cmd_name = "BR_DECREFS";
node->has_weak_ref = 0;
}
if (cmd != BR_NOOP) {/*有引用計數相關的命令需要處理*/
/*將命令先發送給進程用戶態*/
if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
/*BBinder的引用計數器的地址發送給進程的用戶態地址空間read_buffer*/
if (put_user(node->ptr,
(binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
/*BBinder的地址發送給進程*/
if (put_user(node->cookie,
(binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
/*更新統計數據*/
binder_stat_br(proc, thread, cmd);
binder_debug(BINDER_DEBUG_USER_REFS,
"%d:%d %s %d u%016llx c%016llx\n",
proc->pid, thread->pid, cmd_name,
node->debug_id,
(u64)node->ptr, (u64)node->cookie);
} else {/*不需要增加/減少binder_node的強/弱引用計數*/
list_del_init(&w->entry);/*從todo隊列中移出*/
if (!weak && !strong) {/*binder_node的強弱引用計數都爲0,釋放該binder_node*/
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d:%d node %d u%016llx c%016llx deleted\n",
proc->pid, thread->pid,
node->debug_id,
(u64)node->ptr,
(u64)node->cookie);
rb_erase(&node->rb_node, &proc->nodes);/*從proc->nodes紅黑樹中移除*/
kfree(node);/*釋放binder_node所佔內存空間*/
binder_stats_deleted(BINDER_STAT_NODE);/*更新統計數據*/
} else {
binder_debug(BINDER_DEBUG_INTERNAL_REFS,
"%d:%d node %d u%016llx c%016llx state unchanged\n",
proc->pid, thread->pid,
node->debug_id,
(u64)node->ptr,
(u64)node->cookie);
}
}
} break;
/*binder service死亡相關的幾個命令處理*/
case **BINDER_WORK_DEAD_BINDER**:
case **BINDER_WORK_DEAD_BINDER_AND_CLEAR**:
case **BINDER_WORK_CLEAR_DEATH_NOTIFICATION**: {
struct binder_ref_death *death;
uint32_t cmd;
death = container_of(w, struct binder_ref_death, work);/*根據偏移計算出包含它的binder_ref_death對象的地址*/
if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION)/*死亡通知清理完畢的消息*/
cmd = BR_CLEAR_DEATH_NOTIFICATION_DONE;/*回覆命令設爲BR_CLEAR_DEATH_NOTIFICATION,告知用戶進程清除通知完畢的相關處理已完成*/
else
cmd = BR_DEAD_BINDER;/*告訴用戶進程,binder service已經死亡*/
if (put_user(cmd, (uint32_t __user *)ptr)) /*將命令發送給用戶*/
return -EFAULT;
ptr += sizeof(uint32_t);
if (put_user(death->cookie,
(binder_uintptr_t __user *)ptr))/*客戶端對象(BpBinder)對應的地址發送到用戶進程*/
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
binder_stat_br(proc, thread, cmd);
binder_debug(BINDER_DEBUG_DEATH_NOTIFICATION,
"%d:%d %s %016llx\n",
proc->pid, thread->pid,
cmd == BR_DEAD_BINDER ?
"BR_DEAD_BINDER" :
"BR_CLEAR_DEATH_NOTIFICATION_DONE",
(u64)death->cookie);
if (w->type == BINDER_WORK_CLEAR_DEATH_NOTIFICATION) {
list_del(&w->entry);
kfree(death);
binder_stats_deleted(BINDER_STAT_DEATH);
} else
/*`BINDER_WORK_DEAD_BINDER`和`BINDER_WORK_DEAD_BINDER_AND_CLEAR`移到proc->delivered_deat隊列*/
list_move(&w->entry, &proc->delivered_death);
if (cmd == BR_DEAD_BINDER)
goto done; /* DEAD_BINDER notifications can cause transactions */
} break;
}//end of switch
/*當binder_work的類型是BINDER_WORK_TRANSACTION時,t不爲NULL*/
if (!t)
continue;
/*接下來開始處理**TRANSACTION**,將binder_transaction轉換爲進程用戶態使用的binder_transaction_data*/
BUG_ON(t->buffer == NULL);
/* 在binder_transaction章節中我們知道,當binder客戶端向binder服務端發送請求時,
* target_node爲binder服務端的binder_node地址,如果是binder服務端回覆客戶端,則target_node爲NULL。
*/
if (t->buffer->target_node) {/*Client->Server的binder請求*/
struct binder_node *target_node = t->buffer->target_node;
/*將引用計數器地址及BBinder地址寫入transaction data中, 即將struct binder_transaction轉化爲進程用戶態可處理struct binder_transaction_data結構體*/
tr.target.ptr = target_node->ptr;
tr.cookie = target_node->cookie;
/*設置線程優先級信息*/
t->saved_priority = task_nice(current);
if (t->priority < target_node->min_priority &&
!(t->flags & TF_ONE_WAY))
binder_set_nice(t->priority);
else if (!(t->flags & TF_ONE_WAY) ||
t->saved_priority > target_node->min_priority)
binder_set_nice(target_node->min_priority);
cmd = BR_TRANSACTION;
} else {/*Client->Server的binder請求的回覆*/
/* 將引用計數器地址及BBinder地址從transaction data清空,
* 因爲Client無法從地址中獲取相應的對象,這個地址只有在服務端的進程的地址空間纔有效。
*/
tr.target.ptr = 0;
tr.cookie = 0;
cmd = BR_REPLY;
}
tr.code = t->code;/*設置transacton的業務代碼,一種代碼對應一種binder server提供的服務*/
tr.flags = t->flags;/*設置transacton的標識位*/
tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);/*請求線程的有eudi*/
/*設置發送端進程id*/
if (t->from) {
struct task_struct *sender = t->from->proc->tsk;
tr.sender_pid = task_tgid_nr_ns(sender,
task_active_pid_ns(current));
} else {
tr.sender_pid = 0;
}
/*數據相關信息*/
tr.data_size = t->buffer->data_size; /*數據大小*/
tr.offsets_size = t->buffer->offsets_size; /*offsets區大小*/
/*將binder_transaction_data中數據指針直接轉換成binder_buffer映射到用戶態地址,**這樣就無需再從內核態拷貝到用戶態的操作了***/
tr.**data.ptr.buffer** = (binder_uintptr_t)(
(uintptr_t)t->buffer->data +
proc->user_buffer_offset);
/*將binder_transaction_data的offset區地址設置爲binder_buffer中相應的offset區用戶態地址*/
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
if (**put_user(cmd, (uint32_t __user *)ptr**))/*將BINDER_TRANACTION或者BINDER_REPLEY命令發送到進程用戶態*/
return -EFAULT;
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, sizeof(tr)))/*將binder_transaction_data拷貝到進程用戶態*/
return -EFAULT;
ptr += sizeof(tr);
trace_binder_transaction_received(t);
binder_stat_br(proc, thread, cmd);/*更新統計數據*/
binder_debug(BINDER_DEBUG_TRANSACTION,
"%d:%d %s %d %d:%d, cmd %d size %zd-%zd ptr %016llx-%016llx\n",
proc->pid, thread->pid,
(cmd == BR_TRANSACTION) ? "BR_TRANSACTION" :
"BR_REPLY",
t->debug_id, t->from ? t->from->proc->pid : 0,
t->from ? t->from->pid : 0, cmd,
t->buffer->data_size, t->buffer->offsets_size,
(u64)tr.data.ptr.buffer, (u64)tr.data.ptr.offsets);
list_del(&t->work.entry);/*從todo隊列中移除*/
t->buffer->allow_user_free = 1; /*因爲這塊地址已經交給進程用戶態使用了,因此運行進程釋放該段地址空間*/
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {/*BINDER_TRANSACTION*/
/*加入事務棧*/
t->to_parent = thread->transaction_stack;
t->to_thread = thread;
thread->transaction_stack = t;
} else {/*BINDER_REPLY*/
/*一次binder通信已完成,釋放binder_transaction*/
t->buffer->transaction = NULL;
kfree(t);
binder_stats_deleted(BINDER_STAT_TRANSACTION);/*更新被刪除相關的統計數據*/
}
**break;/*處理完一個BINDER_WORK_TRANSACTION,就退出循環。說明對於BINDER_WORK_TRANSACTION每次至多隻處理一個*/**
}//end while
done:
*consumed = ptr - buffer;/*發送給用戶態的字節數*/
if (proc->requested_threads + proc->ready_threads == 0 &&
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED)) /* the user-space code fails to */
/*spawn a new thread if we leave this out */) {
/* 沒有可用的binder線程,且之前已經進入到BINDER_LOOPER_STATE_REGISTERED
* 或者BINDER_LOOPER_STATE_ENTERED狀態啓動一個新的binder線程 */
proc->requested_threads++;
binder_debug(BINDER_DEBUG_THREADS,
"%d:%d BR_SPAWN_LOOPER\n",
proc->pid, thread->pid);
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
binder_stat_br(proc, thread, BR_SPAWN_LOOPER);
}
return 0;
}
2.3 註冊Context Manager —— BINDER_SET_CONTEXT_MGR
註冊Context Manager是Android系統進程SM(ServiceManager)啓動時執行的操作,它是整個Android系統中binder service的大管家,所有想通過binder提供服務的service都要在SM中註冊(匿名的binder service通過已註冊的service間接查找),才能被使用者找到。
該命令在驅動是通過binder_ioctl_set_ctx_mgr
函數完成的,函數實現如下:
static int binder_ioctl_set_ctx_mgr(struct file *filp)
{
int ret = 0;
struct binder_proc *proc = filp->private_data;
kuid_t curr_euid = current_euid();/*獲取當前進程的有效用戶id*/
if (binder_context_mgr_node != NULL) {
pr_err("BINDER_SET_CONTEXT_MGR already set\n");
ret = -EBUSY;
goto out;
}
ret = security_binder_set_context_mgr(proc->tsk);/*檢查當前進程是否有權限執行註冊Context Manager的權限,在4.4版本的內核中該函數直接返回0.`kernel/include/linux/security.h`*/
if (ret < 0)
goto out;
if (uid_valid(binder_context_mgr_uid)) {/*檢查已有的uid是否有效*/
if (!uid_eq(binder_context_mgr_uid, curr_euid)) {/*有效但是與當前運行線程的有效用戶ID不相等,則出錯。即線程只能註冊自己,且只能有一個線程設置爲Context Manager*/
pr_err("BINDER_SET_CONTEXT_MGR bad uid %d != %d\n",
from_kuid(&init_user_ns, curr_euid),
from_kuid(&init_user_ns,
binder_context_mgr_uid));
ret = -EPERM;
goto out;
}
} else {/*之前設置的無效(如初始化狀態,它的值是INVALID_UID),設置成當前進程的有效用戶id。Service Manager啓動第一次註冊時走這個分支*/
binder_context_mgr_uid = curr_euid;
}
binder_context_mgr_node = binder_new_node(proc, 0, 0);/*分配一個全新的binder_node,並賦值到全局變量binder_context_mgr_node中*/
if (binder_context_mgr_node == NULL) {
ret = -ENOMEM;
goto out;
}
/*更新相關引用計數*/
binder_context_mgr_node->local_weak_refs++;
binder_context_mgr_node->local_strong_refs++;
binder_context_mgr_node->has_strong_ref = 1;
binder_context_mgr_node->has_weak_ref = 1;
out:
return ret;
}
- 可以看到主體邏輯其實非常簡單,先做了一下權限檢查,再判斷是否已經註冊過,接着如果沒有註冊過就將當前進程的有效用戶id設置到全局變量
binder_context_mgr_uid
中,並分配一個新的binder_node
,更新相關引用計數就大功告成了。
2.4 線程退出命令 —— BINDER_THREAD_EXIT
該命令是在線程析構時(IPCThreadState::threadDestructor
)發出的。Android線程在初始化時(IPCThreadState::self())
會調用pthread_key_create
註冊TLS(變量名爲:gTLS
)銷燬時的函數,即IPCThreadState::threadDestructor
,它在線程退出時會自動被系統調用。驅動收到該命令後調用binder_free_thread
來處理,函數實現如下:
static int binder_free_thread(struct binder_proc *proc,
struct binder_thread *thread)
{
struct binder_transaction *t;
struct binder_transaction *send_reply = NULL;
int active_transactions = 0;
rb_erase(&thread->rb_node, &proc->threads);/*在進程的線程紅黑樹中刪除該線程節點*/
t = thread->transaction_stack;
if (t && t->to_thread == thread) /*事務棧中還有需要本線程處理的事務,記錄到send_reply中,稍後回覆失敗消息給對方*/
send_reply = t;
/* 依次處理事務棧的兩類事務:
* a)需要本線程處理的事務,to_thread == thread.
* b) 等待其他線程回覆的事務,from_thread == thread.
*/
while (t) {
active_transactions++;
binder_debug(BINDER_DEBUG_DEAD_TRANSACTION,
"release %d:%d transaction %d %s, still active\n",
proc->pid, thread->pid,
t->debug_id,
(t->to_thread == thread) ? "in" : "out");
if (t->to_thread == thread) {
t->to_proc = NULL;
t->to_thread = NULL;
if (t->buffer) {
t->buffer->transaction = NULL;
t->buffer = NULL;
}
t = t->to_parent;
} else if (t->from == thread) {
t->from = NULL;
t = t->from_parent;
} else
BUG();
}
if (send_reply)/*事務棧中有待本線程處理的事務,給對端線程發送BR_DEAD_REPLY消息*/
binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
binder_release_work(&thread->todo);/*將todo隊列上面所有待處理binder_work全部釋放*/
kfree(thread); /*釋放binder_thread本身所佔用的結構體*/
binder_stats_deleted(BINDER_STAT_THREAD); /*更新統計數據*/
return active_transactions; /*返回待處理的事務數*/
}
對於線程退出命令BINDER_THREAD_EXIT
的處理主體邏輯可分爲四個步驟:
a)在進程的線程紅黑樹(proc->threads)中移除該線程節點。
b)刪除事務棧沒有處理完的事務,並向對端發送一個BR_READ_REPLY
消息。
c)釋放線程的待處理列表(todo)上的所有binder_work。
d)釋放thread結構體,更新相關統計數據。
2.5 獲取Binder版本信息 ----- BINDER_VERSION
這部分內容比較簡單,相關邏輯都已在binder_ioctrl
函數switch
的BINDER_VERSION
分支中做了註釋,這裏就不再贅述了。
3. 總結
-
Binder驅動中的命令可分爲兩類,一類是以
BC_xxx的形式
,如BC_TRANSACTION
,BC_REPLY
,它們是進程用戶態發送Binder驅動的處理的命令;一類是以BR_xxx
的形式,它們是Binder驅動回覆給進程用戶態處理的命令,如BR_REPLY
,BR_TRANSACTION
,BR_OK
等。Binder驅動中的處理邏輯基本都是圍繞這些命令展開的。這些命令總結起來大體可以分爲以下幾類:binder_node
和binder_ref
引用計數處理相關。因爲每一個BBinder
和BpBinder
都要一個binder_node
和binder_ref
與其相對應,需要正確處理其引用計數才能保證不會造成內存泄漏發生。BR_INCREFS
,BR_ACQUIRE
,BR_RELEASE
,BR_DECREFS
BC_INCREFS
,BC_ACQUIRE
,BC_RELEASE
,BC_DECREFS
,BC_ACQUIRE_DONE
,BC_INCREF_DONE
,BC_ATTEMPT_ACQUIRE
(這個命令還不支持)。
- Binder請求和回複相關的命令。
BR_ERROR
,BR_OK
,BR_TRANSACTION
,BR_TRANSACTION_COMPLETE
,BR_REPLY
,BR_DEAD_REPLY
,BR_FAILED_REPLY
,BR_NOOP
BC_TRANSACTION
,BC_REPLY
,
- Binder線程/進程狀態,包括註冊成ServiceManager,創建線程。
BR_SPAWN_LOOPER
BC_REGISTER_LOOPER
,BC_ENTER_LOOPER
,BC_EXIT_LOOPER
- Binder死亡即"訃告"(通知)相關的命令。
BR_DEAD_BINDER
,BR_CLEAR_DEATH_NOTIFICATION_DONE
BC_REQUEST_DEATH_NOTIFICATION
,BC_CLEAR_DEATH_NOTIFICATION
,BC_DEAD_BINDER_DONE
- Binder緩衝區釋放命令
BC_FREE_BUFFER
-
struct binder_transaction_data
和struct binder_transaction
分別Binder通信中數據的用戶態和驅動層的表示,當數據從用戶態用binder_transaction_data
發送到內核驅動後,驅動會將其轉化成一個binder_transaction
,然後加上自己處理需要的一些數據結構,如work等。其中數據部分(data域)轉換成binder_buffer
; 如果是client發送給binder server的請求,還會根據target
,cookie
的值找到binder service對應的binder_node
,存放在binder_buffer
的target_node
中。如果是binder server回覆給client數據,則binder_buffer.target_node
爲NULL
。