Android7.0 Binder通信(1) ServiceManger

来源:互联网 发布:办公网络设计方案 编辑:程序博客网 时间:2024/06/07 20:00

背景
Android是基于Linux的操作系统,在其中运行的应用或者系统服务,实际上就是一个个Linux进程。这意味着它们彼此之间是隔离的,必须通过进程间通信(IPC)来相互传输数据。Binder就是Android实现的一种IPC通信方式。

然而我们知道,Linux已经提供了一些进程间通信的机制,例如socket和pipes等,为什么Android还要重新“造轮子”,创造一种新的IPC机制呢?
为了弄明白这个问题,自己参考了一些外文资料,其中比较让人信服的答案是:为了更好的性能。

我们知道Android中所有的系统功能都是由不同的服务进程提供的。
客户进程如果想使用某个功能,必须发送请求给对应的服务进程,然后等待结果。
由于Android有大量的这种通信需求,因此整个系统内部可能会频繁地发生进程间通信。也就是说,Android对进程间通信有高度的依赖性。
Android为了提高系统整体的传输效率,需要一种优化过的进程间通信方式。于是,Binder机制应运而生。

Binder机制起源于一个简单的想法:将申请服务的请求和对应的响应信息,写入一个所有进程均能够访问的地址空间中。
当进程需要使用这些数据时,只需要访问对应的内存地址,以减小内容复制引入的开销。
为此,Binder机制利用kernel空间作为共享区域,并由Binder driver来建立起每个进程的内存地址与kernel空间中存储地址的映射。


版本
android 7.0

Binder通信的整体架构
引入了Binder机制后,Android中基于Binder的进程间通信,整体上仍是一种C/S结构。


1.如上图所示,ServiceManger负责管理系统中的各种服务。Server进程首先要注册一些服务一些服务到ServiceManager中,所以在这个过程中,Server进程是ServiceManager的客户端。
2.如果某个Client进程要使用某个Service,必须先到ServiceManager中获取该Service相关的信息,所以在此过程中,Client进程是ServiceManager的客户端。
3.Client进程获取到Service信息,与Server进程建立通信后,就可以直接与Server进程通信了,在此过程中Client进程是Server进程的客户端。

以上3种通信方式,均是基于Binder通信的,我们将按照先后顺序依次分析:ServiceManager,Server进程的注册,Client进程的查询及使用。
在这篇博客中,我们先一起看一下ServiceManager中主要的流程。

ServiceManager

1 服务启动

service servicemanager /system/bin/servicemanager    class core    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    writepid /dev/cpuset/system-background/tasks

如上所示,在android7.0中,文件frameworks/native/cmds/servicemanager/servicemanager.rc文件中定义了servicemanager,可以看到servicemanager对应的keyword是service。在分析init进程时,我们知道init进程解析rc文件时,遇到service关键字后,仅利用其后的信息构造出service对象,并不会立即启动service。
那么servicemanager是如何被加载的呢?

在init.rc中,定义了early-init, init, late-init, early-boot, boot这样的字段,以定义命令的先后执行顺序,在init.cpp的main函数中有:

.............ActionManager& am = ActionManager::GetInstance();am.QueueEventTrigger("early-init");.............am.QueueEventTrigger("init");.............am.QueueEventTrigger("late-init");

可以明显的看到,这些字段对应的触发事件被先后加入到执行队列中。在init.rc中:

on late-init    ......    trigger early-boot    trigger boot

可以看到在late-init对应的一系列执行命令的最后,将触发操作early-boot和boot阶段。

在boot阶段的最后:

on boot    .......    class_start core

看到没?在boot阶段的最后,将要进行class_start core的操作。在system/core/init/builtins.cpp中,class_start对应的处理函数是do_class_start:

static int do_class_start(const std::vector<std::string>& args) {    //此时,args为“core”    ServiceManager::GetInstance().        ForEachServiceInClass(args[1], [] (Service* s) { s->StartIfNotDisabled(); });    return 0;}

从上面的代码,我们容易看出,将对service对象中class字段等于“core”的所有服务,执行StartIfNotDisabled操作。

bool Service::StartIfNotDisabled() {    if (!(flags_ & SVC_DISABLED)) {        //启动服务        return Start();    } else {        flags_ |= SVC_DISABLED_START;    }    return true;}bool Service::Start() {    //参数检查等操作    ..........    pid_t pid = fork();    if (pid == 0) {        //fork出子进程后,为子进程设置参数        ......        //启动对应的main函数        if (execve(args_[0].c_str(), (char**) &strs[0], (char**) ENV) < 0) {            ..............        }    }    ......}

至此我们终于知道servicemanager是如何启动的。
根据上面的分析,可以于看出servicemanager运行于独立的进程中,是init进程的子进程。相比之下,zygote进程的class字段为main,启动顺序在servicemanager之后。

2 Native层的service_manager
现在我们看看定义于frameworks/native/cmds/servicemanager/service_manager.c中的main函数:

int main(int argc, char **argv) {    ........    //打开binder驱动    bs = binder_open(128*1024);    ........    //设置为service manager    if (binder_become_context_manager(bs)) {        ................    }    //配合selinux的一些工作    .........    //处理请求    binder_loop(bs, svcmgr_handler);}

2.1 binder_open

struct binder_state *binder_open(size_t mapsize) {    ..........    //打开binder设备    bs->fd = open("/dev/binder", O_RDWR | O_CLOEXEC);    ..........    //判断内核版本和用户空间的版本是否一致    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) || (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {        .............    }    ............    //完成内存映射    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);    ............}

2.2 binder_become_context_manager

int binder_become_context_manager(struct binder_state *bs){    //将ServiceManager对应binder的句柄设为0    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);}

可以看到,ServiceManager将自己的句柄定义为0,于是其它的进程与其通信时,不需要进行额外的查询。

2.3 binder_loop

//此处func为svcmgr_handlervoid binder_loop(struct binder_state *bs, binder_handler func) {    ......    for (;;) {        .........        //servicemanager打开binder设备,将自己的句柄设为0后,就不断的监听是否有发往自己的数据        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);        .........        //当收到数据后,利用binder_parse解析数据,然后适当的条件下,调用svcmgr_handler处理        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);        ..........    }}

我们看看binder_parse函数:

int binder_parse(struct binder_state *bs, struct binder_io *bio,        uintptr_t ptr, size_t size, binder_handler func) {    ...........    //ptr指向将读取数据的首地值,size为数据的总长度    uintptr_t end = ptr + (uintptr_t) size;    while (ptr < end) {        //解析出数据        uint32_t cmd = *(uint32_t *) ptr;        //移动ptr        ptr += sizeof(uint32_t);        switch(cmd) {        ...........        //收到一个实际的传输数据        case BR_TRANSACTION: {            struct binder_transaction_data *txn = (struct binder_transaction_data *) ptr;            //判断读出的数据大小是否符合要求            if ((end - ptr) < sizeof(*txn)) {                ALOGE("parse: txn too small!\n");                return -1;            }            .........            if (func) {                ..........                //调用svcmgr_handler处理收到的数据                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_DEAD_BINDER: {            struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr;            ptr += sizeof(binder_uintptr_t);            //如果与serviceManager通信的binder死亡,需要调用对应的处理函数            death->func(bs, death->ptr);        }        ...........        }    }}

最后,我们来看看svcmgr_handler函数:

int svcmgr_handler(struct binder_state *bs,                   struct binder_transaction_data *txn,                   struct binder_io *msg,                   struct binder_io *reply) {    //进行参数有效性检查等操作    .............    //根据收到数据中携带的code字段,执行相应的操作    switch(txn->code) {    case SVC_MGR_GET_SERVICE:    case SVC_MGR_CHECK_SERVICE:        //从收到数据中读出需查找服务的名称        s = bio_get_string16(msg, &len);        .......        //得到服务对应的句柄        //根据名称进行匹配,在返回信息前会进行权限检查        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);        ......        //将句柄信息写入reply        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;        //向servicemanager注册服务        //在注册前会进行权限检查,然后利用参数中的信息,构建出服务对象,加入到全局变量svclist中        //同时会调用binder_link_to_death监听新加入服务进程是否死亡        if (do_add_service(bs, s, len, handle, txn->sender_euid,                allow_isolated, txn->sender_pid))            return -1;        break;    case SVC_MGR_LIST_SERVICES: {        //从收到的数据中,取出需要服务的编号        uint32_t n = bio_get_uint32(msg);        //权限检查        if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {            //log            ......            return -1;        }        //svclist中记录了注册到servicemanager的服务的信息        si = svclist;        while ((n-- > 0) && si)            si = si->next;        if (si) {            //得到当前注册到servicemanager的服务中,第n个服务的名称            bio_put_string16(reply, si->name);            return 0;        }        return -1;    }    .............}

从svcmgr_handler函数,我们可以看出ServiceManager的主要功能包括:
1. 集中管理系统内的所有服务,无论其它进程增加服务还是查询服务,ServiceManager均会进行权限检查。
2. ServiceManager支持通过字符串查找对应的Service。这一点与DNS很像,用户将域名发送给DNS,DNS返回实际的IP地址给用户。
3. ServiceManager监控服务是否正常。由于各种原因的影响,Android中的服务进程可能异常终止。如果让每个Client都去进行检测,那么开销太大。假设同时存在n个Client、n个Service,那么每个Client为了知道Service的状态,将进行n2次通信。
ServiceManager来负责统一监控后,ServiceManager来监听每个Service的状态,Client只需要通过ServiceManager就能得到服务端的状态,此时只需要2n次通信即可。

结束语
本篇博客主要目的是分析Binder通信的起源、基本架构,以及ServiceManager的主要作用。

2 0
原创粉丝点击