Binder系列--获取ServiceManager

news2024/12/26 12:03:11

获取ServiceManager
hongxi.zhu 2023-7-1

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

主要流程

在这里插入图片描述

SurfaceFlinger中获取SM服务

frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp

    // publish surface flinger
    sp<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单例模式确保每个进程只有一个ProcessState
        if (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 = 15
    result = 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-8KB
        if (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_entry

    if (e != nullptr) {
        IBinder* b = e->binder;  //handle_entry会保存handle对应的BpBinder对象
        if (b == nullptr || !e->refs->attemptIncWeak(this)) {
            if (handle == 0) {  //ServiceManager很特殊,固定的handle = 0
                IPCThreadState* 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_ERROR

                ipc->setCallRestriction(originalCallRestriction);

                if (status == DEAD_OBJECT)
                   return nullptr;
            }

            sp<BpBinder> b = BpBinder::PrivateAccessor::create(handle);  //根据handle创建BpBinder
            e->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为nullptr
        e.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.coloradmin.cn/o/715501.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

适合初中生用的台灯有哪些?这样的台灯最适合学生!

对于学生而言台灯主要的点就是能够护眼、缓解眼睛疲劳&#xff0c;因为学生需要长时间的学习和用眼而且可以休息放松的时间比较少&#xff0c;导致眼睛过度疲劳&#xff0c;这也是为什么这么多中小学生近视的原因。那么我们应该怎么样选好一款台灯呢&#xff1f; 要想台灯能护眼…

Linux系统之dnf包管理器的基本使用

Linux系统之dnf包管理器的基本使用 一、dnf工具介绍1. dnf工具简介2. dnf的功能 二、DNF的安装1. 检查本地操作系统版本2. 安装epel3. 检查本地yum仓库状态4. 安装dnf包 三、dnf的使用帮助1. 查看dnf版本2. 查看dnf命令的帮助信息3. dnf命令的选项解释 四、dnf命令的基本使用1.…

浅谈Unicode与UTF-8

我们都知道&#xff0c;在Golang中字符都是以UTF-8编码的形式存储&#xff0c;当我们使用range遍历字符串的时候&#xff0c;go会为我们取出一个字符(rune)而不是一个byte&#xff0c;例如以下例子&#xff0c;我们使用range迭代取出第一个字符“你”&#xff0c;并且打印输出取…

TechSmith Camtasia for Mac 2023.0.3 中文破解版 Win/Mac上强大的屏幕录像工具

Camtasia 是Win/Mac上最强大的屏幕录像工具之一&#xff0c;该软件集成了视频录制、编辑、导出等一系列功能&#xff0c;支持鼠标光标样式、草绘示意插图、冰冻区域等实用的功能&#xff0c;还具有移动客户端让你录制视频&#xff0c;然后通过无线传输到 Camtasia 中进行编辑&a…

判断数组中所有元素是否均为实数对象 numpy.isrealobj()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 判断数组中所有元素 是否均为实数对象 numpy.isrealobj() [太阳]选择题 请问关于以下代码的说法错误的是&#xff1f; import numpy as np a np.array([1, 2, 3]) b np.array([1, 1 2j, …

剑指 Offer 19: 正则表达式匹配

可能存在一个现象&#xff0c;就是aaab&#xff0c;然后a*ab&#xff0c;那么这样*只能代表一个a。 这道题可以使用动态规划的方式来解决。 这道题就是状态的判断&#xff1a;是否两个都为0&#xff1f;只有两个都为0才为true&#xff0c;并且判断*&#xff0c;有两个情况&…

Docker WebRTC容器部署方案

文章目录 WebRTC简介WebRTC Docker容器部署优势方案&#xff08;mpromonet/webrtc-streamer&#xff09;步骤 WebRTC简介 WebRTC&#xff08;Web Real-Time Communication&#xff09;是一种开放的实时通信技术&#xff0c;它允许浏览器之间进行音频、视频和数据的实时传输。W…

从古代八卦探究计算机的八进制

八进制&#xff0c;即八卦&#xff0c;是中国古代哲学体系中非常重要的一个概念&#xff0c;它被广泛应用于易经、道家、儒家等诸多领域。随着计算机科学的快速发展&#xff0c;人们开始思考&#xff1a;八进制是否可以应用到计算机上&#xff1f; 一、什么是八进制&#xff1…

Javaee技术目的总结

一.前节回顾 在前一节中&#xff0c;我们了解了&#xff1a; 1.将中央控制器中的Action容器&#xff0c;变成可控制! 2.针对于反射调用业务代码&#xff0c;最终页面跳转 3.jsp页面参数传递后台的代码优化字段太多有影响&#xff01; 二.项目部署前期准备工作 1.项目运行环境…

c语言 va_start/va_end函数

c语言 va_satrt和va_end函数介绍 头文件&#xff1a;#include <stdarg.h> 函数原型&#xff1a;void va_start(va_list ap, last) 和 void va_end(va_list ap); 可以被参数数量和类型可变的函数调用。 可变参数用…&#xff08;3个省略号表示可变参数列表&#xff09; …

深入理解 http 反向代理

要理解什么是 反向代理(reverse proxy) , 自然你得先知道什么是 正向代理(forward proxy). 另外需要说的是, 一般提到反向代理, 通常是指 http 反向代理, 但反向代理的范围可以更大, 比如 tcp 反向代理, 在这里, 不打算讨论 tcp 之类的反向代理, 当文中说到反向代理时, 指的就是…

C++day5

2、 #include <iostream> using namespace std; static int blood 10000; class hero { protected:string name;int hp;int attack; public:hero(){}//无参构造hero(string name,int hp,int attack):name(name),hp(hp),attack(attack){}//有参构造virtual void Atk(){b…

使用supervisor管理进程

写目录 一、supervisor简介二 、supervisor安装2.1下载supervisor2.2配置文件详解2.3把squid服务加入到supervisor管理当中 一、supervisor简介 supervisor是Python开发的c/s服务&#xff0c;是Linux系统下的进程管理工具。可以监听、启动、停止、重启一个或多个进程用supervi…

卡尔曼滤波原理和使用

随着传感技术&#xff0c;机器人&#xff0c;自动驾驶等不断发展&#xff0c;对控制系统的精度以及稳定性要求越来越高。卡尔曼滤波作为一种状态最优估计方法&#xff0c;应用也越来越普遍、 对于Kalman Filter的理解&#xff0c;用过都知道“黄金五条”公式&#xff0c;且通过…

pytorch实战13:基于pytorch实现YOLOv1(长长文)

基于pytorch实现YOLOv1&#xff08;长长文&#xff09; 前言 ​ 本篇文章的目的是记录自己实现yolo v1的过程&#xff0c;在此过程中&#xff0c;参考了许多开源的代码和博客&#xff0c;赞美大佬们。 参考文献和代码 YOLO v1代码参考&#xff1a;&#xff08;读书人的事情&…

华为OD机试真题 Python 实现【猜字谜】【2023Q1 100分】,附详细解题思路

目录 一、题目描述二、输入描述三、输出描述补充说明:四、解题思路五、Python算法源码六、效果展示1、输入2、输出3、说明一、题目描述 小王设计了一人简单的清字谈游戏,游戏的迷面是一人错误的单词,比如nesw,玩家需要猜出谈底库中正确的单词。猜中的要求如 对于某个谜面和…

武汉抖音seo,抖音关键词排名

抖音seo怎么做 抖音作为一款热门的社交娱乐应用&#xff0c;其SEO关键词排名对于提升内容曝光和用户流量非常重要。 1. 关键词研究&#xff1a;在进行SEO关键词排名时&#xff0c;首先需要进行关键词研究&#xff0c;了解用户在抖音上搜索的热门关键词。可以通过使用相关的关…

如何使用ArcGIS自定义图框样式

GIS制图过后&#xff0c;一般都会涉及到出图&#xff0c;大家肯定都希望自己的出图成果越美观越好&#xff0c;除了图本身之外&#xff0c;还可以从周边的一些修饰入手&#xff0c;比如图框&#xff0c;那么今天就给大家讲解一下如何自定义ArcGIS图框风格&#xff0c;希望能对大…

Docker-Dockerfile-DockerCompose的那些事

文章目录 一、Docker背景1.1 环境不一致1.2 隔离性1.3 弹性伸缩1.4 学习成本 二、Docker介绍2.1 Docker的由来2.2 Docker的思想 三、Docker的安装3.1 下载Docker依赖的环境3.2 指定Docker镜像源3.3 安装Docker3.4 Docker服务启停3.5 Docker容器运行解析3.6 Docker运行流程解析3…

暑期学JavaScript【第五天】

BOM Browser Object Model&#xff0c; 浏览器对象模型 DOM是BOM的一部分 1.navigator 用于判断当前的用户端。 用navigator.userAgent获取 2. location location.href www.baidu.com&#xff0c; 赋值后&#xff0c;执行直接跳转到百度location.search; 获取地址中?后面部…