Android8 binder源码学习分析笔记(四)——ServiceManager启动

前文回顾:

Android8 binder源码学习分析笔记(三):
https://blog.csdn.net/g_i_a_o_giao/article/details/151365630?spm=1001.2014.3001.5502

Android8 binder源码学习分析笔记(二):
https://blog.csdn.net/g_i_a_o_giao/article/details/151221566?spm=1011.2415.3001.5331

Android8 binder源码学习分析笔记(一):

https://blog.csdn.net/g_i_a_o_giao/article/details/151365630?spm=1011.2415.3001.5331

在上一篇文章中,我们探讨了Binder驱动如何建立连接、创建线程池以及处理命令。现在,让我们把目光转向ServiceManager的启动过程,看看Binder在其中扮演的角色。

首先来看frameworks/native/cmds/servicemanager/servicemanager.rc中的配置。此处可以看到可执行文件位于/system/bin/servicemanager。(可以看到当servicemanager服务启动以后,会重启zygote service,证明servicemanager是在zygote之前启动的)。

bash 复制代码
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

执行这个可执行文件,会调用frameworks/native/cmds/servicemanager/service_manager.c的main函数。那么我们来看看这个方法。可以看到主要是调用了binder_open方法来打开binder驱动,然后调用了binder_become_context_manager方法来使得serviceManager成为binder的服务管理器,最后就是调用binder_loop进入循环,处理客户端请求。

java 复制代码
int main(int argc, char** argv)
{
    struct binder_state *bs;  // Binder驱动状态结构体指针
    union selinux_callback cb; // SELinux回调函数联合体
    char *driver;             // Binder驱动设备路径

    // 处理命令行参数:如果提供了参数,使用指定的Binder驱动设备
    // 否则默认使用"/dev/binder"
    if (argc > 1) {
        driver = argv[1];
    } else {
        driver = "/dev/binder";
    }

    // 打开Binder驱动并初始化Binder状态
    // 128*1024指定了Binder映射内存的大小(128KB)
    bs = binder_open(driver, 128*1024);
    if (!bs) {
        // 如果打开Binder驱动失败
#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;
    }

    // 将自己设置为Binder上下文管理器(服务管理器)
    // 这是Binder IPC机制中的核心角色,负责管理所有服务注册和查找
    if (binder_become_context_manager(bs)) {
        ALOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1;
    }

    // 设置SELinux回调函数
    // 审计回调:用于SELinux访问决策的审计
    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    
    // 日志回调:用于SELinux日志记录
    cb.func_log = selinux_log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);

    // 根据编译类型获取相应的SELinux句柄
#ifdef VENDORSERVICEMANAGER
    // 供应商服务管理器使用供应商服务上下文句柄
    sehandle = selinux_android_vendor_service_context_handle();
#else
    // 系统服务管理器使用系统服务上下文句柄
    sehandle = selinux_android_service_context_handle();
#endif
    selinux_status_open(true);  // 打开SELinux状态监视

    // 检查SELinux句柄是否有效
    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();  // 如果获取失败,终止进程
    }

    // 进入Binder循环,处理来自客户端的请求
    // bs: Binder状态
    // svcmgr_handler: 处理服务管理器请求的回调函数
    binder_loop(bs, svcmgr_handler);

    return 0;
}

首先来看看binder_open函数。这个方法主要是调用open函数打开binder驱动,然后调用mmap方法映射共享内存(重要)。

java 复制代码
/**
 * 打开Binder驱动并初始化Binder状态
 * 
 * @param driver: Binder设备路径,如"/dev/binder"
 * @param mapsize: 要映射的共享内存大小
 * @return: 成功返回binder_state结构体指针,失败返回NULL
 */
struct binder_state *binder_open(const char* driver, size_t mapsize)
{
    struct binder_state *bs;
    struct binder_version vers;

    // 分配binder_state结构体内存
    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;  // 设置错误号为内存不足
        return NULL;
    }

    // 1. 打开Binder设备文件
    // O_RDWR: 读写模式打开
    // O_CLOEXEC: 执行exec()时自动关闭文件描述符
    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;  // 跳转到错误处理
    }

    // 2. 检查Binder驱动版本是否兼容
    if ((ioctl(bs->fd, BINDER_VERSION, &vers) == -1) ||
        (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
        fprintf(stderr,
                "binder: kernel驱动版本 (%d) 与用户空间版本 (%d) 不同\n",
                vers.protocol_version, BINDER_CURRENT_PROTOCOL_VERSION);
        goto fail_open;  // 版本不匹配,跳转到错误处理
    }

    // 3. 映射共享内存 - 这是Binder通信的核心
    bs->mapsize = mapsize;
    // mmap参数:
    // NULL: 由内核选择映射地址
    // mapsize: 映射区域大小
    // PROT_READ: 只读保护
    // MAP_PRIVATE: 私有映射,写时复制
    // bs->fd: 映射的文件描述符
    // 0: 偏移量为0
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: 无法映射设备 (%s)\n",
                strerror(errno));
        goto fail_map;  // 映射失败,跳转到错误处理
    }

    return bs;  // 成功返回初始化好的binder_state

// 错误处理标签
fail_map:
    close(bs->fd);  // 关闭文件描述符
fail_open:
    free(bs);      // 释放分配的内存
    return NULL;   // 返回NULL表示失败
}

再来看看binder_become_context_manager方法和binder_loop方法。在binder_become_context_manager方法中,根据之前创建的binder_state对象,将serviceManager设置为binder的服务管理器。在binder_loop方法中,创建一个循环来处理客户端的请求,与binder驱动进行通信。有点类似上篇文章提到的joinThreadPool方法。(详情可查看这篇笔记https://blog.csdn.net/g_i_a_o_giao/article/details/151365630?spm=1001.2014.3001.5502)

cpp 复制代码
/**
 * 将当前进程设置为Binder上下文管理器
 * 
 * @param bs: binder_state结构体指针
 * @return: ioctl调用结果,0表示成功,-1表示失败
 */
int binder_become_context_manager(struct binder_state *bs)
{
    // 使用ioctl设置当前进程为Binder上下文管理器
    // BINDER_SET_CONTEXT_MGR: 特殊的ioctl命令
    // 0: 参数,在此命令中未使用
    return ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
}

/**
 * 进入Binder消息循环,处理传入的Binder请求
 * 
 * @param bs: binder_state结构体指针
 * @param func: 处理Binder事务的回调函数
 */
void binder_loop(struct binder_state *bs, binder_handler func)
{
    int res;
    struct binder_write_read bwr;  // Binder读写结构
    uint32_t readbuf[32];          // 读取缓冲区

    // 初始化写操作参数(本次循环没有数据要写)
    bwr.write_size = 0;
    bwr.write_consumed = 0;
    bwr.write_buffer = 0;

    // 1. 通知Binder驱动本线程进入循环状态
    readbuf[0] = BC_ENTER_LOOPER;  // 命令码:进入循环
    binder_write(bs, readbuf, sizeof(uint32_t));

    // 2. 主循环 - 持续处理Binder请求
    for (;;) {
        // 设置读操作参数
        bwr.read_size = sizeof(readbuf);    // 读取缓冲区大小
        bwr.read_consumed = 0;              // 已消耗数据初始为0
        bwr.read_buffer = (uintptr_t) readbuf;  // 读取缓冲区地址

        // 3. 执行Binder读写操作(主要等待读取)
        // BINDER_WRITE_READ: 最常用的Binder ioctl命令
        res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);

        if (res < 0) {
            ALOGE("binder_loop: ioctl失败 (%s)\n", strerror(errno));
            break;  // ioctl失败,退出循环
        }

        // 4. 解析并处理接收到的Binder数据
        res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
        if (res == 0) {
            ALOGE("binder_loop: 收到意外回复?!\n");
            break;  // 解析结果异常,退出循环
        }
        if (res < 0) {
            ALOGE("binder_loop: IO错误 %d %s\n", res, strerror(errno));
            break;  // 解析错误,退出循环
        }
    }
}

我们继续分析一下这个binder_parse方法。这个方法主要负责解析从Binder驱动接收到的数据并处理相应的Binder命令。首先是读取命令码,然后根据命令码进行不同的处理。如果有回调的函数,则进行处理。

cpp 复制代码
/**
 * 解析从Binder驱动接收到的数据并处理相应的Binder命令
 * 
 * @param bs: binder状态结构体指针,包含Binder设备信息
 * @param bio: binder_io结构体指针,用于处理回复数据(可为NULL)
 * @param ptr: 要解析的数据缓冲区起始地址
 * @param size: 数据缓冲区的大小
 * @param func: 处理Binder事务的回调函数
 * @return: 1表示成功处理,0表示收到回复,-1表示错误
 */
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) {
        // 1. 读取命令码(32位无符号整数)
        uint32_t cmd = *(uint32_t *) ptr;
        ptr += sizeof(uint32_t);  // 移动指针到下一个数据位置
        
#if TRACE  // 调试跟踪
        fprintf(stderr,"%s:\n", cmd_name(cmd));
#endif
        
        // 2. 根据命令码进行不同的处理
        switch(cmd) {
        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
            // 这些命令后跟一个binder_ptr_cookie结构,跳过这个结构
            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;       // 输入消息结构
                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;
            
            // 检查数据长度是否足够
            if ((end - ptr) < sizeof(*txn)) {
                ALOGE("parse: reply too small!\n");
                return -1;  // 数据不足,返回错误
            }
            
            binder_dump_txn(txn);  // 调试输出回复信息(如果启用)
            
            // 如果有提供的bio结构,用回复数据初始化它
            if (bio) {
                bio_init_from_txn(bio, txn);
                bio = 0;  // 置零防止后续重复处理
            } else {
                /* todo FREE BUFFER */  // 需要释放缓冲区(TODO注释)
            }
            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;
            ptr += sizeof(binder_uintptr_t);  // 移动指针
            
            // 调用注册的死亡回调函数
            death->func(bs, death->ptr);
            break;
        }
        
        case BR_FAILED_REPLY:  // 回复失败
            r = -1;  // 设置返回值为错误
            break;
            
        case BR_DEAD_REPLY:    // 对方已死亡的回复
            r = -1;  // 设置返回值为错误
            break;
            
        default:  // 未知命令
            ALOGE("parse: OOPS %d\n", cmd);
            return -1;  // 返回错误
        }
    }

    return r;  // 返回处理结果
}
相关推荐
GilgameshJSS2 小时前
【学习K230-例程23】GT6700-音频FFT柱状图
python·学习·音视频
今天我要乾重生2 小时前
泛型的学习
学习
前端码虫2 小时前
2.9Vue创建项目(组件)的补充
javascript·vue.js·学习
听情歌落俗2 小时前
MATLAB3-1变量-台大郭彦甫
开发语言·笔记·算法·matlab·矩阵
PigeonGuan3 小时前
强化学习中重要性采样
学习
~kiss~3 小时前
MLLM学习~M3-Agent Prompt学习
学习
Naiva3 小时前
ESP32-C3 入门09:基于 ESP-IDF + LVGL + ST7789 的 1.54寸 WiFi 时钟(SquareLine Studio 移植)
ide·笔记·vscode
..过云雨3 小时前
03.【Linux系统编程】基础开发工具1(yum软件安装、vim编辑器、编辑器gcc/g++)
linux·c语言·笔记·学习
肥肠可耐的西西公主4 小时前
后端(FastAPI)学习笔记(CLASS 3):Tortoise ORM
笔记·学习·fastapi