前言
經(jīng)過前面三篇binder驅(qū)動的初始化闡述,我大致上稍微復(fù)習(xí)一邊linux內(nèi)核的基礎(chǔ)知識,也對binder的理解更加深刻。接下來我們來看看binder 的服務(wù)是怎么注冊到service_manager。
如果遇到問題請到:http://m.itdecent.cn/p/04e53fd86ca2
正文
很多文章一開始直接放出了binder在framework中類的之間的uml圖。我先不放出,因為一開始就直接上這種圖的確不怎么好理解。等我們探索完binder 服務(wù)初始化架構(gòu)之后再放出來。
ServiceManager 注冊service
還記得我之前寫的SystemServiceManager那一章節(jié)。我稍微的提及到了服務(wù)的注冊,但是我并沒有深入源碼。這一次我連同binder一起解析一次。
文件:/frameworks/base/services/java/com/android/server/SystemServer.java
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore, new PhoneWindowManager());
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
在這里為系統(tǒng)添加了windowManagerService的服務(wù)。這個服務(wù)Android應(yīng)該十分熟悉,用來控制窗口的服務(wù)。我之后會專門取出一節(jié)來詳細(xì)解析其中的代碼。
讓我們看看ServiceManager.addService方法。
文件:/frameworks/base/core/java/android/os/ServiceManager.java
public static void addService(String name, IBinder service, boolean allowIsolated,
int dumpPriority) {
try {
getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
看這個代表著遠(yuǎn)程的異常,我們猜測這里是不是通過某種手段進(jìn)行進(jìn)程間通信,把IBinder添加到遠(yuǎn)程端中。
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}
首先判斷IServiceManager 這個接口對象是否存在。不存在則從從遠(yuǎn)程端獲取這個實例。
妙啊妙,我在Binder第一節(jié)就探討過進(jìn)程間通信,無一不是通過兩端通過協(xié)議進(jìn)行交流,而在這里的IPC通行居然就能抽象著兩個遠(yuǎn)程端直接繼承一個接口,進(jìn)行通信。但是還記得嗎?進(jìn)程之間的地址都有自己保護(hù),我們不可能真的抽離一個接口讓兩個進(jìn)程之間通信,只是在這個過程中把進(jìn)程間通信透明化了。
這么棒的設(shè)計,讓我們看看Google工程師是怎么設(shè)計的。
文件:/frameworks/base/core/java/android/os/ServiceManagerNative.java
static public IServiceManager asInterface(IBinder obj)
{
if (obj == null) {
return null;
}
IServiceManager in =
(IServiceManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ServiceManagerProxy(obj);
}
ProcessState 初始化
此時通過傳入的IBinder查詢IServiceManager這個接口對象。那么這個IBinder對象是怎么生成的?看看下面這個方法:
public static final native IBinder getContextObject();
這個native方法究竟做了什么?
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
}
這里可以看到一個很關(guān)鍵的類。我們可以見到在Binder 服務(wù)中十分重要的一個類ProcessState。我們先看看這個類的self方法。
文件:/frameworks/native/libs/binder/ProcessState.cpp
sp<ProcessState> ProcessState::self()
{
Mutex::Autolock _l(gProcessMutex);
if (gProcess != NULL) {
return gProcess;
}
gProcess = new ProcessState("/dev/binder");
return gProcess;
}
從這里我們可以的得知這是一個單例,如果在這個進(jìn)程的ProcessState沒有實例化則會實例化ProcessState。
接著我們看看這個構(gòu)造函數(shù)。
ProcessState::ProcessState(const char *driver)
: mDriverName(String8(driver))
//注意這里
, mDriverFD(open_driver(driver))
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
, mStarvationStartTimeMs(0)
, mManagesContexts(false)
, mBinderContextCheckFunc(NULL)
, mBinderContextUserData(NULL)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
{
if (mDriverFD >= 0) {
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
close(mDriverFD);
mDriverFD = -1;
mDriverName.clear();
}
}
LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating.");
}
這里的操作和service_manager十分相似,先調(diào)用open打開binder驅(qū)動,再進(jìn)行映射。
static int open_driver(const char *driver)
{
int fd = open(driver, O_RDWR | O_CLOEXEC);
if (fd >= 0) {
int vers = 0;
status_t result = ioctl(fd, BINDER_VERSION, &vers);
if (result == -1) {
ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno));
close(fd);
fd = -1;
}
if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) {
ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! ioctl() return value: %d",
vers, BINDER_CURRENT_PROTOCOL_VERSION, result);
close(fd);
fd = -1;
}
size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;
result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);
if (result == -1) {
ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno));
}
} else {
ALOGW("Opening '%s' failed: %s\n", driver, strerror(errno));
}
return fd;
}
這里的初始化和service_manager的初始化極其相似,首先寫入Binder_Version,接著寫入BINDER_SET_MAX_THREADS 設(shè)置最大線程數(shù)。
經(jīng)過ProcessState構(gòu)造函數(shù)之后,我們也把當(dāng)前這個類已經(jīng)映射到了binder驅(qū)動中。實際上這個類我在第一篇啟動系統(tǒng)中已經(jīng)驚鴻一現(xiàn),系統(tǒng)啟動的時候,zygote進(jìn)程孵化的SystemServer進(jìn)程也會映射到binder驅(qū)動中。
細(xì)節(jié)不繼續(xù)贅述了,我們繼續(xù)看看getContextObject。
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
return getStrongProxyForHandle(0);
}
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
handle_entry* e = lookupHandleLocked(handle);
if (e != NULL) {
// We need to create a new BpBinder if there isn't currently one, OR we
// are unable to acquire a weak reference on this current one. See comment
// in getWeakProxyForHandle() for more info about this.
IBinder* b = e->binder;
if (b == NULL || !e->refs->attemptIncWeak(this)) {
if (handle == 0) {
Parcel data;
status_t status = IPCThreadState::self()->transact(
0, IBinder::PING_TRANSACTION, data, NULL, 0);
if (status == DEAD_OBJECT)
return NULL;
}
b = BpBinder::create(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
// This little bit of nastyness is to allow us to add a primary
// reference to the remote proxy when this team doesn't have one
// but another team is sending the handle to us.
result.force_set(b);
e->refs->decWeak(this);
}
}
return result;
}
lookupHandleLocked 就算是第一次來查找,控制引用的mHandleToObject的vector對象還是會自己添加一份進(jìn)去。所以一般情況下不用擔(dān)心找不到引用,至于為什么說是引用,放到之后再說。
因此,此時我們會走到b==null的分支當(dāng)中,調(diào)用了BpBinder::create(handle)的方法生成一個遠(yuǎn)程端的binder代理對象。
IPCThreadState 初始化
文件:/frameworks/native/libs/binder/IPCThreadState.cpp
此時handle為0,會走到下面的方法:
status_t status = IPCThreadState::self()->transact(
0, IBinder::PING_TRANSACTION, data, NULL, 0);
此時出現(xiàn)了另一個十分關(guān)鍵的類IPCThreadState??吹竭@個調(diào)用,我們也可以知道這是一個單例設(shè)計。
IPCThreadState* IPCThreadState::self()
{
if (gHaveTLS) {
restart:
const pthread_key_t k = gTLS;
IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
if (st) return st;
return new IPCThreadState;
}
if (gShutdown) {
ALOGW("Calling IPCThreadState::self() during shutdown is dangerous, expect a crash.\n");
return NULL;
}
pthread_mutex_lock(&gTLSMutex);
if (!gHaveTLS) {
int key_create_value = pthread_key_create(&gTLS, threadDestructor);
if (key_create_value != 0) {
pthread_mutex_unlock(&gTLSMutex);
ALOGW("IPCThreadState::self() unable to create TLS key, expect a crash: %s\n",
strerror(key_create_value));
return NULL;
}
gHaveTLS = true;
}
pthread_mutex_unlock(&gTLSMutex);
goto restart;
}
從這這里可以清楚實際上,這個IPCThreadState的單例是做到線程單例。把IPCThreadState實例存儲到線程的TLS中。這個做法實際上和Java的ThreadLocal思想相似,如果不熟悉pthread相關(guān)的操作,不妨可以代入思考。
IPCThreadState 和binder驅(qū)動交互
我們接下來看看transact方法。
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags)
{
status_t err;
flags |= TF_ACCEPT_FDS;
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
if (err != NO_ERROR) {
if (reply) reply->setError(err);
return (mLastError = err);
}
if ((flags & TF_ONE_WAY) == 0) {
#if 0
if (code == 4) { // relayout
...
} else {
...
}
#endif
if (reply) {
err = waitForResponse(reply);
} else {
Parcel fakeReply;
err = waitForResponse(&fakeReply);
}
#if 0
if (code == 4) { // relayout
...
} else {
...
}
#endif
...
} else {
err = waitForResponse(NULL, NULL);
}
return err;
}
這就是整個Binder server和client在framework層中向binder驅(qū)動傳遞數(shù)據(jù)最為核心的的機制。
這里我們分為兩段來討論。注意此時flags & TF_ONE_WAY (0x10)將用來控制整個流程是否是等待遠(yuǎn)程端的回應(yīng)標(biāo)志位。此時flag為而0,因此會等待。當(dāng)flag為1的時候則不等待。在這里的表現(xiàn)就是aidl中oneway開頭的方法。
writeTransactionData
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
{
binder_transaction_data tr;
//是這遠(yuǎn)程端的目標(biāo)
tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
tr.target.handle = handle;
tr.code = code;
tr.flags = binderFlags;
tr.cookie = 0;
tr.sender_pid = 0;
tr.sender_euid = 0;
//設(shè)置通信的數(shù)據(jù)
const status_t err = data.errorCheck();
if (err == NO_ERROR) {
tr.data_size = data.ipcDataSize();
tr.data.ptr.buffer = data.ipcData();
tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
tr.data.ptr.offsets = data.ipcObjects();
} else if (statusBuffer) {
tr.flags |= TF_STATUS_CODE;
*statusBuffer = err;
tr.data_size = sizeof(status_t);
tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer);
tr.offsets_size = 0;
tr.data.ptr.offsets = 0;
} else {
return (mLastError = err);
}
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
在這里我們就能看到之前為什么binder_buffer中offset代表著元數(shù)據(jù)的偏移量以及data_size代表著有效數(shù)據(jù)的大小。其實是在Parcel中已經(jīng)做好了管理了。在Parcel中mData對象代表著普通數(shù)據(jù),而mObjects是一個偏移數(shù)組,它保存了在數(shù)據(jù)緩沖區(qū)mData中所有Binder對象的位置。Binder會通過這個對象來尋找Binder對象。
此時需要進(jìn)去的數(shù)據(jù)是:
- cmd:BC_TRANSACTION
- tr : binder_transaction_data
- code: IBinder::PING_TRANSACTION
BC_TRANSACTION 代表此時有個事務(wù)處理,binder_transaction_data代表事務(wù)數(shù)據(jù)。
這里為mOut Parcel設(shè)置了第一段數(shù)據(jù)。
waitForResponse(reply)
我們接著看循環(huán)第二段。waitForResponse 這里把這個循環(huán)拆成兩段。
status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
uint32_t cmd;
int32_t err;
while (1) {
if ((err=talkWithDriver()) < NO_ERROR) break;
err = mIn.errorCheck();
if (err < NO_ERROR) break;
if (mIn.dataAvail() == 0) continue;
....
}
talkWithDriver
在這個循環(huán)里的talkWithDriver函數(shù),看到名字就知道是和binder驅(qū)動進(jìn)行溝通。
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
if (mProcess->mDriverFD <= 0) {
return -EBADF;
}
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
// We don't want to write anything if we are still reading
// from data left in the input buffer and the caller
// has requested to read the next data.
const size_t outAvail = (!doReceive || needRead) ? mOut.dataSize() : 0;
bwr.write_size = outAvail;
bwr.write_buffer = (uintptr_t)mOut.data();
// This is what we'll read.
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (uintptr_t)mIn.data();
} else {
bwr.read_size = 0;
bwr.read_buffer = 0;
}
// Return immediately if there is nothing to do.
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__)
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 err;
}
在這里我們又看到了binder_write_read這個結(jié)構(gòu)體。
這個結(jié)構(gòu)體的讀取部分設(shè)置了Parcel全局對象mIn,寫入部分設(shè)置Parcel全局對象mOut。當(dāng)童星結(jié)束之后將會清空全局的Parcel變量mOut以及mIn中的寫入/讀取標(biāo)記位置。
binder_transaction
文件:/drivers/staging/android/binder.c
binder_transaction 雖然之前已經(jīng)提過,但是這個方法過于長,這里我們繼續(xù)只關(guān)注我們需要的。
此時我們通過ioctl通信到Binder驅(qū)動中。我們這邊直接進(jìn)入binder驅(qū)動的對應(yīng)方法看看。
方法 binder_thread_write.
case BC_TRANSACTION:
case BC_REPLY: {
struct binder_transaction_data tr;
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
binder_transaction(proc, thread, &tr, cmd == BC_REPLY);
break;
此時cmd是BC_TRANSACTION而不是BC_REPLY
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;
...
//1.通過handle查找對應(yīng)的binder引用
if (reply) {
...
} else {
if (tr->target.handle) {
...
} else {
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_proc = target_node->proc;
...
//尋找事務(wù)依賴
if (!(tr->flags & TF_ONE_WAY) && thread->transaction_stack) {
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;
}
while (tmp) {
if (tmp->from && tmp->from->proc == target_proc)
target_thread = tmp->from;
tmp = tmp->from_parent;
}
}
}
//2.設(shè)置當(dāng)前的目標(biāo)等待隊列以及目標(biāo)todo 隊列
if (target_thread) {
e->to_thread = target_thread->pid;
target_list = &target_thread->todo;
target_wait = &target_thread->wait;
} else {
target_list = &target_proc->todo;
target_wait = &target_proc->wait;
}
e->to_proc = target_proc->pid;
//申請binder_transaction
/* TODO: reuse incoming transaction for reply */
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);
...
if (!reply && !(tr->flags & TF_ONE_WAY))
t->from = thread;
else
t->from = NULL;
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);
//為事務(wù)分配一個binder_buffer
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)
binder_inc_node(target_node, 1, 0, NULL);
offp = (binder_size_t *)(t->buffer->data +
ALIGN(tr->data_size, sizeof(void *)));
//拷貝
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
...
}
if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
...
}
...
這里分為以下幾步來說明:
1.通過handle查找對應(yīng)的binder引用。在這里我們的cmd是BC_TRANSCATION。因此會直接拿binder_context_mgr_node。而這個就是我在上一篇說過的service_manager的binder實體。此時我們也相當(dāng)于拿到了service_manager進(jìn)程映射的地址了。
2.找到我們需要的binder實體之后,我們看看該進(jìn)程的binder_thread對應(yīng)的事務(wù)棧transaction_stack。我們需要通過這個棧去尋找,當(dāng)前分發(fā)下來的事務(wù)是否有依賴其他事務(wù)。
while (tmp) {
if (tmp->from && tmp->from->proc == target_proc)
target_thread = tmp->from;
tmp = tmp->from_parent;
}
因此,binder驅(qū)動會通過一個循環(huán),直接從棧頂開始查找依賴的事務(wù)的線程(binder_transaction->from)。會去查找依賴的事務(wù)所對應(yīng)的binder_proc是不是當(dāng)前的目標(biāo)進(jìn)程。是則說明有線程也在使用這個目標(biāo)進(jìn)程,我們需要把把這個binder_thread取出來,先把依賴的事務(wù)給處理了。
3.判斷此時有沒有找到目標(biāo)的binder_thread。找到則設(shè)置目標(biāo)binder_thread的todo list和等待隊列。不然則是binder_proc的todo list和等待隊列。
4.申請內(nèi)存 t 和 tcomplete 事務(wù)對象binder_transaction,工作項 binder_work。并且把相關(guān)的數(shù)據(jù)如目標(biāo)進(jìn)程,目標(biāo)binder_thread,code,flag等寫入binder_transaction。并且通過binder_alloc_buf 為這一次事務(wù)在目標(biāo)進(jìn)程中申請一段binder_buffer(內(nèi)核緩沖區(qū)).
5.通過copy_from_user把binder中普通數(shù)據(jù)拷貝到binder_transaction_data的data中,元數(shù)據(jù)拷貝到offsets中。
為了弄清楚這一點,我們看看binder_transaction_data數(shù)據(jù)結(jié)構(gòu)
struct binder_transaction_data {
union {
//引用
__u32 handle;
//對應(yīng)用戶空間binder_transaction_data 的ptr
binder_uintptr_t ptr;
} target;
//對應(yīng)用戶空間binder_transaction_data的cookie
binder_uintptr_t cookie;
//對應(yīng)用戶空間binder_transaction_data的code
__u32 code;
//對應(yīng)用戶空間binder_transaction_data的flags
__u32 flags;
pid_t sender_pid;
uid_t sender_euid;
//數(shù)據(jù)大小
binder_size_t data_size;
//偏移量大小
binder_size_t offsets_size;
//元數(shù)據(jù)
union {
//binder_buffer
struct {
binder_uintptr_t buffer;
//binder數(shù)組
binder_uintptr_t offsets;
} ptr;
//數(shù)據(jù)
__u8 buf[8];
} data;
};
此時拷貝tr->data.ptr.buffer到了binder_transaction的內(nèi)核緩沖區(qū)的數(shù)據(jù)區(qū)。tr->data.ptr.offsets拷貝到offp 地址中。而這個offp地址是這么計算的
offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
這么計算的結(jié)果是把binder_transaction的binder_buffer的末尾地址計算出來,相當(dāng)于定位了mData中Binder偏移數(shù)組的起始位置。
off_end = (void *)offp + tr->offsets_size;
off_min = 0;
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
...
//獲取flat_binder_object 結(jié)構(gòu)體
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
off_min = *offp + sizeof(struct flat_binder_object);
switch (fp->type) {
//處理flat_binder_object 中type為 BINDER_TYPE_BINDER BINDER_TYPE_WEAK_BINDER
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER: {
struct binder_ref *ref;
struct binder_node *node = binder_get_node(proc, fp->binder);
//生成一個新的binder對象
if (node == NULL) {
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);
}
//cookie中保存著本地binder對象
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;
}
//為binder對象添加引用
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;
}
//判斷是不是弱引用的binder type
if (fp->type == BINDER_TYPE_BINDER)
fp->type = BINDER_TYPE_HANDLE;
else
fp->type = BINDER_TYPE_WEAK_HANDLE;
fp->handle = ref->desc;
//找到對應(yīng)的binder 引用并且增加
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;
//處理flat_binder_object 中type為 BINDER_TYPE_HANDLE BINDER_TYPE_WEAK_HANDLE
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE: {
//獲取binder 引用
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;
}
//如果引用對象剛好是目標(biāo)進(jìn)程,轉(zhuǎn)化為Binder 本地
if (ref->node->proc == target_proc) {
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 {
struct binder_ref *new_ref;
//不是則獲取目標(biāo)進(jìn)程中的引用
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;
binder_inc_ref(new_ref, fp->type == BINDER_TYPE_HANDLE, NULL);
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: {
...
} 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);
binder_pop_transaction(target_thread, in_reply_to);
} else if (!(t->flags & TF_ONE_WAY)) {
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ài),添加到目標(biāo)進(jìn)程的工作隊列
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);
if (target_wait)
wake_up_interruptible(target_wait);
return;
...
}
增加引用計數(shù)
在這個循環(huán)中有一個關(guān)鍵的結(jié)構(gòu)體,flat_binder_object
struct flat_binder_object {
/* 8 bytes for large_flat_header. */
__u32 type;
__u32 flags;
/* 8 bytes of data. */
union {
binder_uintptr_t binder; /* local object */
__u32 handle; /* remote object */
};
/* extra data associated with local object */
binder_uintptr_t cookie;
};
而這個flat_binder_object 在Parcel中也有一個一模一樣的數(shù)據(jù)結(jié)構(gòu),這個結(jié)構(gòu)體實際上就是Binder偏移數(shù)組中的數(shù)據(jù)。很有意思,當(dāng)我們嘗試著寫內(nèi)核和用戶空間交互數(shù)據(jù)的時候,我們往往可以在兩側(cè)構(gòu)造一樣的的數(shù)據(jù)結(jié)構(gòu)再進(jìn)行copy。
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
但是這是怎么回事?為什么這樣子能夠強轉(zhuǎn)flat_binder_object對象。這個東西我研究了老半天?
文件:/frameworks/native/libs/binder/Parcel.cpp
status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;
const bool enoughObjects = mObjectsSize < mObjectsCapacity;
if (enoughData && enoughObjects) {
restart_write:
*reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
原來如此,這里提前說一下,Binder經(jīng)常借助Parcel傳遞消息,而Binder的傳遞恰好就是調(diào)用這個方法。而在framework層換算flat_binder_object對象,也是通過普通數(shù)據(jù)+偏移量轉(zhuǎn)型為flat_binder_object并且賦值。實際上這里的內(nèi)核算法只是和用戶空間的算法保持一致。
在這個循環(huán)中,主要處理了三種flat_binder_object 對應(yīng)的5種type。首先看看flat_binder_object 是怎么獲得的。這些type
1.BINDER_TYPE_BINDER/BINDER_TYPE_WEAK_BINDER
該標(biāo)志位是在Parcel中設(shè)置,判斷如果是本地binder對象則,加入此標(biāo)志位。此時binder驅(qū)動接受到之后,說明此時是本地端發(fā)起的交互,因此此時需要獲取目標(biāo)進(jìn)程的引用,并且增加引用計數(shù),同時把狀態(tài)轉(zhuǎn)化為BINDER_TYPE_HANDLE / BINDER_TYPE_WEAK_HANDLE。2.BINDER_TYPE_HANDLE / BINDER_TYPE_WEAK_HANDLE
該標(biāo)志位也是在Parcel中設(shè)置。判斷到此時不是binder本地而是遠(yuǎn)程端,則會加入此標(biāo)記。當(dāng)binder驅(qū)動接受到,說明此時是遠(yuǎn)程端發(fā)起的交互。需要通過handle獲取本地端的引用。當(dāng)handle獲取到的引用剛好事目標(biāo)進(jìn)程,則把標(biāo)志位設(shè)置為BINDER_TYPE_BINDER/BINDER_TYPE_WEAK_BINDER 。不是則直接從目標(biāo)進(jìn)程查找引用。3.BINDER_TYPE_FD
該標(biāo)志控制著一個文件描述符,并且讀寫進(jìn)數(shù)據(jù),做跨進(jìn)程的數(shù)據(jù)透傳。
細(xì)節(jié):binder_ref的處理
static struct binder_ref *binder_get_ref_for_node(struct binder_proc *proc,
struct binder_node *node)
{
struct rb_node *n;
struct rb_node **p = &proc->refs_by_node.rb_node;
struct rb_node *parent = NULL;
struct binder_ref *ref, *new_ref;
//從binder_proc中尋找合適ref
while (*p) {
parent = *p;
ref = rb_entry(parent, struct binder_ref, rb_node_node);
if (node < ref->node)
p = &(*p)->rb_left;
else if (node > ref->node)
p = &(*p)->rb_right;
else
return ref;
}
//沒找到則新建一個
new_ref = kzalloc(sizeof(*ref), GFP_KERNEL);
if (new_ref == NULL)
return NULL;
binder_stats_created(BINDER_STAT_REF);
new_ref->debug_id = ++binder_last_id;
new_ref->proc = proc;
new_ref->node = node;
//把binder實體添加到binder_proc的rb_node_node
rb_link_node(&new_ref->rb_node_node, parent, p);
rb_insert_color(&new_ref->rb_node_node, &proc->refs_by_node);
//處理binder_proc中所有的引用描述(句柄)
new_ref->desc = (node == binder_context_mgr_node) ? 0 : 1;
for (n = rb_first(&proc->refs_by_desc); n != NULL; n = rb_next(n)) {
ref = rb_entry(n, struct binder_ref, rb_node_desc);
if (ref->desc > new_ref->desc)
break;
new_ref->desc = ref->desc + 1;
}
//把引用添加到rb_node_desc
p = &proc->refs_by_desc.rb_node;
while (*p) {
parent = *p;
ref = rb_entry(parent, struct binder_ref, rb_node_desc);
if (new_ref->desc < ref->desc)
p = &(*p)->rb_left;
else if (new_ref->desc > ref->desc)
p = &(*p)->rb_right;
else
BUG();
}
rb_link_node(&new_ref->rb_node_desc, parent, p);
rb_insert_color(&new_ref->rb_node_desc, &proc->refs_by_desc);
return new_ref;
}
做的事情分為以下幾個步驟:
- 1.從refs_by_node紅黑樹查找查找,當(dāng)前的node是否有binder的引用,因為此時要做的行為是要把binder往對端的用戶空間傳遞。此時不可能做到把binder實體交給另外的進(jìn)程,因此會傳遞一個引用。
- 2.沒有找到binder引用說明此時這個binder是新加入的,需要需要把當(dāng)前生成的新的binder_node(binder實體)添加到binder_proc的refs_by_node。
3.把當(dāng)前的binder_ref的描述設(shè)置為1(如果當(dāng)前的binder的實體是service_manager則設(shè)置為0)。循環(huán)后面的引用描述(句柄),當(dāng)遇到引用(句柄)比當(dāng)前新的大則跳出,否則則新的引用是老引用+1.實際上就是就是調(diào)整整個算法。因為在整個過程是獲取refs_by_desc第二個管理binder引用的紅黑樹(一個以引用描述為key的)的后繼,比較句柄大小,找到當(dāng)前最大的那個往后加1。這個break的存在就是為了對付加入一直加的普通的binder對象,突然添加了service_manager的對象,此時我們必須要保證這個binder引用描述為0才設(shè)計。
4.把引用以desc 為key添加到refs_by_desc中。這個紅黑樹更加重要,因為我們在上層的handle就是對應(yīng)這個描述符,通過這個描述符去查找對應(yīng)的引用,從而傳輸binder對象引用。
對于binder來說,是不會直接使用binder實體類,因為binder實體里包含這個其他進(jìn)程的地址,會導(dǎo)致無法訪問的情況。因此,需要一層binder引用包裹。
小總結(jié)
為什么要做這一步?對于智能指針來講,每一次減少意味這個這個binder引用可能有被析構(gòu)的可能,反過來當(dāng)我們不需要這個引用的時候,我們也可以把這個計數(shù)減少的步驟向后挪移也沒問題。所以對于這種情況來說,我們在離開本次作用域之前需,把需要傳遞的binder引用對象新增加一個引用計數(shù),來防止其被析構(gòu)。
更為關(guān)鍵的的是,在Parcel讀取的時候,會通過這個type,BINDER_TYPE_BINDER轉(zhuǎn)型為BBinder(代表本地端對象),還是BINDER_TYPE_HANDLE轉(zhuǎn)型為BpBinder(代表遠(yuǎn)程端對象)。
喚醒目標(biāo)進(jìn)程
if (reply) {
BUG_ON(t->buffer->async_transaction != 0);
binder_pop_transaction(target_thread, in_reply_to);
} else if (!(t->flags & TF_ONE_WAY)) {
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ài),添加到目標(biāo)進(jìn)程的工作隊列
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);
if (target_wait)
wake_up_interruptible(target_wait);
return;
第一段是判斷當(dāng)前是否是BC_REPLY,這里先不去研究。當(dāng)時t->flags & TF_ONE_WAY為0的時候,則設(shè)置當(dāng)前的binder_transaction的need_reply為1,設(shè)置依賴事務(wù)為當(dāng)前的binder_thread的transaction_stack。不然則是選擇todo list為目標(biāo)binder實體的異步隊列。
最后把工作加入到目標(biāo)工作(todo)隊列。最后調(diào)用wake_up_interruptible喚醒目標(biāo)進(jìn)程。tcomplete添加到當(dāng)前線程的todo中。
回到當(dāng)前場景
當(dāng)前場景,我們發(fā)送了的mOut中實際上沒有設(shè)置任何的data數(shù)據(jù)。因此在整個binder_transaction(事務(wù)交易)中,循環(huán)offsets的那部分直接跳過,將會直接尋找目標(biāo)對象,設(shè)置好目標(biāo)binder_thread以及工作隊列還有等待隊列。事務(wù)項的type設(shè)置為BINDER_WORK_TRANSACTION。tcomplete設(shè)置BINDER_WORK_TRANSACTION_COMPLETE。
還記得,此時我們喚醒的target_wait是誰嗎?就是指service_manager。此時service_manager在binder_thread_read方法被阻塞了。我們看看binder_thread_read方法吧。喚醒的同時,ServiceManager調(diào)用者本身的進(jìn)程也在繼續(xù)進(jìn)行。我們在這里記住,我們繼續(xù)回去看看IPCThreadState的waitForResponse方法。實際上這是兩個進(jìn)程同時進(jìn)行的事務(wù)工作。
ServiceManager調(diào)用者進(jìn)程的talkWithDriver的情景分析
還記得嗎。此時talkwithDriver的doneed默認(rèn)為true,因此會走binder_thread_read的阻塞。等待service_manager的binder_thread_read的返回。
service_manager的binder_thread_read
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
struct binder_work *w;
struct binder_transaction *t = NULL;
// 獲取todo列表的首部
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) {
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;
//判斷當(dāng)前工作項的type
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
t = container_of(w, struct binder_transaction, work);
} break;
...
}
if (!t)
continue;
//如果對應(yīng)的目標(biāo)node不為空
if (t->buffer->target_node) {
struct binder_node *target_node = t->buffer->target_node;
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 {
//為空,則設(shè)置cmd為BR_REPLY
tr.target.ptr = 0;
tr.cookie = 0;
cmd = BR_REPLY;
}
tr.code = t->code;
tr.flags = t->flags;
tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
//設(shè)置pid
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;
}
//設(shè)置參數(shù)
tr.data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size;
tr.data.ptr.buffer = (binder_uintptr_t)(
(uintptr_t)t->buffer->data +
proc->user_buffer_offset);
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
//拷貝數(shù)據(jù)到用戶空間
if (put_user(cmd, (uint32_t __user *)ptr))
return -EFAULT;
ptr += sizeof(uint32_t);
if (copy_to_user(ptr, &tr, sizeof(tr)))
return -EFAULT;
ptr += sizeof(tr);
trace_binder_transaction_received(t);
binder_stat_br(proc, thread, cmd);
...
list_del(&t->work.entry);
t->buffer->allow_user_free = 1;
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
t->to_parent = thread->transaction_stack;
t->to_thread = thread;
thread->transaction_stack = t;
} else {
t->buffer->transaction = NULL;
kfree(t);
binder_stats_deleted(BINDER_STAT_TRANSACTION);
}
break;
}
當(dāng)ServiceManager對應(yīng)的進(jìn)程喚醒當(dāng)前service_manager的時候,函數(shù)將會繼續(xù)往下走,此時就會進(jìn)入到這個循環(huán)。
1.可以看到的是,此時會從service_manager進(jìn)程中獲取出todo列表的頭部,優(yōu)先獲取binder_thread的todo雙向隊列。這樣就能拿到我們就能拿到從ServiceManager加入進(jìn)去的工作項。
如果binder_thread沒有則去binder_proc中獲取。如果都是空,但是判斷后面的data數(shù)據(jù)區(qū)域還有位置(地址差為4和前文為binder_buffer劃分的時候呼應(yīng)),則說明此時沒有數(shù)據(jù),就沒有必要去讀取從新進(jìn)入阻塞。2.此時再去解析命令,轉(zhuǎn)化命令從BC的驅(qū)動命令轉(zhuǎn)化RR的framework命令。此時剛好是BINDER_WORK_TRANSACTION,我們就取出里面的binder_work的binder_transaction 事務(wù)項t。注意,當(dāng)t為空的時候會while向后獲取命令,執(zhí)行命令,直到達(dá)到了底部。底部的判斷就是binder_buffer申請時候的上一個和下一個的binder_buffer差值為4.
3.如果此時該事務(wù)項中的目標(biāo)進(jìn)程不為空,則把命令轉(zhuǎn)化為BR_TRANSACTION,并且把里面的數(shù)據(jù)賦值到binder_transaction_data中。
4.光這些不夠,我們還需要把數(shù)據(jù)從內(nèi)核空間往用戶空間拷貝。也就是使用put_user和copy_to_user方法。由于此時正還是BR_TRANSACTION,所以把目標(biāo)線程和目標(biāo)對象設(shè)置為當(dāng)前線程。以此來宣告此時此時處理在事務(wù)的是當(dāng)前進(jìn)程。
回到service_manager的binder_parse
binder_thread_read 方法返回到用戶空間。此時進(jìn)入的binder_parse是用于解析從驅(qū)動傳遞上來的BR命令的解析。
文件:/frameworks/native/cmds/servicemanager/binder.c
int binder_parse(struct binder_state *bs, struct binder_io *bio,
uintptr_t ptr, size_t size, binder_handler func)
{
int r = 1;
uintptr_t end = ptr + (uintptr_t) size;
while (ptr < end) {
uint32_t cmd = *(uint32_t *) ptr;
ptr += sizeof(uint32_t);
#if TRACE
fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
switch(cmd) {
case BR_NOOP:
break;
case BR_TRANSACTION_COMPLETE:
break;
case BR_INCREFS:
case BR_ACQUIRE:
case BR_RELEASE:
case BR_DECREFS:
...
case BR_TRANSACTION: {
struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
...
if (func) {
unsigned rdata[256/4];
struct binder_io msg;
struct binder_io reply;
int res;
bio_init(&reply, rdata, sizeof(rdata), 4);
bio_init_from_txn(&msg, txn);
res = func(bs, txn, &msg, &reply);
if (txn->flags & TF_ONE_WAY) {
binder_free_buffer(bs, txn->data.ptr.buffer);
} else {
binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
}
}
ptr += sizeof(*txn);
break;
}
case BR_REPLY: {
...
case BR_DEAD_BINDER: {
...
case BR_FAILED_REPLY:
...
case BR_DEAD_REPLY:
...
default:
ALOGE("parse: OOPS %d\n", cmd);
return -1;
}
}
return r;
}
此時傳到service_manager已經(jīng)轉(zhuǎn)化為BR_TRANSACTION命令。在這個分支中出現(xiàn)了全新的一種結(jié)構(gòu)體
struct binder_io
{
char *data; /* pointer to read/write from */
binder_size_t *offs; /* array of offsets */
size_t data_avail; /* bytes available in data buffer */
size_t offs_avail; /* entries available in offsets array */
char *data0; /* start of data buffer */
binder_size_t *offs0; /* start of offsets buffer */
uint32_t flags;
uint32_t unused;
};
這個結(jié)構(gòu)體從名字上可以看得出實際上指代的是一個binder_buffer在ioctl交互時候調(diào)用的對象.分為兩個區(qū)域,四個地址。兩個區(qū)域分別是binder_buffer的元數(shù)據(jù)以及有效數(shù)據(jù)去。并且0為結(jié)尾的字段就是指的是元數(shù)據(jù)以及有效數(shù)據(jù)區(qū)的起始地址。得知這些之后,我們嘗試閱讀其源碼。因此在bio_init中初始化reply,bio_init_from_txn 把從內(nèi)核中傳遞上來的數(shù)據(jù)拷貝的到reply中。
而func是從binder_loop傳遞下來的方法指針是指方法svcmgr_handler,這個方法負(fù)責(zé)從驅(qū)動從底層轉(zhuǎn)化的code命令的執(zhí)行。
文件:/frameworks/native/cmds/servicemanager/service_manager.c
int svcmgr_handler(struct binder_state *bs,
struct binder_transaction_data *txn,
struct binder_io *msg,
struct binder_io *reply)
{
struct svcinfo *si;
uint16_t *s;
size_t len;
uint32_t handle;
uint32_t strict_policy;
int allow_isolated;
uint32_t dumpsys_priority;
//ALOGI("target=%p code=%d pid=%d uid=%d\n",
// (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);
if (txn->target.ptr != BINDER_SERVICE_MANAGER)
return -1;
if (txn->code == PING_TRANSACTION)
return 0;
...
switch(txn->code) {
...
}
bio_put_uint32(reply, 0);
return 0;
}
還記得我再一開始要注意到傳遞下來的code的嗎?此時code:PING_TRANSACTION,此時我們不需要任何的處理。因為此時,第一次的transact行為用tcp里面的術(shù)語來說,只是為了ping通service_manager確定Android服務(wù)系統(tǒng)的核心是否被正確初始化。
此時(txn->flags & TF_ONE_WAY)的結(jié)果為0.我們直接看看此時service_manager reply了什么命令回去。
void binder_send_reply(struct binder_state *bs,
struct binder_io *reply,
binder_uintptr_t buffer_to_free,
int status)
{
struct {
uint32_t cmd_free;
binder_uintptr_t buffer;
uint32_t cmd_reply;
struct binder_transaction_data txn;
} __attribute__((packed)) data;
data.cmd_free = BC_FREE_BUFFER;
data.buffer = buffer_to_free;
data.cmd_reply = BC_REPLY;
data.txn.target.ptr = 0;
data.txn.cookie = 0;
data.txn.code = 0;
if (status) {
...
} else {
data.txn.flags = 0;
data.txn.data_size = reply->data - reply->data0;
data.txn.offsets_size = ((char*) reply->offs) - ((char*) reply->offs0);
data.txn.data.ptr.buffer = (uintptr_t)reply->data0;
data.txn.data.ptr.offsets = (uintptr_t)reply->offs0;
}
binder_write(bs, &data, sizeof(data));
}
此時我們能看到的是,此時定義了一個新的結(jié)構(gòu)體。該結(jié)構(gòu)體相當(dāng)于之前的2個binder_read_write結(jié)構(gòu)體。不同的是,第一段的標(biāo)志位是BC_FREE_BUFFER,數(shù)據(jù)是之前通過binder_alloc_buf切割下來的結(jié)構(gòu)體地址。第二段是BC_REPLY,后面binder_transaction_data 是需要返回的數(shù)據(jù),如binder對象或者引用之類的。此時并有任何數(shù)據(jù)。接下來將進(jìn)入binder驅(qū)動中
binder 驅(qū)動處理service_manager返回的數(shù)據(jù)
此時的流程和之前的binder_thread_write的解析十分相似,所以這里我只點出不一樣的邏輯看看里面做了什么事情.首先是第一段返回的命令是BC_FREE_BUFFER;
case BC_FREE_BUFFER: {
binder_uintptr_t data_ptr;
struct binder_buffer *buffer;
if (get_user(data_ptr, (binder_uintptr_t __user *)ptr))
return -EFAULT;
ptr += sizeof(binder_uintptr_t);
buffer = binder_buffer_lookup(proc, data_ptr);
if (buffer->transaction) {
buffer->transaction->buffer = NULL;
buffer->transaction = NULL;
}
if (buffer->async_transaction && buffer->target_node) {
if (list_empty(&buffer->target_node->async_todo))
buffer->target_node->has_async_transaction = 0;
else
list_move_tail(buffer->target_node->async_todo.next, &thread->todo);
}
binder_transaction_buffer_release(proc, buffer, NULL);
binder_free_buf(proc, buffer);
break;
}
此時我能夠發(fā)現(xiàn)到此時釋放當(dāng)前的使用的binder_buffer實際上就是把數(shù)據(jù)清空后,使用內(nèi)核緩沖區(qū),再回歸到空閑內(nèi)核緩沖區(qū),并且減少對應(yīng)binder對象的引用計數(shù)。
第二段下傳下來的數(shù)據(jù)的命令是BC_REPLY,此時把需要返回的數(shù)據(jù)添加到事務(wù)棧中。
if (reply) {
in_reply_to = thread->transaction_stack;
...
binder_set_nice(in_reply_to->saved_priority);
...
thread->transaction_stack = in_reply_to->to_parent;
target_thread = in_reply_to->from;
if (target_thread == NULL) {
return_error = BR_DEAD_REPLY;
goto err_dead_binder;
}
...
target_proc = target_thread->proc;
}
.....
if (!reply && !(tr->flags & TF_ONE_WAY))
....
else
t->from = NULL;
....
if (reply) {
binder_pop_transaction(target_thread, in_reply_to);
} else if (!(t->flags & TF_ONE_WAY)) {
...
} else {
if (target_node->has_async_transaction) {
target_list = &target_node->async_todo;
target_wait = NULL;
} else
...
}
縮減下來就是這么點邏輯。這里需要和上面BC_TRANSACTION解析的binder_transaction進(jìn)行聯(lián)動。還記得在上方,結(jié)束BC_TRANSACTION命令時候的添加的行為不?
- 1.從當(dāng)前的事務(wù)棧拿出來的事務(wù)項,就是從之前ServiceManager調(diào)用者進(jìn)程中的生成加在transaction_stack的頂部。
- 2.此時,我們能夠這個事務(wù)項獲取到目標(biāo)進(jìn)程的對象binder_proc。下面的邏輯就就和上面一樣,不一樣的地方僅僅只是調(diào)用的方向逆轉(zhuǎn)過來。
- binder_pop_transaction 最后再把這個從之前進(jìn)程挪移來的事務(wù)彈出,并且釋放內(nèi)存。
- 最后再添加一個事務(wù)工作(binder_transaction)type為BINDER_WORK_TRANSACTION,一個事務(wù)工作(binder_work)為BINDER_WORK_TRANSACTION_COMPLETE.喚醒ServiceManager調(diào)用者進(jìn)程。
此時兩個進(jìn)程如上方一樣同時進(jìn)行,service_manager將會直接進(jìn)入binder_thread_read阻塞休眠。
ServiceManager調(diào)用者進(jìn)程的喚醒
當(dāng)ServiceManager調(diào)用進(jìn)程復(fù)蘇之后,將會進(jìn)入到binder_thread_read的cmd解析循環(huán)中。
case BINDER_WORK_TRANSACTION: {
t = container_of(w, struct binder_transaction, work);
} break;
case BINDER_WORK_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);
kfree(w);
binder_stats_deleted(BINDER_STAT_TRANSACTION_COMPLETE);
} break;
...
if (t->buffer->target_node) {
....
} else {
tr.target.ptr = 0;
tr.cookie = 0;
cmd = BR_REPLY;
}
還記得嗎?此時我們在第一次調(diào)用BC_TRANSACTION的時候會生成一個BINDER_WORK_TRANSACTION_COMPLETE添加到本進(jìn)程線程對應(yīng)的事務(wù)棧中,在BC_REPLY的時候又添加了BINDER_WORK_TRANSACTION事務(wù)棧。
因此此時做的事情很簡單,就是通過循環(huán)執(zhí)行這兩個事務(wù)。并且回收掉之前binder_work的申請的內(nèi)存。此時做的事情就是為了告訴binder驅(qū)動binder服務(wù)已經(jīng)響應(yīng)回原來的進(jìn)程了。
此時你可以看到當(dāng)service_manager寫入BC_REPLY的時候,對應(yīng)的binder_transaction的reply參數(shù)不為0,此時并不設(shè)置target_node,因此在這里讀取的時候,會把cmd從驅(qū)動命令換成framework命令。傳回上層。
此時read已經(jīng)結(jié)束.總結(jié)一下我們能夠發(fā)現(xiàn)的時候,在binder驅(qū)動中以BINDER開頭的命令將會在binder_thread_read中處理,以BC開頭的命令會在binder_thread_write中處理,RR開頭的命令將會在service_manager/IPCThreadState中處理。
ServiceManager進(jìn)程調(diào)用waitForResponse第二段
此時binder驅(qū)動已經(jīng)完成了所有事務(wù),讓我們回到頂層吧。
文件:/frameworks/native/libs/binder/IPCThreadState.cpp
waitForResponse的命令解析:
switch (cmd) {
case BR_TRANSACTION_COMPLETE:
...
case BR_DEAD_REPLY:
...
case BR_FAILED_REPLY:
...
case BR_ACQUIRE_RESULT:
....
case BR_REPLY:
{
binder_transaction_data tr;
err = mIn.read(&tr, sizeof(tr));
ALOG_ASSERT(err == NO_ERROR, "Not enough command data for brREPLY");
if (err != NO_ERROR) goto finish;
if (reply) {
if ((tr.flags & TF_STATUS_CODE) == 0) {
reply->ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t),
freeBuffer, this);
} else {
err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), this);
}
} else {
freeBuffer(NULL,
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
tr.data_size,
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), this);
continue;
}
}
goto finish;
default:
err = executeCommand(cmd);
if (err != NO_ERROR) goto finish;
break;
}
此時reply不為空并且(tr.flags & TF_STATUS_CODE)結(jié)果為0.此時把Parcel reply字段封裝從驅(qū)動穿上來的數(shù)據(jù)。此時我們略過中間的步驟,看到service_manager進(jìn)程中實際上設(shè)置binder數(shù)據(jù)的時候,實際上因為判斷到是PING的命令,已經(jīng)返回了。此時并沒有任何數(shù)據(jù)在Parcel里面。
回到ProcessState
由于嵌套過深,為了避免邏輯斷鏈,我實際上一直在研究這部分代碼:
status_t status = IPCThreadState::self()->transact(
0, IBinder::PING_TRANSACTION, data, NULL, 0);
此時,我們可以得到一個結(jié)論,當(dāng)發(fā)送code的時候,控制的是service_manager那一塊的邏輯。而PING_TRANSACTION什么事情都沒做,僅僅只是為了ping 通service_manager進(jìn)程。但是在這個流程中我們卻摸透整個binder在傳輸時候的設(shè)計模型。
總結(jié)
整個Android的service Binder架構(gòu)嵌套十分的深,涉及面十分廣。因此本文就停筆總結(jié),讓我們歸納一遍Android系統(tǒng)中的Binder事務(wù)流程。
經(jīng)過這一次,我們能夠繪制出比上一篇更加詳細(xì)的binder傳輸數(shù)據(jù)的封包,當(dāng)頂層往下層傳輸命令時候

特別的,在Android整個協(xié)議中BC開頭的命令時用來控制binder驅(qū)動寫的行為,BINDER開頭的命令用來控制binder驅(qū)動讀時候的行為,BR開頭的命令用來控制路由器接受端和發(fā)送端的解析行為,這里是指service_manager和IPCThreadState。中間的code控制service_manager和IPCThreadState的具體動作行為。
再來總結(jié)一份Binder傳輸時候的時序圖

注意這里紅色代表著跨越了進(jìn)程。
光是時序圖還是沒辦法看到細(xì)節(jié),進(jìn)一步的整個進(jìn)程間通信模型
我們把模型分為三部分去看,第一部分是從ServiceManager的調(diào)用端發(fā)送數(shù)據(jù),service_manager進(jìn)程獲取數(shù)據(jù)。

第二部分是返回消息在清除binder_transaction_stack之前:

第三部分:

這只是大致的原理圖,實際上binder_transaction存在的意義就是為了查找依賴棧,以及當(dāng)每一次加入一個新的事務(wù)時候,把當(dāng)前要處理的事務(wù)放在棧頂。當(dāng)需要恢復(fù)回復(fù)信息的時候,以此為依據(jù)來找到原來發(fā)送端。上面那個看起來像是棧,實際上只是一個todo鏈表。
以上就是整個Binder在進(jìn)程間通信的流程。這里就是它巧妙的地方,通過兩個不同進(jìn)程的事務(wù)棧在傳遞事務(wù),從而達(dá)到數(shù)據(jù)傳遞的過程。同時通過tcomplete來結(jié)束讀取的行為。而上面留下的tcomplete將會在下次讀取數(shù)據(jù)的數(shù)據(jù)刪除。這就是我在上面說的,引用刪除可以留到下次讀取刪除,但是添加引用計數(shù)必須立即添加,不然會被智能指針析構(gòu)掉同一道理。
Binder傳輸過程中,內(nèi)存拷貝次數(shù)
最后再來計算一次,在整個通信過程中,一共做了幾次虛擬內(nèi)存中的數(shù)據(jù)拷貝,一次完整的通信過程中必定包含本地端的寫入以及遠(yuǎn)程端的讀取,都是在binder_ioctl_write_read方法中進(jìn)行數(shù)據(jù)的拷貝,一共12次:
- 第一次:本地端寫入數(shù)據(jù)到內(nèi)核,遠(yuǎn)程端寫回數(shù)據(jù)進(jìn)行應(yīng)答:
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
- 第二次遠(yuǎn)程端讀取數(shù)據(jù)處理,本地段讀取遠(yuǎn)程端寫回的數(shù)據(jù):
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
這里加起來就有四次。
- 第三次,第四次,第五次:方法binder_thread_write,執(zhí)行binder_transaction準(zhǔn)備寫入到事務(wù)隊列中,讀取寫入到數(shù)據(jù)中的binder_transaction_data結(jié)構(gòu)體進(jìn)行一次拷貝,再對binder_transaction_data中中的元數(shù)據(jù)大小和偏移量大小數(shù)據(jù)進(jìn)行拷貝,最后再轉(zhuǎn)移到目標(biāo)進(jìn)程binder緩沖區(qū)事務(wù)隊列:
if (copy_from_user(&tr, ptr, sizeof(tr)))
return -EFAULT;
if (copy_from_user(t->buffer->data, (const void __user *)(uintptr_t)
tr->data.ptr.buffer, tr->data_size)) {
...
}
if (copy_from_user(offp, (const void __user *)(uintptr_t)
tr->data.ptr.offsets, tr->offsets_size)) {
...
}
- 第六次:方法binder_thread_read中解析事務(wù)隊列中的內(nèi)容,拷貝到遠(yuǎn)程端:
if (copy_to_user(ptr, &tr, sizeof(tr)))
return -EFAULT;
這是一次寫入讀取,最后遠(yuǎn)程端一般還有再做一次應(yīng)答,因此是這個步驟加起來就有8次.
那么一來一回一共就是12次。但是有趣的是,整個過程中binder的進(jìn)程對應(yīng)的內(nèi)核緩沖區(qū)都是通過mmap映射到物理頁。實際上,這僅僅只是一次從本地物理頁直接拷貝到遠(yuǎn)端物理頁的過程。