安卓系统启动流程
目录
光看分析文章还是不够的,还是要和实践结合。
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,并将创建设备节点文件的工作托付给ueventd
return 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》. 完成解析相关的类是ActionManager
、Parser
和XXParser
,均位于system/core/init
目录下面。除此之外,还有Action
和Service
等类。它们的作用是,各种Parser
用来解析 rc 文件中的指令。解析出的指令会被封装成Action
和Service
等对象。
作者:开发者如是说
链接: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()
方法。该方法会调用Service
的start()
方法。该方法主要是调用clone
或fork
创建子进程,然后调用execve
执行配置的二进制文件,另外根据之前在rc
文件中的配置,去执行这些配置。
作者:开发者如是说
链接:https://juejin.cn/post/6844904137268674568
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
builtins
类system/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
这里的runtime
是AndroidRuntime
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 0
if (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");
}
各种启动服务的操作是通过调用 SystemServiceManager
的 startService()
。
在 startBootstrapServices()
中会拉起Activity Manager Service
和Package Manager Service
Apex
服务是指Android
操作系统中的一种应用程序启动方式,它允许应用程序在设备启动时以系统服务的形式自动运行。这些服务通常包括系统应用、框架服务和系统UI等。它们在设备启动时会自动运行,并为用户提供各种基础功能和界面。
startApexServices
方法会遍历所有已安装的Apex
服务,并调用它们的启动方法,使它们在系统启动时自动运行。该方法在系统启动过程中被调用,是Android
操作系统启动过程中的一部分。
5 总结
6 引用
Android 系统启动.md
Android 系统启动流程 | 皮皮小黑屋 Android 系统启动流程分析
Android 系统启动过程源码分析 - 掘金社区
Android 12 S 系统开机流程分析 - SetupSelinux(二)-CSDN博客