美文网首页
Binder之ServiceManger

Binder之ServiceManger

作者: jtsky | 来源:发表于2018-02-07 16:14 被阅读29次

    以下文章摘录自Android7.0 Binder通信(1) ServiceManger

    简介

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

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

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

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


    image.png

    Binder通信的整体架构

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


    image.png

    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中主要的流程。

    Android版本:

    ServiceManager服务启动

    [->frameworks\native\cmds\servicemanager\servicemanager.rc]

    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
    

    可以看到对应的kwyWord是service。在分析init进程时,我们知道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");
    
    //action.cpp 
    void ActionManager::QueueEventTrigger(const std::string& trigger) {
        trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
    }
    

    trigger_queue_的类型是:queue<std::unique_ptr<Trigger>>,遵守先进先出的原则,所以这里就定了了先后顺序。
    下面来看下init.rc中的late-init
    [->system/core/rootdir/init.rc]

    on late-init
        trigger early-fs
        trigger fs
        trigger post-fs
    
        # Load properties from /system/ + /factory after fs mount. Place
        # this in another action so that the load will be scheduled after the prior
        # issued fs triggers have completed.
        trigger load_system_props_action
    
        # Now we can mount /data. File encryption requires keymaster to decrypt
        # /data, which in turn can only be loaded when system properties are present
        trigger post-fs-data
        trigger load_persist_props_action
    
        # Remove a file to wake up anything waiting for firmware.
        trigger firmware_mounts_complete
        //重点关注 触发以下2个操作
        trigger early-boot
        trigger boot
    
    on boot
        .......
        class_start core
    

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

    //对应的映射表
    static const Map builtin_functions = {
            {"bootchart_init",          {0,     0,    do_bootchart_init}},
            {"chmod",                   {2,     2,    do_chmod}},
            {"chown",                   {2,     3,    do_chown}},
            {"class_reset",             {1,     1,    do_class_reset}},
            {"class_start",             {1,     1,    do_class_start}},
            {"class_stop",              {1,     1,    do_class_stop}},
            {"copy",                    {2,     2,    do_copy}},
            {"domainname",              {1,     1,    do_domainname}},
            {"enable",                  {1,     1,    do_enable}},
            {"exec",                    {1,     kMax, do_exec}},
            {"export",                  {2,     2,    do_export}},
            {"hostname",                {1,     1,    do_hostname}},
            {"ifup",                    {1,     1,    do_ifup}},
            {"init_user0",              {0,     0,    do_init_user0}},
            {"insmod",                  {1,     kMax, do_insmod}},
            {"installkey",              {1,     1,    do_installkey}},
            {"load_persist_props",      {0,     0,    do_load_persist_props}},
            {"load_system_props",       {0,     0,    do_load_system_props}},
            {"loglevel",                {1,     1,    do_loglevel}},
            {"mkdir",                   {1,     4,    do_mkdir}},
            {"mount_all",               {1,     kMax, do_mount_all}},
            {"mount",                   {3,     kMax, do_mount}},
            {"powerctl",                {1,     1,    do_powerctl}},
            {"restart",                 {1,     1,    do_restart}},
            {"restorecon",              {1,     kMax, do_restorecon}},
            {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
            {"rm",                      {1,     1,    do_rm}},
            {"rmdir",                   {1,     1,    do_rmdir}},
            {"setprop",                 {2,     2,    do_setprop}},
            {"setrlimit",               {3,     3,    do_setrlimit}},
            {"start",                   {1,     1,    do_start}},
            {"stop",                    {1,     1,    do_stop}},
            {"swapon_all",              {1,     1,    do_swapon_all}},
            {"symlink",                 {2,     2,    do_symlink}},
            {"sysclktz",                {1,     1,    do_sysclktz}},
            {"trigger",                 {1,     1,    do_trigger}},
            {"verity_load_state",       {0,     0,    do_verity_load_state}},
            {"verity_update_state",     {0,     0,    do_verity_update_state}},
            {"wait",                    {1,     2,    do_wait}},
            {"write",                   {2,     2,    do_write}},
        };
    
    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之后。
    关于Android7.0下加载native .rc的具体信息请参考Android 7.0 init.rc的一点改变

    native层的service_manager.c

    [->frameworks/native/cmds/servicemanager/service_manager.c]

    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);
    }
    

    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);
    ............
    }

    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,于是其它的进程与其通信时,不需要进行额外的查询就可以直接得到。

    binder_loop

    //此处func为svcmgr_handler
    void 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处理 readbuf为数据头指针
            res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
            ..........
        }
    }
    
    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的状态,将进行n^2次通信。
    ServiceManager来负责统一监控后,ServiceManager来监听每个Service的状态,Client只需要通过ServiceManager就能得到服务端的状态,此时只需要2n次通信即可。

    好了,关于ServiceManger我们就先分析到这里,下一篇我们将深入到系统服务是如何注册到ServiceManger中的。

    相关文章

      网友评论

          本文标题:Binder之ServiceManger

          本文链接:https://www.haomeiwen.com/subject/mrlczxtx.html