Binder系列--获取ServiceManager

获取ServiceManager
hongxi.zhu 2023-7-1

以SurfaceFlinger为例,分析客户端进程如何获取ServiceManager代理服务对象

主要流程

在这里插入图片描述

SurfaceFlinger中获取SM服务

frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp

    // publish surface flingersp<IServiceManager> sm(defaultServiceManager());sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);

SurfaceFlingermain函数里,首先是获取IServiceManager的对象,其实也就是BpServiceManager对象,然后再通过BpServiceManager->addService()注册相关SurfaceFlinger相关的两个服务。那么看下这个IServiceManager的对象是怎么获取的, 从上面可以看到是从defaultServiceManager()方法获取到的ServiceManager。

defaultServiceManager()

frameworks/native/libs/binder/IServiceManager.cpp

using AidlServiceManager = android::os::IServiceManager;
...
[[clang::no_destroy]] static std::once_flag gSmOnce;
[[clang::no_destroy]] static sp<IServiceManager> gDefaultServiceManager;sp<IServiceManager> defaultServiceManager()
{std::call_once(gSmOnce, []() {sp<AidlServiceManager> sm = nullptr;while (sm == nullptr) {sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));if (sm == nullptr) {ALOGE("Waiting 1s on context object on %s.", ProcessState::self()->getDriverName().c_str());sleep(1);  //循环等待SM就绪}}gDefaultServiceManager = sp<ServiceManagerShim>::make(sm);});return gDefaultServiceManager;
}

这个方法是libbinder中的IServiceManager.cpp中实现,这是一个call_once实现的单例方法,通过interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr))获取一个IServiceManager对象, 然后再构造一个IServiceManager子类ServiceManagerShim对象返回给客户端使用(ServiceManagerShim是Google AIDL化改造ServiceManager后新的一个中间类,负责SM具体的功能)

ProcessState::self()

frameworks/native/libs/binder/ProcessState.cpp

sp<ProcessState> ProcessState::self()
{return init(kDefaultDriver, false /*requireDefault*/); //kDefaultDriver = "/dev/binder";
}sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault)
{...[[clang::no_destroy]] static std::once_flag gProcessOnce;std::call_once(gProcessOnce, [&](){  //call_once单例模式确保每个进程只有一个ProcessStateif (access(driver, R_OK) == -1) {  //测试下binder的节点是否可读ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver);driver = "/dev/binder";}...std::lock_guard<std::mutex> l(gProcessMutex);gProcess = sp<ProcessState>::make(driver);  //构造ProcessState实例});...return gProcess;
}

ProcessState::self()主要是通过单例的方式获取一个进程独有的ProcessState对象, 那首次构造时需要走构造方法。

#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
#define DEFAULT_MAX_BINDER_THREADS 15
#define DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION 1
...
ProcessState::ProcessState(const char* driver): mDriverName(String8(driver)),mDriverFD(-1),mVMStart(MAP_FAILED),mThreadCountLock(PTHREAD_MUTEX_INITIALIZER),mThreadCountDecrement(PTHREAD_COND_INITIALIZER),mExecutingThreadsCount(0),mWaitingForThreads(0),mMaxThreads(DEFAULT_MAX_BINDER_THREADS),mStarvationStartTimeMs(0),mForked(false),mThreadPoolStarted(false),mThreadPoolSeq(1),mCallRestriction(CallRestriction::NONE) {base::Result<int> opened = open_driver(driver);if (opened.ok()) {// mmap the binder, providing a chunk of virtual address space to receive transactions.mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE,opened.value(), 0);if (mVMStart == MAP_FAILED) {close(opened.value());// *sigh*opened = base::Error()<< "Using " << driver << " failed: unable to mmap transaction memory."; //内存不足mDriverName.clear();}}...
}

构造方法里做的最主要的两件事就是通过open_driver()mmap()

open_driver

static base::Result<int> open_driver(const char* driver) {int fd = open(driver, O_RDWR | O_CLOEXEC);  //通过open打开binder节点,获取binder设备驱动的fd...int vers = 0;status_t result = ioctl(fd, BINDER_VERSION, &vers);  //通过ioctl和binder驱动通信,查询binder驱动的binder版本,binder驱动的版本要和用户空间的binder协议的版本保持匹配,不然无法工作...size_t maxThreads = DEFAULT_MAX_BINDER_THREADS;  //DEFAULT_MAX_BINDER_THREADS = 15result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);  //通过ioctl告知binder驱动,用户进程支持的最大binder工作线程数,默认是15+1 = 16个(加上本身)...uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION;result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable);  //开启oneway方式垃圾请求攻击检测(类似于垃圾邮件攻击检测)...return fd;  //返回驱动的fd
}

open_driver主要做四件事:

  1. 通过系统调用open打开设备节点,获取设备驱动fd
  2. 通过系统调用ioctl获取驱动的binder版本
  3. 通过系统调用ioctl告知驱动用户进程支持的最大线程数,(默认是15+1,SystemServer进程默认是31+1)
  4. 通过系统调用ioctl设置垃圾oneway异步通信检测

mmap

		...if (opened.ok()) {// mmap the binder, providing a chunk of virtual address space to receive transactions.mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE,opened.value(), 0);  //BINDER_VM_SIZE = 1MB-8KBif (mVMStart == MAP_FAILED) {close(opened.value());// *sigh*opened = base::Error()<< "Using " << driver << " failed: unable to mmap transaction memory."; //内存不足(没有满足需求的连续内存)mDriverName.clear();}}...

ProcessState构造函数接着会通过系统调用mmap将当前进程的一块虚拟内存(内核分配的虚拟地址,这个地址是进程地址空间的)映射到内核空间,这个系统调用最终实现是binder驱动中的binder_mmap(), binder驱动会在内核也申请一块空间(内核空间),并指向一块物理地址,注意这个仅仅用在这个进程作为服务端时,接收来自binder的消息,这个过程没有发生IO的拷贝。

回归上面的分析,我们当前是注册服务,从上面我们分析了ProcessState::self(), 它获取了ProcessState对象,然后接着调用它的getContextObject()方法

getContextObject(nullptr)

sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{sp<IBinder> context = getStrongProxyForHandle(0);  //BpServiceManager的handle是特殊的(handle = 0)...return context;
}

getStrongProxyForHandle(0)

sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{sp<IBinder> result;AutoMutex _l(mLock);handle_entry* e = lookupHandleLocked(handle);  //查找handle对应的handle_entryif (e != nullptr) {IBinder* b = e->binder;  //handle_entry会保存handle对应的BpBinder对象if (b == nullptr || !e->refs->attemptIncWeak(this)) {if (handle == 0) {  //ServiceManager很特殊,固定的handle = 0IPCThreadState* ipc = IPCThreadState::self();  //创建IPCThreadState实例,线程独有,用于发起真正的跨进程通信动作。CallRestriction originalCallRestriction = ipc->getCallRestriction();  //权限检查ipc->setCallRestriction(CallRestriction::NONE);Parcel data;status_t status = ipc->transact(0, IBinder::PING_TRANSACTION, data, nullptr, 0);  //向handle(0)发起PING_TRANSACTION事务,检测Binder是否已经正常工作,对端的BBdiner里会处理这个请求并返回NO_ERRORipc->setCallRestriction(originalCallRestriction);if (status == DEAD_OBJECT)return nullptr;}sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle);  //根据handle创建BpBindere->binder = b.get();if (b) e->refs = b->getWeakRefs();result = b;} else {...}}return result;
}

查找handle(0)对应的BpBinder对象,如果是首次调用就,先通过IPCThreadState::transact向binder驱动发起往对端进程一个Ping的事务,看看往SM端的binder链路是否正常工作,然后创建handle(0)对应的BpBinder(0), 并保存到handle_entry中,实际上就是返回new BpBinder(0)

lookupHandleLocked(0)

ProcessState::handle_entry* ProcessState::lookupHandleLocked(int32_t handle)
{const size_t N=mHandleToObject.size();if (N <= (size_t)handle) {  //如果handle大于mHandleToObject.size,就创建handle+1-N个新的handle_entry并插入,第一次进入时,N = 0,handle = 0,所以handle = 0肯定是第一个元素,从这里也看出,除了第一个元素外,每个进程里Bpbinder对应的handle值不一定相同handle_entry e;e.binder = nullptr;  //首次创建时binder为nullptre.refs = nullptr;status_t err = mHandleToObject.insertAt(e, N, handle+1-N);if (err < NO_ERROR) return nullptr;}return &mHandleToObject.editItemAt(handle);  //返回对应元素的地址
}

handle_entry是在用户进程中保存BpBinder映射关系的结构体,mHandleToObject通过handle作为下标可以查询到对应的BpBinder
所以ProcessState::self()->getContextObject(nullptr)返回的实际上就是new BpBInder(0)是个BpBInder对象, 通过IInterfaceinterface_cast转换为android::os::IServiceManager的接口实现对象BpServiceManager

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/5434.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

进制转换(十进制与十六进制互转)

之前的一家公司基本上都是基于单片机进行开发&#xff0c;一般与上位机的通信都是按照自定义的协议进行操作&#xff0c;测试的时候会经常都对协议进行修改并且涉及到进制之间的转换&#xff0c;例如获取版本是十六进制的需要转换成十进制的版本信息&#xff0c;例如修改时间需…

主流特征工程平台(一)

一. 目标 对于Feature Store的能力与边界&#xff0c;每家的定义略微不同&#xff0c;《Feature Stores - A Hierarchy of Needs》&#xff09;这篇文章做了很好的总结&#xff0c;大体分为如下几个层次&#xff1a; 特征管理&#xff1a;特征抽取、处理、存储、元数据管理&am…

【复杂网络建模】——使用PyTorch和DGL库实现图神经网络进行链路预测

&#x1f935;‍♂️ 个人主页&#xff1a;Lingxw_w的个人主页 ✍&#x1f3fb;作者简介&#xff1a;计算机科学与技术研究生在读 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4a…

05-Redis初步使用

关系型数据的ACID特性:事务的四大特性:原子性,一致性,隔离性,持久性 关系型数据库应对的三高问题:高并发,高效率,高扩展 关系型数据库和非关系型数据库 关系型数据库的数据存储在表中,无法应对陡增的数据 非关系型数据库使用键值对的方式进行存储数据:redis可以用作缓存 r…

【运维工程师学习二】OS系统管理

【运维工程师学习二】OS系统管理 1、操作系统管理2、进程管理3、进程的启动4、进程信息的查看4.1、STAT 进程的状态&#xff1a;进程状态使用字符表示的&#xff08;STAT的状态码&#xff09;,其状态码对应的含义&#xff1a;4.2、ps命令常用用法&#xff08;方便查看系统进程&…

go语言环境安装

文章目录 环境介绍安装软件包步骤环境变量设置来一个经典的hello worldNice 最近的项目需要用到go来开发了&#xff0c;前几天就已经在看书了&#xff0c;今天是个周末&#xff0c;先在家里的机器上把环境搭好&#xff0c;特此记录一下。 环境介绍 下载地址&#xff1a;https:…

oracle 过滤字段中的中文,不再洋不洋土不土

目录 前言&#xff1a; 一、知己知彼 1.1业务场景 1.2错误案例 二、思路整理 2.1存储长度与字符串长度比较 三、还有没有其他思路 3.1ascii表查找法 3.2正式案例 四、总结 前言&#xff1a; 随着数字化建设的不断深入&#xff0c;企业越来越注重&#xff0c;企业数据治理&am…

算法笔记——排序算法

&#x1f44c;&#xff0c;begin&#xff1a; 排序算法很重要&#xff0c;它可以使数据按照一定的规律进行排序&#xff0c;各个语言的代码都有自己的排序函数&#xff0c;那么排序到底有哪几种方法&#xff0c;✌&#xff0c;如下&#xff1a; 按照效率分类如上图&#xff1a…

【Spring】设计思想

一、Spring 是什么&#xff1f; Spring是一个开源的Java框架&#xff0c;有着活跃而庞大的社区&#xff08;例如&#xff1a;Apache&#xff09;&#xff0c;Spring 提供了一系列的工具和库&#xff0c;可以帮助开发者构建高效、可靠、易于维护的企业级应用程序。Spring的核心…

短信压力测试系统,支持自定义接口

短信压力测试系统,支持自定义接口 支持卡密充值&#xff0c;短信压力测试系统&#xff0c;解决一切骚扰电话&#xff0c;教程在压缩包里面 可多个服务器挂脚本分担压力&#xff0c;套了cdn导致无法正常执行脚本可以尝试添加白名单 这边建议使用MySQL方式 同服务器下直接配置…

lesson 12 Zigbee绑定通信

目录 Zigbee绑定通信 通信原理 实验过程 实现步骤 实验现象 实验分析 Zigbee绑定通信 通信原理 1、Zigbee一共有五种通信方式&#xff1a;单播、广播、组播、MAC、广播 2、绑定是Zigbee的一种基本通信方式&#xff0c;具体绑定通信又分为三种模式&#xff0c;模式大同…

tomcat概述,优化,多实例部署

目录 一、概述 二、三个容器 1、Web 容器&#xff1a; 2、Servlet 容器&#xff1a; 3、JSP 容器&#xff1a; 三、Tomcat 功能组件结构 四、优化 1、启动速度优化 2、配置参数优化 五、多实例部署 一、概述 Tomcat 是 Java 语言开发的&#xff0c;Tomcat 服务器是一…