(1) Android中Binder原理 --- 匿名和实名Binder调用

Source

       本系列文章主要结合源码讲解Android下Binder的通讯机制,好不夸张的说,Binder是Android下最核心的组件之一,其英文叫粘合剂,没有它,Android下四大组件、各种组件之间的通讯失去了应有的意义。

       其实,网上介绍Binder的文章太多了,这里我们换另外一个视角来理解下Binder,主要结合源码和实际的使用流程来看看Binder在Android开发中扮演什么样的角色,具有什么样的功能,这一节主要从整体上了解下Android开发中用到的各种Service怎么样获取、调用,以及我们自己开发的Service又是怎么样在多进程中完成数据交换的。

       首先,我们把Android中的Service分为实名Service和匿名Service,那什么是实名Service和匿名Service呢?我们先来看看实名Service,这里所谓的实名Service主要就是系统提供的各种Service,比如ActivityManagerService、PakcageManagerService等,这些Service是系统在初始化的时候通过ServiceManager注册到系统中的,所以,我们把这类Service叫做实名Service,下面的流程图描述使用实名Service的整体流程。

实名Service

       从上图看出,实名Service的使用涉及到四个进程:ServiceManager、SystemServer、Client、Binder驱动。除了Binder驱动运行在内核,其它三个进程都运行在用户空间,为了实现进程之间相互的数据交换,运行在内核层的Binder驱动扮演了这一角色,实现数据在用户进程之间的交换,Binder驱动这节不讲,大家只要知道,Binder驱动实现mmap和ioctl系统调用,完成内核空间和用户空间缓存的映射以及对缓存和Service的管理。

       我们先看下Service的注册(只有注册了客户端才能查询到需要的服务),这里涉及到两个用户空间的两个进程:系统进程SystemServer和ServiceManager服务进程。

ServiceManager进程启动

frameworks/native/cmds/servicemanager/service_manager.c

       先说说ServiceManager进程的启动到底做了些什么?下面是ServiceManager的main函数:

int main(int argc, char** argv)
363{
......
374    bs = binder_open(driver, 128*1024);    // (1)
375    if (!bs) {
......
385    }
386
387    if (binder_become_context_manager(bs)) {       // (2)
388        ALOGE("cannot become context manager (%s)\n", strerror(errno));
389        return -1;
390    }
391
......
415    binder_loop(bs, svcmgr_handler);       // (3)
416
417    return 0;
418}
419

       代码(1)处的 bs = binder_open(driver, 128*1024); 作用是打开Binder设备,同时初始化缓存空间大小128KB,这里ServiceManager就是完成Service的注册、获取等简单操作,因此缓存大小为128KB够用了。

       代码(2)处的 binder_become_context_manager(bs作用就是告诉Binder驱动我们这个线程是Binder的管理线程,当然ServiceManager进程自然就是Binder的管理进程,这里要注意,这个就是Binder设计巧妙之处,因为成为Binder的管理线程时Binder驱动会在其内部初始化一个全局的binder_node对象,其handle为0作为IServiceManager的远程代理节点信息,因此,IServiceManager是没有具体的实现类的,它的实体Binder就是ServiceManager进程,为什么呢,因为ServiceManager进程实现了IServiceManager的接口,下面代码(3)实现的。

frameworks/native/cmds/servicemanager/binder.c

       代码(3)是个死循环,不断调用ioctl来读取客户端发来的服务查询请求,收到客户端请求后经过一系列的解析,最终交由svcmgr_handler处理,其实现如下:

252int svcmgr_handler(struct binder_state *bs,
253                   struct binder_transaction_data *txn,
254                   struct binder_io *msg,
255                   struct binder_io *reply)
256{
......
296
297    switch(txn->code) {
298    case SVC_MGR_GET_SERVICE:
299    case SVC_MGR_CHECK_SERVICE:
300        ......
309
310    case SVC_MGR_ADD_SERVICE:
311        ......
321
322    case SVC_MGR_LIST_SERVICES: {
323        ......
338    }
339    ......
344    bio_put_uint32(reply, 0);
345    return 0;
346}

       看到了吧,这里就是实现IServiceManager接口的功能了,所以我们说IServiceManager接口没有具体的实现类,而是由ServiceManager的svcmgr_handler函数完成的,这就是Binder实现的巧妙,第一个IBinder对象就是这样实现的,接下来其它的系统提供的Service就可以通过IServiceManager来查询获取了。

       下面看下各种系统服务的注册,系统服务一般是在SystemServer进程里注册的,我们是为了讲解Service的调用流程,这里仅仅看看ActivityManagerService注册即可。

ActivityManagerService服务注册

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

2554    public void setSystemProcess() {
2555        try {
2556            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
2557            ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
2558            ServiceManager.addService("meminfo", new MemBinder(this));
2559            ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
2560            ServiceManager.addService("dbinfo", new DbBinder(this));

       SystemServer启动的时候会初始化ActivityManagerService,然后调用ActivityManagerService的setSystemProcess方法把ActivityManagerService注册到ServiceManager中,ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);这句代码就是。

       首先ServiceManager是一个IServiceManager的远程代理(handler为0),其本地实现就是BpBinder类,这里有很多知识点,不明白先看流程就行,后面我们会说,这只是说下整体流程。

       通过ServiceManager的远程代理,使用操作码SVC_MGR_ADD_SERVICE通过ioctl系统调用向Binder驱动发送请求,还记得之前讲的吗?ServiceManager进程在死循环里等待读取客户端的请求,就是这里了,我们向Binder驱动发送操作码SVC_MGR_ADD_SERVICE的请求后,Binder驱动发现Binder实体的handle为0(handle为0对应ServiceManager进程),那么它就知道处理的进程是ServiceManager,然后把唤醒ServiceManager进程让其处理这个请求。

       ServiceManager收到这个请求,开始解析最终处理SVC_MGR_ADD_SERVICE操作码的分支,完成Service的注册,注意,因为是跨进程的,ServiceManager注册的Service仅仅是记录实体Binder的引用、指针地址等信息,真实的IBinder实体类是在SystemServer进程中的(Binder实体类的真实宿主)。

       到这里大家比较疑惑,那远程调用怎么才能调用真正的IBinder实体类来处理业务逻辑呢?答案还是在Binder驱动层,因为Binder驱动也记录了我们注册的Service信息(binder_node),这些信息包括Service属于哪个进程、其引用等,具体细节后面会讲,这里只要知道大概就行。

       到这里Service已经注册ServiceManager里了,后面就可以通过名称来查询系统提供的服务并使用其提供的功能了。

匿名Service

       ​匿名Service我们这里仅仅指出其和实名Service区别,其它请参考实名Service。

       ​匿名Service和实名Service区别就在于Service的存储方式,实名Service是存储在ServiceManager进程里的,其实现了key/value的方式来保存和检索服务,因此,外部可以通过名称来访问指定的Service,系统的服务都是注册在ServiceManager里的,所以对于客户端来说是可以通过名称来查询服务的。

        而匿名Service存储的地方根据具体实现而定,比如bindService里的IServiceConnection就是存储在LoadApk对象的内部类ServiceDispatcher里的,如果服务端不向外告知,外部是不知道有这个IServiceConnection对象存在的,这也是ActivityManagerService的远程方法bindService为什么有IServiceConnection这个参数的原因,因为它需要明确获取IServiceConnection这个客户端对象,当服务绑定成功后才可以通过这个对象把服务传给远程的客户端。

        本系列文章均为原创,主要总结作者多年在软件行业的一些经验,和大家共同学习、进步,转载请注明出处,谢谢!