安卓系统启动流程解析

news/2024/9/20 2:40:40/文章来源:https://www.cnblogs.com/Joooook/p/18415190

安卓系统启动流程

目录

  • 1 init阶段
    • 1.1 FirstStage
    • 1.2 SELinux Setup
    • 1.3 SecondStage
  • 2 init.rc的配置
  • 3 Zygote的启动
    • 3.1 app_process
    • 3.2 Zygoteinit.java
  • 4 SystemServer
  • 5 总结
  • 6 引用

光看分析文章还是不够的,还是要和实践结合。

1 init阶段

init 命令的入口是init/main.cpp的main()

int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)__asan_set_error_report_callback(AsanReportCallback);
#elif __has_feature(hwaddress_sanitizer)__hwasan_set_error_report_callback(AsanReportCallback);
#endif// //以上部分是和编译相关的语法,满足条件时则保留后面的语句。它具体针对的是两种不同的内存检查工具:AddressSanitizer (ASan) 和 Hardware Address Sanitizer (HWAsan)。// Boost prio which will be restored later// 提升权限,后续会恢复setpriority(PRIO_PROCESS, 0, -20);if (!strcmp(basename(argv[0]), "ueventd")) {//init进程创建子进程ueventd,并将创建设备节点文件的工作托付给ueventdreturn ueventd_main(argc, argv);}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);}if (!strcmp(argv[1], "selinux_setup")) {return SetupSelinux(argv);}if (!strcmp(argv[1], "second_stage")) {return SecondStageMain(argc, argv);}}// 最开始执行第一阶段return FirstStageMain(argc, argv);
}

init的启动阶段图

1.1 FirstStage

firstStage的源码在/system/core/init/first_stage_init.cpp

大部分是创建启动所需的各种目录,还初始化kernel log,最下面有这么一段

     const char* path = "/system/bin/init"; const char* args[] = {path, "selinux_setup", nullptr}; auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);close(fd);execv(path, const_cast<char**>(args)); // execv() only returns if an error happened, in which case we// panic and never fall through this conditional.PLOG(FATAL) << "execv(\"" << path << "\") failed";return 1;
}

这就是进入下一阶段的代码

1.2 SELinux Setup

SetupSelinux主要功能就是管理selinux和相关策略。

SELinux(Security Enhanced Linux)是一种为Linux内核提供的强制访问控制(MAC, Mandatory Access Control)框架。它的设计目的是为了提高Linux系统的安全性,通过严格的权限管理机制来限制进程和用户的访问权限,从而减少系统被攻击的风险。

在SELinux Setup的结尾也有进入下一阶段的代码。

1.3 SecondStage

在SecondStage中有一个LoadBootScripts的函数

init.rc是和init相关的配置文件

这里会在 LoadBootScripts() 方法中解析 init.rc 文件。关于该文件指令的含义可以参考 AOSP 中的文档:《Android Init Language》. 完成解析相关的类是 ActionManagerParserXXParser,均位于 system/core/init 目录下面。除此之外,还有 ActionService 等类。它们的作用是,各种 Parser 用来解析 rc 文件中的指令。解析出的指令会被封装成 ActionService 等对象。
作者:开发者如是说
链接:https://juejin.cn/post/6844904137268674568
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

简单摘要一下SecondStage的代码

int SecondStageMain(int argc, char** argv) {...ActionManager& am = ActionManager::GetInstance();...am.QueueEventTrigger("early-init"); // Trigger all the boot actions to get us started.am.QueueEventTrigger("init"); // Don't mount filesystems or start core system services in charger mode.std::string bootmode = GetProperty("ro.bootmode", "");if (bootmode == "charger") {am.QueueEventTrigger("charger"); } else {am.QueueEventTrigger("late-init"); }...
}

这里是构造了一个EventTrigger对象, 放到事件队列中,但是并没有触发

直到ExecuteOneCommand才会依次触发

具体做什么,就在读取的配置文件中。源代码中位于/system/core/rootdir/init.rc

init中会start service manager

在lateinit中会trigger zygote-start

zygote-start会根据不同的配置,执行不同的操作,但最终都会执行start zygote

2 init.rc的配置

上面提到在init.rc中有start zygote等启动服务的配置,那么这个start又做了什么。

根据https://chromium.googlesource.com/aosp/platform/system/core/+/refs/heads/master/init

这里的解释还是只有启动服务。

这里的 start 会被映射到 builtins 类的 do_start() 方法。该方法会调用 Servicestart() 方法。该方法主要是调用 clonefork 创建子进程,然后调用 execve 执行配置的二进制文件,另外根据之前在 rc 文件中的配置,去执行这些配置。
作者:开发者如是说
链接:https://juejin.cn/post/6844904137268674568
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

builtinssystem/core/init/builtins.cpp

system/core/init/service.cpp

servicemanager的配置位于/framework/native/cmds/servicemanager

zygote的配置是根据系统类型变化的,在init.rc的开头被import

zygote64为例,配置文件如下

主要是启动了/system/bin/app_process64

相关源码在/frameworks/base/cmds/app_process/

3 Zygote的启动

3.1 app_process

/frameworks/base/cmds/app_process/app_main.cpp

主要是进行参数解析

还有启动ZygoteInit

这里的runtimeAndroidRuntime

AndroidRuntime定义于/frameworks/base/core/jni/AndroidRuntime.cpp

所以这里的 start() 方法是一种 JNI (Java Native Interface)调用。这里将会调用 Java 中的静态 main() 方法继续执行。这里是在 C++ 中调用 Java 的方法。

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{.../* 获取环境变量 */const char* rootDir = getenv("ANDROID_ROOT");if (rootDir == NULL) {rootDir = "/system";if (!hasDir("/system")) {LOG_FATAL("No root directory specified, and /system does not exist.");return;}setenv("ANDROID_ROOT", rootDir, 1);}.../* start the virtual machine *//* startVM启动虚拟机 */JniInvocation jni_invocation;jni_invocation.Init(NULL);JNIEnv* env;if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {return;}onVmCreated(env);.../** Start VM.  This thread becomes the main thread of the VM, and will* not return until the VM exits.*//* 获取类及其Main,并Call */char* slashClassName = toSlashClassName(className != NULL ? className : "");jclass startClass = env->FindClass(slashClassName);if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);/* keep going */} else {jmethodID startMeth = env->GetStaticMethodID(startClass, "main","([Ljava/lang/String;)V");if (startMeth == NULL) {ALOGE("JavaVM unable to find main() in '%s'\n", className);/* keep going */} else {env->CallStaticVoidMethod(startClass, startMeth, strArray);#if 0if (env->ExceptionCheck())threadExitUncaughtException(env);
#endif}}free(slashClassName);ALOGD("Shutting down VM\n");if (mJavaVM->DetachCurrentThread() != JNI_OK)ALOGW("Warning: unable to detach main thread\n");if (mJavaVM->DestroyJavaVM() != 0)ALOGW("Warning: VM did not shut down cleanly\n");
}

3.2 Zygoteinit.java

/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

public static void main(String[] argv) {ZygoteServer zygoteServer = null;// Mark zygote start. This ensures that thread creation will throw// an error.ZygoteHooks.startZygoteNoThreadCreation();// Zygote goes into its own process group.// 设置进程权限try {Os.setpgid(0, 0);} catch (ErrnoException ex) {throw new RuntimeException("Failed to setpgid(0,0)", ex);}Runnable caller;try {// Store now for StatsLogging later.final long startTime = SystemClock.elapsedRealtime();final boolean isRuntimeRestarted = "1".equals(SystemProperties.get("sys.boot_completed"));String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,Trace.TRACE_TAG_DALVIK);bootTimingsTraceLog.traceBegin("ZygoteInit");RuntimeInit.preForkInit();boolean startSystemServer = false;String zygoteSocketName = "zygote";String abiList = null;boolean enableLazyPreload = false;// 解析参数for (int i = 1; i < argv.length; i++) {if ("start-system-server".equals(argv[i])) {startSystemServer = true;} else if ("--enable-lazy-preload".equals(argv[i])) {enableLazyPreload = true;} else if (argv[i].startsWith(ABI_LIST_ARG)) {abiList = argv[i].substring(ABI_LIST_ARG.length());} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());} else {throw new RuntimeException("Unknown command line argument: " + argv[i]);}}// ...  资源预加载与日志// 启动zygoteServer zygoteServer = new ZygoteServer(isPrimaryZygote); if (startSystemServer) {// 启动SystemServer Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);// {@code r == null} in the parent (zygote) process, and {@code r != null} in the// child (system_server) process.if (r != null) {r.run();return;}}Log.i(TAG, "Accepting command socket connections");// The select loop returns early in the child process after a fork and// loops forever in the zygote.caller = zygoteServer.runSelectLoop(abiList);} catch (Throwable ex) {Log.e(TAG, "System zygote died with fatal exception", ex);throw ex;} finally {if (zygoteServer != null) {zygoteServer.closeServerSocket();}}// ...
}

这里主要做了

  • 创建ZygoteServer 对象。提供了等待 UNIX 套接字的命令,并且提供了 fork 虚拟机的方法。
  • 资源预加载
  • 调用 forkSystemServer() 。其中会调用 Zygote 的静态方法来 Fork 一个子进程。该方法内部又会调用 JNI 层的 nativeForkSystemServer 方法最终完成 Fork 操作。
  • 启动 select 循环,等待连接。会等待接受命令,fork子进程,并返回子进程的 main 方法

4 SystemServer

启动了SystemServer后,SystemServer还会拉起其他的系统内服务。

/frameworks/base/services/java/com/android/server/SystemServer.java

摘要一些代码分析

// frameworks/base/services/java/com/android/server/SystemServer.java
public static void main(String[] args) {new SystemServer().run();
}// frameworks/base/services/java/com/android/server/SystemServer.java
private void run() {TimingsTraceAndSlog t = new TimingsTraceAndSlog();try {// ...// Create the system service manager.// 启动system service manager mSystemServiceManager = new SystemServiceManager(mSystemContext);mSystemServiceManager.setStartInfo(mRuntimeRestart,mRuntimeStartElapsedTime, mRuntimeStartUptime);mDumper.addDumpable(mSystemServiceManager);// Start services.try {t.traceBegin("StartServices");startBootstrapServices(t);   // 启动引导服务 startCoreServices(t);     // 启动核心服务 startOtherServices(t);     // 启动其他服务 startApexServices(t);     // 启动Apex服务 } catch (Throwable ex) {Slog.e("System", "******************************************");Slog.e("System", "************ Failure starting system services", ex);throw ex;} finally {t.traceEnd(); // StartServices}// ...// Loop forever.Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");
}

各种启动服务的操作是通过调用 SystemServiceManagerstartService()

startBootstrapServices() 中会拉起Activity Manager ServicePackage Manager Service

Apex服务是指Android操作系统中的一种应用程序启动方式,它允许应用程序在设备启动时以系统服务的形式自动运行。这些服务通常包括系统应用、框架服务和系统UI等。它们在设备启动时会自动运行,并为用户提供各种基础功能和界面。
startApexServices方法会遍历所有已安装的Apex服务,并调用它们的启动方法,使它们在系统启动时自动运行。该方法在系统启动过程中被调用,是Android操作系统启动过程中的一部分。

5 总结

6 引用

Android 系统启动.md
Android 系统启动流程 | 皮皮小黑屋 Android 系统启动流程分析
Android 系统启动过程源码分析 - 掘金社区
Android 12 S 系统开机流程分析 - SetupSelinux(二)-CSDN博客

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

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

相关文章

安卓架构

安卓架构 目录1 Linux 内核层 2 硬件抽象层 HAL 3 Native C/C++ 库 && Android Runtime 4 Java Framework 层 5 System Apps 层1 Linux 内核层 Android 平台的基础是 Linux 内核。例如,ART 依靠 Linux 内核来执行底层功能。Linux 内核的安全机制为 Android 提供了相应…

陈柯烽的第一次作业

这个作业属于哪个课程 https://edu.cnblogs.com/campus/zjlg/rjjc这个作业的目标 建立一个属于自己的博客,介绍自己以及表达对课程的期望姓名-学号 陈柯烽-2022329301006软件技术基础第一次作业 一、自我介绍 1、基本信息我叫陈柯烽,来自浙江宁波,是自动化22级一班的学生,我…

chm 文件打开只显示目录,不显示内容

chm 文件打开只显示目录,不显示内容解决方法:解除锁定OK 了

【好用安全保密】不用插件,压缩js、html、css、code【一眼就会系列】【亲测有效】

​ 仅用离线版Notepad搞定。不用插件及辅助工具,有效保证了文件信息安全。 (一般发布版本都是无注释的-压缩文件和已编译文件。为了信息安全性,所有都是离线-区域网研发。) ​ 总结: 先把文本中注释去掉。 notepad++ 【编辑】-【空白字符操作】-【移除行首和行尾空格】 …

《密码系统设计》第二周

第二周预习报告 学习内容Head First C 嗨翻 C 语言 第8章 《Windows C/C++加密解密实战》第3,5章 第三章重点 3.4,3.5, 特别3.4.4 第五章重点 5.3.7 课程 mindmapAI 对学习内容的总结(1分) 要求让AI阅读学习内容并进行总结总结 1. Head First C 嗨翻 C 语言 第8章函数指针:…

活动召集丨实时多模态 AI Builder 团聚!RTE Open Day@S创上海,9.20/21

9 月 20~21 日,上海, S创上海 2024,看见不一样的创新和技术。这场年轻、多元、活力十足的科技盛会,将汇聚创业者、开发者、艺术家和众多无法定义边界的跨界者。RTE 开发者社区的 Builders 和 RTE Open Day 也将玩乐其中!「有一群人在一起,就很好」。 来到第四期的 RTE O…

问题1:pytorch版本不同,文件运行速度不同

好像新版本更快一点哦,为什么呢? 版本1: PyTorch version: 1.12.1Is CUDA available? TrueCUDA version: 11.3cuDNN version: 8302版本2: PyTorch version: 2.3.1+cu121Is CUDA available? TrueCUDA version: 12.1cuDNN version: 8907

踩坑日志2:dataloader的num_workers问题

当我想用dataloader多开子进程加快图片加载速度时,发现报有关进程的错误:RuntimeError: An attempt has been made to start a new process before thecurrent process has finished its bootstrapping phase.This probably means that you are not using fork to start your…

深度!程序员生涯的垃圾时间(上)

深度好文!程序员的垃圾时间! 垃圾时间(Garbage time)是体育赛事中的术语,指一场比赛中双方分差过大,胜负已定。此时,比赛剩余的时间不再对最终结果产生决定性影响,剩下的时间就被称为垃圾时间。将这个词用在浩浩荡荡的技术革命、汹涌向前的历史车轮上,再合适不过。时代…

基于Java+Springboot+Vue开发的民宿预订管理系统

项目简介该项目是基于Java+Springboot+Vue开发的民宿预订管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的民宿预订管理系统项目,大学生可以在实践中学…

基于Java+Springboot+Vue开发的电影订票管理系统

项目简介该项目是基于Java+Springboot+Vue开发的电影订票管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的电影订票管理系统项目,大学生可以在实践中学…

解决c盘无故被大量占用问题

解决c盘无故被大量占用问题 问题引出 在用win10、win11的时候经常遇到c盘莫名其妙的就被占用完了,即便清理后也还是没将内存释放出来,这可能是因为虚拟内存过大导致的。 虚拟内存是什么 1.Windows虚拟内存机制 Windows虚拟内存是一种内存管理技术,它允许计算机在物理内存不足…