本篇是第二篇,主要是涉及线程与进程的唤醒,数据传输的封装与解析,
- Binder线程的睡眠与唤醒(请求线程睡在哪个等待队列上,唤醒目标端哪个队列上的线程)
- Binder协议中BC与BR的区别
- Binder在传输数据的时候是如何层层封装的–不同层次使用的数据结构(命令的封装)
- Binder驱动传递数据的释放(释放时机)
- 一个简单的Binder通信C/S模型
Client端线程睡眠在哪个队列上,唤醒Server端哪个等待队列上的线程
先看第一部分:发送端线程睡眠在哪个队列上?
发送端线程一定睡眠在自己binder_thread的等待队列上,并且,该队列上有且只有自己一个睡眠线程
再看第二部分:在Binder驱动去唤醒线程的时候,唤醒的是哪个等待队列上的线程?
理解这个问题需要理解binder_thread中的 struct binder_transaction * transaction_stack栈,这个栈规定了transaction的执行顺序:栈顶的一定先于栈内执行。
如果本地操作是BC_REPLY,一定是唤醒之前发送等待的线程,这个是100%的,但是如果是BC_TRANSACTION,那就不一定了,尤其是当两端互为服务相互请求的时候,场景如下:
- 进程A的普通线程AT1请求B进程的B1服务,唤醒B进程的Binder线程,AT1睡眠等待服务结束
- B进程的B1服务在执行的的时候,需要请求进程A的A1服务,则B进程的Binder线程BT1睡眠,等待服务结束。
这个时候就会遇到一个问题:唤醒哪个线程比较合适?是睡眠在进程队列上的线程,还是之前睡眠的线程AT1?答案是:之前睡眠等待B服务返回的线程AT1,具体看下面的图解分析
首先第一步A普通线程去请求B进程的B1服务,这个时候在A进程的AT1线程的binder_ref中会将binder_transaction1入栈,而同样B的Binder线程在读取binder_work之后,也会将binder_transaction1加入自己的堆栈,如下图:
而当B的Binder线程被唤醒后,执行Binder实体中的服务时,发现服务函数需要反过来去请求A端的A1服务,那就需要通过Binder向A进程发送请求,并新建binder_transaction2压入自己的binder_transaction堆栈,这个没有任何问题。但是,在A端入栈的时候,会面临一个抉择,写入那个队列?是binder_proc上的队列,还是正在等候B1服务返回的AT1线程的队列?
结果已经说过,是AT1的队列,为什么呢?因为AT1队列上的之前的binder_transaction1在等待B进程执行完,但是B端执行binder_transaction1时候,需要等待binder_transaction2执行完,也就是说,在binder_transaction2执行完毕前,A端的binder_transaction1一定是不会被执行的,也就是线程AT1在B执行binder_transaction2的时候,一定是空闲的,那么,不妨唤醒AT1线程,让它帮忙执行完binder_transaction2,执行完之后,AT1又会睡眠等待B端返回,这样,既不妨碍binder_transaction1的执行,同样也能提高AT1线程利用率,出栈的过程其实就简单了,
- AT1 执行binder_transaction2,唤醒B端BT1 Binder线程,并且AT1继续睡眠(因为还有等待的transaction)
- BT1 处理binder_transaction2结果,并执行完binder_transaction1,唤醒AT1
- AT1处理binder_transaction1返回结果 执行结束
不妨再深入一点,如果A端binder_transaction2又需要B进程B2服务,这个时候是什么效果唤醒谁,答案是BT1,这就杜绝了两端循环请求的,不断增加线程池容量。
从这里可以看出,Binder其实设计的还是很巧妙的,让线程复用,提高了效率,还避免了新建不必要的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;
... }
} }
Binder协议中BC与BR的区别
BC与BR主要是标志数据及Transaction流向,其中BC是从用户空间流向内核,而BR是从内核流线用户空间,比如Client向Server发送请求的时候,用的是BC_TRANSACTION,当数据被写入到目标进程后,target_proc所在的进程被唤醒,在内核空间中,会将BC转换为BR,并将数据与操作传递该用户空间。
Binder在传输数据的时候是如何层层封装的–不同层次使用的数据结构(命令的封装)
内核中,与用户空间对应的结构体对象都需要新建,但传输数据的数据只拷贝一次,就是一次拷贝的时候。
从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_TRANSAC