三.Android系统的启动流程

news2024/11/18 21:40:10

Android系统总体启动流程

  • Boot ROM (启动只读存储器,Loader层)
    • 当电源按下时,引导芯片代码会从预定义的地方(固化在ROM中,Read Only Memory)开始执行,加载引导程序BootLoader到RAM(Random Access Memory),然后执行
  • Boot Loader (启动加载器,类似于Windows的bios系统,Loader层)
    • 引导操作系统启动
  • idle进程(pid=0,process id) (Kernel层)
    • 这是Android的第1个进程,也叫swapper进程
    • 初始化进程管理、内存管理,加载Binder Driver、Display、Camera Driver等相关工作
    • 启动kthreadd进程(pid=2,这个是Kernel层的进程)和init进程(pid=1,这个是native层的进程)
  • init(pid=1) (C/C++ Framework Native层)
    • 用户空间的鼻祖,用户空间的进程都是由init进程或其子孙进程创建而来的
  • zygote进程 (少部分Native层和大部分Java Framework层)
    • Java进程的鼻祖,Java层的进程都是通过zygote创建的
    • 我们的进程一般都是通过zygote fork出来的,
      比如:要启动我们的app进程,是通过SystemServer进程中的AMS服务,去通知zygote进程fork出我们的app进程
  • SystemServer进程 (Java Framework层)
    • 系统服务有90多种,如:AMS、WMS、PMS等
  • App进程 (App层)

init进程的启动
首先,init进程是由idle进程启动的,而idle进程是属于kernel层的,所以我们从kernel层去寻找启动init进程的地方.
代码执行流程:(kernel\common\init\main.c)

  • android-kernel\common\init\main.c 找到kernal层init进程的main.c文件
  • -> kernel_init()
  • -> try_to_run_init_process(“/bin/init”) //这里就是启动我们手机系统中的init进程
  • -> run_init_process()
  • -> kernel_execve()

init的入口函数:(system/core/init/main.cpp)

  • init/main.cpp文件下的main()函数,下面是这个main()函数中的几个步骤.
    •   if (argc > 1) {
            if (!strcmp(argv[1], "subcontext")) {
                android::base::InitLogging(argv, &android::base::KernelLogger);
                const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
      
                return SubcontextMain(argc, argv, &function_map);
            }
            //TOOD 第二步
            if (!strcmp(argv[1], "selinux_setup")) {
                return SetupSelinux(argv);
            }
            //TODO 第三步
            if (!strcmp(argv[1], "second_stage")) {
                return SecondStageMain(argc, argv);
            }
        }
        //TODO 第一步
        return FirstStageMain(argc, argv);
      
  • FirstStageMain 第1阶段 文件目录:system/core/init/first_stage_init.cpp
    • 挂载创建文件
      • 通过mount命令
    • 重定向输入输出
      • SetStdioToDevNull(argv);
    • 初始化内核的日志打印
      • InitKernelLogging(argv);
    • 启动selinux_setup liunux这块的安全策略 Android权限:最小权限原则
  • SecondStageMain 第2阶段 文件目录:system/core/init/init.cpp SecondStageMain
    • 初始化属性域
      • PropertyInit()
    • 监听子进程终止信号,并销毁子进程
      • (防止僵尸进程.所谓僵尸进程,就是子进程挂掉了,但是父进程没有监听并销毁他,从而导致了已经挂掉的子进程依然占用系统资源)
      •   InstallSignalFdHandler(&epoll);
          InstallInitNotifier(&epoll);
          StartPropertyService(&property_fd);
        
    • 匹配命令和函数之间的关系(GetBuiltinFuctionMap)
      • 如:为mkdir、mount、chmod命令的使用提前做好准备
    • 解析init.rc文件
      • LoadBootScrips(am, sm);
        • CreateParser 创建解析器
          •   parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, GetSubcontext(), std::nullopt));
              parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
              parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
            
        • parser.ParseConfig(“/system/etc/init/hw/init.rc”); 解析init.rc文件,解析器ParserConfig的目录在:system/core/init/parse.cpp
          • ParseConfigDir(解析文件目录)->ParseConfigFile(最终解析文件目录中的文件)
          • ParseConfigFile(解析文件)
            • ParseData 解析数据
              • 解析init.rc文件(二进制格式)
    • 进入while循环(等待)
      • auto pending_functions = epoll.Wait(epoll_timeout);

init处理的重要事情:(重点)

  • 挂在创建文件
    • 通过mount命令实现挂载,比如:我们的U盘插入Linux系统的电脑,会将我们的U盘中的文件和Linux系统中的文件夹绑定起来,从而我们可以通过这个文件夹读取U盘中的文件
  • 设置selinux(安全策略)
    • SelinuxInitialize();
  • 开启属性服务,注册到epoll中
    • StartPropertyService(&property_fd);
  • 解析init.rc文件
    • 解析init.rc文件中就包含了启动zygote指令(trigger zygote-start),在下面的while循环中就会去执行这个命令
  • 循环处理脚本 (包括启动zygote进程)
    • 通过while循环
  • 循环等待 (防止init进程结束)
    • 通过epoll.Wait(epoll_timeout);

总结:

  1. init进程是在idle进程中启动的,而idle进程属于内核层,所以我们从kernel目录下的main.c文件中的main()函数中去分析init进程的启动流程,最终执行到kernel_execve()方法.
  2. 接着我们我们从init目录下,main.cpp文件中的main()函数中,去分析init进程启动流程中做了哪些处理,里面分为了两个阶段:FirstStageMain和SecondStageMain,分别做了如下操作:挂起文件、设置Linux安全策略(selinux)、开启属性服务并注册到epoll中、解析init.rc文件、循环处理脚本和循环等待

扩展:Android.mk(旧)->Android.bp(新) 脚本文案,用于代码编译;启动init进程的init文件,也就是手机系统system/bin/init文件,是通过Android.bp编译init/main.cpp文件生成的,所以我们可以通过init/main.cpp文件去研究init进程的启动流程.

init进程启动流程图如下:
请添加图片描述

zygote进程的启动
在init.rc(所在目录:system/core/rootdir)文件中就包含了启动zygote的指令(start zogote),通过执行这个命令就会去启动zygote进程;然后启动zygote会解析如下文件,根据手机系统解析相应的文件:init.zygote32.rcinit.zygote64.rcinit.zygote32_64.rcinit.zygote64_32.rc.
同时在AS中的Device File Explore工具中,可以在system/bin目录下查看到启动的zogote文件,也就是app_process、app_process32和app_process64,根据手机cpu系统来分别执行相应的文件,这些文件的来源如下:

  • frameworks/base/cmds/app_process/Android.bp文件中会执行app_main.cpp相关代码,也就是说Android.bp通过编译app_main.cpp得到我们的system/bin/app_process文件,下来我们就通过app_main.cpp文件中的main()函数来研究zygote的启动.

zygote的启动流程:

  1. Native层
    • main() @app_main.cpp
      • 目录:frameworks/base/cmds/app_process/app_main.cpp
    • 解析app_process目录下的init.zygote32.rc文件中的参数
      • 该文件中第1行中,后面标记选中部分就是参数,从-Xzygote开始:service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    • 解析到参数时做变量标记,后续执行相应的代码逻辑
      • zygote=true和startSystemmServer=true,启动完成zygote进程后,接着就会根据startSystemServer变量启动SystemServer进程
    • 调用ART的start()方法
      • runtime.start(“com.android.internal.os.ZygoteInit”, args, zygote);
        • 注意:"com.android.internal.os.ZygoteInit"这个目录是Java Framework中ZygoteInit.java的全类名路径,所以最终会调用到Java Framework层中ZygoteInit.java中的main()方法.
      • 这里AppRuntime中没有实现start方法,该方法是由他的父类AndroidRuntime实现的.
    • start() @AndroidRuntime.cpp
      • startVm() 启动虚拟机
        • 由于Android虚拟机都是通过zygote进程通过一句代码来完成的,所以虚拟机仅仅是用来实现进程对内存空间的管理.
      • startReg(env) 启动注册表,也就是注册jni
        • 我们项目中通过Java中的native方法,去调用C++中的方法,就是通过这个注册的jni作为桥梁来实现的,因为Java层是无法直接与Native层进行通信的.
        • 代码举例:AndroidRuntime.cpp
            int register_com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env)
            {
                //TODO 这里Java中的nativeZygoteInit方法,
                //就会调用到C++中的com_android_internal_os_ZygoteInit_nativeZygoteInit方法,
                //从而将Java层与Native关联起来
                const JNINativeMethod methods[] = {
                    { "nativeZygoteInit", "()V",
                        (void*) com_android_internal_os_ZygoteInit_nativeZygoteInit },
                };
                return jniRegisterNativeMethods(env, "com/android/internal/os/ZygoteInit",
                    methods, NELEM(methods));
            }
        
      • CallStaticVoidMethod(startClass, startMeth, strArray);
        • jmethodID startMeth = env->GetStaticMethodID(startClass, “main”, “([Ljava/lang/String;)V”);
        • 这里就会调用到Java Framework层中的ZygoteInit.java中的main()方法,也就完成了从Native层到Java Framework层的跨越.
        • 这里之所以能实现,是因为native层中的方法同样可以通过jni,调用到Java层类中的方法;我们熟悉的是Java层通过jni作为桥梁,实现了Java中的native方法调用到native层中的方法;
  2. java层: main() @ZygoteInit.java
    • 预加载,会去加载一些class文件、resource文件和共享的包等
      • preload(bootTimingsTraceLog);
    • 创建Soket服务器,以便于接收socket消息,然后fork进程
      • zygoteServer = new ZygoteServer(isPrimaryZygote);
    • 通过fork创建SystemServer进程
      • Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
    • 通过Looper进入循环
      • Runnable caller = zygoteServer.runSelectLoop(abiList);

总结:

  • 通过app_main.cpp的main()函数去解析init.zagote.rc文件
    • 通过解析init.rc文件中的命令,从而去启动zygote进程;启动zygote进程时,需要通过app_main.cpp文件中的main()函数,根据手机系统型号去解析相应的init.zygote.rc文件,解析init.zygote.rc文件中的参数和命令时会相应的去启动Zygote和添加启动SystemServer的参数.
  • 启动zagote的流程:
    • Native层
      • 通过app_main.cpp中的main函数去解析init.zygote.rc文件,
      • 调用AndroidRuntme.cpp中的start()方法,
        • runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
        • 这个方法中包含了启动虚拟机startVm、启动JNI注册startReg和调用静态方法CallStaticVoidMethod(startClass, startMeth, strArray);注意:这个静态方法其实是一个jni方法,可以调用到Java层ZygoteInit.java类中的main()方法.
    • Java层
      • ZygoteInit.java类中的main()方法做了哪些事情:
        • 预加载,加载一些class和resource资源,preload(bootTimingsTraceLog);
        • 创建Soket服务器ZygoteServer,用于接收soket消息并fork进程.
        • 通过fork创建SystemServer进程
        • 通过Looper进入无限循环,通过接收soket消息然后fork出进程.

zygote进程启动流程图如下:
请添加图片描述

SystemServer进程的启动
SystemServer进程是用来做服务管理的,他会启动一些引导服务、核心服务和其他服务,总共有90多个服务,通过如下代码来启动服务:

startBootstrapServices(t);//启动引导服务10个,如:ATMS、AMS、PowerManagerService等
startCoreServices(t);//启动核心服务9个,如:BatteryService、LooperStatusService、WebViewUpdateService等
startOtherServices(t);//启动其他服务70多个,如:BluetoothService、ShortcutService等

启动SystemServer进程流程:

  • Runnable r = forkSystemServer() @ZygoteInit.main()
    返回Runnable,在最后会调用他的run()方法
    • Zygote.forkSystemServer() @ZygoteInit.main()
      该方法会返回进程的pid,pid=0表示子进程,pid!=0表示zygote进程
      • nativeForkSystemServer() @Zygote.java
        • 在zagote的app_main.cpp注册jni中去找这个native方法的实现,先找zygote类的注册,然后再从zygote类中的方法里去寻找.
      • com_android_internal_os_Zygote_nativeForkSystemServer()
        • 路径:/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
      • forkCommon()
      • pid_t pid = fork();
        • 这里会调用Linux系统的fork()方法创建进程
    • handleSystemServerProcess() @ZygoteInit.main()
      根据pid=0的逻辑判断,去处理创建的SystemServer进程,并返回Runnable,这个Runnable是通过反射得来的;
      pid=0表示孩子进程(这里就是Zygote启动的孩子SystemServer进程),pid!=0表示zygote进程
      • ZygoteInit.zygoteInit()
      • RuntimeInit.applicationInit()
      • findStaticMain() @RuntimeInit.java
        • 通过反射得到SystemServer的Class对象,并获取到main()方法;
          Class<?> cl = Class.forName(className, true, classLoader);
          Method m = cl.getMethod("main", new Class[] { String[].class });
      • MethodAndArgsCaller() @RuntimeInit.java
        继承自Runnable,包含了Method mMethod和String[] mArgs两个成语变量
        • run()
          • mMethod.invoke(null, new Object[] { mArgs });
  • r.run(); @ZygoteInit.main()
    • 启动Runnable的run方法,最后就会调用到MethodAndArgsCaller类中的run()方法,然后调用这段代码:
      mMethod.invoke(null, new Object[] { mArgs });
      执行SystemServer.java的main()方法
  • SystemServer.main() 里面会直接调用其run()方法
    通过反射调用SystemServer中的main方法,这里面做了如下几件事情:
    1. 创建SystemServiceManager,用于启动服务和管理服务
    • new SystemServiceManager(mSystemContext);
    • mSystemServiceManager.startService(new AlarmManagerService(context));//启动服务
    1. 启动引导服务、核心服务和其他服务,代码如下:
    startBootstrapServices(t);//启动引导服务
    startCoreServices(t);//启动核心服务
    startOtherServices(t);//启动其他服务
    
    1. Looper.loop();
    • 启动Looper循环,保持SystemServer进程一直存活

SystemServer进程涉及到的几个类:

  • SystemServiceManager
    • 启动和管理Service
  • SystemService
    • 抽象类,所有的服务要么直接继承自SystemService;要么在该类中重写一个LifeCycle内部类继承自SystemService,并且LifeCycle里面包含了一个当前类的成员变量.
  • AMS
    • 具体的服务实现类
  • ServiceManager 是一个进程
    • 用于存储我们的服务,以map的形式存储服务,通过Name和服务的Binder的方式存储
    • 通过具体的Service,如:ATMS类的内部类LifeCycle的onStart()方法里,通过这个方法将这个服务添加到ServiceManager中去:
      publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);

总结:

  • 在ZygoteInit.java的main()方法中,通过forkSystemServer()方法,创建SystemServer进程,这个方法会调用到Native层Zygote.cpp文件中的nativeForkSystemServer()函数,最终会调用Linux系统的fork()方法,创建SystemServer进程并返回进程的pid;
  • 根据创建的进程pid=0的逻辑判断,最终通过反射调用到SystemServer.java中的main()方法,在SystemServer进程中做一些处理.
    该方法主要做了以下几个操作:
    1. 创建SystemServiceManager,用于启动和管理服务;
    2. 启动一些引导服务、核心服务和其他服务,总共有90多个服务;
    3. 通过Looper开启循环,保证SystemServer进程一直存活.

App进程的启动流程
应该就是通过从ServiceManager中取出AMS,然后通过AMS给Zygote进程发送Sockct消息,Zygote进程在ZygoteServer中接收到消息后,通过fork()创建App进程,然后通过配置文件中的去启动我们的app的首页.

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

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

相关文章

C++:C++PrimerPlus第六版:Chapter9:内存模型和名称空间

Chapter9&#xff1a;内存模型和名称空间 1. C源文件组织策略我们先看下面程序清单&#xff1a; coordin.hmain.cppCoordin.cpp 2. 内存存储方案2.1 自动存储持续性2.2 静态存储持续变量2.3 存储方案和动态分配 本章节从三个大方面做总结 C多个源文件组织方式C存储方案C名称空间…

案例告诉你 ChatGPT 最有可能取代哪些职业

ChatGPT 的应用场景ChatGPT 最可能取代哪些职业&#xff1f;写在最后 ChatGPT 的应用场景 ChatGPT 的应用场景大体上可以归类为三大模块。 第一类是 代码相关 的任务场景。包含程序语言之间的相互转换&#xff08;如 python 转 java&#xff09;、程序命令的生成、代码 bug 的…

考虑多能负荷不确定性的区域综合能源系统鲁棒规划(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

单链表OJ题:LeetCode--21.合并两个有序链表

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;今天给大家带来的是LeetCode中876题&#xff1a;链表的中间结点 数 据 结 构&#xff1a;数据结构专栏 作 者&#xff1a;stackY、 C 语 言 &#xff1a;C语言专栏 LeetCode &#xff1a;LeetCode刷题训练营 L…

5月16日,亚马逊云科技邀你一起探索游戏的无限可能

游戏精品化趋势越发明显&#xff0c;如何才能让技术帮助创意更好地实现&#xff1f;游戏出海如火如荼&#xff0c;如何才能在全球市场分一杯羹&#xff1f;生成式AI横空出世&#xff0c;如何才能充分利用&#xff0c;实现降本增效、利润提升&#xff1f;相信每个游戏人&#xf…

Word控件Spire.Doc 【文本框】教程(3):如何在 Word 中插入或删除文本框

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

基于VC + MSSQL实现的县级医院医学影像PACS

一、概述&#xff1a; 基于VC MSSQL实现的一套三甲医院医学影像PACS源码&#xff0c;集成3D后处理功能&#xff0c;包括三维多平面重建、三维容积重建、三维表面重建、三维虚拟内窥镜、最大/小密度投影、心脏动脉钙化分析等功能。 二、医学影像PACS实现功能&#xff1a; 1、…

漏洞分析丨CVE-2012-1873

一、漏洞简述 cve-2012-1873同样是一个著名的堆溢出漏洞&#xff0c;他是IE6-8中MSHTL.dll中的CTableLayout::CalculateMinMax函数里&#xff0c;程序在执行时会以HTML代码中的元素span属性作为循环控制次数向堆中写入数据。第一次会优先根据span申请堆空间&#xff0c;当我们…

【企业信息化】第3集 免费开源ERP: Odoo 16 POS终端管理系统

文章目录 前言一、概览二、硬件三、使用功能 前言 世界排名第一的免费开源ERP: Odoo 16 POS终端管理系统。几分钟内完成设置&#xff0c;几秒内完成销售。 一、概览 Odoo POS 基于智能界面&#xff0c;任何零售公司均可毫不费力地使用 因为其极具灵活性&#xff0c;您可配置 …

2023最全 Java 高频面试合集,掌握这些你也能进大厂!

进大厂是大部分程序员的梦想&#xff0c;而进大厂的门槛也是比较高的&#xff0c;所以这里整理了一份阿里、美团、滴滴、头条等大厂面试大全&#xff0c;对于 Java 后端的朋友来说应该是最全面最完整的面试备战仓库&#xff0c;为了更好地整理每个模块&#xff0c;我也参考了很…

版本控制器git

目录 一、版本控制系统 二、工作流程和使用命令 &#xff08;1&#xff09;工作流程 &#xff08;2&#xff09;一次完整流程的相关命令 1.初始化1个空的本地仓库 2.克隆方式1个远程仓库到本地仓库 3.新文件添加到暂存区 4.查看仓库状态&#xff0c;显示有变更的文件 5…

Java实现杨辉三角

1 问题 实现杨辉三角。 2 方法 public class textttt01 { public static void main(String[] args) { //定义了一个长度为10&#xff0c;高度为10的二维数组&#xff0c;数组中的值都为0&#xff1b; int[][] arrnew int[10][10]; for (int i0;i<ar…

开源轻量级 IM 框架 MobileIMSDK 的Uniapp客户端库已发布

一、基本介绍 MobileIMSDK-Uniapp端是一套基于Uniapp跨端框架的即时通讯库&#xff1a; 1&#xff09;超轻量级、无任何第3方库依赖&#xff08;开箱即用&#xff09;&#xff1b;2&#xff09;纯JS编写、ES6语法、高度提炼&#xff0c;简单易用&#xff1b;3&#xff09;基于…

html实现经典捕鱼达人小游戏

文章目录 1.设计来源1.1 游戏界面 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/130638634 html实现经典捕鱼达人小游戏源码 &#xff0c;99.99%的还原实物&#xff0c;起…

【办公】解决京瓷打印机总是出现烦人的“在手送纸盘中装纸 彩色纸”的问题

问题 打印机是日常办公的常见工具&#xff0c;京瓷是著名的打印机品牌&#xff0c;而且是很多事业单位首选的打印机品牌。然而在日常使用中京瓷打印机总是会出现烦人的“在手送纸盘中装纸 彩色纸”的问题&#xff0c;如下图所示&#xff1a; 一旦出现该问题&#xff0c;就需要…

给XZZ准备的小攻略(私人向)

定时发送邮件功能&#xff1a; 定时发送邮件的功能位于 homework 的 views.py 中 使用的模块是 apscheduler &#xff08;我读作ap司改就&#xff09; 准备的部分&#xff1a;&#xff08;了解即可&#xff09; 安装好 django-apscheduler 后&#xff0c;在 setting.py 中添…

python 调用golang 注意事项

1.调用编译后的动态库文件&#xff0c;报头文件错误 原因&#xff1a; 不同平台下编译的add.so 不能通用&#xff0c;Windows下可以运行的so文件&#xff0c;linux下就不能运行&#xff0c;需要重新编译linux的so文件&#xff1b; 该报错可能就是跨平台使用动态库文件了&…

yolov5爬坑小作文

第一坑 做完训练集&#xff0c;配置要yaml文件后&#xff0c;笔者启动了训练命令 python train.py --data 我的yaml位置 --batch-size 我的每次进行一次反向传播之前需要前向计算的图片张数 --device 我的GPU编号 之后报错 OSError: [WinError 1455] 页面文件太小,无法完成…

检测数据类型

//typeof() 对于基本数据类型没问题&#xff0c;遇到引用数据类型不管用 console.log(typeof 666) //number console.log(typeof [1,2,3]) //object //instanceof() 只能判断引用数据类型&#xff0c;不能判断基本数据类型 console.log( [] instanceof Array) //true …

Acunetix 15.6 (Linux, Windows) - Web 应用程序安全测试

Acunetix 15.6 (Linux, Windows) - Web 应用程序安全测试 请访问原文链接&#xff1a;https://sysin.org/blog/acunetix-15/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Acunetix 漏洞扫描器&#xff0c;管理您的网络安全。…