Android 进阶——Binder IPC之Native 服务的启动及代理对象的获取详解
Posted CrazyMo_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 进阶——Binder IPC之Native 服务的启动及代理对象的获取详解相关的知识,希望对你有一定的参考价值。
文章大纲
引言
我们都知道Binder IPC可以支持并发访问和响应,但是你知道是为什么么?
一、Binder线程池的启动
Binder线程池的启动主要就是依赖以下两个函数。一个进程通过调用其内部的ProcessState对象的成员startThreadPool函数来启动线程池,通过IPCThreadState对象的成员函数joinThreadPool,将启动的线程加入到Binder线程池并注册成为一个Binder线程。当线程启动之后,会不断与binder驱动进行通信,读取本线程和本进程中待处理的事务,如果接受到Binder驱动的请求,则处理之。而当进程繁忙时, Binder驱动会向目标进程发送一个BR_SPAWN_LOOPER命令,申请一个新的进程。
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
当Binder驱动接到Server发来的IPC请求时,就会回复一个BR_SPAWN_LOOPER
通知创建Binder线程用于处理这个请求,当threadLoop函数返回false时则会被回收相应的对象。
1、ProcessState#startThreadPool函数来启动线程池
ProcessState在同一进程内是唯一的,主要用于初始化Binder设备,而IPCThreadState 用于与Binder驱动通信。
通常Service组件注册完毕之后对应的进程就会自动开启一个Binder线程池来处理Client进程发送过来的IPC请求。通常进程是通过调用其内部的ProcessState对象的startThreadPool函数来启动的。
\\frameworks\\native\\libs\\binder\\ProcessState.cpp
void ProcessState::startThreadPool()
AutoMutex _l(mLock);
if (!mThreadPoolStarted) //防止重复启动线程池
mThreadPoolStarted = true;
spawnPooledThread(true);
从上面我们可以得知一个进程中有且只有一个Binder线程池且只启动一次,接着是真正通过ProcessState#spawnPooledThread来启动线程池的。
isMain为true表示是线程是进程主动创建并加入到它Binder线程池的,对应的是BC_ENTER_LOOPER协议,而Binder驱动请求进程创建的线程则isMain为false,对应的是BC_REGISTER_LOOPER。
void ProcessState::spawnPooledThread(bool isMain)
if (mThreadPoolStarted)
String8 name = makeBinderThreadName();//Binder线程的名称
sp<Thread> t = new PoolThread(isMain);
t->run(name.string());
String8 ProcessState::makeBinderThreadName()
int32_t s = android_atomic_add(1, &mThreadPoolSeq);
pid_t pid = getpid();
String8 name;
name.appendFormat("Binder:%d_%X", pid, s);
return name;
主要就是创建PoolThread对象并调用其run函数启动一个新线程,而PoolThread 继承Thread并重写了线程入口函数threadLoop
class PoolThread : public Thread
public:
PoolThread(bool isMain)
: mIsMain(isMain)
protected:
virtual bool threadLoop()
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
const bool mIsMain;
;
接着IPCThreadState#joinThreadPool 将当前线程进入到线程池中去等待和处理IPC请求。
Binder驱动接到IPC 请求的时候就会发一个 BR_SPAWN_LOOPE ,然后Server 端就创建一个线程来处理,但是一个fd 最多绑定15线程。
2、IPCThreadState#joinThreadPool 将当前线程进入到线程池中去等待和处理IPC请求
IPCThreadState#joinThreadPool 函数将当前线程注册到Binder驱动中,成为一个Binder线程,以便Binder驱动可以分发IPC请求给它处理,在进入Binder驱动前talkWithDriver会自动检测IPCThreadState内部的协议输出缓冲区是否还有协议,有则发给BInder驱动处理,处理完成返回后Binder驱动回复返回协议保存至IPCThreadState内部的协议输入缓冲区中,函数getAndExecuteCommand将会去处理。
void IPCThreadState::joinThreadPool(bool isMain)
//将协议写入到IPCThreadState的输出缓冲区
mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);
set_sched_policy(mMyThreadId, SP_FOREGROUND);
status_t result;
do
processPendingDerefs();
// now get the next command to be processed, waiting if necessary
result = getAndExecuteCommand();
if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF)
abort();
if(result == TIMED_OUT && !isMain)
break;
while (result != -ECONNREFUSED && result != -EBADF);
LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p\\n",
(void*)pthread_self(), getpid(), (void*)result);
mOut.writeInt32(BC_EXIT_LOOPER);
talkWithDriver(false);
若talkWithDriver函数长期没有等到IPC请求或者getAndExecuteCommand函数执行超时,且isMain为false时就跳出循环,并向Binder驱动发送BC_EXIT_LOOPER协议告知Binder驱动,前面创建的这个线程它要退出Binder线程池了。
二、Service 代理对象的获取
Server进程和Client进程的通信要依靠Binder驱动来进行。
Service组件在启动时,会将自己注册到ServiceManager组件中,以便Client组件通过ServiceManager来找到这个Service组件。
在Binder IPC机制中Client要与Server通信,Client组件必须要先获取Service组件的代理对象,使用的时候很方便直接通过Service Manager代理对象的getService
接口就可以获取,前面分析了ServiceManager 自身代理对象的获取,普通Service 组件代理对象获取的流程也大同小异,虽然说要与Binder驱动沟通,但是对于我们开发者来说这一部分的工作被Service Manager 帮忙承担了。
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16(DETECT_SERVICE_NAME));
sp<IDetectService> bpDetectService = interface_cast <IDetectService>(binder);
总结起来在Client进程(本例中名称为DetectClient)获取一个普通Service 代理对象三部曲为:
1、获取Service Manager 代理对象BpServiceManager
主要就是通过IServiceManager#defaultServiceManager函数获取,预知详情参见前文。
2、调用BpServiceManager#getService
BpServiceManager继承自BpInterface,而BpInterface是一个继承BpRefsBase的模板类,在getService函数中最多会尝试100次来尝试获取名称对应的Service组件代理对象
class BpServiceManager : public BpInterface<IServiceManager>
public:
BpServiceManager(const sp<IBinder>& impl)
: BpInterface<IServiceManager>(impl)
virtual sp<IBinder> getService(const String16& name) const
unsigned n;
for (n = 0; n < 100; n++)
if (n > 0)
ALOGI("Waiting for service %s...", String8(name).string());
usleep(50000);
sp<IBinder> svc = checkService(name);
if (svc != NULL) return svc;
return NULL;
...
;
在checkService函数来真正去尝试获取Service组件代理对象,对比前文addService的核心流程大同小异(通过Service Manager代理对象请求Service Manager进程执行ADD_SERVICE_TRANSACTION),把协议换为CHECK_SERVICE_TRANSACTION协议即可。
virtual sp<IBinder> checkService( const String16& name) const
Parcel data, reply;
//执行writeInterfaceToken函数,拼装Binder协议头
data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
data.writeString16(name);
//remote()即0号引用
remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);
return reply.readStrongBinder();
换言之,getService 的实现就一个标准的Binder IPC 五部曲:
步骤 | 说明 |
---|---|
1 | DetectClient将DetectService代理对象对应的名称封装为Parcel对象并传递到Binder驱动。 |
2 | DetectClient向Binder驱动发送BC_TRANSACTION_COMPLETE协议,Binder驱动根据协议内容找到目标Service Manager进程后回复一个BR_TRANSACTION_COMPLETE告知DetectClient其通信请求已被接受,Client接到BR_TRANSACTION_COMPLETE并处理后,会再次进入到Binder驱动程序中等待Server Manager进程的返回它想要的代理对象的句柄。 |
3 | Binder驱动在给DetectClient回复一个BR_TRANSACTION_COMPLETE的同时,向Service Manager进程发送一个BR_TRANSACTION返回协议,请求目标Server Manager 进程执行CHECK_SERVICE_TRANSACTION指令。 |
4 | Service Manager进程接收BR_TRANSACTION并处理CHECK_SERVICE_TRANSACTION后,给驱动回复一个BC_REPLY协议(包含了DetectClient申请Service组件的信息),驱动根据协议内容为DetectClient进程创建一个对应的Binder引用对象,给Service Manager进程发送一个BR_TRANSACTION_COMPLETE返回协议,告知Service Manager它返回的目标Service 组件信息已经收到了,已接到返回的相应结果,Service Manager接到并处理了BR_TRANSACTION_COMPLETE协议后,一次IPC流程就结束了,接着会重新进入到驱动程序中等待下一次IPC请求。 |
5 | Binder驱动在给Service Manager进程发送BR_TRANSACTION_COMPLETE的同时,也会向DetectClient进程发送一个BR_REPLY返回协议(内容包含了前面所创建的Binder引用对象的举止值),DetectClient进程就可以拿着这个句柄来创建一个目标Binder代理对象,同时也代表Service Manager进程已经处理完成IPC请求了并将结果返回给Client |
通过这样子就可以拿到了Service 代理对象,简而言之,Client进程通过Service Manager进程拿到目标Service代理对象的引用对象的句柄值
,再根据这个句柄值创建对应的Service 代理对象
。
2.1、Service Manager 处理CHECK_SERVICE_TRANSACTION
前面说过Service Manager是在svcmgr_handle
函数中统一处理Client进程的IPC请求。
\\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;
...
switch(txn->code)
case SVC_MGR_GET_SERVICE:
case SVC_MGR_CHECK_SERVICE:
//从binder_io结构体中的msg数据缓冲区得到代理对象的名称
s = bio_get_string16(msg, &len);
if (s == NULL)
return -1;
//获取目标Binder引用对象的句柄
handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
if (!handle)
break;
//传入目标引用对象的句柄到Binder驱动,并封装为一个对应的binder_object结构体并下入reply中,Binder驱动拿到的返回值就是reply
bio_put_ref(reply, handle);
return 0;
case SVC_MGR_ADD_SERVICE:
...
break;
case SVC_MGR_LIST_SERVICES:
...
bio_put_uint32(reply, 0);
return 0;
从binder_io结构体中的msg数据缓冲区还原出代理对象的名称后,通过do_find_service函数从已注册的Service组件列表svclist中查找与之对应的一个svcinfo结构体,并返回Service 组件的句柄。
在Service Manager中每一个被注册了的Service组件对应一个svcinfo 结构并保存在一个全局队列svclist中,其中svcinfo的成员next指向下一个svcinfo结构体、prt是一个Service组件Binder引用对象的句柄值、name是Service组件名称,death指向一个用于描述死亡接收通知的结构体binder_death。
uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid)
// 查找与字符串s对应一个svcinfo结构体
struct svcinfo *si = find_svc(s, len);
...
if (!svc_can_find(s, len, spid, uid))
return 0;
return si->handle;
2.2、Binder 驱动为Client进程创建对应的Service组件的Binder引用对象
找到Service组件结构体后就得到了其他Binder引用对象的句柄,返回到svcmgr_handler函数中,Service Manager接着通过bio_put_ref函数把句柄传给Binder驱动,Binder驱动就根据它找到对应的Binder引用对象,进而找到该引用对象所指向(引用)的Binder实体对象,最后Binder驱动就在请求该Service组件的代理对象时创建另一个Binder引用对象了。
\\frameworks\\native\\cmds\\servicemanager\\binder.c
void bio_put_ref(struct binder_io *bio, uint32_t handle)
struct flat_binder_object *obj;
if (handle)//非0位true
obj = bio_alloc_obj(bio);
else
obj = bio_alloc(bio, sizeof(*obj));
if (!obj)
return;
obj->flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
obj->type = BINDER_TYPE_HANDLE;
obj->handle = handle;
obj->cookie = 0;
bio_alloc函数在binder_io结构体的数据缓冲区分配一个未初始化的binder_object结构体并赋值给obj,保存了位置后返回到bio_put_ref函数,继续对创建的结构体进行初始化后
static struct flat_binder_object *bio_alloc_obj(struct binder_io *bio)
struct flat_binder_object *obj;
obj = bio_alloc(bio, sizeof(*obj));//在binder_io结构体的数据缓冲区分配一个未初始化的binder_object结构体
//在binder_io的bio偏移数组中分配一个元素来保存binder_object结构体在数据缓冲区的位置,方便Binder驱动获知Service Manager给它返回的IPC结果中包含了一个Binder对象
if (obj && bio->offs_avail)
bio->offs_avail--;
*bio->offs++ = ((char*) obj) - ((char*) bio->data0);
return obj;
bio->flags |= BIO_F_OVERFLOW;
return NULL;
再回到svcmgr_handler函数中将Binder驱动IPC结果保存到binder_io类型结构体reply中了,接着回到binder_parse中最后调用binder_send_reply函数将reply的内容返回给Binder驱动(即向Binder驱动发送BC_REPLY协议)。
Binder驱动是在binder_transaction函数中处理Service Manager 发来的协议
Service Manager进程返回给Binder驱动的IPC结果中包含了BINDER_TYPE_WEAK_HANDLE类型的binder_object结构体(即flat_binder_object 结构体),Binder驱动就对这个结构体进行解包并从其中找到Binder引用对象ref(即指向的是运行在Server进程中的DetectService组件),然后查找是否存在对应的Binder引用对象,存在则直接返回给调用者,不存在则为Client进程创建一个新的Binder引用对象再返回,再经过Binder驱动一系列的处理之后
static void binder_transaction(struct binder_proc *proc,
struct binder_thread *thread,
struct binder_transaction_data *tr, int reply,
binder_size_t extra_buffers_size)
int ret;
struct binder_transaction *t;
struct binder_work *tcomplete;
struct binder_proc *target_proc = NULL;
struct binder_thread *target_thread = NULL;
struct binder_node *target_node = NULL;
struct binder_transaction *in_reply_to = NULL;
struct binder_buffer_object *last_fixup_obj = NULL;
struct binder_context *context = proc->context;
...
/* TODO: reuse incoming transaction for reply */
t = kzalloc(sizeof(*t), GFP_KERNEL);
binder_stats_created(BINDER_STAT_TRANSACTION_COMPLETE);
...
for (; offp < off_end; offp++)
struct binder_object_header *hdr;
switch (hdr->type)
case BINDER_TYPE_BINDER:
case BINDER_TYPE_WEAK_BINDER:
...
break;
case BINDER_TYPE_HANDLE:
case BINDER_TYPE_WEAK_HANDLE:
struct flat_binder_object *fp;
...
break;
2.3、Binder库为Client进程创建Binder代理对象
最终目标线程target_thread返回到用户空间之后,再次进去到IPCThreadState#waitForResponse
函数处理从Binder驱动读取回来的BR_REPLY协议,将Binder 驱动IPC结果封装为Parcel的对象reply中,再次返回到Service Manager代理对象的成员函数checkService
中,最后通过Parcel的readStrongBinder
函数得到Binder的代理对象。
status_t Parcel::readStrongBinder(sp<IBinder>* val) const
return unflatten_binder(ProcessState::self(), *this, val);
status_t unflatten_binder(const sp<ProcessState>& proc,
const Parcel& in, sp<IBinder>* out)
const flat_binder_object* flat = in.readObject(false);
if (flat)
switch (flat->type)
case BINDER_TYPE_BINDER:
*out = reinterpret_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE:
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast<BpBinder*>(out->get()), *flat, in);
return BAD_TYPE;
本质上还是通过ProcessState#getStrongProxyForHandle函数从其内部的Binder代理对象列表中查找是否存在一个与flat_binder_object结构体句柄值handle对应的Binder代理对象,存在则返回,不存在则根据handle创建相应的BInder代理对象再返回,而且flat_binder_object的handle同时是指向了Service组件引用对象,因此ProcessState#getStrongProxyForHandle函数返回的Binder代理对象也指向了Service组件的引用对象。
3、asInterface “转换”
当BpServiceManager#checkService 函数执行完成之后,就将一个Binder代理对象的IBinder接口返回到DetectClient进程的入口函数main,接着最差最后一步——"转换"为具体的Binder代理对象。
interface_cast并不是指针转换,而是利用BpBinder指针,构建出一个新的BpServiceManager对象。宏函数实现的。
android::sp<IDetectService> IDetectService::asInterface(
const android::sp<android::IBinder>& obj)
android::sp<IDetectService> intr;
if (obj != NULL)
intr = static_cast<IDetectService*>(
obj->queryLocalInterface(IDetectService::descriptor).get());
if (intr == NULL)
intr = new BpIDetectService(obj);
return intr;
以上是关于Android 进阶——Binder IPC之Native 服务的启动及代理对象的获取详解的主要内容,如果未能解决你的问题,请参考以下文章
Android 进阶——Binder IPC之Native 服务的启动及代理对象的获取详解
Android 进阶——Binder IPC之Native 服务的启动及代理对象的获取详解
Android 进阶——Framework 核心之Binder 相关预备理论