Android Binder IPC分析

来源:互联网 发布:宁静马景涛 知乎 编辑:程序博客网 时间:2024/05/16 00:49

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 住。

 

 

 

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 的代码部分
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 的信息。

 



这里需要声明一下,当 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 的
    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 发送时,可以调用此函数。例如
        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 的实体
status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

接着往里看 flatten_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) {
                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 ,
    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 中做了如下操作 (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 读出。


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


往里查看 unflatten_binder


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. 经典桥段分析

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 做了一个封装。这一部分基本上没有太复杂的过程,这里不再赘述了。

0 0
原创粉丝点击