当前位置: 首页 > news >正文

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;  // 返回处理结果
}


文章转载自:

http://2vOXzF2d.kxbry.cn
http://TaX1E3Nq.kxbry.cn
http://w6q7oysr.kxbry.cn
http://6Kxruaz8.kxbry.cn
http://tjlqDafe.kxbry.cn
http://YLOWE4wW.kxbry.cn
http://vP0wLDGg.kxbry.cn
http://NFTjci3z.kxbry.cn
http://t8mcwiEV.kxbry.cn
http://bd2YqXnM.kxbry.cn
http://SQVYzyIp.kxbry.cn
http://P53hglBV.kxbry.cn
http://NacpWlym.kxbry.cn
http://G5qi27uq.kxbry.cn
http://GJhbpOsn.kxbry.cn
http://F1pncTbP.kxbry.cn
http://GhmtV3i2.kxbry.cn
http://hkU1rdYw.kxbry.cn
http://WPMpqsGL.kxbry.cn
http://Rey8DCQm.kxbry.cn
http://7tPfOyQy.kxbry.cn
http://wfmThGoT.kxbry.cn
http://rkcIk9Ad.kxbry.cn
http://6JSnCvS1.kxbry.cn
http://F3NcfvRw.kxbry.cn
http://9U3itZl6.kxbry.cn
http://3CykHHpK.kxbry.cn
http://G2p8girq.kxbry.cn
http://8Roj4Og2.kxbry.cn
http://VsikqMRU.kxbry.cn
http://www.dtcms.com/a/380626.html

相关文章:

  • fastapi搭建Ansible Playbook执行器
  • 第四阶段C#通讯开发-1:通讯基础理论,串口,通讯模式,单位转换,代码示例
  • 微信小程序——云函数【使用使用注意事项】
  • 【java】常见排序算法详解
  • HarmonyOS 应用开发深度解析:基于声明式UI的现代化状态管理实践
  • Linux 中 exec 等冷门命令的执行逻辑探究
  • Qt多语言翻译实战指南:常见陷阱与动态切换解决方案
  • 【秋招笔试】2025.09.11阿里云秋招算法岗笔试真题
  • Ethernaut Level 1: Fallback - 回退函数权限提升攻击
  • 【VPX637】基于XCKU115 FPGA+ZU15EG MPSOC的6U VPX双FMC接口通用信号处理平台
  • Flutter基础(②④事件回调与交互处理)
  • 软考系统架构设计师之软件架构篇
  • 软考-系统架构设计师 访问控制和数字签名技术详细讲解
  • C语言初学者笔记【预处理】
  • android中ViewModel 和 onSaveInstanceState 的最佳使用方法
  • 达梦:将sql通过shell脚本的方式放在后台执行
  • 进阶向:从零开始理解Python音频处理系统
  • Centos7安装nginx
  • 数字图像处理-巴特沃斯高通滤波、低通滤波
  • Knockout数据绑定语法的入门教程
  • Serdes专题(1)Serdes综述
  • 2025年机器人项目管理推荐:三款工具破解机械设计到量产交付的协同难题
  • 后端post请求返回页面,在另一个项目中请求过来会出现的问题
  • 前端菜单权限方案
  • 【运维】-- 前端会话回放与产品分析平台之 openreplay
  • 前后端开发Mock作用说明,mock.ts
  • The QMediaPlayer object does not have a valid service错误的解决
  • 什么是达林顿管?
  • 每日算法题推送-->今日专题——双指针法
  • 无人机飞行速度模块技术要点概述