Android 启动流程及 init 进程解析

一、Android 启动流程概括

按下电源键触发开机,从 ROM 加载引导程序 BootLoader 到 RAM 中,BootLoader 执行启动 Linux kernel,然后启动第一个用户进程 init,init 进程的工作包括挂载文件、创建文件目录、设置 selinux 安全策略,解析 init.rc 脚本等。随后 init 进程会启动 Zygote 进程,Zygote 进程做一些资源预加载的工作,并启动 SystemServer 进程。SystemServer 进程作为 Socket 服务端,启动包括 AMS、WMS、PMS 等 90 多个服务在内的系统服务。在众多服务启动完毕后,AMS 会打开 Launcher 应用的 Home Activity,进入手机桌面。

附:kernel 的初始化流程(详细代码自行下载 linux kernel 源码)

传统的加载器包含两个文件

init.s:初始化堆栈,调用 main.c 的 main() 函数

main.c:初始化硬件(主板、闹钟等),创建 linux 标签

当内核完成系统设置后,会在系统文件中寻找 init 文件。

Dir:kernel/common/init/main.c

(1)执行 kernel_init() 函数

(2)启动 /bin/init  文件:try_to_run_init_process("/bin/init");

(3)try_to_run_init_process() ---> run_init_process()--->kernel_execve()

init/android.bp 中指明了 init 的入口函数:init/main.cpp,随后会执行 main.cpp 中的 main 方法


二、init 进程的启动流程

Dir:system/core/init/main.cpp ---> main() 方法

FirstStageMain() ---- SetupSelinux() ---- SecondStageMain()

第一阶段

FirstStageMain()
1、mount()--挂载文件、mkdir()--创建文件目录
2、SetStdioToDevNull()--重定向标准输入输出
3、InitKernelLogging()--初始化内核日志
4、启动 setupSelinux

SetupSelinux()
配置安全策略 -- 对应安卓的权限策略

第二阶段

SecondStageMain()
1、初始化属性系统

PropertyInit()


2、监听子进程的终止信号,释放资源,防止僵尸进程

InstallSignalFdHandler(&epoll);
InstallInitNotifier(&epoll);
StartPropertyService(&property_fd);


3、匹配 linux 命令和实际执行函数之间的关系

GetBuiltinFunctionMap()


4、解析 init.rc

LoadBootScripts(am, sm)-->CreateParser() //创建解析器-->//添加 rc 文件的解析组件 service、on、importparser.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));-->//解析 rc 文件parser.ParseConfig("/system/etc/init/hw/init.rc");-->ParserConfigDir-->ParserConfigFile-->ParserConfigFile-->ParserData


5、While(true) 循环监听
 

auto pending_functions = epoll.Wait(epoll_timeout);


总结:init 进程处理的事情:
1、 挂载文件
2、设置 selinux -- 安全策略
3、开启属性服务,注册到 epoll 中
4、解析 init.rc
5、循环处理脚本,包括启动 zygote
6、循环等待

                
启动 Service 的过程

/system/core/rootdir/init.rc-->on nonencryptedclass_start main --> do_class_start(const BuiltinArguments& args)class_start late_start/system/core/init/builtins.cpp-->do_class_start(const BuiltinArguments& args)-->StartIfNotDisabled()/system/core/init/service.cpp-->StartIfNotDisabled()-->Start()

三、Zygote 启动流程

1、触发 Zygote

Dir:/system/core/rootdir/init.rc

(1)init.rc 中引入的 zygote.rc 脚本

import /system/etc/init/hw/init.${ro.zygote}.rc
不同的 rc 配置文件对应不同的启动策略
根据不同厂商共有四个属性:
init.zygote32.rc  -- 执行 app_process
init.zygote64.rc --执行 app_process64
init.zygote32_64.rc -- 启动两个 zygote 进程,名为 zygote 和 zygote_secondary,分别执行 app_process32、app_process64
init.zygote64_32.rc -- -- 启动两个 zygote 进程,名为 zygote 和 zygote_secondary,分别执行 app_process64、app_process32


(2)zygote 触发时机

on late-init-->trigger zygote-starton zygote-start-->start zygoteDir:/system/core/init/init.cpp
if (bootmode == "charger") {am.QueueEventTrigger("charger");
} else {am.QueueEventTrigger("late-init");
}

附:app_process 位于手机系统的 bin 目录下,在 AS File Explorer 中可以看到,app_process 会
读取到 /frameworks/base/cmds/app_process/Android.bp 文件,然后执行 app_main.cpp,
/frameworks/base/cmds/app_process/app_main.cpp 的 main 方法解析的参数,则来源于 init.zygote.rc

2、Zygote 初始化

Dir:/frameworks/base/cmds/app_process/app_main.cpp
(1)main() 进行参数解析【--zygote  --start-system-server】

if (strcmp(arg, "--zygote") == 0) {zygote = true;niceName = ZYGOTE_NICE_NAME;
}
if (zygote) {runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
}

(2)创建虚拟机及注册 JNI

Dir:frameworks/base/core/jni/AndroidRuntime.cpp

startVm() -- 创建虚拟机
startReg() -- 注册 JNI--> register_jni_procs(gRegJNI, NELEM(gRegJNI), env) //gRegJNI 是一个 jni 数组对象env->CallStaticVoidMethod(startClass, startMeth, strArray);  //startClass 即传入的ZygoteInit//接下来就会执行 ZygoteInit.java 的 main 方法,从 native 层进入 java 层//JVM :虚拟机,其实就是一块代码,负责实现内存管理,因为是 zygote 通过 fork 创建的进程,所以每个进程都拥有一个独立的 JVM

3、Zygote 的 java 启动

(1)预加载,加快 app 进程的启动

Dir:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java -- main()

preload(bootTimingsTraceLog);
(1)bootTimingsTraceLog.traceBegin("PreloadClasses");  
//preloadClassess 将framework.jar里的preloaded-classes 定义的所有class load到内存里,preloaded-classes 编译Android后可以在framework/base下找到。
//会加载手机 system/etc/preloaded-classes 文件中记录好的类文件
(2)preloadResources();
//preloadResources 将系统的Resource(不是在用户apk里定义的resource)load到内存。资源preload到Zygoted的进程地址空间,所有fork的子进程将共享这份空间而无需重新load, 这大大减少了应用程序的启动时间,但反过来增加了系统的启动时间。通过对preload 类和资源数目进行调整可以加快系统启动。Preload也是Android启动最耗时的部分之一

(2)通知 VM 进行垃圾回收

//gc()必须在fork之前完成(接下来的StartSystemServer就会有fork操作),这样将来被复制出来的子进程才能有尽可能少的垃圾内存没有释放
gcAndFinalize();

(3)创建 zygote 服务端,本质上是一个 socket

//frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
zygoteServer = new ZygoteServer(isPrimaryZygote);-->//frameworks/base/core/java/com/android/internal/os/ZygoteServer.javamZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);-->//frameworks/base/core/java/com/android/internal/os/Zygote.javareturn new LocalServerSocket(fd);

 

(4)创建 SystemServer 进程

Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);ZygoteInit.java--forkSystemServer()-->Zygote.java--nativeForkSystemServer()-->com_android_internal_os_Zygote_nativeForkSystemServer.cpp【通过 JNI 的映射】-->pid_t pid = zygote::ForkCommon(env, true,fds_to_close,fds_to_ignore,true);-->pid_t pid = fork();  //最终调用 linux 的 fork()

(5)循环等待

caller = zygoteServer.runSelectLoop(abiList);


四、SystemServer 启动流程

1、参数处理

//ZygoteInit.java
//在 handleSystemServerProcess() 中进行 server 的初始化工作if (pid == 0) {if (hasSecondZygote(abiList)) {waitForSecondaryZygote(socketName);}zygoteServer.closeServerSocket();return handleSystemServerProcess(parsedArgs);
}//(1) prepareSystemServerProfile(systemServerClasspath);
//(2) 判断fork args 中是否有 invokWith 参数,如果有则进行WrapperInit.execApplicationif (parsedArgs.mInvokeWith != null) {String[] args = parsedArgs.mRemainingArgs;// If we have a non-null system server class path, we'll have to duplicate the// existing arguments and append the classpath to it. ART will handle the classpath// correctly when we exec a new process.if (systemServerClasspath != null) {String[] amendedArgs = new String[args.length + 2];amendedArgs[0] = "-cp";amendedArgs[1] = systemServerClasspath;System.arraycopy(args, 0, amendedArgs, 2, args.length);args = amendedArgs;}WrapperInit.execApplication(parsedArgs.mInvokeWith,parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,VMRuntime.getCurrentInstructionSet(), null, args);throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");} else {ClassLoader cl = getOrCreateSystemServerClassLoader();if (cl != null) {Thread.currentThread().setContextClassLoader(cl);}/** Pass the remaining arguments to SystemServer.*/return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,parsedArgs.mDisabledCompatChanges,parsedArgs.mRemainingArgs, cl);
}

2、初始化

(1) ZygoteInit

//ZygoteInit.javaZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,parsedArgs.mDisabledCompatChanges,parsedArgs.mRemainingArgs, cl);-->ZygoteInit.nativeZygoteInit();--> //JNIcom_android_internal_os_ZygoteInit_nativeZygoteInit-->//AndroidRuntime.cppgCurRuntime->onZygoteInit();-->//app_main.cppvirtual void onZygoteInit(){sp<ProcessState> proc = ProcessState::self();ALOGV("App process: starting thread pool.\n");//启动一个 Binder 线程池,用于 SystemServer 和其他线程的通信proc->startThreadPool();}

(2)applicationInit

//ZygoteInit.javareturn RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,classLoader);-->//RuntimeInit.javareturn findStaticMain(args.startClass, args.startArgs, classLoader);-->//RuntimeInit.java; 通过反射找到SystemServer 的 main 方法m = cl.getMethod("main", new Class[] { String[].class });return new MethodAndArgsCaller(m, argv);//耗时操作通过线程完成,MethodAndArgsCaller 的 run 方法中执行 mMethod.invoke(null, new Object[] {null, new Object[]{ mArgs });-->//SystemServer 的 main 方法执行:public static void main(String[] args) {new SystemServer().run();}

五、SystemServer 执行流程

1、初始化

(1)一些属性的设置

(2)初始化上下文

// Initialize the system context.
createSystemContext();private void createSystemContext() {ActivityThread activityThread = ActivityThread.systemMain();mSystemContext = activityThread.getSystemContext();mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);final Context systemUiContext = activityThread.getSystemUiContext();systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
}

2、创建 SystemServiceManager

//初始化SystemServiceManager,用来管理启动service,SystemServiceManager中封装了启动Service的startService方法启动系统必要的Service
mSystemServiceManager = new SystemServiceManager(mSystemContext);

3、启动一系列系统服务

try {t.traceBegin("StartServices");startBootstrapServices(t);startCoreServices(t);startOtherServices(t);startApexServices(t);
} catch (Throwable ex) {Slog.e("System", "******************************************");Slog.e("System", "************ Failure starting system services", ex);throw ex;
} finally {t.traceEnd(); // StartServices
}

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

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

相关文章

gitlab添加ssh公钥

一&#xff1a;生成公钥 桌面鼠标右击打开 Open Git Bash here (前提是安装了Git)&#xff1b; 2.输入命令 ssh-keygen -t rsa -C "123*****90qq.com"来生成新的密钥对,将其中的"123*****90qq.com"替换为你自己的电子邮件地址。 命令&#xff1a;ssh-keyg…

提升Vue3应用效率的秘诀:深入比较ref与reactive!

ref 和 reactive 是 Vue3 中实现响应式数据的核心 API。ref 用于包装基本数据类型&#xff0c;而 reactive 用于处理对象和数组。尽管 reactive 似乎更适合处理对象&#xff0c;但 Vue3 官方文档更推荐使用 ref。 我的想法&#xff0c;ref就是比reactive好用&#xff0c;官方也…

可分矩阵和k-拟可分矩阵

可分矩阵 可分矩阵&#xff08;Separable Matrix&#xff09;是线性代数和多变量数据分析中的一个重要概念。它关系到一种特殊类型的矩阵分解&#xff0c;这种分解可以将矩阵简化为更小的、更易处理的组成部分。在不同的应用背景中&#xff0c;可分矩阵的定义和性质可能有所不…

Oracle RMAN 备份恢复

Oracle RMAN 备份恢复 1.什么是RMAN RMAN在数据库服务器的帮助下实现数据库文件、控制文件、数据库文件和控制文件的映像副本&#xff0c;以及归档日志文件&#xff0c;数据库服务器参数文件的备份。RMAN也允许使用脚本文件实现数据的备份与恢复&#xff0c;而且这些脚本保存…

CentOS 升级 Glibc-2.28

安装 glibc-2.28 # 下载并解压 glibc-2.28 $ wget https://ftp.gnu.org/gnu/glibc/glibc-2.28.tar.gz $ tar -xzvf glibc-2.28.tar.gz $ cd glibc-2.28 # 创建临时文件 $ mkdir build && cd build $ ../configure --prefix/usr --disable-profile --enable-add-ons --…

Springboot 使用小记-集成 MyBatis 单多数据源

文章目录 1.单数据源1.1 application.properties 配置参考1.2应用类参考配置 2.多数据源(主从)2.1 application.properteis 如下配置2.2 主从数据源的配置2.3 Mybatis 配置动态数据源2.4 动态数据源线程安全处理2.5 获取动态数据源2.6 AOP 配置&#xff0c;在 DAO 层切入&#…

QT获取当前路径方法

一、获取应用程序运行路径 假设当前运行的应用程序在 ...\build-qt_python-Desktop_Qt_5_12_10_MinGW_64_bit-Debug\debug下&#xff0c;我们需要获取...\build-qt_python-Desktop_Qt_5_12_10_MinGW_64_bit-Debug\debug这个路径&#xff0c; 可以使用QCoreApplication提供的…

Spring: Springboot 框架集成不同版本的spring redis

文章目录 一、集成不同版本的spring redis1、Spring Data Redis 1.x&#xff1a;2、Spring Data Redis 2.x&#xff1a;3、Spring Data Redis 3.x&#xff08;Spring Boot 2.x&#xff09;&#xff1a; 二、springboot集成Spring Data Redis 2.x1、首先&#xff0c;确保在 pom.…

Vue:vue的安装与环境的搭建

文章目录 环境搭建安装node.js&#xff08;比较简单&#xff09;安装Vue脚手架初始化启动 环境搭建 安装node.js&#xff08;比较简单&#xff09; 首先要安装node.js&#xff0c;进入官网下载即可。 更改安装路径&#xff0c;保持默认配置&#xff0c;一直点击下一步安装即可…

Linux系统运维命令:查看cache里的URL,即查看系统访问了哪些包含http的url(使用grep结合awk,组合命令实现功能)

目 录 一、需求 二、解决方法 1、解决思路 2、命令 三、实例演示和命令解释 1、实例演示 2、命令解释 四、扩展 一、需求 想知道某台linux机器最近访问了哪些http的web地址&#xff0c;如何处理&#xff1f; 二、解决方法 1、解决思路 Linux系统访问外…

(undone) 如何计算 Hessian Matrix 海森矩阵 海塞矩阵

参考视频1&#xff1a;https://www.bilibili.com/video/BV1H64y1T7zQ/?spm_id_from333.337.search-card.all.click 参考视频2&#xff08;正定矩阵&#xff09;&#xff1a;https://www.bilibili.com/video/BV1Ag411M76G/?spm_id_from333.337.search-card.all.click&vd_…

如何电脑录屏?教你3分钟快速掌握!

在当今数字化时代&#xff0c;电脑录屏已成为一项必不可少的技能。无论是录制游戏画面、线上课程还是软件演示&#xff0c;录屏都可以帮助用户更好地保存和分享信息。可是如何电脑录屏呢&#xff1f;在本文中&#xff0c;我们将介绍两种常用的电脑录屏方法&#xff0c;并分步骤…

【nvm切换node版本,发现npm无法使用,简单粗暴的解决方案】

nvm切换node版本&#xff0c;发现npm无法使用&#xff0c;简单粗暴的解决方案 使用了nvm切换指定node版本后&#xff0c;发现npm命令无法使用。 在nodejs官网找到这部分内容 找到需要安装的压缩包 把解压的文件放入到自己的nvm文件夹内 这部分是解压的nodejs 示例

HarmonyOS—低代码开发Demo示例

接下来为大家展示一个低代码开发的JS工程的Demo示例&#xff0c;使用低代码开发如下华为手机介绍列表的HarmonyOS应用/服务示例。 1.删除模板页面中的控件后&#xff0c;选中组件栏中的List组件&#xff0c;将其拖至中央画布区域&#xff0c;松开鼠标&#xff0c;实现一个List组…

从初步的需求收集到详细的规划和评估

综合需求分析建议 明确与细化用户故事 确保每个用户故事清晰、具体,包含角色、目标和成功标准。对用户故事进行优先级排序,以指导开发过程中的功能实现顺序。用户参与和原型制作 创建用户旅程图,以理解用户在使用产品或服务时的整体流程与体验。制作原型或草图,展示用户界面…

产品可靠性设计-浪涌防护设计中TVS选型与计算

产品可靠性设计-浪涌防护设计中TVS选型与计算 上一篇 有对产品浪涌和雷电脉冲的瞬态抑制方法和思路进行了分析&#xff0c;我们再对浪涌防护中TVS的能量计算方法举例给电子工程师们进行参考。 1&#xff09;浪涌与EFT及在产品抗扰度测试时的问题及差异 EFT测试是一种高频的现…

Golang 关于 interface 接口的理解

package mainimport "fmt"// 定义一个存储器接口&#xff1a;支持mysql存储、redis存储 type StorageManager interface {insert(data string) int // 增加update(id int, data string) int // 更新 }// 实现一个Mysql存储器 type Mysql struct{}func (mysql…

Langchain-Chatchat:离线运行的大模型知识库 | 开源日报 No.182

chatchat-space/Langchain-Chatchat Stars: 22k License: Apache-2.0 基于 ChatGLM 等大语言模型与 Langchain 等应用框架实现的开源、可离线部署的检索增强生成 (RAG) 大模型知识库项目。该项目是一个可以实现完全本地化推理的知识库增强方案&#xff0c;重点解决数据安全保护…

【JSON2WEB】05 前端开发三件套 HTML CSS JavaScript 速成

前端三件套是指在网页开发中常用的三种技术&#xff1a;HTML&#xff0c;CSS和JavaScript。它们分别负责网页的结构&#xff0c;样式和交互&#xff0c;是构建网页的基础。下面我们来简单介绍一下它们的作用和特点&#xff0c;并举一些例子。 1 HTML——描述页面结构 HTML&am…

投票项目_登录功能的版本迭代

V0版本: 实现最简单的登录,写个接口去数据库查有没有前端传来的账号和密码,查到且密码正确返回“登录成功”&#xff0c;其余情况统一返回“账号或密码错误”。 V1版本: 用session代替cookie 先导入依赖,将session加入到redis中 1.导入依赖 <dependency><groupId>…