Binder之ServiceManger

以下文章摘录自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中的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容