作者:李尚
先看第一部分:发送端线程睡眠在哪个队列上?
发送端线程一定睡眠在自己binder_thread的等待队列上,并且,该队列上有且只有自己一个睡眠线程
再看第二部分:在Binder驱动去唤醒线程的时候,唤醒的是哪个等待队列上的线程?
理解这个问题需要理解binder_thread中的 struct binder_transaction transaction_stack栈,这个栈规定了transaction的执行顺序:*栈顶的一定先于栈内执行。
如果本地操作是BC_REPLY,一定是唤醒之前发送等待的线程,这个是100%的,但是如果是BC_TRANSACTION,那就不一定了,尤其是当两端互为服务相互请求的时候,场景如下:
这个时候就会遇到一个问题:唤醒哪个线程比较合适?是睡眠在进程队列上的线程,还是之前睡眠的线程BT1?答案是:之前睡眠的线程BT1,具体看下面的图解分析
首先第一步A普通线程去请求B进程的B1服务,这个时候在A进程的AT1线程的binder_ref中会将binder_transaction1入栈,而同样B的Binder线程在读取binder_work之后,也会将binder_transaction1加入自己的堆栈,如下图:
而当binder_transaction3完成,出栈的过程其实就简单了,
从这里可以看出,其实设计的还是很巧妙的,让线程复用,提高了效率,还避免了新建不必要的Binder线程,在binder驱动中岛实现代码,其实就是根据binder_transaction中堆栈记录查询, static void binder_transaction(struct binder_proc proc, struct binder_thread thread, struct binder_transaction_data *tr, int reply) {.. while (tmp) { // 找到对方正在等待自己进程的线程,如果线程没有在等待自己进程的返回,就不要找了
// 判断是不target_proc中,是不是有线程,等待当前线程
// thread->transaction_stack,这个时候,
// 是binder线程的,不是普通线程 B去请求A服务,
// 在A服务的时候,又请求了B,这个时候,A的服务一定要等B处理完,才能再返回B,可以放心用B
if (tmp->from && tmp->from->proc == target_proc)
target_thread = tmp->from;
tmp = tmp->from_parent;
... }
} }
BC与BR主要是标志数据及Transaction流向,其中BC是从用户空间流向内核,而BR是从内核流线用户空间,比如Client向Server发送请求的时候,用的是BC_TRANSACTION,当数据被写入到目标进程后,target_proc所在的进程被唤醒,在内核空间中,会将BC转换为BR,并将数据与操作传递该用户空间。
内核中,与用户空间对应的结构体对象都需要新建,但传输数据的数据只拷贝一次,就是一次拷贝的时候。
从Client端请求开始分析,暂不考虑java层,只考虑Native,以ServiceManager的addService为例,具体看一下
MediaPlayerService::instantiate();
MediaPlayerService会新建Binder实体,并将其注册到ServiceManager中:
void MediaPlayerService::instantiate() {
defaultServiceManager()->addService(
String16("media.player"), new MediaPlayerService());
}
这里defaultServiceManager其实就是获取ServiceManager的远程代理:
sp<IServiceManager> defaultServiceManager()
{
if (gDefaultServiceManager != NULL) return gDefaultServiceManager;
{
AutoMutex _l(gDefaultServiceManagerLock);
if (gDefaultServiceManager == NULL) {
gDefaultServiceManager = interface_cast<IServiceManager>(
ProcessState::self()->getContextObject(NULL));
}
}
return gDefaultServiceManager;
}
如果将代码简化其实就是
return gDefaultServiceManager = BpServiceManager (new BpBinder(0));
addService就是调用BpServiceManager的addService,
virtual status_t addService(const String16& name, const sp<IBinder>& service,
bool allowIsolated)
{
Parcel data, reply;
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
data.writeStrongBinder(service);
data.writeInt32(allowIsolated ? 1 : 0);
status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
return err == NO_ERROR ? reply.readExceptionCode() : err;
}
这里会开始第一步的封装,数据封装,其实就是讲具体的传输数据写入到Parcel对象中,与Parcel对应是ADD_SERVICE_TRANSACTION等具体操作。比较需要注意的就是data.writeStrongBinder,这里其实就是把Binder实体压扁:
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
return flatten_binder(ProcessState::self(), val, this);
}
具体做法就是转换成flat_binder_object,以传递Binder的类型、指针之类的信息:
status_t flatten_binder(const sp<ProcessState>& proc,
const sp<IBinder>& binder, Parcel* out)
{
flat_binder_object obj;
obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
if (binder != NULL) {
IBinder *local = binder->localBinder();
if (!local) {
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL) {
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
obj.type = BINDER_TYPE_HANDLE;
obj.handle = handle;
obj.cookie = NULL;
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = local->getWeakRefs();
obj.cookie = local;
}
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = NULL;
obj.cookie = NULL;
}
return finish_flatten_binder(binder, obj, out);
}
接下来看 remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); 在上面的环境中,remote()函数返回的就是BpBinder(0),
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
之后通过 IPCThreadState::self()->transact( mHandle, code, data, reply, flags)进行进一步封装:
status_t IPCThreadState::transact(int32_t handle,
uint32_t code, const Parcel& data,
Parcel* reply, uint32_t flags){
if ((flags & TF_ONE_WAY) == 0) {
if (err == NO_ERROR) {
err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);
}
if (reply) {
err = waitForResponse(reply);
}
..
return err;
}
writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);是进一步封装的入口,在这个函数中Parcel& data、handle、code、被进一步封装成binder_transaction_data对象,并拷贝到mOut的data中去,同时也会将BC_TRANSACTION命令也写入mOut,这里与binder_transaction_data对应的CMD是BC_TRANSACTION,binder_transaction_data也存储了数据的指引新信息:
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.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(size_t);
tr.data.ptr.offsets = data.ipcObjects();
} ..
mOut.writeInt32(cmd);
mOut.write(&tr, sizeof(tr));
return NO_ERROR;
}
mOut封装结束后,会通过waitForResponse调用talkWithDriver继续封装:
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
binder_write_read bwr;
// Is the read buffer empty? 这里会有同时返回两个命令的情况 BR_NOOP、BR_COMPLETE
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 = (long unsigned int)mOut.data(); // This is what we'll read.
if (doReceive && needRead) {
bwr.read_size = mIn.dataCapacity();
bwr.read_buffer = (long unsigned int)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 (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
err = NO_ERROR;
if (mProcess->mDriverFD <= 0) {
err = -EBADF;
}
} while (err == -EINTR);
if (err >= NO_ERROR) {
if (bwr.write_consumed > 0) {
if (bwr.write_consumed < (ssize_t)mOut.dataSize())
mOut.remove(0, bwr.write_consumed);
else
mOut.setDataSize(0);
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
return NO_ERROR;
}
return err;
}
talkWithDriver会将mOut中的数据与命令继续封装成binder_write_read对象,其中bwr.write_buffer就是mOut中的data(binder_transaction_data+BC_TRRANSACTION),之后就会通过ioctl与binder驱动交互,进入内核,这里与binder_write_read对象对应的CMD是BINDER_WRITE_READ,进入驱动后,是先写后读的顺序,所以才叫BINDER_WRITE_READ命令,与BINDER_WRITE_READ层级对应的几个命令码一般都是跟线程、进程、数据整体传输相关的操作,不涉及具体的业务处理,比如BINDER_SET_CONTEXT_MGR是将线程编程ServiceManager线程,并创建0号Handle对应的binder_node、BINDER_SET_MAX_THREADS是设置最大的非主Binder线程数,而BINDER_WRITE_READ就是表示这是一次读写操作:
#define BINDER_CURRENT_PROTOCOL_VERSION 7
#define BINDER_WRITE_READ _IOWR('b', 1, struct binder_write_read)
#define BINDER_SET_IDLE_TIMEOUT _IOW('b', 3, int64_t)
#define BINDER_SET_MAX_THREADS _IOW('b', 5, size_t)
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
#define BINDER_SET_IDLE_PRIORITY _IOW('b', 6, int)
#define BINDER_SET_CONTEXT_MGR _IOW('b', 7, int)
#define BINDER_THREAD_EXIT _IOW('b', 8, int)
#define BINDER_VERSION _IOWR('b', 9, struct binder_version)
详细看一下binder_ioctl对于BINDER_WRITE_READ的处理,
static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case BINDER_WRITE_READ: {
struct binder_write_read bwr;
..
<!--拷贝binder_write_read对象到内核空间-->
if (copy_from_user(&bwr, ubuf, sizeof(bwr))) {
ret = -EFAULT;
goto err;
}
<!--根据是否需要写数据处理是不是要写到目标进程中去-->
if (bwr.write_size > 0) {
ret = binder_thread_write(proc, thread, (void __user *)bwr.write_buffer, bwr.write_size, &bwr.write_consumed);
}
<!--根据是否需要写数据处理是不是要读,往自己进程里读数据-->
if (bwr.read_size > 0) {
ret = binder_thread_read(proc, thread, (void __user *)bwr.read_buffer, bwr.read_size, &bwr.read_consumed, filp->f_flags & O_NONBLOCK);
<!--是不是要同时唤醒进程上的阻塞队列-->
if (!list_empty(&proc->todo))
wake_up_interruptible(&proc->wait);
}
break;
}
case BINDER_SET_MAX_THREADS:
if (copy_from_user(&proc->max_threads, ubuf, sizeof(proc->max_threads))) {
}
break;
case BINDER_SET_CONTEXT_MGR:
.. break;
case BINDER_THREAD_EXIT:
binder_free_thread(proc, thread);
thread = NULL;
break;
case BINDER_VERSION:
..
}
binder_thread_write(proc, thread, (void __user )bwr.write_buffer, bwr.write_size, &bwr.write_consumed)这里其实就是把解析的binder_write_read对象再剥离,*bwr.write_buffer 就是上面的(BC_TRANSACTION+ binder_transaction_data),
int binder_thread_write(struct binder_proc *proc, struct binder_thread *thread,
void __user *buffer, int size, signed long *consumed)
{
uint32_t cmd;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
while (ptr < end && thread->return_error == BR_OK) {
// binder_transaction_data BC_XXX+binder_transaction_data
if (get_user(cmd, (uint32_t __user *)ptr)) (BC_TRANSACTION)
return -EFAULT;
ptr += sizeof(uint32_t);
switch (cmd) {
..
case BC_FREE_BUFFER: {
...
}
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;
}
case BC_REGISTER_LOOPER:
..
case BC_ENTER_LOOPER:
...
thread->looper |= BINDER_LOOPER_STATE_ENTERED;
break;
case BC_EXIT_LOOPER:
// 这里会修改读取的数据,
*consumed = ptr - buffer;
}
return 0;
}
binder_thread_write会进一步根据CMD剥离出binder_transaction_data tr,交给binder_transaction处理,其实到binder_transaction数据几乎已经剥离极限,剩下的都是业务相关的,但是这里牵扯到一个Binder实体与Handle的转换过程,同城也牵扯两个进程在内核空间共享一些数据的问题,因此这里又进行了一次进一步的封装与拆封装,这里新封装了连个对象 binder_transaction与binder_work,有所区别的是binder_work可以看做是进程私有,但是binder_transaction是两个交互的进程共享的:binder_work是插入到线程或者进程的work todo队列上去的:
struct binder_thread {
struct binder_proc *proc;
struct rb_node rb_node;
int pid;
int looper;
struct binder_transaction *transaction_stack;
struct list_head todo;
uint32_t return_error; /* Write failed, return error code in read buf */
uint32_t return_error2; /* Write failed, return error code in read */
wait_queue_head_t wait;
struct binder_stats stats;
};
这里主要关心一下binder_transaction:binder_transaction主要记录了当前transaction的来源,去向,同时也为了返回做准备,buffer字段是一次拷贝后数据在Binder的内存地址。
struct binder_transaction {
int debug_id;
struct binder_work work;
struct binder_thread *from;
struct binder_transaction *from_parent;
struct binder_proc *to_proc;
struct binder_thread *to_thread;
struct binder_transaction *to_parent;
unsigned need_reply:1;
/* unsigned is_dead:1; */ /* not used at the moment */
struct binder_buffer *buffer;
unsigned int code;
unsigned int flags;
long priority;
long saved_priority;
uid_t sender_euid;
};
相关阅读:
https://www.163yun.com/gift
本文来自网易实践者社区,经作者李尚授权发布。