啃下Binder这块硬骨头(二)

1,972 阅读7分钟

在笔者的上一篇博客中主要分享了 Binder的通信过程是如何实现的,大致内容就是下面这张图:

binder的通信过程

笔者也提到Binder机制主要对象有Binder Client、Binder Server、Binder Driver、Service Manager ,它的完整过程是这样的:(此处是从Gityuan大佬的博客copy过来的一张图)

Binder完整通信过程

对比两者或者说仔细看了笔者的上篇博客,会发现介绍通信过程忽略了Service Manager这个角色,所以本篇的目的就是介绍它了。

  • ServerManagerBinder的关系?
  • ServerManager的作用
  • ServerManager的大致工作过程

Service Manager 与 实名Binder

ServiceManager的角色作用什么呢?

ServiceManager是安卓中一个重要的类,正如它的名字所表达的意思,用于管理所有的系统服务,维护着系统服务和客户端的binder通信。

ServiceManagerBinder是什么样的关系呢?

简单点说,ServiceManager作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。(这个注册了名字的 Binder 叫实名 Binder

但现在就有个问题,Service Manager 是一个进程,Server 是一个进程,ServerService Manager 中注册 Binder就涉及到进程间通信。但当前进程间通信又要用到进程间通信,这就好像蛋可以孵出鸡的前提却是要先找只鸡下蛋。那如何解决这个没有鸡生蛋的问题呢?

巧妙的实现:

要打破这个问题,就要预先创造一只鸡去生蛋,这只鸡就是ServiceManagerBinder实体。ServiceManager 提供的 Binder 比较特殊,它没有名字也不需要注册。当一个进程使用 BINDER_SET_CONTEXT_MGR 命令就会将自己注册成 ServiceManager 时, Binder 驱动会自动为它创建 Binder 实体(这就是那只预先造好的那只鸡)

Server端的注册方式

这个 Binder 实体的引用在所有 Client 中都固定为 0 而无需通过其它手段获得。也就是说,一个 Server 想要向 ServiceManager 注册自己的 Binder 就必须通过这个 0 号引用和 ServiceManagerBinder 通信。

类比网络通信,这个0号引用就像是 DNS 的地址,是需要首先配置好的(相对于这个DNS服务器,其他所有都是客户端)。同理 ,一个进程或者应用程序即使是提供服务的Server,相对于 SM,它也是客户端。

整个过程大致是:

当一个Binder Service创建后,它们就将自己的[名称、Binder句柄]对应关系告知SM进行备案,完成注册。

Client端获取Service的Binder引用

ServerServiceManager 中注册了 Binder 后, Client 就能通过名字获得 Binder 的引用了。由于 ClientSM 通信也需要Binder,所以Client 也是通过这个0号引用去向SM获取某个ServiceBinder

因为ServiceManager对于Client端来说Handle句柄是固定的,都是0,所以ServiceManager服务并不需要查询,可以直接使用。

整个过程大致是:

  1. Client发送数据包向SM请求某个名字的Binder引用
  2. SM 收到这个请求后,从请求数据包中去找名称,在查找表里找到对应的Binder的引用
  3. 将找到的Binder 引用作为回复发送给请求的Client

现在Client和Server 与 SM 的注册与获取有了基本的了解,具体实现细节就不再这里阐述了,可以去看源码了解。我们要着重关注一下 SM

SM的启动和创建

通过init程序解析init.rc时启动的,当它重启后,其他的系统服务的zygotemediasurfaceflinger 也会被重新加载

//frameworks/native/cmds/servicemanager/servicemanager.rc
service servicemanager /system/bin/servicemanager
    class core animation
    user system
    group system readproc
    critical
    onrestart restart healthd
    onrestart restart zygote
    onrestart restart audioserver
    onrestart restart media
    onrestart restart surfaceflinger
    onrestart restart inputflinger
    onrestart restart drm
    onrestart restart cameraserver
    onrestart restart keystore
    onrestart restart gatekeeperd
    writepid /dev/cpuset/system-background/tasks
    shutdown critical

因为SM启动的比较早,所以能保证它是系统中第一个向Binder驱动注册成“大管家”的程序。

//frameworks/native/cmds/servicemanager/service_manager.c
int main(int argc, char** argv)
{
    struct binder_state *bs;
    //1.打开Binder设备,申请128kb内存
    bs = binder_open(driver, 128*1024);
    //2.将自己设置成Binder大管家,系统中只允许一个ServiceManager存在
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }
    //3.进入循环,等待并处理客户端的请求
    binder_loop(bs, svcmgr_handler); 
    return 0;
}

当准备工作做完后,SM就开始等待客户端的请求。这部分是SM的一个重点。

SM的工作过程

SM的工作过程,如何处理请求是较为重要的一步,根据上面分析的源码,我们可以从 binder_loop(bs, svcmgr_handler) 入手来看SM是如何来处理请求的?

//frameworks/native/cmds/servicemanager/binder.c
void binder_loop(struct binder_state *bs, binder_handler func){
    struct binder_write_read bwr;
    uint32_t readbuf[32];
    
    //向Binder驱动发送BC_ENTER_LOOPER协议,让SM进入循环
    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(uint32_t));
    
    for (;;) {
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
        //等待客户端的数据
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
        //解析binder信息
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
    }
}

它通过不断循环去处理请求队列,感觉和Handler的消息管理机制有点相似,但要注意的是,SM中是没有消息队列的,它的消息是通过Binder驱动去获取的。

具体大概分三个步骤:

  1. Binder驱动中读取消息
  2. 处理消息
  3. 不断循环,永远不会主动退出(除非出现错误)

处理消息是较为复杂的一步,它是怎么处理的呢?

Binder驱动中读取到消息后,它会调用 binder_parse()的方法解析消息,主要工作就是针对不同的BR协议,采用不同的处理:

int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    int r = 1;
    uintptr_t end = ptr + (uintptr_t) size;
    while (ptr < end) {
        switch(cmd) {
        case BR_NOOP:
            break;
        case BR_TRANSACTION_COMPLETE:
            break;
        case BR_INCREFS:
        case BR_ACQUIRE:
        case BR_RELEASE:
        case BR_DECREFS:
        case BR_TRANSACTION: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            binder_dump_txn(txn);
            if (func) {
                unsigned rdata[256/4];
                struct binder_io msg;
                struct binder_io reply;
                int res;

                bio_init(&reply, rdata, sizeof(rdata), 4);
                bio_init_from_txn(&msg, txn);
                //具体处理消息的操作
                res = func(bs, txn, &msg, &reply);
                if (txn->flags & TF_ONE_WAY) {
                    binder_free_buffer(bs, txn->data.ptr.buffer);
                } else {
                    //回应消息处理结果
                    binder_send_reply(bs, &reply, txn->data.ptr.buffer, res);
                }
            }
            ptr += sizeof(*txn);
            break;
        }
        case BR_REPLY: {
            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;
            binder_dump_txn(txn);
            if (bio) {
                bio_init_from_txn(bio, txn);
                bio = 0;
            } 
            ptr += sizeof(*txn);
            r = 0;
            break;
        }
        case BR_DEAD_BINDER: 
        case BR_FAILED_REPLY:
        case BR_DEAD_REPLY:
        default:
        }
    }
    return r;
}

从代码中可以看出有很多BR协议,其中要着重关注其中的两个: 一个是BR_TRANSCATIONBR_REPLY,尤其是BR_TRANSCATION

BR_TRANSCATION

BR_TRANSCATION命令的处理主要是由func函数完成,然后将结果返回给Binder驱动,Binder驱动收到消息进而传给客户端。

追溯之前的代码,这个fun函数其实是 binder_loop(bs, svcmgr_handler)函数 传递过来的,也就是svcmgr_handler 函数,它的实现如下:

int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    switch(txn->code) {
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        //服务名
        s = bio_get_string16(msg, &len);
        //根据名称查找对应的服务的handle句柄
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
        if (!handle)
            break;
        bio_put_ref(reply, handle);
        return 0;
    case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len);
        handle = bio_get_ref(msg);
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        dumpsys_priority = bio_get_uint32(msg);
         //注册指定服务
        if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority,
                           txn->sender_pid))
            return -1;
        break;
    case SVC_MGR_LIST_SERVICES: {
        uint32_t n = bio_get_uint32(msg);
        uint32_t req_dumpsys_priority = bio_get_uint32(msg);
        while (si) {
            if (si->dumpsys_priority & req_dumpsys_priority) {
                if (n == 0) break;
                n--;
            }
            si = si->next;
        }
        if (si) {
            bio_put_string16(reply, si->name);
            return 0;
        }
        return -1;
    }
    bio_put_uint32(reply, 0);
    return 0;
}

可以清晰的看到,该方法的功能是根据不同的code去做不同的处理,比如查询服务,注册服务,以及列举所有服务。

BR_REPLYBR_TRANSCATION 实现类似。

举个栗子

//获取WindowManager服务的对象引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE); 
//准备一个view
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);  
//添加view到wm
wm.addView(view, layoutParams);  

上面这段代码我们很熟悉,通过getSystemService(getApplication().WINDOW_SERVICE)获取WindowManager对象的引用,接着调用WindowManageraddView函数,把一个view添加到窗口上。

代码看着很简单,但我们知道,系统服务都是运行在SystemServer进程中,所以整个过程涉及到跨进程通信。

从跨进程的角度分析一下,它是如何实现addView过程的呢?

根据以上的知识,getSystemService(getApplication().WINDOW_SERVICE)函数内部原理就是向ServiceManager查询标识符为getApplication().WINDOW_SERVICE的远程对象的引用。即WindowManager对象的引用。

不过这个引用的真正实现是WindowManager的某个代理。得到这个引用后,在调用addView时,真正的实现是在代理里面,代理把参数打包到Parcel对象中,然后调用transact函数,再触发Binder驱动的一系列调用过程。