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 五部曲:

步骤说明
1DetectClient将DetectService代理对象对应的名称封装为Parcel对象并传递到Binder驱动。
2DetectClient向Binder驱动发送BC_TRANSACTION_COMPLETE协议,Binder驱动根据协议内容找到目标Service Manager进程后回复一个BR_TRANSACTION_COMPLETE告知DetectClient其通信请求已被接受,Client接到BR_TRANSACTION_COMPLETE并处理后,会再次进入到Binder驱动程序中等待Server Manager进程的返回它想要的代理对象的句柄
3Binder驱动在给DetectClient回复一个BR_TRANSACTION_COMPLETE的同时,向Service Manager进程发送一个BR_TRANSACTION返回协议,请求目标Server Manager 进程执行CHECK_SERVICE_TRANSACTION指令。
4Service 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请求。
5Binder驱动在给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 相关预备理论

Android 进阶——Framework 核心之Binder 相关预备理论

Android进阶——AIDL详解

android ipc通信机制之二序列化接口和Binder