摘要:本節主要來講解Android10.0 Binder的數據是如何完成定向打擊
閱讀本文大約需要花費30分鐘。
文章首發微信公衆號:IngresGe
專注於Android系統級源碼分析,Android的平臺設計,歡迎關注我,謝謝!
[Android取經之路] 的源碼都基於Android-Q(10.0) 進行分析
[Android取經之路] 系列文章:
《系統啓動篇》
- Android系統架構
- Android是怎麼啓動的
- Android 10.0系統啓動之init進程
- Android10.0系統啓動之Zygote進程
- Android 10.0 系統啓動之SystemServer進程
- Android 10.0 系統服務之ActivityMnagerService
- Android10.0系統啓動之Launcher(桌面)啓動流程
- Android10.0應用進程創建過程以及Zygote的fork流程
- Android 10.0 PackageManagerService(一)工作原理及啓動流程
- Android 10.0 PackageManagerService(二)權限掃描
- Android 10.0 PackageManagerService(三)APK掃描
- Android 10.0 PackageManagerService(四)APK安裝流程
《日誌系統篇》
- Android10.0 日誌系統分析(一)-logd、logcat 指令說明、分類和屬性
- Android10.0 日誌系統分析(二)-logd、logcat架構分析及日誌系統初始化
- Android10.0 日誌系統分析(三)-logd、logcat讀寫日誌源碼分析
- Android10.0 日誌系統分析(四)-selinux、kernel日誌在logd中的實現
《Binder通信原理》:
- Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
- Android10.0 Binder通信原理(二)-Binder入門篇
- Android10.0 Binder通信原理(三)-ServiceManager篇
- Android10.0 Binder通信原理(四)-Native-C\C++實例分析
- Android10.0 Binder通信原理(五)-Binder驅動分析
- Android10.0 Binder通信原理(六)-Binder數據如何完成定向打擊
- Android10.0 Binder通信原理(七)-Framework binder示例
- Android10.0 Binder通信原理(八)-Framework層分析
- Android10.0 Binder通信原理(九)-AIDL Binder示例
- Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub設計模式
- Android10.0 Binder通信原理(十一)-Binder總結
1.概述
前面介紹了Binder驅動的機制、ServiceManager加載的流程、Native層服務註冊、獲取的流程,但是這些都是浮在表面的接口,真正的數據時如何傳輸的?協議碼如何轉向,讓我們這一節深入來分析一下
2.Binder的線程與進程
對於底層Binder驅動,通過binder_procs鏈表記錄所有創建的binder_proc結構體,binder驅動層的每一個binder_proc結構體都與用戶空間的一個用於binder通信的進程一一對應,且每個進程有且只有一個ProcessState對象,這是通過單例模式來保證的。在每個進程中可以有很多個線程,每個線程對應一個IPCThreadState對象,IPCThreadState對象也是單例模式,即一個線程對應一個IPCThreadState對象,在Binder驅動層也有與之相對應的結構,那就是Binder_thread結構體。在binder_proc結構體中通過成員變量rb_root threads,來記錄當前進程內所有的binder_thread。
如下圖所示:
Binder線程池:每個Server進程在啓動時會創建一個binder線程池,並向其中註冊一個Binder線程;之後Server進程也可以向binder線程池註冊新的線程,或者Binder驅動在探測到沒有空閒binder線程時會主動向Server進程註冊新的的binder線程。對於所有Client端進程的binder請求都是交由Server端進程的binder線程來處理的。
3 Binder傳輸過程
Binder-IPC機制,就是指在進程間傳輸數據(binder_transaction_data),一次數據的傳輸,稱爲事務(binder_transaction)。
對於多個不同進程向同一個進程發送事務時,這個同一個進程或線程的事務需要串行執行,在Binder驅動中爲binder_proc和binder_thread都有todo隊列。
也就是說對於進程間的通信,就是發送端把binder_transaction節點,插入到目標進程或其子線程的todo隊列中,等目標進程或線程不斷循環地從todo隊列中取出數據並進行相應的操作。
在Binder驅動層,每個接收端進程都有一個todo隊列,用於保存發送端進程發送過來的binder請求,這類請求可以由接收端進程的任意一個空閒的binder線程處理。
接收端進程存在一個或多個binder線程,在每個binder線程裏都有一個todo隊列,也是用於保存發送端進程發送過來的binder請求,這類請求只能由當前binder線程來處理。
binder線程在空閒時進入可中斷的休眠狀態,當自己的todo隊列或所屬進程的todo隊列有新的請求到來時便會喚醒,如果是由所需進程喚醒的,那麼進程會讓其中一個線程處理響應的請求,其他線程再次進入休眠狀態。
4 Binder協議的演變
下面展示了Binder協議碼的演變過程,在Android9.0之前,當client向Binder驅動發送BC_TRANSACTION,Binder驅動喚醒Server進程時,會向client進程發送BR_TRANSACTION_COMPLETE,現在這一步被移到了 喚醒Client之後再做,減少了數據延遲。
Android9.0之前的協議碼流程:
Android9.0及之後的協議碼流程:
上面第5步的 BR_TRANSACTION_COMPLETE 被延遲到 第 10步 ,Android做了deferred_thread_work,延遲 TRANSACTION_COMPLETE,因此不會立即返回到用戶空間;這允許目標進程立即開始處理此事務,從而減少延遲。然後,當目標回覆(或出現錯誤)時,我們將返回TRANSACTION_COMPLETE。
5.通信流程-binder如何定向打擊
在 《Native-C\C++實例分析》 一節中,我們知道了Native層的服務註冊和獲取流程,我們以服務註冊爲示例來繼續分析
5.1 talkWithDriver()
當BpBinder 調用transact()準備進行事務處理,其中發送的語義是BC_TRANSACTION.
IPCThreadState waitForResponse()中啓動了while循環來和binder驅動進行通信,進行事務處理。通過talkWithDriver進行通信.
talkWithDriver()用來不停的和Binder驅動進行通信,ioctl()函數在傳遞BINDER_WRITE_READ語義時,既會使用“輸入buffer”,也會使用“輸出buffer”,所以IPCThreadState專門搞了兩個Parcel類型的成員變量:mIn和mOut。mOut中的內容發出去,發送後的回覆寫進mIn。
BINDER_WRITE_READ的命令發給Binder驅動後,ServiceManager有個循環不停的解析Binder驅動的BINDER_WRITE_READ信息,調用binder_parse()進行解析,我們前面addService()時,傳入的code爲ADD_SERVICE_TRANSACTION,在ServiceManager中解析後,對應的值爲SVC_MGR_ADD_SERVICE,最終把服務的name和handle(對象) 存入到svclist中,ServiceManager再通過binder_send_reply()把返回的數據發送出來,在talkWithDriver()中填入mIn中,waitForResponse()進行最終的語義處理,發送出去。
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {
return -EBADF;
}
binder_write_read bwr;
...
//如果仍在讀取輸入緩衝區中剩餘的數據,並且調用方已請求讀取下一個數據,則不希望寫入任何內容。
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data(); //把mOut的數據存入 write_buffer中,
// This is what we'll read.
if (doReceive && needRead) {
//接收數據緩衝區信息的填充。如果以後收到數據,就直接填在mIn中了
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
//沒有收到數據時,把read置空,binder只進行write處理
bwr.read_size = 0;
bwr.read_buffer = 0;
}
...
//當讀緩衝和寫緩衝都爲空,則直接返回
if ((bwr.write_size == 0) && (bwr.read_size == 0)) return NO_ERROR;
bwr.write_consumed = 0;
bwr.read_consumed = 0;
status_t err;
do {
...
#if defined(__ANDROID__)
//通過ioctl不停的讀寫操作,跟Binder Driver進行通信
if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
else
err = -errno;
#else
err = INVALID_OPERATION;
#endif
if (mProcess->mDriverFD <= 0) {
err = -EBADF;
}
...
} while (err == -EINTR);//當被中斷,則繼續執行
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else {
mOut.setDataSize(0);
processPostWriteDerefs();
}
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
...
return NO_ERROR;
}
return err;
}
第一次mOut裏面存的是BC_TRANSACTION及binder_transaction_data的值,mIn沒有值,talkWithDriver()中此時,bwr.write_size >0 ; bwr.read_size = 0,調用ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) 和binder驅動進行交互。在Binder驅動一節,我們瞭解到,當write_size>0, read_size=0時,會進入binder_thread_write()
流程轉換:binder_ioctl()->binder_ioctl_write_read()->binder_thread_write()
5.2 binder_thread_write
主要是從用戶空間拷貝binder_transaction_data的數據,並傳給binder_transaction()函數進行實際的傳輸。
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;
struct binder_context *context = proc->context; //原來Service進程的proc的context
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed; //buffer的
void __user *end = buffer + size;
while (ptr < end && thread->return_error.cmd == BR_OK) {
int ret;
//拷貝用戶空間的cmd命令,addService和getService時,爲 BC_TRANSACTION
if (get_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
trace_binder_command(cmd);
if (_IOC_NR(cmd) < ARRAY_SIZE(binder_stats.bc)) {
atomic_inc(&binder_stats.bc[_IOC_NR(cmd)]);
atomic_inc(&proc->stats.bc[_IOC_NR(cmd)]);
atomic_inc(&thread->stats.bc[_IOC_NR(cmd)]);
}
switch (cmd) {
...
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
//拷貝用戶空間的 binder_transaction_data ,存入tr
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
//binder事務處理
binder_transaction(proc, thread, &tr,
cmd == BC_REPLY, 0);
break;
}
...
default:
pr_err("%d:%d unknown command %d\n",
proc->pid, thread->pid, cmd);
return -EINVAL;
}
*consumed = ptr - buffer;
}
return 0;
}
5.3 binder_transaction
流程:
- 我們向ServiceManager註冊服務時時,Native中 new BpBinder(0,xxx),這裏的handle=0,在binder驅動中找到了目標binder_node--target_node, 指向ServiceManager; 如果此時的handle 不爲0,會根據handle 找到對應的binder_ref節點,最終找到binder_node;
- 根據target_node找到目標binder_proc--target_proc;
- 創建binder_transaction節點,並將其插入目標進程的todo列表;
- 嘗試喚醒目標進程。
處理Binder實體與Handle轉化的時候,有下面幾點注意的:
- 第一次註冊Binder實體的時候,是向別的進程註冊的,ServiceManager,或者SystemServer中的AMS服務
- Client請求服務的時候,一定是由Binder驅動爲Client分配binder_ref,如果本進程的線程請求,fp->type = BINDER_TYPE_BINDER,否則就是fp->type = BINDER_TYPE_HANDLE。
- Android中的Parcel裏面的對象一定是flat_binder_object
本地是從用戶空間進入binder_transaction(),因此這裏的proc、thread爲前面IPCThreadState 所在進程的內容。
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size)
{
int ret;
struct binder_transaction *t;
struct binder_work *tcomplete;
binder_size_t buffer_offset = 0;
binder_size_t off_start_offset, off_end_offset;
binder_size_t off_min;
binder_size_t sg_buf_offset, sg_buf_end_offset;
struct binder_proc *target_proc = NULL;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
struct binder_transaction *in_reply_to = NULL;
struct binder_transaction_log_entry *e;
uint32_t return_error = 0;
uint32_t return_error_param = 0;
uint32_t return_error_line = 0;
binder_size_t last_fixup_obj_off = 0;
binder_size_t last_fixup_min_off = 0;
struct binder_context *context = proc->context;
int t_debug_id = atomic_inc_return(&binder_last_id);
char *secctx = NULL;
u32 secctx_sz = 0;
...
//addService第一次進來時,reply爲空
if (reply) {
...
} else {
//addService和getService時,傳入的handle=0,表明想訪問ServiceManager
if (tr->target.handle) {
//先從tr->target.handle句柄值,找到對應的binder_ref節點,及binder_node節點
struct binder_ref *ref;
binder_proc_lock(proc);
ref = binder_get_ref_olocked(proc, tr->target.handle,
true);
if (ref) {
target_node = binder_get_node_refs_for_txn(
ref->node, &target_proc,
&return_error);
} else {
return_error = BR_FAILED_REPLY;
}
binder_proc_unlock(proc);
} else {
mutex_lock(&context->context_mgr_node_lock); //互斥鎖
// handle=0則找到servicemanager實體
target_node = context->binder_context_mgr_node;
if (target_node)
//獲取ServiceManager進程的target_proc
target_node = binder_get_node_refs_for_txn(
target_node, &target_proc,
&return_error);
else
return_error = BR_DEAD_REPLY;
mutex_unlock(&context->context_mgr_node_lock); //釋放鎖
if (target_node && target_proc == proc) {
binder_user_error("%d:%d got transaction to context manager from process owning it\n",
proc->pid, thread->pid);
return_error = BR_FAILED_REPLY;
return_error_param = -EINVAL;
return_error_line = __LINE__;
goto err_invalid_target_handle;
}
}
...
//檢查Client進程是否有權限向Server進程發送請求
if (security_binder_transaction(proc->tsk,
target_proc->tsk) < 0) {
return_error = BR_FAILED_REPLY;
return_error_param = -EPERM;
return_error_line = __LINE__;
goto err_invalid_target_handle;
}
binder_inner_proc_lock(proc);
//如果flag不是oneway,並且現場的transaction_stack存在內容
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
struct binder_transaction *tmp;
tmp = thread->transaction_stack;
if (tmp->to_thread != thread) {
spin_lock(&tmp->lock);
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);
spin_unlock(&tmp->lock);
binder_inner_proc_unlock(proc);
return_error = BR_FAILED_REPLY;
return_error_param = -EPROTO;
return_error_line = __LINE__;
goto err_bad_call_stack;
}
while (tmp) {
struct binder_thread *from;
spin_lock(&tmp->lock);
from = tmp->from;
if (from && from->proc == target_proc) {
atomic_inc(&from->tmp_ref);
target_thread = from;
spin_unlock(&tmp->lock);
break;
}
spin_unlock(&tmp->lock);
tmp = tmp->from_parent;
}
}
binder_inner_proc_unlock(proc);
}
if (target_thread)
e->to_thread = target_thread->pid;
e->to_proc = target_proc->pid;
// 創建binder_transaction節點
t = kzalloc(sizeof(*t), GFP_KERNEL);
if (t == NULL) {
return_error = BR_FAILED_REPLY;
return_error_param = -ENOMEM;
return_error_line = __LINE__;
goto err_alloc_t_failed;
}
binder_stats_created(BINDER_STAT_TRANSACTION);
spin_lock_init(&t->lock);
//創建一個binder_work節點
tcomplete = kzalloc(sizeof(*tcomplete), GFP_KERNEL);
...
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
t->debug_id = t_debug_id;
...
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread; // 返回proc的當前線程
else
t->from = NULL;
t->sender_euid = task_euid(proc->tsk); // 源線程用戶id
t->to_proc = target_proc; // 負責處理該事務的進程--ServiceManager
t->to_thread = target_thread; // 負責處理該事務的線程
// 將binder_transaction_data的code、flags域記入binder_transaction節點
t->code = tr->code; // ADD_SERVICE_TRANSACTION
t->flags = tr->flags; // TF_ACCEPT_FDS
// 源線程優先級設置
if (!(t->flags & TF_ONE_WAY) &&
binder_supported_policy(current->policy)) {
/* Inherit supported policies for synchronous transactions */
t->priority.sched_policy = current->policy;
t->priority.prio = current->normal_prio;
} else {
/* Otherwise, fall back to the default priority */
t->priority = target_proc->default_priority;
}
if (target_node && target_node->txn_security_ctx) {
u32 secid;
size_t added_size;
security_task_getsecid(proc->tsk, &secid);
ret = security_secid_to_secctx(secid, &secctx, &secctx_sz);
if (ret) {
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
goto err_get_secctx_failed;
}
added_size = ALIGN(secctx_sz, sizeof(u64));
extra_buffers_size += added_size;
if (extra_buffers_size < added_size) {
/* integer overflow of extra_buffers_size */
return_error = BR_FAILED_REPLY;
return_error_param = EINVAL;
return_error_line = __LINE__;
goto err_bad_extra_size;
}
}
//分配一塊buffer用於保存mOut中的data和mObjects 中的offset數據
t->buffer = binder_alloc_new_buf(&target_proc->alloc, tr->data_size,
tr->offsets_size, extra_buffers_size,
!reply && (t->flags & TF_ONE_WAY));
...
if (secctx) {
size_t buf_offset = ALIGN(tr->data_size, sizeof(void *)) +
ALIGN(tr->offsets_size, sizeof(void *)) +
ALIGN(extra_buffers_size, sizeof(void *)) -
ALIGN(secctx_sz, sizeof(u64));
t->security_ctx = (uintptr_t)t->buffer->user_data + buf_offset;
binder_alloc_copy_to_buffer(&target_proc->alloc,
t->buffer, buf_offset,
secctx, secctx_sz);
security_release_secctx(secctx, secctx_sz);
secctx = NULL;
}
t->buffer->debug_id = t->debug_id;
t->buffer->transaction = t; // 緩衝區正交給哪個事務使用
t->buffer->target_node = target_node; // 緩衝區正交給哪個Binder實體對象使用
trace_binder_transaction_alloc_buf(t->buffer);
//拷貝用戶空間的binder_transaction_data中ptr.buffer到內核,存入t->buffer
//從mOut中將data拷貝到buffer中
if (binder_alloc_copy_user_to_buffer(
&target_proc->alloc,
t->buffer, 0,
(const void __user *)
(uintptr_t)tr->data.ptr.buffer,
tr->data_size)) {
...
}
//拷貝用戶空間的binder_transaction_data中ptr.offsets到內核,存入t->buffer
//從mObjects 中將offset數據拷貝到buffer中data之後
if (binder_alloc_copy_user_to_buffer(
&target_proc->alloc,
t->buffer,
ALIGN(tr->data_size, sizeof(void *)),
(const void __user *)
(uintptr_t)tr->data.ptr.offsets,
tr->offsets_size)) {
...
}
...
off_start_offset = ALIGN(tr->data_size, sizeof(void *));
buffer_offset = off_start_offset;
off_end_offset = off_start_offset + tr->offsets_size;
sg_buf_offset = ALIGN(off_end_offset, sizeof(void *));
sg_buf_end_offset = sg_buf_offset + extra_buffers_size -
ALIGN(secctx_sz, sizeof(u64));
off_min = 0;
//從data中解析出所有的binder實體併爲其創建binder_node和binder_ref
for (buffer_offset = off_start_offset; buffer_offset < off_end_offset;
buffer_offset += sizeof(binder_size_t)) {
struct binder_object_header *hdr;
size_t object_size;
struct binder_object object;
binder_size_t object_offset;
//得到object_offset的大小
binder_alloc_copy_from_buffer(&target_proc->alloc,
&object_offset,
t->buffer,
buffer_offset,
sizeof(object_offset));
//從t->buffer中解析object,object_size=sizeof(flat_binder_object)
object_size = binder_get_object(target_proc, t->buffer,
object_offset, &object);
if (object_size == 0 || object_offset < off_min) {
...
return_error = BR_FAILED_REPLY;
return_error_param = -EINVAL;
return_error_line = __LINE__;
goto err_bad_offset;
}
hdr = &object.hdr;
off_min = object_offset + object_size;
//根據addService時,writeStrongBinder()寫的是實體Binder--BINDER_TYPE_BINDER,還是代理Binder--BINDER_TYPE_HANDLE類型來決定
switch (hdr->type) {
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
// 如果是binder實體
struct flat_binder_object *fp;
fp = to_flat_binder_object(hdr);
//在目標進程的binder_proc中創建對應的binder_ref紅黑樹節點
//BINDER_TYPE_BINDER 轉成BINDER_TYPE_HANDLE
//爲binder實體創建binder_node並添加到紅黑樹proc->nodes.rb_node中
ret = binder_translate_binder(fp, t, thread);
if (ret < 0) {
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
goto err_translate_failed;
}
binder_alloc_copy_to_buffer(&target_proc->alloc,
t->buffer, object_offset,
fp, sizeof(*fp));
} break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
// 如果是 Binder引用,拿到Binder的handle
struct flat_binder_object *fp;
fp = to_flat_binder_object(hdr);
//在遠端進程的binder_proc中找到一個binder_ref紅黑樹節點
//解析出引用的flat_binder_object,並在請求proc下創建服務的引用
ret = binder_translate_handle(fp, t, thread);
if (ret < 0) {
return_error = BR_FAILED_REPLY;
return_error_param = ret;
return_error_line = __LINE__;
goto err_translate_failed;
}
binder_alloc_copy_to_buffer(&target_proc->alloc,
t->buffer, object_offset,
fp, sizeof(*fp));
} break;
...
}
}
//transaction事務處理完成
tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;
t->work.type = BINDER_WORK_TRANSACTION; //設置事務類型
if (reply) {
...
} else if (!(t->flags & TF_ONE_WAY)) {
BUG_ON(t->buffer->async_transaction != 0);
binder_inner_proc_lock(proc);
//延遲 TRANSACTION_COMPLETE,因此我們不會立即返回到用戶空間;
//這允許目標進程立即開始處理此事務,從而減少延遲。
//然後,當目標回覆(或出現錯誤)時,我們將返回TRANSACTION_COMPLETE。
//把binder_transaction節點插入target_list(即目標todo隊列)
binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete);
t->need_reply = 1; //是雙向的所以需要回復
t->from_parent = thread->transaction_stack;
thread->transaction_stack = t; //將傳遞數據保存在請求線程的中,以後後續緩存釋放等
binder_inner_proc_unlock(proc);
//將數據放到目標進程的todo list中去,喚醒目標進程處理請求,這裏喚醒ServiceManager進行處理
if (!binder_proc_transaction(t, target_proc, target_thread)) {
binder_inner_proc_lock(proc);
binder_pop_transaction_ilocked(thread, t);
binder_inner_proc_unlock(proc);
goto err_dead_proc_or_thread;
}
} else {
...
}
if (target_thread)
binder_thread_dec_tmpref(target_thread);
binder_proc_dec_tmpref(target_proc);
if (target_node)
binder_dec_node_tmpref(target_node);
smp_wmb();
WRITE_ONCE(e->debug_id_done, t_debug_id);
return;
...
}
整理了一張binder_transaction的示意圖:
Client的數據傳來後,進行事務處理後,喚醒Server端,Server端進行數據讀取。假設我們這裏的Server端爲ServiceManager,在ServiceManager中有一個循環,不停的向Binder驅動發送讀寫的信息,讀到內容後調用binder_parse()進行解析。
binder_loop()中請求時,write_size=0,到binder驅動中會進入binder_thread_read
5.3 binder_thread_read
由於本次是通過Servicemanager進入了binder_thread_read(),因此,這裏的proc,thread都是ServiceManager的內容。
當目標進程ServiceManager被喚醒時,會接着執行自己的binder_thread_read(),嘗試解析並執行那些剛收來的工作。
流程:
- 利用wait_event_xxxx()讓自己掛起,等待下一次被喚醒;
- 喚醒後找到合適的待處理的工作節點,即binder_transaction節點;
- 把binder_transaction中的信息整理到一個binder_transaction_data中;
- 整理一個cmd整數值,具體數值或者爲BR_TRANSACTION,或者爲BR_REPLY;
- 將cmd數值和binder_transaction_data拷貝到用戶態;
- 如有必要,將得到的binder_transaction節點插入目標端線程的transaction_stack堆棧中。
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)
{
...
retry:
//優先考慮thread節點的todo鏈表中有沒有工作需要完成
wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
...
if (wait_for_proc_work) {
if (!(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
wait_event_interruptible(binder_user_error_wait,
binder_stop_on_user_error < 2);
}
binder_restore_priority(current, proc->default_priority);
}
if (non_block) {
if (!binder_has_work(thread, wait_for_proc_work))
ret = -EAGAIN;
} else {
//休眠在這裏, wait_for_proc_work爲false
ret = binder_wait_for_work(thread, wait_for_proc_work);
}
while (1) {
...
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
binder_inner_proc_unlock(proc);
t = container_of(w, struct binder_transaction, work);
} break;
}
...
//數據在內核中封裝成結構體binder_transaction傳輸,
//據返回到應用層之後要封裝成結構體 binder_transaction_data,所以這裏就是將數據從新封裝成binder_transaction_data返回給用戶空間
if (t->buffer->target_node) {
struct binder_node *target_node = t->buffer->target_node;
struct binder_priority node_prio;
trd->target.ptr = target_node->ptr;
// 用目標binder_node中記錄的cookie值給binder_transaction_data的cookie域賦值
// 這個值就是目標binder實體的地址
trd->cookie = target_node->cookie;
node_prio.sched_policy = target_node->sched_policy;
node_prio.prio = target_node->min_priority;
binder_transaction_priority(current, t, node_prio,
target_node->inherit_rt);
cmd = BR_TRANSACTION;
} else {
trd->target.ptr = 0;
trd->cookie = 0;
cmd = BR_REPLY;
}
trd->code = t->code;
trd->flags = t->flags;
trd->sender_euid = from_kuid(current_user_ns(), t->sender_euid);
t_from = binder_get_txn_from(t);
if (t_from) {
struct task_struct *sender = t_from->proc->tsk;
trd->sender_pid =
task_tgid_nr_ns(sender,
task_active_pid_ns(current));
} else {
trd->sender_pid = 0;
}
trd->data_size = t->buffer->data_size;
trd->offsets_size = t->buffer->offsets_size;
trd->data.ptr.buffer = (uintptr_t)t->buffer->user_data; //獲取buffer在用戶空間中的地址
trd->data.ptr.offsets = trd->data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
tr.secctx = t->security_ctx;
if (t->security_ctx) {
cmd = BR_TRANSACTION_SEC_CTX;
trsize = sizeof(tr);
}
//將cmd命令寫入用戶態,此時應該是BR_TRANSACTION
if (put_user(cmd, (uint32_t __user *)ptr)) {
if (t_from)
binder_thread_dec_tmpref(t_from);
binder_cleanup_transaction(t, "put_user failed",
BR_FAILED_REPLY);
return -EFAULT;
}
ptr += sizeof(uint32_t);
//將重新封裝後的數據拷貝到用戶空間
if (copy_to_user(ptr, &tr, trsize)) {
if (t_from)
binder_thread_dec_tmpref(t_from);
binder_cleanup_transaction(t, "copy_to_user failed",
BR_FAILED_REPLY);
return -EFAULT;
}
ptr += trsize;
}
if (t_from)
binder_thread_dec_tmpref(t_from);
t->buffer->allow_user_free = 1;
if (cmd != BR_REPLY && !(t->flags & TF_ONE_WAY)) {
//binder_transaction節點插入了目標線程的transaction_stack堆棧,而且是以to_thread域來連接堆棧中的其他節點
binder_inner_proc_lock(thread->proc);
t->to_parent = thread->transaction_stack;
t->to_thread = thread;
thread->transaction_stack = t;
binder_inner_proc_unlock(thread->proc);
} else {
// TF_ONE_WAY情況,此時會刪除binder_transaction節點
binder_free_transaction(t);
}
break;
}
如果沒有工作需要做,binder_thread_read()函數就進入睡眠或返回,否則binder_thread_read()函數會從todo隊列摘下了一個節點,並把節點裏的數據整理成一個binder_transaction_data結構,然後通過copy_to_user()把該結構傳到用戶態。
5.4 目標端事務處理
5.4.1.當ServiceManager爲Server端時
- 當Binder驅動把 BR_TRANSACTION 放入用戶空間後,其實此時ServiceManager爲Server端,那麼進入ServiceManager,binder_parse()解析BR_TRANSACTION 調用svcmgr_handler處理,code=SVC_MGR_ADD_SERVICE, 然後調用binder_send_reply() cmd_reply=BC_REPLY,cmd_free=BC_FREE_BUFFER, 發給Binder驅動,其中write_size > 0
- Binder驅動讀取ServiceManager傳來的數據,進入binder_transaction(),cmd=BC_REPLY,喚醒Client進程,把BR_TRANSACTION_COMPLETE分別發給ServiceManager 和Client進程
- IPCThreadState 收到BR_TRANSACTION_COMPLETE後,把數據寫入mIn,mOut移除內容,繼續調用talkWithDriver(),向Binder驅動發起BINDER_WRITE_READ請求,此時mOut無值,mIn有內容
- Binder驅動進入binder_thread_read(),根據ServiceManager發來的reply數據,發送BR_REPLY給client
- IPCThreadState waitForResponse()收到BR_REPLY後,釋放內存空間
5.4.2 當ProcessState中的服務爲Server端時
- 當Binder驅動把 BR_TRANSACTION 放入用戶空間後,waitForResponse()讀到CMD=BR_TRANSACTION,會調用executeCommand()進行處理,最終調用到BBinder的transact(),最終會調到onTransact()
- 在調用完transact()動作後,executeCommand()會判斷tr.flags有沒有攜帶TF_ONE_WAY標記,如果沒有攜帶,說明這次傳輸是需要回復的,於是調用sendReply()進行回覆
6.總結:
6.1 服務註冊
服務註冊過程(addService)核心功能:在服務所在進程創建binder_node,在servicemanager進程創建binder_ref。其中binder_ref的desc在同一個進程內是唯一的:
- 每個進程binder_proc所記錄的binder_ref的handle值是從1開始遞增的;
- 所有進程binder_proc所記錄的handle=0的binder_ref都指向service manager;
- 同一個服務的binder_node在不同進程的binder_ref的handle值可以不同;
在《Native-C\C++實例分析》一節,我們說到了 MediaPlayerService 向ServiceManager 進行服務註冊的流程,其中 MediaPlayerService是Client,ServiceManager是Server進程,通信流程圖如下所示:
過程分析:
- MediaPlayerService進程調用ioctl()向Binder驅動發送IPC數據,該過程可以理解成一個事務binder_transaction(記爲T1),執行當前操作的線程binder_thread(記爲thread1),則T1>from_parent=NULL,T1>from=thread1,thread1>transaction_stack=T1。
- Binder驅動收到該Binder請求,生成BR_TRANSACTION命令,選擇目標處理該請求的線程,即ServiceManager的binder線程(記爲thread2),則 T1>to_parent = NULL,T1>to_thread = thread2。並將整個binder_transaction數據(記爲T2)插入到目標線程的todo隊列;
- ServiceManager不斷地從todo鏈表檢查是否有節點,如果有的話就會摘取下來並解析、執行,ServiceManager的線程thread2收到T2後,調用服務註冊函數將服務”media.player”註冊到服務目錄中。當服務註冊完成後,生成IPC應答數據(BC_REPLY),T2>form_parent = T1,T2>from = thread2, thread2>transaction_stack = T2。
- Binder驅動收到該Binder應答請求,生成BR_REPLY命令,T2>to_parent = T1,T2>to_thread = thread1, thread1>transaction_stack = T2。在MediaPlayerService收到該命令後,知道服務註冊完成便可以正常使用。
整個過程中,BC_TRANSACTION和BR_TRANSACTION過程是一個完整的事務過程;BC_REPLY和BR_REPLY是一個完整的事務過程。到此,其他進行便可以獲取該服務,使用服務提供的方法。
6.2獲取服務
獲取服務(getService)過程,就是向servicemanager進程查詢指定服務,當執行binder_transaction()時,會區分請求服務所屬進程情況:
- 當請求服務的進程與服務屬於不同進程,則爲請求服務所在進程創建binder_ref對象,指向服務進程中的binder_node;最終readStrongBinder(),返回的是BpBinder對象
- 當請求服務的進程與服務屬於同一進程,則不再創建新對象,只是引用計數加1,並且修改type爲BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER。最終readStrongBinder(),返回的是BBinder對象的真實子類
至此,Binder的定向打擊,IPC數據流轉就介紹完成了,下一節我們一起來看看Binder在Framework層是如何工作的。
我的微信公衆號:IngresGe