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之前启动的)。
service servicemanager /system/bin/servicemanagerclass core animationuser systemgroup system readproccriticalonrestart restart healthdonrestart restart zygoteonrestart restart audioserveronrestart restart mediaonrestart restart surfaceflingeronrestart restart inputflingeronrestart restart drmonrestart restart cameraserverwritepid /dev/cpuset/system-background/tasksshutdown critical
执行这个可执行文件,会调用frameworks/native/cmds/servicemanager/service_manager.c的main函数。那么我们来看看这个方法。可以看到主要是调用了binder_open方法来打开binder驱动,然后调用了binder_become_context_manager方法来使得serviceManager成为binder的服务管理器,最后就是调用binder_loop进入循环,处理客户端请求。
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);
#endifreturn -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();
#endifselinux_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方法映射共享内存(重要)。
/*** 打开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: 偏移量为0bs->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)
/*** 将当前进程设置为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; // 已消耗数据初始为0bwr.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命令。首先是读取命令码,然后根据命令码进行不同的处理。如果有回调的函数,则进行处理。
/*** 解析从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 TRACEfprintf(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; // 返回处理结果
}