前言
经过前面三篇binder驱动的初始化阐述,我大致上稍微复习一边linux内核的基础知识,也对binder的理解更加深刻。接下来我们来看看binder 的服务是怎么注册到service_manager。
如果遇到问题请到:https://www.jianshu.com/p/04e53fd86ca2
正文
很多文章一开始直接放出了binder在framework中类的之间的uml图。我先不放出,因为一开始就直接上这种图的确不怎么好理解。等我们探索完binder 服务初始化架构之后再放出来。
ServiceManager 注册service
还记得我之前写的SystemServiceManager那一章节。我稍微的提及到了服务的注册,但是我并没有深入源码。这一次我连同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);
在这里为系统添加了windowManagerService的服务。这个服务Android应该十分熟悉,用来控制窗口的服务。我之后会专门取出一节来详细解析其中的代码。
让我们看看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);
}
}
看这个代表着远程的异常,我们猜测这里是不是通过某种手段进行进程间通信,把IBinder添加到远程端中。
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}
首先判断IServiceManager 这个接口对象是否存在。不存在则从从远程端获取这个实例。
妙啊妙,我在Binder第一节就探讨过进程间通信,无一不是通过两端通过协议进行交流,而在这里的IPC通行居然就能抽象着两个远程端直接继承一个接口,进行通信。但是还记得吗?进程之间的地址都有自己保护,我们不可能真的抽离一个接口让两个进程之间通信,只是在这个过程中把进程间通信透明化了。
这么棒的设计,让我们看看Google工程师是怎么设计的。
文件:/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);
}
这里可以看到一个很关键的类。我们可以见到在Binder 服务中十分重要的一个类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;
}
从这里我们可以的得知这是一个单例,如果在这个进程的ProcessState没有实例化则会实例化ProcessState。
接着我们看看这个构造函数。
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十分相似,先调用open打开binder驱动,再进行映射。
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 设置最大线程数。
经过ProcessState构造函数之后,我们也把当前这个类已经映射到了binder驱动中。实际上这个类我在第一篇启动系统中已经惊鸿一现,系统启动的时候,zygote进程孵化的SystemServer进程也会映射到binder驱动中。
细节不继续赘述了,我们继续看看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对象还是会自己添加一份进去。所以一般情况下不用担心找不到引用,至于为什么说是引用,放到之后再说。
因此,此时我们会走到b==null的分支当中,调用了BpBinder::create(handle)的方法生成一个远程端的binder代理对象。
IPCThreadState 初始化
文件:/frameworks/native/libs/binder/IPCThreadState.cpp
此时handle为0,会走到下面的方法:
status_t status = IPCThreadState::self()->transact(
0, IBinder::PING_TRANSACTION, data, NULL, 0);
此时出现了另一个十分关键的类IPCThreadState。看到这个调用,我们也可以知道这是一个单例设计。
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相关的操作,不妨可以代入思考。
IPCThreadState 和binder驱动交互
我们接下来看看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驱动传递数据最为核心的的机制。
这里我们分为两段来讨论。注意此时flags & TF_ONE_WAY (0x10)将用来控制整个流程是否是等待远程端的回应标志位。此时flag为而0,因此会等待。当flag为1的时候则不等待。在这里的表现就是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;
//是这远程端的目标
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;
//设置通信的数据
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代表着元数据的偏移量以及data_size代表着有效数据的大小。其实是在Parcel中已经做好了管理了。在Parcel中mData对象代表着普通数据,而mObjects是一个偏移数组,它保存了在数据缓冲区mData中所有Binder对象的位置。Binder会通过这个对象来寻找Binder对象。
此时需要进去的数据是:
- cmd:BC_TRANSACTION
- tr : binder_transaction_data
- code: IBinder::PING_TRANSACTION
BC_TRANSACTION 代表此时有个事务处理,binder_transaction_data代表事务数据。
这里为mOut Parcel设置了第一段数据。
waitForResponse(reply)
我们接着看循环第二段。waitForResponse 这里把这个循环拆成两段。
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
在这个循环里的talkWithDriver函数,看到名字就知道是和binder驱动进行沟通。
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这个结构体。
这个结构体的读取部分设置了Parcel全局对象mIn,写入部分设置Parcel全局对象mOut。当童星结束之后将会清空全局的Parcel变量mOut以及mIn中的写入/读取标记位置。
binder_transaction
文件:/drivers/staging/android/binder.c
binder_transaction 虽然之前已经提过,但是这个方法过于长,这里我们继续只关注我们需要的。
此时我们通过ioctl通信到Binder驱动中。我们这边直接进入binder驱动的对应方法看看。
方法 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查找对应的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;
...
//寻找事务依赖
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.设置当前的目标等待队列以及目标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);
//为事务分配一个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查找对应的binder引用。在这里我们的cmd是BC_TRANSCATION。因此会直接拿binder_context_mgr_node。而这个就是我在上一篇说过的service_manager的binder实体。此时我们也相当于拿到了service_manager进程映射的地址了。
2.找到我们需要的binder实体之后,我们看看该进程的binder_thread对应的事务栈transaction_stack。我们需要通过这个栈去寻找,当前分发下来的事务是否有依赖其他事务。
while (tmp) {
if (tmp->from && tmp->from->proc == target_proc)
target_thread = tmp->from;
tmp = tmp->from_parent;
}
因此,binder驱动会通过一个循环,直接从栈顶开始查找依赖的事务的线程(binder_transaction->from)。会去查找依赖的事务所对应的binder_proc是不是当前的目标进程。是则说明有线程也在使用这个目标进程,我们需要把把这个binder_thread取出来,先把依赖的事务给处理了。
3.判断此时有没有找到目标的binder_thread。找到则设置目标binder_thread的todo list和等待队列。不然则是binder_proc的todo list和等待队列。
4.申请内存 t 和 tcomplete 事务对象binder_transaction,工作项 binder_work。并且把相关的数据如目标进程,目标binder_thread,code,flag等写入binder_transaction。并且通过binder_alloc_buf 为这一次事务在目标进程中申请一段binder_buffer(内核缓冲区).
5.通过copy_from_user把binder中普通数据拷贝到binder_transaction_data的data中,元数据拷贝到offsets中。
为了弄清楚这一点,我们看看binder_transaction_data数据结构
struct binder_transaction_data {
union {
//引用
__u32 handle;
//对应用户空间binder_transaction_data 的ptr
binder_uintptr_t ptr;
} target;
//对应用户空间binder_transaction_data的cookie
binder_uintptr_t cookie;
//对应用户空间binder_transaction_data的code
__u32 code;
//对应用户空间binder_transaction_data的flags
__u32 flags;
pid_t sender_pid;
uid_t sender_euid;
//数据大小
binder_size_t data_size;
//偏移量大小
binder_size_t offsets_size;
//元数据
union {
//binder_buffer
struct {
binder_uintptr_t buffer;
//binder数组
binder_uintptr_t offsets;
} ptr;
//数据
__u8 buf[8];
} data;
};
此时拷贝tr->data.ptr.buffer到了binder_transaction的内核缓冲区的数据区。tr->data.ptr.offsets拷贝到offp 地址中。而这个offp地址是这么计算的
offp = (binder_size_t *)(t->buffer->data + ALIGN(tr->data_size, sizeof(void *)));
这么计算的结果是把binder_transaction的binder_buffer的末尾地址计算出来,相当于定位了mData中Binder偏移数组的起始位置。
off_end = (void *)offp + tr->offsets_size;
off_min = 0;
for (; offp < off_end; offp++) {
struct flat_binder_object *fp;
...
//获取flat_binder_object 结构体
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;
//找到对应的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;
}
//如果引用对象刚好是目标进程,转化为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;
//不是则获取目标进程中的引用
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->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;
...
}
增加引用计数
在这个循环中有一个关键的结构体,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中也有一个一模一样的数据结构,这个结构体实际上就是Binder偏移数组中的数据。很有意思,当我们尝试着写内核和用户空间交互数据的时候,我们往往可以在两侧构造一样的的数据结构再进行copy。
fp = (struct flat_binder_object *)(t->buffer->data + *offp);
但是这是怎么回事?为什么这样子能够强转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经常借助Parcel传递消息,而Binder的传递恰好就是调用这个方法。而在framework层换算flat_binder_object对象,也是通过普通数据+偏移量转型为flat_binder_object并且赋值。实际上这里的内核算法只是和用户空间的算法保持一致。
在这个循环中,主要处理了三种flat_binder_object 对应的5种type。首先看看flat_binder_object 是怎么获得的。这些type
1.BINDER_TYPE_BINDER/BINDER_TYPE_WEAK_BINDER
该标志位是在Parcel中设置,判断如果是本地binder对象则,加入此标志位。此时binder驱动接受到之后,说明此时是本地端发起的交互,因此此时需要获取目标进程的引用,并且增加引用计数,同时把状态转化为BINDER_TYPE_HANDLE / BINDER_TYPE_WEAK_HANDLE。2.BINDER_TYPE_HANDLE / BINDER_TYPE_WEAK_HANDLE
该标志位也是在Parcel中设置。判断到此时不是binder本地而是远程端,则会加入此标记。当binder驱动接受到,说明此时是远程端发起的交互。需要通过handle获取本地端的引用。当handle获取到的引用刚好事目标进程,则把标志位设置为BINDER_TYPE_BINDER/BINDER_TYPE_WEAK_BINDER 。不是则直接从目标进程查找引用。3.BINDER_TYPE_FD
该标志控制着一个文件描述符,并且读写进数据,做跨进程的数据透传。
细节: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红黑树查找查找,当前的node是否有binder的引用,因为此时要做的行为是要把binder往对端的用户空间传递。此时不可能做到把binder实体交给另外的进程,因此会传递一个引用。
- 2.没有找到binder引用说明此时这个binder是新加入的,需要需要把当前生成的新的binder_node(binder实体)添加到binder_proc的refs_by_node。
3.把当前的binder_ref的描述设置为1(如果当前的binder的实体是service_manager则设置为0)。循环后面的引用描述(句柄),当遇到引用(句柄)比当前新的大则跳出,否则则新的引用是老引用+1.实际上就是就是调整整个算法。因为在整个过程是获取refs_by_desc第二个管理binder引用的红黑树(一个以引用描述为key的)的后继,比较句柄大小,找到当前最大的那个往后加1。这个break的存在就是为了对付加入一直加的普通的binder对象,突然添加了service_manager的对象,此时我们必须要保证这个binder引用描述为0才设计。
4.把引用以desc 为key添加到refs_by_desc中。这个红黑树更加重要,因为我们在上层的handle就是对应这个描述符,通过这个描述符去查找对应的引用,从而传输binder对象引用。
对于binder来说,是不会直接使用binder实体类,因为binder实体里包含这个其他进程的地址,会导致无法访问的情况。因此,需要一层binder引用包裹。
小总结
为什么要做这一步?对于智能指针来讲,每一次减少意味这个这个binder引用可能有被析构的可能,反过来当我们不需要这个引用的时候,我们也可以把这个计数减少的步骤向后挪移也没问题。所以对于这种情况来说,我们在离开本次作用域之前需,把需要传递的binder引用对象新增加一个引用计数,来防止其被析构。
更为关键的的是,在Parcel读取的时候,会通过这个type,BINDER_TYPE_BINDER转型为BBinder(代表本地端对象),还是BINDER_TYPE_HANDLE转型为BpBinder(代表远程端对象)。
唤醒目标进程
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->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;
第一段是判断当前是否是BC_REPLY,这里先不去研究。当时t->flags & TF_ONE_WAY为0的时候,则设置当前的binder_transaction的need_reply为1,设置依赖事务为当前的binder_thread的transaction_stack。不然则是选择todo list为目标binder实体的异步队列。
最后把工作加入到目标工作(todo)队列。最后调用wake_up_interruptible唤醒目标进程。tcomplete添加到当前线程的todo中。
回到当前场景
当前场景,我们发送了的mOut中实际上没有设置任何的data数据。因此在整个binder_transaction(事务交易)中,循环offsets的那部分直接跳过,将会直接寻找目标对象,设置好目标binder_thread以及工作队列还有等待队列。事务项的type设置为BINDER_WORK_TRANSACTION。tcomplete设置BINDER_WORK_TRANSACTION_COMPLETE。
还记得,此时我们唤醒的target_wait是谁吗?就是指service_manager。此时service_manager在binder_thread_read方法被阻塞了。我们看看binder_thread_read方法吧。唤醒的同时,ServiceManager调用者本身的进程也在继续进行。我们在这里记住,我们继续回去看看IPCThreadState的waitForResponse方法。实际上这是两个进程同时进行的事务工作。
ServiceManager调用者进程的talkWithDriver的情景分析
还记得吗。此时talkwithDriver的doneed默认为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;
//判断当前工作项的type
switch (w->type) {
case BINDER_WORK_TRANSACTION: {
t = container_of(w, struct binder_transaction, work);
} break;
...
}
if (!t)
continue;
//如果对应的目标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 {
//为空,则设置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);
//设置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;
}
//设置参数
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 *));
//拷贝数据到用户空间
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;
}
当ServiceManager对应的进程唤醒当前service_manager的时候,函数将会继续往下走,此时就会进入到这个循环。
1.可以看到的是,此时会从service_manager进程中获取出todo列表的头部,优先获取binder_thread的todo双向队列。这样就能拿到我们就能拿到从ServiceManager加入进去的工作项。
如果binder_thread没有则去binder_proc中获取。如果都是空,但是判断后面的data数据区域还有位置(地址差为4和前文为binder_buffer划分的时候呼应),则说明此时没有数据,就没有必要去读取从新进入阻塞。2.此时再去解析命令,转化命令从BC的驱动命令转化RR的framework命令。此时刚好是BINDER_WORK_TRANSACTION,我们就取出里面的binder_work的binder_transaction 事务项t。注意,当t为空的时候会while向后获取命令,执行命令,直到达到了底部。底部的判断就是binder_buffer申请时候的上一个和下一个的binder_buffer差值为4.
3.如果此时该事务项中的目标进程不为空,则把命令转化为BR_TRANSACTION,并且把里面的数据赋值到binder_transaction_data中。
4.光这些不够,我们还需要把数据从内核空间往用户空间拷贝。也就是使用put_user和copy_to_user方法。由于此时正还是BR_TRANSACTION,所以把目标线程和目标对象设置为当前线程。以此来宣告此时此时处理在事务的是当前进程。
回到service_manager的binder_parse
binder_thread_read 方法返回到用户空间。此时进入的binder_parse是用于解析从驱动传递上来的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已经转化为BR_TRANSACTION命令。在这个分支中出现了全新的一种结构体
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;
};
这个结构体从名字上可以看得出实际上指代的是一个binder_buffer在ioctl交互时候调用的对象.分为两个区域,四个地址。两个区域分别是binder_buffer的元数据以及有效数据去。并且0为结尾的字段就是指的是元数据以及有效数据区的起始地址。得知这些之后,我们尝试阅读其源码。因此在bio_init中初始化reply,bio_init_from_txn 把从内核中传递上来的数据拷贝的到reply中。
而func是从binder_loop传递下来的方法指针是指方法svcmgr_handler,这个方法负责从驱动从底层转化的code命令的执行。
文件:/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里面的术语来说,只是为了ping通service_manager确定Android服务系统的核心是否被正确初始化。
此时(txn->flags & TF_ONE_WAY)的结果为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));
}
此时我们能看到的是,此时定义了一个新的结构体。该结构体相当于之前的2个binder_read_write结构体。不同的是,第一段的标志位是BC_FREE_BUFFER,数据是之前通过binder_alloc_buf切割下来的结构体地址。第二段是BC_REPLY,后面binder_transaction_data 是需要返回的数据,如binder对象或者引用之类的。此时并有任何数据。接下来将进入binder驱动中
binder 驱动处理service_manager返回的数据
此时的流程和之前的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;
}
此时我能够发现到此时释放当前的使用的binder_buffer实际上就是把数据清空后,使用内核缓冲区,再回归到空闲内核缓冲区,并且减少对应binder对象的引用计数。
第二段下传下来的数据的命令是BC_REPLY,此时把需要返回的数据添加到事务栈中。
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进行联动。还记得在上方,结束BC_TRANSACTION命令时候的添加的行为不?
- 1.从当前的事务栈拿出来的事务项,就是从之前ServiceManager调用者进程中的生成加在transaction_stack的顶部。
- 2.此时,我们能够这个事务项获取到目标进程的对象binder_proc。下面的逻辑就就和上面一样,不一样的地方仅仅只是调用的方向逆转过来。
- binder_pop_transaction 最后再把这个从之前进程挪移来的事务弹出,并且释放内存。
- 最后再添加一个事务工作(binder_transaction)type为BINDER_WORK_TRANSACTION,一个事务工作(binder_work)为BINDER_WORK_TRANSACTION_COMPLETE.唤醒ServiceManager调用者进程。
此时两个进程如上方一样同时进行,service_manager将会直接进入binder_thread_read阻塞休眠。
ServiceManager调用者进程的唤醒
当ServiceManager调用进程复苏之后,将会进入到binder_thread_read的cmd解析循环中。
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;
}
还记得吗?此时我们在第一次调用BC_TRANSACTION的时候会生成一个BINDER_WORK_TRANSACTION_COMPLETE添加到本进程线程对应的事务栈中,在BC_REPLY的时候又添加了BINDER_WORK_TRANSACTION事务栈。
因此此时做的事情很简单,就是通过循环执行这两个事务。并且回收掉之前binder_work的申请的内存。此时做的事情就是为了告诉binder驱动binder服务已经响应回原来的进程了。
此时你可以看到当service_manager写入BC_REPLY的时候,对应的binder_transaction的reply参数不为0,此时并不设置target_node,因此在这里读取的时候,会把cmd从驱动命令换成framework命令。传回上层。
此时read已经结束.总结一下我们能够发现的时候,在binder驱动中以BINDER开头的命令将会在binder_thread_read中处理,以BC开头的命令会在binder_thread_write中处理,RR开头的命令将会在service_manager/IPCThreadState中处理。
ServiceManager进程调用waitForResponse第二段
此时binder驱动已经完成了所有事务,让我们回到顶层吧。
文件:/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)结果为0.此时把Parcel reply字段封装从驱动穿上来的数据。此时我们略过中间的步骤,看到service_manager进程中实际上设置binder数据的时候,实际上因为判断到是PING的命令,已经返回了。此时并没有任何数据在Parcel里面。
回到ProcessState
由于嵌套过深,为了避免逻辑断链,我实际上一直在研究这部分代码:
status_t status = IPCThreadState::self()->transact(
0, IBinder::PING_TRANSACTION, data, NULL, 0);
此时,我们可以得到一个结论,当发送code的时候,控制的是service_manager那一块的逻辑。而PING_TRANSACTION什么事情都没做,仅仅只是为了ping 通service_manager进程。但是在这个流程中我们却摸透整个binder在传输时候的设计模型。
总结
整个Android的service Binder架构嵌套十分的深,涉及面十分广。因此本文就停笔总结,让我们归纳一遍Android系统中的Binder事务流程。
经过这一次,我们能够绘制出比上一篇更加详细的binder传输数据的封包,当顶层往下层传输命令时候
特别的,在Android整个协议中BC开头的命令时用来控制binder驱动写的行为,BINDER开头的命令用来控制binder驱动读时候的行为,BR开头的命令用来控制路由器接受端和发送端的解析行为,这里是指service_manager和IPCThreadState。中间的code控制service_manager和IPCThreadState的具体动作行为。
再来总结一份Binder传输时候的时序图
注意这里红色代表着跨越了进程。
光是时序图还是没办法看到细节,进一步的整个进程间通信模型
我们把模型分为三部分去看,第一部分是从ServiceManager的调用端发送数据,service_manager进程获取数据。
第二部分是返回消息在清除binder_transaction_stack之前:
第三部分:
这只是大致的原理图,实际上binder_transaction存在的意义就是为了查找依赖栈,以及当每一次加入一个新的事务时候,把当前要处理的事务放在栈顶。当需要恢复回复信息的时候,以此为依据来找到原来发送端。上面那个看起来像是栈,实际上只是一个todo链表。
以上就是整个Binder在进程间通信的流程。这里就是它巧妙的地方,通过两个不同进程的事务栈在传递事务,从而达到数据传递的过程。同时通过tcomplete来结束读取的行为。而上面留下的tcomplete将会在下次读取数据的数据删除。这就是我在上面说的,引用删除可以留到下次读取删除,但是添加引用计数必须立即添加,不然会被智能指针析构掉同一道理。
Binder传输过程中,内存拷贝次数
最后再来计算一次,在整个通信过程中,一共做了几次虚拟内存中的数据拷贝,一次完整的通信过程中必定包含本地端的写入以及远程端的读取,都是在binder_ioctl_write_read方法中进行数据的拷贝,一共12次:
- 第一次:本地端写入数据到内核,远程端写回数据进行应答:
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
- 第二次远程端读取数据处理,本地段读取远程端写回的数据:
if (copy_to_user(ubuf, &bwr, sizeof(bwr))) {
ret = -EFAULT;
goto out;
}
这里加起来就有四次。
- 第三次,第四次,第五次:方法binder_thread_write,执行binder_transaction准备写入到事务队列中,读取写入到数据中的binder_transaction_data结构体进行一次拷贝,再对binder_transaction_data中中的元数据大小和偏移量大小数据进行拷贝,最后再转移到目标进程binder缓冲区事务队列:
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中解析事务队列中的内容,拷贝到远程端:
if (copy_to_user(ptr, &tr, sizeof(tr)))
return -EFAULT;
这是一次写入读取,最后远程端一般还有再做一次应答,因此是这个步骤加起来就有8次.
那么一来一回一共就是12次。但是有趣的是,整个过程中binder的进程对应的内核缓冲区都是通过mmap映射到物理页。实际上,这仅仅只是一次从本地物理页直接拷贝到远端物理页的过程。