ServiceManager启动源码分析

1.servicemanager与binder之间的关系

  1. ServiceManager的角色

    • ServiceManager是Android系统中的核心服务管理器,负责管理系统级的服务,如ActivityManager、PackageManager、WindowManager等。
    • 它是一个Binder服务,handle固定为0。
    • ServiceManager提供注册服务和查询服务的功能。应用程序相要通过Binder向一个service发送数据,必须先通过ServiceManager获取该service的handle,然后才能通过Binder驱动与service通信。
    • ServiceManager运行在一个独立进程当中,由init进程负责启动,属于系统关键服务,一旦异常退出,系统会尝试重启它。
  2. Binder的角色

    • Binder是Android系统中的一个进程间通信(IPC)机制,它允许不同进程之间相互调用方法和传递数据。
    • Binder的核心是Binder驱动程序,它负责管理不同进程之间的通信。
    • 每个进程都可以创建自己的Binder对象作为服务提供者,也可以获取其他进程提供的Binder对象作为客户端使用。
    • 在Android开发中,可以通过AIDL(Android Interface Definition Language)来定义自己的Binder接口,并实现相应的服务提供者和客户端。
  3. ServiceManager与Binder的协作

    • ServiceManager是Binder通信机制的核心,扮演了Binder通信机制上下文管理者的角色。
    • 客户端可以从ServiceManager获取Binder服务,服务端可以注册Binder服务。
    • 当一个服务通过Binder注册到ServiceManager时,ServiceManager会保存该服务的引用,并允许其他进程通过Binder机制与之通信。
    • 应用程序通过Binder与服务通信时,首先需要通过ServiceManager获取服务的handle,然后使用这个handle与服务进行通信。

2.servicemanager启动,通过init.rc启动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
    writepid /dev/cpuset/system-background/tasks
    shutdown critical


//rc文件解析
service servicemanager /system/bin/servicemanager
这定义了一个名为servicemanager的服务。
该服务的可执行文件位于/system/bin/servicemanager。
class core animation
这将servicemanager服务分类为core和animation类别。这通常用于系统内部,以决定服务的启动顺序和依赖关系。
user system group system readproc
这指定了servicemanager服务的运行用户和组。在这里,它以system用户身份运行,并属于system和readproc组。
readproc组通常允许服务读取其他进程的信息。
critical
这表示servicemanager是一个关键服务。如果它崩溃或停止,系统可能会变得不稳定或不可操作。
onrestart ... restart ...
这些指令指定了当servicemanager服务重启时,哪些其他服务也应该被重启。
例如,当servicemanager重启时,healthd、zygote、audioserver等也会重启。
writepid /dev/cpuset/system-background/tasks
这将servicemanager的PID(进程ID)写入/dev/cpuset/system-background/tasks文件。这通常与CPU集(cpuset)有关,用于限制或分配进程到特定的CPU或CPU集。
shutdown critical
这表示在系统关闭时,servicemanager被视为一个关键服务。这可能意味着在关闭过程中,系统会特别关注此服务的状态或行为。

接下来看下servicemanager里面作了什么

源码路径:frameworks/native/cmds/servicemanager/service_manager.c,看看他的main方法里面做了什么

int main(int argc, char** argv)
{
    struct binder_state *bs;
    union selinux_callback cb;
    char *driver;

    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }

    bs = binder_open(driver, 128*1024);//打开binder驱动,分配内存
    if (!bs) {
#ifdef VENDORSERVICEMANAGER
        ALOGW("failed to open binder driver %s\n", driver);
        while (true) {
            sleep(UINT_MAX);
        }
#else
        ALOGE("failed to open binder driver %s\n", driver);
#endif
        return -1;
    }

    if (binder_become_context_manager(bs)) {//告诉binder驱动,自己是servicemanager(注册dns服务器)
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    cb.func_log = selinux_log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);

#ifdef VENDORSERVICEMANAGER
    sehandle = selinux_android_vendor_service_context_handle();
#else
    sehandle = selinux_android_service_context_handle();
#endif
    selinux_status_open(true);

    if (sehandle == NULL) {
        ALOGE("SELinux: Failed to acquire sehandle. Aborting.\n");
        abort();
    }

    if (getcon(&service_manager_context) != 0) {
        ALOGE("SELinux: Failed to acquire service_manager context. Aborting.\n");
        abort();
    }  

可以看见在service_manager.c的main方法里面主要做了三件事,一、首先就是打开binder驱动,并分配内存空间 二、告诉binder驱动自己是servicemanager 三、启动binder循环接下来逐一分析下这三步的具体实现

(1)binder_open

struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return NULL;
    }

    bs->fd = open(driver, O_RDWR | O_CLOEXEC);//打开驱动
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open %s (%s)\n",
                driver, strerror(errno));
        goto fail_open;
    }

    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||//与驱动通信
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        fprintf(stderr,
                "binder: kernel driver version (%d) differs from user space version (%d)\n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;
    }

    bs->mapsize = mapsize;
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);//内存映射
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }

    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return NULL;
}

(2)binder_become_context_manager()

int binder_become_context_manager(struct binder_state *bs)
{
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);//与驱动通信,告诉驱动自己是manager(dns服务器)
}

(3)binder_loop(关键)

void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;
    uint32_t readbuf[32];

    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    readbuf[0] = BC_ENTER_LOOPER;
    binder_write(bs, readbuf, sizeof(uint32_t));

    for (;;) {//无限循环,等待并处理Binder事务。
    //设置 bwr 结构体以从Binder驱动读取数据。readbuf 被用作读取缓冲区。
        bwr.read_size = sizeof(readbuf);
        bwr.read_consumed = 0;
        bwr.read_buffer = (uintptr_t) readbuf;
    //使用 ioctl 系统调用与Binder驱动通信,执行 BINDER_WRITE_READ 操作。这个操作允许线程同时写入和读取数据到Binder驱动。
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

        if (res < 0) {//如果 ioctl 调用失败,记录错误并退出循环。
            ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
            break;
        }
        调用 binder_parse 函数来解析从Binder驱动接收到的数据。这个函数会调用传入的 func 回调函数来处理Binder事务。
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {//binder_parse 函数的返回值表示解析和处理结果。如果返回0,表示收到了一个意外的回复(这通常是一个错误情况),记录错误并退出循环。
            ALOGE("binder_loop: unexpected reply?!\n");
            break;
        }
        if (res < 0) {//如果 binder_parse 返回负数,表示在IO过程中发生了错误,记录错误并退出循环。
            ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
            break;
        }
    }
}

这里看见binder_parse函数是对binder驱动里面读取到的数据进行解析处理,接下来继续看下binder_parse这个函数是如何对数据进行解析的


int binder_parse(struct binder_state *bs, struct binder_io *bio,
                 uintptr_t ptr, size_t size, binder_handler func)
{
    int r = 1;  // 初始化解析结果为1
    uintptr_t end = ptr + (uintptr_t) size;  // 获取数据结束位置

    while (ptr < end) {  // 遍历数据
        uint32_t cmd = *(uint32_t *) ptr;  // 读取命令类型
        ptr += sizeof(uint32_t);  // 移动指针到下一个数据

#if TRACE
        fprintf(stderr,"%s:\n", cmd_name(cmd));  // 如果启用跟踪,则打印命令名称
#endif

        switch() {  // 根据命令类型进行处理
        case BR_NOOP:  // 无操作
            break;
        case BR_TRANSACTION_COMPLETE:  // 事务完成
            break;
        case BR_INCREFS:  // 增加引用
        case BR_ACQUIRE:  // 获取引用
        case BR_RELEASE:  // 释放引用
        case BR_DECREFS:  // 减少引用
#if TRACE
            fprintf(stderr,"  %p, %p\n", (void *)ptr, (void *)(ptr + sizeof(void *)));  // 如果启用跟踪,则打印指针位置
#endif
            ptr += sizeof(struct binder_ptr_cookie);  // 移动指针到下一个数据
            break;
        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;  // 数据不合法,解析失败
            }
            binder_dump_txn(txn);  // 输出事务数据
            if (func) {  // 如果存在处理函数
                unsigned rdata[256/4];  // 设置回复数据缓冲区
                struct binder_io msg;  // 设置消息 I/O
                struct binder_io reply;  // 设置回复 I/O
                int res;

                bio_init(&reply, rdata, sizeof(rdata), 4);  // 初始化回复 I/O
                bio_init_from_txn(&msg, txn);  // 从事务数据初始化消息 I/O
                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;  // 获取事务数据
            if ((end - ptr) < sizeof(*txn)) {  // 检查回复数据大小是否合法
                ALOGE("parse: reply too small!\n");
                return -1;  // 数据不合法,解析失败
            }
            binder_dump_txn(txn);  // 输出事务数据
            if (bio) {
                bio_init_from_txn(bio, txn);  // 从事务数据初始化 Binder I/O
                bio = 0;  // 清空 Binder I/O
            } else {
                /* todo FREE BUFFER */  // 待处理的释放缓冲区逻辑
            }
            ptr += sizeof(*txn);  // 移动指针到下一个数据
            r = 0;  // 设置解析结果为0
            break;
        }
        case BR_DEAD_BINDER: {  // 处理已死的 Binder
            struct binder_death *death = (struct binder_death *)(uintptr_t) *(binder_uintptr_t *)ptr;  // 获取 Binder 死亡信息
            ptr += sizeof(binder_uintptr_t);  // 移动指针到下一个数据
            death->func(bs, death->ptr);  // 调用死亡处理函数
            break;
        }
        case BR_FAILED_REPLY:  // 处理失败回复
            r = -1;  // 设置解析结果为-1
            break;
        case BR_DEAD_REPLY:  // 处理已死的回复
            r = -1;  // 设置解析结果为-1
            break;
        default:
            ALOGE("parse: OOPS %d\n", cmd);  // 默认情况下输出错误日志
            return -1;  // 解析失败
        }
    }

    return r;  // 返回解析结果
}

这里看见最终是调用func函数对解析之后的事件进行处理,看看是如何进行处理的。

/**
 * bs:Binder状态结构体指针。
 * txn:包含事务数据的Binder事务数据结构体指针。
 * msg:包含传入消息数据的Binder I/O结构体指针。
 * reply:用于发送回复消息的Binder I/O结构体指针。
 */
int svcmgr_handler(struct binder_state *bs,
                   struct binder_transaction_data *txn,
                   struct binder_io *msg,
                   struct binder_io *reply)
{
    struct svcinfo *si;
    uint16_t *s;
    size_t len;
    uint32_t handle;
    uint32_t strict_policy;
    int allow_isolated;

    //ALOGI("target=%p code=%d pid=%d uid=%d\n",
    //      (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid);

    if (txn->target.ptr != BINDER_SERVICE_MANAGER)//检查事务的目标(txn->target.ptr)是否是ServiceManager(BINDER_SERVICE_MANAGER)。如果不是,函数返回-1。
        return -1;

    if (txn->code == PING_TRANSACTION)//如果事务的代码(txn->code)是PING_TRANSACTION,则函数返回0,表示已成功处理PING事务。
        return 0;

    // Equivalent to Parcel::enforceInterface(), reading the RPC   相当于Parcel:: enforinterface(),读取RPC
    // header with the strict mode policy mask and the interface name.   带有严格模式策略掩码和接口名称的报头。
    // Note that we ignore the strict_policy and don't propagate it    注意,我们忽略了strict_policy,并且不传播它
    // further (since we do no outbound RPCs anyway).   进一步(因为我们不做出站rpc)。
    strict_policy = bio_get_uint32(msg);
    s = bio_get_string16(msg, &len);
    if (s == NULL) {
        return -1;
    }

    if ((len != (sizeof(svcmgr_id) / 2)) ||
        memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {
        fprintf(stderr,"invalid id %s\n", str8(s, len));
        return -1;
    }
    //如果SELinux句柄(sehandle)存在并且SELinux状态已更新,则尝试获取新的SELinux句柄(针对服务或供应商服务的上下文),并替换旧的句柄。
    if (sehandle && selinux_status_updated() > 0) {
#ifdef VENDORSERVICEMANAGER
        struct selabel_handle *tmp_sehandle = selinux_android_vendor_service_context_handle();
#else
        struct selabel_handle *tmp_sehandle = selinux_android_service_context_handle();
#endif
        if (tmp_sehandle) {
            selabel_close(sehandle);
            sehandle = tmp_sehandle;
        }
    }

    switch(txn->code) {
    //这两个case现在共享相同的代码块,用于从消息中提取服务名称并查找服务的句柄。如果找到句柄,它会将句柄写入回复消息并返回0(成功)。
    //如果没有找到句柄,它将跳出这个case(break)并执行switch语句之后的代码
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = do_find_service(s, len, txn->sender_euid, txn->sender_pid);
        if (!handle)
            break;
        bio_put_ref(reply, handle);//后续分析
        return 0;
    //这个case处理添加服务到ServiceManager的请求。它首先提取服务名称和句柄,然后检查一个布尔值来决定是否允许服务在隔离进程中运行。如果do_add_service函数成功,它会跳出这个case。否则,它返回-1表示错误。
    case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len);
        if (s == NULL) {
            return -1;
        }
        handle = bio_get_ref(msg);//使用bio_get_ref从传入消息中读取服务的句柄。
        //使用bio_get_uint32从传入消息中读取一个布尔值,该值指示是否允许服务在隔离进程中运行,并转换为整数allow_isolated。
        allow_isolated = bio_get_uint32(msg) ? 1 : 0;
        //(关键:后续深入分析)
        //调用do_add_service函数来注册服务。该函数接收服务名称、句柄、是否允许隔离等参数,并尝试在ServiceManager中注册服务。
        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);//使用bio_get_uint32从传入消息(msg)中读取一个整数n,表示要列出的服务的索引。

        if (!svc_can_list(txn->sender_pid, txn->sender_euid)) {//调用svc_can_list函数来检查发送者是否有权限列出服务。
            ALOGE("list_service() uid=%d - PERMISSION DENIED\n",
                    txn->sender_euid);
            return -1;
        }
        si = svclist;
        while ((n-- > 0) && si)//遍历服务列表(从svclist开始),直到找到索引为n的服务或列表结束
            si = si->next;
        if (si) {
            bio_put_string16(reply, si->name);//如果找到服务,使用bio_put_string16将服务名称写入回复消息(reply),并返回0。
            return 0;
        }
        return -1;//如果没有找到服务或发生错误,返回-1。
    }
    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }

    bio_put_uint32(reply, 0);
    return 0;
}

****这段代码是ServiceManager用于处理来自其他进程的Binder事务的关键部分,特别是与服务注册、查找和检查相关的事务。

现在继续来关注,刚才遗留的地方,(1)也就是do_add_service这个函数,具体是怎么将service添加到dns(servicemanager)服务中的。

/*
 * 向服务管理器中添加一个新的服务
 *
 * 参数:
 *   bs: Binder状态
 *   s: 服务名称
 *   len: 服务名称长度
 *   handle: 服务句柄
 *   uid: 用户ID
 *   allow_isolated: 是否允许隔离
 *   spid: 服务进程ID
 *
 * 返回值:
 *   成功返回0,失败返回-1
 */
int do_add_service(struct binder_state *bs,
                   const uint16_t *s, size_t len,
                   uint32_t handle, uid_t uid, int allow_isolated,
                   pid_t spid)
{
    struct svcinfo *si;

    // ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle,
    //        allow_isolated ? "allow_isolated" : "!allow_isolated", uid);
    
    // 检查参数有效性
    if (!handle || (len == 0) || (len > 127))
        return -1;
    
    // 检查服务是否有注册权限
    if (!svc_can_register(s, len, spid, uid)) {
        ALOGE("add_service('%s',%x) uid=%d - 权限被拒绝\n",
             str8(s, len), handle, uid);
        return -1;
    }

    // 查找服务是否已存在
    si = find_svc(s, len);
    if (si) {
        if (si->handle) {
            ALOGE("add_service('%s',%x) uid=%d - 已经注册,将被覆盖\n",
                 str8(s, len), handle, uid);
            // 注销之前的服务
            svcinfo_death(bs, si);
        }
        // 更新服务句柄
        si->handle = handle;
    } else {
        // 服务不存在,创建新的服务信息
        si = malloc(sizeof(*si) + (len + 1) * sizeof(uint16_t));
        if (!si) {
            ALOGE("add_service('%s',%x) uid=%d - 内存不足\n",
                 str8(s, len), handle, uid);
            return -1;
        }
        // 初始化服务信息
        si->handle = handle;
        si->len = len;
        memcpy(si->name, s, (len + 1) * sizeof(uint16_t));
        si->name[len] = '\0';
        si->death.func = (void*) svcinfo_death;
        si->death.ptr = si;
        si->allow_isolated = allow_isolated;
        si->next = svclist;
        svclist = si;
    }

    // 增加引用计数
    binder_acquire(bs, handle);
    // 监听服务进程的死亡
    binder_link_to_death(bs, handle, &si->death);
    return 0;
}

总结:

1、添加系统中新的binder服务,包装成了一个本地结构体svcinfo,它的最重要参数就是handle,返回了handle才可以在binder驱动中寻找到真正的binder服务实体对象。

2、根据传递过来的service名字查询但其servicemanager是否已经注册了该服务,如果注册则返回该服务对应handle。

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值