Binder系列--ServiceManager的启动

news2024/11/16 3:27:05

ServiceManager的启动
hongxi.zhu
Android 13

主要流程:
在这里插入图片描述

1. 启动ServiceManager进程

ServiceManager是由init(pid = 1)进程启动的
system/core/rootdir/init.rc

on init
	...
	...
    # Start essential services.
    start servicemanager  //framework层使用
    start hwservicemanager  //hal层
    start vndservicemanager  //vendor内部

进程被创建后,进入main方法

2. 执行main方法

frameworks/native/cmds/servicemanager/main.cpp

int main(int argc, char** argv) {
	...
    const char* driver = argc == 2 ? argv[1] : "/dev/binder";

    sp<ProcessState> ps = ProcessState::initWithDriver(driver); //创建ProcessState对象(进程唯一),并进行open、ioctl、mmap操作
    ps->setThreadPoolMaxThreadCount(0);  //初始线程数实际为0
    ps->setCallRestriction(ProcessState::CallRestriction::FATAL_IF_NOT_ONEWAY);

    sp<ServiceManager> manager = sp<ServiceManager>::make(std::make_unique<Access>());  //构造自己时构造Access对象,这个是用于权限检测
    //先注册自己作为服务
    if (!manager->addService("manager", manager, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()) {
        LOG(ERROR) << "Could not self register servicemanager";
    }

    IPCThreadState::self()->setTheContextObject(manager); //保存ServiceManager作为BBinder对象到IPCThreadState实例中
    ps->becomeContextManager();  //向驱动注册自己成为全局唯一的ContextManager,全局只有一个ServiceManager

    sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/); //获取一个Looper

    BinderCallback::setupTo(looper);  //将binder fd添加到Looper中监听,当驱动有事件时,回调handleEvent()处理
    ClientCallbackCallback::setupTo(looper, manager);  //这个是用于告知客户端当前服务端有多少个客户端绑定的回调监听

    while(true) {  //循环等待事件到来
        looper->pollAll(-1);  //阻塞等待event的到来,然后进行ioctl和驱动交互获取数据
    }

    // should not be reached
    return EXIT_FAILURE;
}
  1. 创建ProcessState对象,通过这个对象打开驱动节点,检验Binder版本和进行内存映射等操作,
  2. 设置线程池的初始大小为0,实际线程数并不是固定的,这个要根据实际任务决定是否新加线程到线程池(binder驱动中处理这个逻辑
  3. 然后创建ServiceManager对象,并通过addService添加自己到服务列表中。
  4. 保存ServiceManager作为BBinder对象到IPCThreadState实例中,并向Binder驱动注册自己成为全局唯一的ContextManager
  5. 然后创建Looper,通过Looper监听binder驱动的消息,当驱动有消息时回调handleEvent()处理(然后真正读数据是ioctl)
  6. 进入大循环阻塞等待事件到来

3. ProcessState::initWithDriver(driver)

frameworks/native/libs/binder/ProcessState.cpp

sp<ProcessState> ProcessState::initWithDriver(const char* driver)
{
    return init(driver, true /*requireDefault*/);
}

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;
}

#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)  //ServiceManager申请用于binder通信的虚拟内存大小也是 1MB-8KB
#define DEFAULT_MAX_BINDER_THREADS 15   //最大工作线程数 15 + 1(本身)
#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);  //进行open、ioctl

    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()

3.1 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异步通信检测

3.2 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的拷贝。

4. setTheContextObject

void IPCThreadState::setTheContextObject(const sp<BBinder>& obj)
{
    the_context_object = obj;
}

保存ServiceManager作为BBinder对象到IPCThreadState实例中(这样处理其他进程获取SM时就不需要每次都创建BBinder的对象)

5. becomeContextManager

bool ProcessState::becomeContextManager()
{
    AutoMutex _l(mLock);

    flat_binder_object obj {
        .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
    };

    int result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);  //注册自己成为ContextManager
	...
    return result == 0;
}

向驱动注册自己成为全局唯一的SM

6. BinderCallback::setupTo(looper)

class BinderCallback : public LooperCallback {  //继承于LooperCallback,当Looper通过epoll监听到对应fd有event时回调cb.handleEvent
public:
    static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
        sp<BinderCallback> cb = sp<BinderCallback>::make();   //创建BinderCallback对象

        int binder_fd = -1;
        IPCThreadState::self()->setupPolling(&binder_fd);
        LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd);

        int ret = looper->addFd(binder_fd,  //添加binder驱动的fd到Looper的监听fd
                                Looper::POLL_CALLBACK,
                                Looper::EVENT_INPUT,
                                cb,  //传递自己作为callback对象,有event就回调自己的handleEvent方法
                                nullptr /*data*/);
        LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper");

        return cb;
    }

    int handleEvent(int /* fd */, int /* events */, void* /* data */) override {
        IPCThreadState::self()->handlePolledCommands();  //驱动有事件上报时,就去处理
        return 1;  // Continue receiving callbacks.
    }
};

将binder驱动的fd注册到Looper中监听,当驱动上报事件时回调handleEvent, 处理相关事务

6.1 setupPolling

status_t IPCThreadState::setupPolling(int* fd)
{
    if (mProcess->mDriverFD < 0) {
        return -EBADF;
    }

    mOut.writeInt32(BC_ENTER_LOOPER);
    flushCommands();
    *fd = mProcess->mDriverFD;
    return 0;
}

void IPCThreadState::flushCommands()
{
    if (mProcess->mDriverFD < 0)
        return;
    talkWithDriver(false);  //通过ioctl 通知驱动将当前线程加入Looper状态
	...
}

通过ioctl通知驱动将当前线程加入Looper状态

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

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

相关文章

Linux | Ubuntu卸载QQ

Linux | Ubuntu卸载QQ 终端输入&#xff1a; dpkg -l| grep qq如下图&#xff0c;找到QQ文件&#xff1a; 删除命令&#xff1a; sudo apt-get --purge remove 文件名在终端输入&#xff1a; sudo apt-get --purge remove libqqwing2v5:amd64如下图删除成功

【Java程序设计实训】基于B/S架构的MyShop商城

MYSHOP商城 实验目的实验概述系统功能概述Myshop 商城概述系统开发分析功能列表系统用例图系统活动图 数据库设计运作界面展示用户管理模块新用户注册用户登录商城首页与用户退出 商品模块商品分页展示查看商品详情信息 购物车模块空购物车页面加入商品到购物车 订单模块提交订…

4.32UDP通信实现 4.33广播 4.34组播 4.35本地套接字通信

4.32UDP通信实现 ![在这 udp_client.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h>int main() {// 1.创建一个通信的socketint fd socket(PF_INET, SOCK_DGRAM, 0);if(fd -1) {…

springboot项目外卖管理 day08-缓存优化

文章目录 一、缓存优化问题说明环境搭建导入maven坐标配置yml文件设置序列化器&#xff0c;编写配置类 缓存短信验证码缓存菜品数据实现思路 SpringCacheSpring Cache介绍Spring Cache常用注解Spring Cache使用方式 缓存套餐数据实现思路 一、缓存优化 问题说明 环境搭建 导入…

【Framework】startService启动流程

前言 启动service有两种方式&#xff1a;startService和bindService。 这一篇先讲startService&#xff0c;读者如果只想看流程图&#xff0c;可以直接跳到总结。 1. ContextImpl 代码路径&#xff1a;frameworks\base\core\java\android\app\ContextImpl.java 1.1 startServ…

SHA-256算法及示例

1. 引言 SHA-256&#xff08;安全哈希算法&#xff0c;FIPS 182-2&#xff09;是密码学哈希函数&#xff0c;其摘要长度为256位。SHA-256为keyless哈希函数&#xff0c;即为MDC&#xff08;Manipulation Detection Code&#xff09;。【MAC消息认证码有key&#xff0c;不是key…

【数据库】外键的作用

前言 说到外键&#xff0c;一般就会牵扯出约束。不谈约束&#xff0c;起始外键就是一个普通的字段&#xff08;Column&#xff09;&#xff0c;起到一个关联的作用。 先把约束放一边&#xff0c;看看外键有哪些作用。 建立表中记录的一对一的关系 学生表&#xff1a; 手机表…

C++引用计数

文章目录 1. 什么是引用计数2. 引用计数的实现3. 示例代码 1. 什么是引用计数 引用计数&#xff08;reference count&#xff09;的核心思想是使用一个计数器来标识当前指针指向的对象被多少类的对象所使用&#xff08;即记录指针指向对象被引用的次数&#xff09;。它允许有多…

Linux--查找文件指令:find

语法&#xff1a; find pathname -name 文件名 示例&#xff1a; 从根目录下开始查找名字中带file.txt文件的路径

C++思维导图以及作业

定义一个命名空间Myspace&#xff0c;包含以下函数&#xff1a;将一个字符串中的所有单词进行反转&#xff0c;并输出反转后的结果。例如&#xff0c;输入字符串为"Hello World"&#xff0c;输出结果为"olleH dlroW"&#xff0c;并在主函数内测试该函数。 …

战略书籍排行榜前五名

战略管理对企业的重要性不言而喻。有效的战略管理可以帮助企业确定未来的方向和目标、制定长期发展规划、提高企业的竞争力和获得市场份额。本文推荐的这5本优秀的战略管理类书籍&#xff0c;每一本都有其独特的思想和实践价值&#xff0c;值得企业管理者借鉴和学习。 战略书籍…

Leetcode---351周赛

周赛题目 2748. 美丽下标对的数目 2749. 得到整数零需要执行的最少操作数 2750. 将数组划分成若干好子数组的方式 2751. 机器人碰撞 一、美丽下标对的数目 这题没什么好说的&#xff0c;按照题目的要求直接暴力求解&#xff0c;代码如下 bool gcd(int x,int y){if(x1||y1)…

vue父子组件之间相互控制传值,子组件使用$parent直接控制父组件的值

父子组件之间相互控制传值&#xff0c;子组件控制父组件的值 需求概述 父组件在提交表单后&#xff0c;弹框进行提示&#xff0c;子组件是一个弹框。 vue版本 v2.x 实现原理 在父组件内建立控制器isShowModal&#xff0c;使用v-if来控制子组件的显示与隐藏。在子组件通过…

DAY39——动态规划part2

1.考虑障碍在起点和终点的特殊状况&#xff0c;可直接返回0 2.判断是否存在障碍物&#xff1a;初始化时需要设置障碍物后的坐标为0

常见存储引擎

TiKV 简介 TiKV 是一个分布式事务型的键值数据库&#xff0c;提供了满足 ACID 约束的分布式事务接口&#xff0c;并且通过Raft协议保证了多副本数据一致性以及高可用。TiKV 作为 TiDB 的存储层&#xff0c;为用户写入 TiDB 的数据提供了持久化以及读写服务&#xff0c;同时还存…

Spring BeanFactory FactoryBean的区别?

文章目录 前言一、BeanFactory二、FactoryBean 前言 面试中被问到过Spring BeanFactory FactoryBean的区别&#xff1f;当时没答上来&#xff0c;感觉这就是一个文字游戏&#xff0c;后面仔细的了解了一下&#xff0c;分享给大家。 一、BeanFactory 在 Spring 中最核心的就是…

GDB 断点管理

1、b 设置断点 usage 1: b 函数名 usage 2: b 文件名:行号 2、rb 函数名关键字 &#xff1a; 所有带有这个关键字的函数名都设置为断点 (gdb) rb dkauth Breakpoint 7 at 0x34187ae0: file /home/jintao/cvf/apps/cvf/services/dkauth/src/dkauth.c, line 58. void dkauth_…

Python之花舞盛宴:探索列表与元组之美与妙用

前言 在Python编程世界中&#xff0c;列表和元组是两个最常用的数据结构之一。无论是初学者还是经验丰富的开发者&#xff0c;对于这两个数据类型的掌握都至关重要。 列表和元组都是用于存储多个值的容器&#xff0c;但它们在性质和特性上有所不同。列表是可变的&#xff0c;…

VMIC-pci-5565反射内存的优势

优势&#xff1a; &#xff08;1&#xff09;实现远程互连的能力 随着仿真实验复杂度的提高&#xff0c;需要多楼宇多试验室间设备的远程互连&#xff0c;通过单模光纤及光纤HUB将远距离的试验室设备进行连接&#xff0c;单模光纤支持的传输距离可达20km。对于距离300m以内的试…

免费搭建一个有脾气的聊天机器人,1行Python代码就够了!

大家好&#xff0c;这里是程序员晚枫。 之前在小破站&#xff1a;Python自动化办公社区给大家免费分享了用Python制作一个wx机器人&#xff0c;1行代码人人可用&#xff0c;很多人还想要免费的智能聊天功能。 今天终于开发出来了&#xff0c;让我们一起看一下&#xff0c;如何…