一 Binder驱动事务读取
在上一篇文章中,我们知道 Binder 驱动生成了一个 binder_transaction 事务,并把这个事务插入到了目标进程或目标线程的 todo 队列中,并唤醒了目标进程或目标线程.那么目标端是怎么处理的呢?
先说个大概流程:Binder 驱动唤醒目标进程或目标线程后,目标线程会在读取函数中休眠等待的地方醒过来,然后到自己的 todo 队列上把这个事务摘下来,然后分析这个事务,根据事务中携带的数据信息,找到目标 Binder 实体,最后把数据传递给目标Binder实体.这个只是大概的流程,接下来我们详细分析.
1.1 目标线程被唤醒
我们知道负责与 Binder 通信的都是一个个具体的线程, 当目标进程被唤醒时,实际上唤醒的是该目标进程中的某一个 Binder 线程,这个 Binder 线程正常的情况下会通过调用 binder_thread_read() 函数来读取请求者的数据,如果暂时还没有数据传输过来的时候,就会休眠阻塞在这个函数中;而如果有数据传输过来的话,则会在休眠阻塞的地方被唤醒,然后接着处理传输过来的数据,我们来看这个函数:
static int binder_thread_read(struct binder_proc *proc,
struct binder_thread *thread,
binder_uintptr_t binder_buffer, size_t size,
binder_size_t *consumed, int non_block)
{
void __user *buffer = (void __user *)(uintptr_t)binder_buffer;
void __user *ptr = buffer + *consumed;
void __user *end = buffer + size;
int ret = 0;
int wait_for_proc_work;
........
//检查本线程内部的todo队列中是否有待处理的事务,如果没有,那就等待处理本进程todo队列的work
wait_for_proc_work = binder_available_for_proc_work_ilocked(thread);
........
thread->looper |= BINDER_LOOPER_STATE_WAITING;//设置线程looper等待标志
........
if (non_block) {
........
} else {
//重要函数,休眠阻塞等待,被唤醒后开始往下执行
ret = binder_wait_for_work(thread, wait_for_proc_work);
}
//走到这里,证明已经被唤醒了,结束等待,需要去掉线程looper的等待状态
thread->looper &= ~BINDER_LOOPER_STATE_WAITING;
........
while (1) {
uint32_t cmd;
struct binder_transaction_data tr;
struct binder_work *w = NULL;
struct list_head *list = NULL;
struct binder_transaction *t = NULL;
struct binder_thread *t_from;
binder_inner_proc_lock(proc);
//优先处理本线程内部的 todo 队列,如果为空,则处理进程的 todo 队列
if (!binder_worklist_empty_ilocked(&thread->todo))
list = &thread->todo;//选取线程的todo
else if (!binder_worklist_empty_ilocked(&proc->todo) &&
wait_for_proc_work)
list = &proc->todo;//选取进程的todo
else {
//若无数据且当前线程looper_need_return为false, 则重试
if (ptr - buffer == 4 && !thread->looper_need_return)
goto retry;
break;
}
........
//选取好todo队列后,开始从todo队列中摘除待处理的binder_transaction事务
w = binder_dequeue_work_head_ilocked(list);//从list队列中取出第一项work
if (binder_worklist_empty_ilocked(&thread->todo))
//如果线程的todo为空,则设置process_todo false
thread->process_todo = false;
switch (w->type) {//判断binder_work的类型,这里为BINDER_WORK_TRANSACTION
case BINDER_WORK_TRANSACTION: {
//根据work找到对应的binder_transaction事务
t = container_of(w, struct binder_transaction, work);
} break;
........
}//end switch
if (!t)
continue;
//以上我们已经拿到从请求端发送过来的binder_transaction事务
//接下来解析这个事务
if (t->buffer->target_node) {//是否存在目标节点,这里为存在
struct binder_node *target_node = t->buffer->target_node;
struct binder_priority node_prio;
//非常重要,把Binder实体的地址赋值给tr的target.ptr
tr.target.ptr = target_node->ptr;
//非常重要,Binder实体的地址赋值给tr的target.cookie
tr.cookie = target_node->cookie;
........
cmd = BR_TRANSACTION;//构建BR_TRANSACTION命令把tr数据返回到用户空间
} else {
tr.target.ptr = 0;
tr.cookie = 0;
cmd = BR_REPLY;
}
//事务的信息全部赋值给tr这个binder_transaction_data结构,用来返回到用户空间
tr.code = t->code;
tr.flags = t->flags;
tr.sender_euid = from_kuid(current_user_ns(), t->sender_euid);
t_from = binder_get_txn_from(t);//获取事务t的from源线程
........
tr.data_size = t->buffer->data_size;
tr.offsets_size = t->buffer->offsets_size;
// 使tr中的数据缓冲区和偏移数组的地址值指向事务t中的数据缓冲区和偏移数组的地址值
tr.data.ptr.buffer = (binder_uintptr_t)
((uintptr_t)t->buffer->data +
binder_alloc_get_user_buffer_offset(&proc->alloc));
tr.data.ptr.offsets = tr.data.ptr.buffer +
ALIGN(t->buffer->data_size,
sizeof(void *));
//将返回协议BR_TRANSACTION拷贝到目标线程提供的用户空间
if (put_user(cmd, (uint32_t __user *)ptr)) {
........
}
ptr += sizeof(uint32_t);
// 将binder_transaction_data类型的tr拷贝到目标线程提供的用户空间
if (copy_to_user(ptr, &tr, sizeof(tr))) {
........
}
ptr += sizeof(tr);
........
//表示事务t的buffer允许目标线程在用户空间发出BC_FREE_BUFFER命令协议来释放
t->buffer->allow_user_free = 1;
if (cmd == BR_TRANSACTION && !(t->flags & TF_ONE_WAY)) {
//同步操作,要等事务t处理完, 才释放缓存空间
//同时需要对事务栈transaction_stack进行处理
t->to_parent = thread->transaction_stack;
t->to_thread = thread;
thread->transaction_stack = t;
} else {//异步操作可以直接free这个事务t了
binder_free_transaction(t);
}
break;
}
........
//每次读取完事务后,都需要检查进程中的Binder线程是否够用,如果不够用需要通知新增线程
//新增条件是:请求线程数为0,并且等待线程数也为0,并且已启动线程数不超过最大线程数
//并且线程的状态为已经注册或已经进程循环,满足以上条件则向用户空间发送BR_SPAWN_LOOPER
//命令,告诉用户空间创建一个新的Binder线程
if (proc->requested_threads == 0 &&//正在请求还没有启动的线程数量
list_empty(&thread->proc->waiting_threads) &&//线程中空闲等待的线程数量
proc->requested_threads_started < proc->max_threads &&
(thread->looper & (BINDER_LOOPER_STATE_REGISTERED |
BINDER_LOOPER_STATE_ENTERED))) {
proc->requested_threads++;//正在请求还没有启动的线程数量加1
........
//向用户空间发送命令BR_SPAWN_LOOPER, 创建新线程
if (put_user(BR_SPAWN_LOOPER, (uint32_t __user *)buffer))
return -EFAULT;
........
} else
........
return 0;
}
代码中的注释已经很详细了,简单说来就是,如果线程或进程中的 todo 队列中没有事务需要处理,binder_thread_read()函数就会进入休眠等待,否则binder_thread_read()函数就会在本线程或本进程的 todo 队列中摘下一个代表一个事务的work节点,并通过work节点找到对应的Binder事务,然后把这个事务里的数据整理成一个binder_transaction_data结构,然后通过copy_to_user()把该结构传输到用户空间。如果这个事务携带有TF_ONE_WAY标记,那么copy完后,需要把这个事务给free掉,因为已经不需要了,相反如果不是TF_ONE_WAY的话,那么就是同步操作,那么这个事务就不能被free掉,需要把这个事务链接到本线程的事务栈中,并修改这个事务相关的事务栈的信息.
还需要注意的是,在binder_thread_read()函数的最后,也就是读取完毕后,需要检查下本进程的Binder线程数量是否够用,也就是现有启动的Binder线程能否及时地响应和处理其它进程发过来的请求,主要的判断条件是本进程中 waiting_threads 的数量是否为0 ,这个变量表示本进程中空闲等待线程的数量,如果为0表示已经没有空闲线程了,那么就需要新启动一个线程,同时还需要判断开启的线程数量是否已经达到上限,如果没有达到上限那么就可以新增一个Binder线程.
以下是binder_thread_read函数中涉及到的相关函数:
获取本线程是否能够处理本进程的事务,只有本线程的todo队列为空,且事务栈transaction_stack为空的时候,才表示当先线程是空闲的,这个时候才可以处理所属进程的事务,否则的话,这个线程需要处理自己线程内部的todo队列中的事务.
static bool binder_available_for_proc_work_ilocked(struct binder_thread *thread)
{
return !thread->transaction_stack &&
binder_worklist_empty_ilocked(&thread->todo) &&
(thread->looper & (BINDER_LOOPER_STATE_ENTERED |
BINDER_LOOPER_STATE_REGISTERED));
}
休眠等待函数 binder_wait_for_work
static int binder_wait_for_work(struct binder_thread *thread,
bool do_proc_work)
{
DEFINE_WAIT(wait);//建立并初始化一个等待队列项wait
struct binder_proc *proc = thread->proc;
int ret = 0;
freezer_do_not_count();
binder_inner_proc_lock(proc);
for (;;) {//循环的作用是让线程被唤醒后再一次去检查一下condition是否满足
prepare_to_wait(&thread->wait, &wait, TASK_INTERRUPTIBLE);//将wait添加到等待队列头中,并设置进程的状态
if (binder_has_work_ilocked(thread, do_proc_work))//唤醒条件condition,如果满足则跳出循环,否则一直循环等待
break;
if (do_proc_work)//如果是在等待处理本进程的todo队列的任务
list_add(&thread->waiting_thread_node,
&proc->waiting_threads);//把本线程的waiting_thread_node添加到所属进程的waiting_threads中
binder_inner_proc_unlock(proc);
schedule();//调用schedule(),让出cpu资源,开始休眠
binder_inner_proc_lock(proc);
list_del_init(&thread->waiting_thread_node);
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
}
finish_wait(&thread->wait, &wait);//执行清理工作
binder_inner_proc_unlock(proc);
freezer_count();
return ret;
}
二 Binder实体端对请求的处理
2.1 特殊Binder实体SMgr对请求的处理
服务端的Binder实体分两种情况,一种是普通的Binder实体所在的Server端,还有一种是Binder的守护进程SMgr,我们先看SMgr是如何处理的,我们回忆下在Binder系列3中介绍的 SMgr 的 binder_loop循环.
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;//构造BINDER_WRITE_READ命令的数据binder_write_read
uint32_t readbuf[32];
bwr.write_size = 0;//write_size置为0表示只读不写
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint32_t));
//向Binder驱动发送BC_ENTER_LOOPER命令,通知Binder驱动本线程进入loop状态
for (;;) {//无限for循环
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);//通过ioctl读取来自Binder驱动的数据
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
// 调用binder_parse函数解析读取的数据
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == 0) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
SMgr中的 binder_loop 函数通过ioctl和内核空间通信,我们知道当ioctl执行完上面介绍的 binder_thread_read 函数后会通过put_user和copy_to_user 分别把BR_TRANSACTION命令和binder_transaction_data数据拷贝到用户空间,通过代码分析我们知道,就是拷贝到了这个binder_loop函数中定义的binder_write_read数据的read_buffer中,也就是readbuf中,接下来就可以通过binder_parse函数来解析这些数据了,接下来的解析工作,都在binder_parse函数中,大家可以参考Binder系列3中的介绍,这里不再赘述了.
2.2 普通Binder实体所在的Server端进程对请求的处理
我们知道普通Binder实体所在的Server端进程对请求的处理与SMgr是不同的,他们是怎么处理的呢?我们以MediaPlayerService为例分析,代码如下:
int main(int argc __unused, char **argv __unused)
{
signal(SIGPIPE, SIG_IGN);
sp<ProcessState> proc(ProcessState::self());
sp<IServiceManager> sm(defaultServiceManager());
ALOGI("ServiceManager: %p", sm.get());
InitializeIcuOrDie();
MediaPlayerService::instantiate();//初始化
ResourceManagerService::instantiate();
registerExtensions();
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
}
这个是MediaPlayerService的入口函数,我们观察ProcessState的startThreadPool函数
void ProcessState::startThreadPool()
{
AutoMutex _l(mLock);
if (!mThreadPoolStarted) {
mThreadPoolStarted = true;
spawnPooledThread(true);//调用spawnPooledThread
}
}
void ProcessState::spawnPooledThread(bool isMain)
{
if (mThreadPoolStarted) {
String8 name = makeBinderThreadName();
ALOGV("Spawning new pooled thread, name=%s\n", name.string());
sp<Thread> t = new PoolThread(isMain);
t->run(name.string());//生成一个PoolThread对象并调用run运行
}
}
class PoolThread : public Thread
{
public:
explicit PoolThread(bool isMain)
: mIsMain(isMain)
{
}
protected:
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);//调用IPCThreadState的joinThreadPool
return false;
}
const bool mIsMain;
};
void IPCThreadState::joinThreadPool(bool isMain)
{
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);//先把BC_ENTER_LOOPER写入Binder驱动
status_t result;
do {
processPendingDerefs();
// now get the next command to be processed, waiting if necessary
result = getAndExecuteCommand();//循环调用以从Binder驱动读取数据
........
// Let this thread exit the thread pool if it is no longer
// needed and it is not the main process thread.
if(result == TIMED_OUT && !isMain) {
break;
}
} while (result != -ECONNREFUSED && result != -EBADF);
........
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
}
从以上代码可知在joinThreadPool函数中,会循环调用getAndExecuteCommand函数,这个函数主要负责从Binder驱动读取数据并处理,代码如下:
status_t IPCThreadState::getAndExecuteCommand()
{
status_t result;
int32_t cmd;
result = talkWithDriver();//和Binder驱动进行交互
if (result >= NO_ERROR) {
size_t IN = mIn.dataAvail();
if (IN < sizeof(int32_t)) return result;
cmd = mIn.readInt32();
........
pthread_mutex_lock(&mProcess->mThreadCountLock);
mProcess->mExecutingThreadsCount++;
if (mProcess->mExecutingThreadsCount >= mProcess->mMaxThreads &&
mProcess->mStarvationStartTimeMs == 0) {
mProcess->mStarvationStartTimeMs = uptimeMillis();
}
pthread_mutex_unlock(&mProcess->mThreadCountLock);
result = executeCommand(cmd);//对读取的数据进行处理
pthread_mutex_lock(&mProcess->mThreadCountLock);
mProcess->mExecutingThreadsCount--;
if (mProcess->mExecutingThreadsCount < mProcess->mMaxThreads &&
mProcess->mStarvationStartTimeMs != 0) {
int64_t starvationTimeMs = uptimeMillis() - mProcess->mStarvationStartTimeMs;
if (starvationTimeMs > 100) {
ALOGE("binder thread pool (%zu threads) starved for %" PRId64 " ms",
mProcess->mMaxThreads, starvationTimeMs);
}
mProcess->mStarvationStartTimeMs = 0;
}
pthread_cond_broadcast(&mProcess->mThreadCountDecrement);
pthread_mutex_unlock(&mProcess->mThreadCountLock);
}
return result;
}
getAndExecuteCommand 函数主要调用了2个方法,一个是 talkWithDriver 用来和 Binder 交互,主要是从Binder驱动读取数据;另外一个是 executeCommand 函数用来处理从Binder驱动读取过来的数据.
status_t IPCThreadState::talkWithDriver(bool doReceive)
{
........
binder_write_read bwr;
// Is the read buffer empty?
const bool needRead = mIn.dataPosition() >= mIn.dataSize();
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 (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)//调用ioctl与Binder驱动交互
err = NO_ERROR;
else
err = -errno;
........
} 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);
}
if (bwr.read_consumed > 0) {
mIn.setDataSize(bwr.read_consumed);
mIn.setDataPosition(0);
}
........
return NO_ERROR;
}
return err;
}
接下来看 executeCommand 函数,对命令和携带的数据进行解析和处理.
status_t IPCThreadState::executeCommand(int32_t cmd)
{
BBinder* obj;
RefBase::weakref_type* refs;
status_t result = NO_ERROR;
switch ((uint32_t)cmd) {
........
case BR_TRANSACTION:
{
binder_transaction_data tr;
result = mIn.read(&tr, sizeof(tr));//从mIn数据包中读取数据到tr
........
Parcel buffer;
buffer.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);
........
Parcel reply;
status_t error;
........
if (tr.target.ptr) {//Binder实体的地址
if (reinterpret_cast<RefBase::weakref_type*>(
tr.target.ptr)->attemptIncStrong(this)) {
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags);//tr.cookie就是Binder实体的地址,然后执行transact函数
reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
} else {
error = UNKNOWN_TRANSACTION;
}
} else {
error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
}
........
if ((tr.flags & TF_ONE_WAY) == 0) {
LOG_ONEWAY("Sending reply to %d!", mCallingPid);
if (error < NO_ERROR) reply.setError(error);
sendReply(reply, 0);
} else {
LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
}
........
}
break;
........
default:
ALOGE("*** BAD COMMAND %d received from Binder driver\n", cmd);
result = UNKNOWN_ERROR;
break;
}
if (result != NO_ERROR) {
mLastError = result;
}
return result;
}
请注意上面代码中的reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer, &reply, tr.flags); 这里的 tr.cookie 就是Binder实体的地址,然后调用BBinder的 transact 函数.
status_t BBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
data.setDataPosition(0);
status_t err = NO_ERROR;
switch (code) {
case PING_TRANSACTION:
reply->writeInt32(pingBinder());
break;
default:
err = onTransact(code, data, reply, flags);//调用BBinder子类的onTransact方法
break;
}
if (reply != NULL) {
reply->setDataPosition(0);
}
return err;
}
其中最关键的一句是调用onTransact(),因为我们的Binder实体在本质上都是继承于BBinder的,而且我们一般都会重载onTransact()函数,所以上面这句onTransact()实际上调用的是具体的继承于BBinder的Binder实体的onTransact()成员函数,对于MediaPlayerService来说,它的Binder实体是 BnMediaPlayerService 如下:
class BnMediaPlayerService: public BnInterface<IMediaPlayerService>
{
public:
virtual status_t onTransact( uint32_t code,
const Parcel& data,
Parcel* reply,
uint32_t flags = 0);
};
};
所以最终调用的是 BnMediaPlayerService 的 onTransact 函数,代码如下:
status_t BnMediaPlayerService::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
switch (code) {
case CREATE: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
sp<IMediaPlayerClient> client =
interface_cast<IMediaPlayerClient>(data.readStrongBinder());
audio_session_t audioSessionId = (audio_session_t) data.readInt32();
sp<IMediaPlayer> player = create(client, audioSessionId);
reply->writeStrongBinder(IInterface::asBinder(player));
return NO_ERROR;
} break;
case CREATE_MEDIA_RECORDER: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
const String16 opPackageName = data.readString16();
sp<IMediaRecorder> recorder = createMediaRecorder(opPackageName);
reply->writeStrongBinder(IInterface::asBinder(recorder));
return NO_ERROR;
} break;
case CREATE_METADATA_RETRIEVER: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
sp<IMediaMetadataRetriever> retriever = createMetadataRetriever();
reply->writeStrongBinder(IInterface::asBinder(retriever));
return NO_ERROR;
} break;
case GET_OMX: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
sp<IOMX> omx = getOMX();
reply->writeStrongBinder(IInterface::asBinder(omx));
return NO_ERROR;
} break;
........
case GET_CODEC_LIST: {
CHECK_INTERFACE(IMediaPlayerService, data, reply);
sp<IMediaCodecList> mcl = getCodecList();
reply->writeStrongBinder(IInterface::asBinder(mcl));
return NO_ERROR;
} break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
这个 onTransact 函数就是对具体语义进行分析了,其中的 code 就是函数的编号,根据这个值进行逐个解析,然后执行相应的操作,完成服务的远程使用.
以上就是 Binder 传输机制的接收端对数据的接收和处理的调用流程,大家需要多思考并根据代码进行验证.接下来的几篇文章会结合实例把这些操作流程再走一遍,以加深理解.如有问题和建议欢迎大家提出交流.