Android Binder IPC分析
博客专区 > ifindbug 的博客 > 博客详情
Android Binder IPC分析
ifindbug 发表于3年前
Android Binder IPC分析
  • 发表于 3年前
  • 阅读 61
  • 收藏 0
  • 点赞 0
  • 评论 0

新睿云服务器60天免费使用,快来体验!>>>   

1. binder 通信概述

binder 通信是一种client-server 的通信结构,

1. 从表面上来看,是client 通过获得一个server 的代理接口,对server 进行直接调用;
2. 实际上,代理接口中定义的方法与server 中定义的方法是一一对应的;
3. client 调用某个代理接口中的方法时,代理接口的方法会将client 传递的参数打包成为Parcel 对象;
4. 代理接口将该Parcel 发送给内核中的binder driver.
5. server 会读取binder driver 中的请求数据,如果是发送给自己的,解包Parcel 对象,处理并将结果返回;
6. 整个的调用过程是一个同步过程,在server 处理的时候,client 会block 住。

C-S通信方式

2. service manager

Service Manager 是一个linux 级的进程, 顾名思义,就是service 的管理器。这里的service 是什么概念呢?这里的service 的概念和init 过程中init.rc 中的service 是不同,init.rc 中的service 是都是linux 进程,但是这里的service 它并不一定是一个进程,也就是说可能一个或多个service 属于同一个linux 进程。在这篇文章中不加特殊说明均指android native 端的service 。 任何service 在被使用之前,均要向SM(Service Manager) 注册,同时客户端需要访问某个service 时,应该首先向SM 查询是否存在该服务。如果SM 存在这个service ,那么会将该service 的handle 返回给client ,handle 是每个service 的唯一标识符。

SM 的入口函数在service_manager.c 中,下面是SM 的代码部分

<!-- lang: cpp -->
int main(int argc, char **argv)
{
    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;
 
    bs = binder_open(128*1024);
 
    if (binder_become_context_manager(bs)) {
        LOGE("cannot become context manager (%s)/n", strerror(errno));
        return -1;
    }
 
    svcmgr_handle = svcmgr;
    binder_loop(bs, svcmgr_handler);
    return 0;
}

这个进程的主要工作如下:

1. 初始化binder ,打开/dev/binder 设备;在内存中为binder 映射128K 字节空间;
2. 指定SM 对应的代理binder 的handle 为0 ,当client 尝试与SM 通信时,需要创建一个handle 为0 的代理binder ,这里的代理binder 其实就是第一节中描述的那个代理接口;
3. 通知binder driver(BD) 使SM 成为BD 的context manager ;
4. 维护一个死循环,在这个死循环中,不停地去读内核中binder driver ,查看是否有可读的内容;即是否有对service 的操作要求, 如果有,则调用svcmgr_handler 回调来处理请求的操作。
5. SM 维护了一个svclist 列表来存储service 的信息。

SM工作方式

这里需要声明一下,当service 在向SM 注册时,该service 就是一个client ,而SM 则作为了server 。而某个进程需要与service 通信时,此时这个进程为client ,service 才作为server 。因此service 不一定为server ,有时它也是作为client 存在的。

由于下面几节会介绍一些与binder 通信相关的几个概念,所以将SM 的功能介绍放在了后面的部分来讲。

应用和service 之间的通信会涉及到2 次 binder 通信。

1. 应用向SM 查询service 是否存在,如果存在获得该service 的代理binder ,此为一次binder 通信;
2. 应用通过代理binder 调用service 的方法,此为第二次binder 通信。

3. ProcessState

ProcessState 是以单例模式设计的。每个进程在使用binder 机制通信时,均需要维护一个ProcessState 实例来描述当前进程在binder 通信时的binder 状态。

ProcessState 有如下2 个主要功能:

1. 创建一个thread, 该线程负责与内核中的binder 模块进行通信,称该线程为Pool thread ;
2. 为指定的handle 创建一个BpBinder 对象,并管理该进程中所有的BpBinder 对象。

3.1 Pool thread

在Binder IPC 中,所有进程均会启动一个thread 来负责与BD 来直接通信,也就是不停的读写BD ,这个线程的实现主体是一个IPCThreadState 对象,下面会介绍这个类型。

下面是Pool thread 的启动方式:

ProcessState::self()->startThreadPool();

3.2 BpBinder 获取

BpBinder 主要功能是负责client 向BD 发送调用请求的数据。它是client 端binder 通信的核心对象,通过调用transact 函数向BD 发送调用请求的数据,它的构造函数如下:

BpBinder(int32_t handle);

通过BpBinder 的构造函数发现,BpBinder 会将当前通信中server 的handle 记录下来,当有数据发送时,会通知BD 数据的发送目标。

ProcessState 通过如下方式来获取BpBinder 对象:

ProcessState::self()->getContextObject(handle);

在这个过程中,ProcessState 会维护一个BpBinder 的vector mHandleToObject ,每当ProcessState 创建一个BpBinder 的实例时,回去查询mHandleToObject ,如果对应的handle 已经有binder 指针,那么不再创建,否则创建binder 并插入到mHandleToObject 中。

ProcessState 创建的BpBinder 实例,一般情况下会作为参数构建一个client 端的代理接口,这个代理接口的形式为BpINTERFACE , 例如在与SM 通信时,client 会创建一个代理接口BpServiceManager .

4. IPCThreadState

IPCThreadState 也是以单例模式设计的。由于每个进程只维护了一个ProcessState 实例,同时ProcessState 只启动一个Pool thread ,也就是说每一个进程只会启动一个Pool thread ,因此每个进程则只需要一个IPCThreadState 即可。

Pool thread 的实际内容则为:

IPCThreadState::self()->joinThreadPool();

ProcessState 中有2 个Parcel 成员,mIn 和mOut ,Pool thread 会不停的查询BD 中是否有数据可读,如果有将其读出并保存到mIn ,同时不停的检查mOut 是否有数据需要向BD 发送,如果有,则将其内容写入到BD 中,总而言之,从BD 中读出的数据保存到mIn ,待写入到BD 中的数据保存在了mOut 中。

ProcessState 中生成的BpBinder 实例通过调用IPCThreadState 的transact 函数来向mOut 中写入数据,这样的话这个binder IPC 过程的client 端的调用请求的发送过程就明了了 。

IPCThreadState 有两个重要的函数,talkWithDriver 函数负责从BD 读写数据,executeCommand 函数负责解析并执行mIn 中的数据。

5. 主要基类

5.1 基类IInterface

为server 端提供接口,它的子类声明了service 能够实现的所有的方法;

5.2 基类IBinder

BBinder 与BpBinder 均为IBinder 的子类,因此可以看出IBinder 定义了binder IPC 的通信协议,BBinder 与BpBinder 在这个协议框架内进行的收和发操作,构建了基本的binder IPC 机制。

5.3 基类BpRefBase

client 端在查询SM 获得所需的的BpBinder 后,BpRefBase 负责管理当前获得的BpBinder 实例。

6. 两个接口类

6.1 BpINTERFACE

如果client 想要使用binder IPC 来通信,那么首先会从SM 出查询并获得server 端service 的BpBinder ,在client 端,这个对象被认为是server 端的远程代理。为了能够使client 能够想本地调用一样调用一个远程server ,server 端需要向client 提供一个接口,client 在在这个接口的基础上创建一个BpINTERFACE ,使用这个对象,client 的应用能够想本地调用一样直接调用server 端的方法。而不用去关心具体的binder IPC 实现。 下面看一下BpINTERFACE 的原型:

class BpINTERFACE : public BpInterface<IINTERFACE>

顺着继承关系再往上看

template<typename INTERFACE>
class BpInterface : public INTERFACE, public BpRefBase

BpINTERFACE 分别继承自INTERFACE ,和BpRefBase ;

● BpINTERFACE 既实现了service 中各方法的本地操作,将每个方法的参数以Parcel 的形式发送给BD 。

例如BpServiceManager 的

<!-- lang: cpp -->
 virtual status_t addService(const String16& name, const sp<IBinder>& service)
{
    Parcel data, reply;
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
    data.writeString16(name);
    data.writeStrongBinder(service);
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
    return err == NO_ERROR ? reply.readExceptionCode() : err;
}

● 同时又将BpBinder 作为了自己的成员来管理,将BpBinder 存储在mRemote 中,BpServiceManager 通过调用BpRefBase 的remote() 来获得BpBinder 指针。

6.2 BnINTERFACE

在定义android native 端的service 时,每个service 均继承自BnINTERFACE(INTERFACE 为service name) 。BnINTERFACE 类型定义了一个onTransact 函数,这个函数负责解包收到的Parcel 并执行client 端的请求的方法。

顺着BnINTERFACE 的继承关系再往上看,

   class BnINTERFACE: public BnInterface<IINTERFACE>

IINTERFACE 为client 端的代理接口BpINTERFACE 和server 端的BnINTERFACE 的共同接口类,这个共同接口类的目的就是保证service 方法在C-S 两端的一致性。

再往上看

    class BnInterface : public INTERFACE, public BBinder

同时我们发现了BBinder 类型,这个类型又是干什么用的呢?既然每个service 均可视为一个binder ,那么真正的server 端的binder 的操作及状态的维护就是通过继承自BBinder 来实现的。可见BBinder 是service 作为binder 的本质所在。

那么BBinder 与BpBinder 的区别又是什么呢?

其实它们的区别很简单,BpBinder 是client 端创建的用于消息发送的代理,而BBinder 是server 端用于接收消息的通道。查看各自的代码就会发现,虽然两个类型均有transact 的方法,但是两者的作用不同,BpBinder 的transact 方法是向IPCThreadState 实例发送消息,通知其有消息要发送给BD ;而BBinder 则是当IPCThreadState 实例收到BD 消息时,通过BBinder 的transact 的方法将其传递给它的子类BnSERVICE 的onTransact 函数执行server 端的操作。

7. Parcel

Parcel 是binder IPC 中的最基本的通信单元,它存储C-S 间函数调用的参数. 但是Parcel 只能存储基本的数据类型,如果是复杂的数据类型的话,在存储时,需要将其拆分为基本的数据类型来存储。

简单的Parcel 读写不再介绍,下面着重介绍一下2 个函数

7.1 writeStrongBinder

当client 需要将一个binder 向server 发送时,可以调用此函数。例如

    <!-- lang: cpp -->
    virtual status_t addService(const String16& name, const sp<IBinder>& service)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
    }

看一下writeStrongBinder 的实体

<!-- lang: cpp -->

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

接着往里看flatten_binder

<!-- lang: cpp -->

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) {
                LOGE("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);
}

还是拿addService 为例,它的参数为一个BnINTERFACE 类型指针,BnINTERFACE 又继承自BBinder ,

<!-- lang: cpp -->
   
 BBinder* BBinder::localBinder()
    {
        return this;
    }

所以写入到Parcel 的binder 类型为BINDER_TYPE_BINDER ,同时你在阅读SM 的代码时会发现如果SM 收到的service 的binder 类型不为BINDER_TYPE_HANDLE 时,SM 将不会将此service 添加到svclist ,但是很显然每个service 的添加都是成功的,addService 在开始传递的binder 类型为BINDER_TYPE_BINDER ,SM 收到的binder 类型为BINDER_TYPE_HANDLE ,那么这个过程当中究竟发生了什么?

为了搞明白这个问题,花费我很多的事件,最终发现了问题的所在,原来在BD 中做了如下操作

<!-- lang: cpp -->

(drivers/staging/android/Binder.c) :
 
static void binder_transaction(struct binder_proc *proc,
                   struct binder_thread *thread,
                   struct binder_transaction_data *tr, int reply)
{
..........................................
 
    if (fp->type == BINDER_TYPE_BINDER)
        fp->type = BINDER_TYPE_HANDLE;
    else
        fp->type = BINDER_TYPE_WEAK_HANDLE;
    fp->handle = ref->desc;
..........................................
}

阅读完addService 的代码,你会发现SM 只是保存了service binder 的handle 和service 的name ,那么当client 需要和某个service 通信了,如何获得service 的binder 呢?看下一个函数

7.2 readStrongBinder

当server 端收到client 的调用请求之后,如果需要返回一个binder 时,可以向BD 发送这个binder ,当IPCThreadState 实例收到这个返回的Parcel 时,client 可以通过这个函数将这个被server 返回的binder 读出。

<!-- lang: cpp -->

sp<IBinder> Parcel::readStrongBinder() const
{
    sp<IBinder> val;
    unflatten_binder(ProcessState::self(), *this, &val);
    return val;
}

往里查看unflatten_binder

<!-- lang: cpp -->

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 = static_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;
}

发现如果server 返回的binder 类型为BINDER_TYPE_BINDER 的话,也就是返回一个binder 引用的话,直接获取这个binder ;如果server 返回的binder 类型为BINDER_TYPE_HANDLE 时,也就是server 返回的仅仅是binder 的handle ,那么需要重新创建一个BpBinder 返回给client 。

有上面的代码可以看出,SM 保存的service 的binder 仅仅是一个handle ,而client 则是通过向SM 获得这个handle ,从而重新构建代理binder 与server 通信。

这里顺带提一下一种特殊的情况,binder 通信的双方即可作为client ,也可以作为server. 也就是说此时的binder 通信是一个半双工的通信。那么在这种情况下,操作的过程会比单工的情况复杂,但是基本的原理是一样的,有兴趣可以分析一下MediaPlayer 和MediaPlayerService 的例子。

8. 经典桥段分析

<!-- lang: cpp -->

main_ mediaserver.cpp
int main(int argc, char** argv)
{
// 创建进程mediaserver 的ProcessState 实例
    sp<ProcessState> proc(ProcessState::self());
// 获得SM 的BpServiceManager
    sp<IServiceManager> sm = defaultServiceManager();
    LOGI("ServiceManager: %p", sm.get());
// 添加mediaserver 中支持的service 。
    AudioFlinger::instantiate();
    MediaPlayerService::instantiate();
    CameraService::instantiate();
    AudioPolicyService::instantiate();
// 启动ProcessState 的pool thread
    ProcessState::self()->startThreadPool();
// 这一步有重复之嫌,加不加无关紧要。
    IPCThreadState::self()->joinThreadPool();
}

9. Java 层的binder 机制

了解了native 通信机制后,再去分析JAVA 层的binder 机制,就会很好理解了。它只是对native 的binder 做了一个封装。这一部分基本上没有太复杂的过程,这里不再赘述了

摘自 杜文涛的专栏

标签: Android Binder IPC
  • 打赏
  • 点赞
  • 收藏
  • 分享
共有 人打赏支持
粉丝 0
博文 9
码字总数 0
×
ifindbug
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: