Android系统进程间通讯之Binder机制(一)

Android系统进程间通讯之Binder机制(一)

                                                                  

                                                                   ----理论篇


 

    首先我们知道Android是基于Linux内核的,而Linux内核继承和兼容了丰富的Unix系统进程间通讯(IPC:Internet Process Connection),其中包括:

        1、Pipe:管道

        2、Signal:信号

        3、Trace:跟踪

    以上是传统的通讯手段,并且只能用与父进程与子进程之间的通讯,或者兄弟之间的通讯。

        4、Message:报文队列

        5、Share Memory:共享内存

        6、Semaphore:信号量

    以上三种是在AT&T的Unix系统V中,又增加了三种称为“SystemV IPC”的进程间通信机制。

        7、Socket:插口

     其中,Socket是BSD Unix对“System VIPC”机制进行了重要的扩充。

     想对以上几种通讯方式详细了解可参考《Linux内核源代码情景分析》一书

 

     而在Android中采用的是Binder机制实现进程间的通讯。

     Binder是一种进程间通信机制,它是一种类似于COMCORBA分布式组件架构,通俗一点,其实是提供远程过程调用(RPC)功能。使用Client-Server通信方式:一个进程作为Server提供诸如视频/音频解码,视频捕获,地址本查询,网络连接等服务;一个或者多个进程作为ClientServer发起服务请求,获得所需要的服务。

     在AndroidBinder机制中,包含四个角色,分别是:ServerClientServiceManageBinder驱动。关系如下图:

Android系统进程间通讯之Binder机制(一)_第1张图片

 

   其中:

    Binder驱动:运行于内核空间,Binder机制的核心组件,提供设备文件/dev/binder与用户空间交互。

    ServerClientServiceManage:运行于用户空间,Service Manager提供了辅助管理的功能,ClientServerBinder驱动和Service Manager提供的基础设施上,进行Client-Server之间的通信。

    Service ManagerBinder驱动已经在Android平台中实现好,开发者只要按照规范实现自己的ClientServer组件就可以了,具体实现可见下篇:Android系统进程间通讯之Binder机制(二) - 实践篇。


    下面就AndroidBinder所会涉及的情景具体分析。


Service Manage

    Service Manager是一个linux级的进程,实现对Service的管理。同样的,任何service在被使用之前,均要向Service Manager注册(addService函数实现);同时客户端需要访问某个service时,应该首先向Service Manager查询是否存在该服务,并获取到这个ServergetService函数实现)。

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

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.指定Service Manager对应的代理binderhandle0,当client尝试与Service Manager通信时,需要创建一个handle0的代理binder,这里的代理binder其实就是第一节中描述的那个代理接口;

       3.通知binder driver(BD)使Service Manager成为BDcontextmanager
       4.
维护一个死循环,在这个死循环中,不停地去读内核中binder driver,查看是否有可读的内容;即是否有对service的操作要求,如果有,则调用svcmgr_handler回调来处理请求的操作。

       5. Service Manager维护了一个svclist列表来存储service的信息。

Android系统进程间通讯之Binder机制(一)_第2张图片

 注意:

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

    2、 应用和service之间的通信会涉及到2binder通信:

        a)      应用向SM查询service是否存在,如果存在获得该service的代理binder,此为一次binder通信;

        b)     应用通过代理binder调用service的方法,此为第二次binder通信。


Server

    首先,ServerClient的实现都是Service Manager实现的,对Server启动而言:Server通过defaultServiceManager函数获得Service Manager远程接口,然后将自己的Service添加到Service Manager中去,接着把自己启动起来,等待Client的请求。下面将通过分析源代码了解Server的实现和启动过程是怎么样的。

    假设我们需要定义一个ServerExampleService

    首先,我们需要定义一个IExampleService(继承了IInterface类),然后定义BnExampleService(继承了IExampleServiceBBinder类)。其中BBinder类继承了IBinder类,IInterfaceIBinder类又同时继承了RefBase类。

然而BnExampleService并不是直接接收到Client处发送过来的请求,而是使用了IPCThreadState接收Client处发送过来的请求,而IPCThreadState又借助了ProcessState类来与Binder驱动程序交互。IPCThreadState接收到了Client处的请求后,就会调用BBinder类的transact函数,并传入相关参数,BBinder类的transact函数最终调用BnMediaPlayerService类的onTransact函数,于是,就开始真正地处理Client的请求了。(其中有关IPCThreadStateProcessState的关系下面会有介绍)。以上便是Server的实现过程。

 

    那么对ExampleService的启动流程呢?

    首先:

sp proc(ProcessState::self());

    这句代码的作用是通过ProcessState::self()调用创建一个ProcessState实例。ProcessState::self()ProcessState类的一个静态成员变量,定义在frameworks/base/libs/binder/ProcessState.cpp文件中。

    这个函数作用是返回一个全局唯一的ProcessState实例gProcess。全局唯一实例变量gProcess定义在frameworks/base/libs/binder/Static.cpp文件中。

    在ProcessState的构造函数中主要以下操作:

          1、  open_driverframeworks/base/libs/binder/ProcessState.cpp)函数打开Binder设备

文件/dev/binder,并将打开设备文件描述符保存在成员变量mDriverFD中:主要是通过open文件操作函数来打开/dev/binder设备文件,然后再调用ioctl文件控制函数来分别执行BINDER_VERSIONBINDER_SET_MAX_THREADS两个命令来和Binder驱动程序进行交互,前者用于获得当前Binder驱动程序的版本号,后者用于通知Binder驱动程序。(openBinder驱动程序中的具体实现,打开/dev/binder设备文件后,Binder驱动程序就为ExampleService进程创建了一个struct binder_proc结构体实例来维护ExampleService进程上下文相关信息)。

          2、  mmap来把设备文件/dev/binder映射到内存中。 mmap函数调用完成之后,Binder驱动程序就为当前进程预留了BINDER_VM_SIZE大小的内存空间了。

    至此,ProcessState全局唯一变量gProcess就创建完毕了。


    接下来:

       调用defaultServiceManager函数来获得Service Manager的远程接口,定义ExampleService::instantiate函数把ExampleService添加到Service Manger中去了,如下:

void ExampleService::instantiate() {
    defaultServiceManager()->addService(String16("exampleservice"));
}

    其中defaultServiceManager返回的实际是一个BpServiceManger类实例,因此,我们具体了解一下BpServiceManger::addService的实现,这个函数实现在frameworks/base/libs/binder/IServiceManager.cpp文件中。

    对addService的理解:BpServiceMangerClient的形式,将相应的一些数据通过Parcel类中各种Parcel.writexxx的方式写入Parcel,最后通过

status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);

    将data传递,实现信息交互传递(主要是Binder驱动程序之间,传输这个ExampleService)。实现ExampleService的启动过程。

    最终实现的就是就是把ExampleService这个Binder实体的引用写到一个struct svcinfo结构体中,主要是它的名称和句柄值,然后插入到链接svclist的头部去。Client来向Service Manager查询服务接口时,只要给定服务名称,Service Manger就可以返回相应的句柄值了,从而实现调用该Service

    最后

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

在一个无穷循环中等待Client的请求。


Client

    同样的,在这将简单介绍一下Client的实现流程与获取Server的流程,Client的实现流程原理跟Server如出一辙,获取Server的方式也是通过ServiceManager实现的。

    假设我们定义一个Client:ExampleService。

    首先我们需要定义一个 IExampleService (继承了IInterface类),然后定义 BpExampleService(继承了IExampleServiceBpinder类)。其中Bpinder类继承了IBinder类,IInterfaceIBinder类又同时继承了RefBase类。在BpExampleService调用remote()->transact实现数据传递。

    remote()是在BpExampleService的父类BpRefBase中实现的,返回的就是一个BpBinder.实际上调用的就是BpBindertransact,将写入到Parcel中的数据传递出去并取得返回值。

    对于Serve的启动流程主要是调用addService 实现的,对于Client获取Server的流程采用getService实现。

    声明:

sp sm = defaultServiceManager();  
sp binder;

    因为可能存在相对应需要获取的Server可能还没有启动起来,所以这里如果发现取回来的binder接口为NULL,就睡眠0.5秒,然后再尝试获取,这是获取Service接口的标准做法:

do {  
   binder = sm->getService(String16("exampleservice"));  
      if (binder != 0) {  
           break;  
         }  
      LOGW("service not published, waiting...");  
       usleep(500000); // 0.5 s  
        } while(true);

    对于BpServiceManager::getService的具体实现在这就不做详细介绍,跟ServeraddService如出一辙。

    接着,

sExampleService = interface_cast(binder);

    这里的interface_cast实际上最终调用了IExampleService::asInterface函数,创建一个BpExampleService对象供我们得到。有了这个BpExampleService这个远程接口之后,Client就可以调用ExampleService的服务了。

    最后,在 BpExampleService 下定义相应的方法实现信息传递 : 定义一个 Parcel data ,将数据写入,然后调用 remote()->transact 函数将数据传递出去,在 Server 下的 onTransact 函数就能接收到相应的 read 出相应的数据。

IBinder, BBinderBpBinder

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

ProcessState, IPCThreadState

    ProcessState是以单例模式设计的。每个进程在使用binder机制通信时,均需要维护

一个ProcessState实例来描述当前进程在binder通信时的binder状态。ProcessState有如下2个主要功能:
   
 1.创建一个thread,该线程负责与内核中的binder模块进行通信,称该线程为Pool thread;(Poolthread:BinderIPC中,所有进程均会启动一个thread来负责与BD来直接通信,也就是不停的读写BD,这个线程的实现主体是一个IPCThreadState对象. Poolthread的启动方式:ProcessState::self()->startThreadPool();
   
 2.为指定的handle创建一个BpBinder对象,并管理该进程中所有的BpBinder对象。

 

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

    Pool thread的实际内容则为:IPCThreadState::self()->joinThreadPool();

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

    ProcessState&IPCThreadState&BpBinder&BinderDriver关系图:

Android系统进程间通讯之Binder机制(一)_第3张图片

Parcel

    ParcelbinderIPC中的最基本的通信单元,它存储C-S间函数调用的参数.但是Parcel只能存储基本的数据类型,如果是复杂的数据类型的话,在存储时,需要将其拆分为基本的数据类型来存储。Parcel类下定义了相应的writeread方法写入和读取数据。

       注:写入和读取的顺序需要一样,否则会出错。


具体的实现可参见下篇本人具体就项目采用的binder通讯机制的实现Android系统进程间通讯之Binder机制(二) - 实践篇。



你可能感兴趣的:(C++/C,Android系统开发)