Android系统启动流程概述
Android 系统的启动流程大致如下:
Android 系统的启动流程是一个从底层往上层的过程,先由 BootLoader 引导开机,然后依次进入 Kernel -> Native -> Framework -> App
BootLoader
按下电源键后,芯片从固化在 ROM 里预设的代码(BOOT ROM)开始执行,加载引导程序 BootLoader 到 RAM 中,然后启动内核。
Linux Kernel
Linux 内核负责初始化各种软硬件环境,加载驱动程序,挂载根文件系统(/)等,最重要的是,内核启动完成后,它会在根文件系统中寻找 "init" 文件,然后启动 init 进程。
init 进程
init 进程是 Linux 系统中用户空间的第一个进程,进程号为 1,我们可以说它是 root 进程或者所有进程的父进程。
init 进程的主要工作如下:
- 挂载虚拟文件系统;
- 初始化 Property 服务;
- 启动 SELinux 服务;
- 解析执行 init.rc 文件;
Zygote 进程
init 进程在解析 init.rc 时,会创建 Zygote 进程,它是 Android 系统最重要的进程之一。后续 Android 中的 App 进程都是由 Zygote 进程 fork 出来的。因此,Zygote 是 Android 系统所有应用的父进程。Zygote 进程的实际执行文件并不是 Zygote,而是 /system/bin/app_process。源码路径为: frameworks/base/cmds/app_process。 它会调用 frameworks/base/core/jni/AndroidRuntime.cpp 提供的接口启动 Java 层的代码 frameworks/base/core/java/com/android/internal/os/ZygoteInit.java。至此,我们就进入到了 Java 的世界。
Zygote 的主要工作如下:
- 启动虚拟机。
- 注册 JNI 函数到虚拟机。
- 调用 ZygoteInit 的 main() 函数。
ZygoteInit 的主要工作如下:
- 预加载资源。
- 创建 Socket 服务端,用于跨进程通信。
- 通过 fork 创建并启动 SystemServer 进程。
- Sokcet 服务端进入循环监听等待,等待后续 AMS 请求(AMS 会通过 Socket 请求 Zygote 来创建应用程序进程)。
Zygote 进程的出现是为了能更快的启动应用,因为在 Android 中,每个应用都有对应一个虚拟机实例(AndroidRuntime)为应用分配不同的内存地址。如果 Android 系统为每一个应用启动不同的虚拟机实例,就会消耗大量的内存以及时间,因此,更好的办法是通过创建一个虚拟机进程,由该 VM 进程预加载以及初始化核心库类,然后,由该 VM 进程 fork 出其他虚拟机进程,这样就能达到代码共享、低内存占用以及最小的启动时间,而这个 VM 进程就是 Zygote。
SystemServer 进程
与 Zygote 进程一样,SystemServer 进程同样是 Android 系统中最重要的进程之一,它的源码路径为: /frameworks/base/services/java/com/android/server/SystemServer.java
SystemServer 的主要的作用是启动各种系统服务,比如 ActivityManagerService、PackageManagerService、WindowManagerService 以及硬件相关的 Service 等服务,我们平时熟知的各种系统服务其实都是在 SystemServer 进程中启动的,这些服务都运行在同一进程(即 SystemServer 进程)的不同线程中,而当我们的应用需要使用各种系统服务的时候其实也是通过与 SystemServer 进程通讯获取各种服务对象的句柄进而执行相应的操作的。在所有的服务启动完成后,会调用各服务的 service.systemReady() 来通知各对应的服务,系统已经就绪。
Launcher 的启动
Launcher 的启动比较复杂,而且不同版本的 Android 系统启动逻辑可能也不太一样,所以这里就不具体讨论,后续再专门讨论。但我们可以大概说明一下启动的策略。
我们知道 SystemServer 进程在启动的过程中会启动 PackageManagerService,PackageManagerService 启动后会将系统中的应用程序安装完成。SystemServer 启动完所有的服务后,会调用各服务的 service.systemReady(),Launcher 的启动逻辑就在 ActivityManagerService.systemReady() 中。
BootAnimation 退出
Launcher 启动完之后,我们还看不到 Launcher,因为被 BootAnimation 的画面挡住了。BootAnimation 的退出也比较复杂,后续再详细讨论。大概是第一个应用起来之后,其 ActivityThread 线程进入空闲状态时,会通过某种机制把 BootAnimation 给退出。这里的第一个应用自然就是 Launcher了。这样就能确保在 BootAnimation 退出后,用户看到的不是黑屏,而是我们的桌面了。
源码分析
下面我们通过源码来分析 Android 系统的启动流程
1. 内核启动 init 进程
Android 系统底层是 Linux 内核,当用户按下电源键后,引导芯片代码会加载 BootLoader 到 RAM 中,并开始拉起 Linux 内核。Linux 内核初始化的地方在/kernel/common/init/main.c的kernel_init()方法中:
c
static char *execute_command;
static char *ramdisk_execute_command = "/init";
static int __ref kernel_init(void *unused)
{
...
// 在根文件系统中找 init 可执行文件并执行它
if (ramdisk_execute_command) {
ret = run_init_process(ramdisk_execute_command);
// 如果执行成功,直接 return
if (!ret)
return 0;
pr_err("Failed to execute %s (error %d)\n",
ramdisk_execute_command, ret);
}
// 查找其他地方的 init 可执行文件并执行它
if (execute_command) {
ret = run_init_process(execute_command);
if (!ret)
return 0;
panic("Requested init %s failed (error %d).",
execute_command, ret);
}
if (CONFIG_DEFAULT_INIT[0] != '\0') {
ret = run_init_process(CONFIG_DEFAULT_INIT);
if (ret)
pr_err("Default init %s failed (error %d)\n",
CONFIG_DEFAULT_INIT, ret);
else
return 0;
}
// 依次执行下面目录的 init 程序(init程序就是个so库),哪个执行成功就退出
if (!try_to_run_init_process("/sbin/init") ||
!try_to_run_init_process("/etc/init") ||
!try_to_run_init_process("/bin/init") ||
!try_to_run_init_process("/bin/sh"))
return 0;
...
}
内核会在各个路径寻找 init 可执行文件并执行它,执行会调用 run_init_process() 方法,代码如下:
c
static int run_init_process(const char *init_filename)
{
const char *const *p;
argv_init[0] = init_filename;
pr_info("Run %s as init process\n", init_filename);
pr_debug(" with arguments:\n");
for (p = argv_init; *p; p++)
pr_debug(" %s\n", *p);
pr_debug(" with environment:\n");
for (p = envp_init; *p; p++)
pr_debug(" %s\n", *p);
return kernel_execve(init_filename, argv_init, envp_init);
}
最终执行 kernel_execve() 方法。
2. init 进程
init 可执行文件对应的源码在/sytem/core/init/目录下,打开 Android.bp 文件,可以看到 init 进程的入口为main.cpp,我们从它的 main() 方法开始分析:
cpp
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#endif
// 可执行文件名为 ueventd
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
// 第二个参数为 subcontext
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
// 第二个参数为 selinux_setup
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
// 第二个参数为 second_stage
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
return FirstStageMain(argc, argv);
}
里面根据传入的参数主要分为3个阶段:FirstStageMain、SetupSelinux 和 SecondStageMain, 上一节中我们看到内核启动 init 进程时,没有带参数,所以这里会直接执行 FirstStageMain。
2.1 FirstStageMain
FirstStageMain 代码在/system/core/init/first_stage_init.cpp中,这部分代码较长,我们分为6个部分来分析。
FirstStageMain 函数第一部分
代码如下:
cpp
// init crash 时重启引导程序,这个函数主要作用是将各种信号量,
// 如 SIGABRT,SIGBUS 等的行为设置为 SA_RESTART,一旦监听到这些信号即执行重启系统
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
std::vector<std::pair<std::string, int>> errors;
#define CHECKCALL(x) \
if ((x) != 0) errors.emplace_back(#x " failed", errno);
InstallRebootSignalHandlers() 将各种信号量,如 SIGABRT,SIGBUS 等的行为设置为 SA_RESTART,一旦监听到这些信号即执行重启系统:
cpp
void InstallRebootSignalHandlers() {
// 当 init 崩溃时,与 panic'ing the kernel 这种默认行为不同的是,我们倾向于在开发构建时重启到
// bootloader,这样可以防止启动进入死循环,让系统更容易恢复正常
struct sigaction action;
memset(&action, 0, sizeof(action));
sigfillset(&action.sa_mask);
action.sa_handler = [](int signal) {
// 从 init fork 出来的进程会捕获这些 signal handlers,
// 但我们不希望触发重启,我们直接在子进程中调用 _exit() 方法
if (getpid() != 1) {
_exit(signal);
}
// Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.
// RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option
// and probably good enough given this is already an error case and only enabled for
// development builds.
// 调用 DoReboot() 或 LOG(FATAL) 不是一个好的选择,因为这是一个 signal handler。
// 重启系统使用 syscall() 不是 async-signal-safe 的,但这是我们唯一最好的的选择,
// 因为考虑到这已经是一个错误情况,并且我们只在开发构建时启用。
InitFatalReboot(signal);
};
action.sa_flags = SA_RESTART;
sigaction(SIGABRT, &action, nullptr);
sigaction(SIGBUS, &action, nullptr);
sigaction(SIGFPE, &action, nullptr);
sigaction(SIGILL, &action, nullptr);
sigaction(SIGSEGV, &action, nullptr);
#if defined(SIGSTKFLT)
sigaction(SIGSTKFLT, &action, nullptr);
#endif
sigaction(SIGSYS, &action, nullptr);
sigaction(SIGTRAP, &action, nullptr);
}
FirstStageMain 函数第二部分
代码如下:
cpp
// Clear the umask.
umask(0);
CHECKCALL(clearenv());
CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755")); // 挂载tmpfs文件系统
CHECKCALL(mkdir("/dev/pts", 0755));
CHECKCALL(mkdir("/dev/socket", 0755)); // 创建dev/socket设备节点
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL)); // 挂载devpts文件系统
#define MAKE_STR(x) __STRING(x)
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC))); // 挂载 proc 文件系统
#undef MAKE_STR
// Don't expose the raw commandline to unprivileged processes.
CHECKCALL(chmod("/proc/cmdline", 0440));
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
gid_t groups[] = {AID_READPROC};
CHECKCALL(setgroups(arraysize(groups), groups));
CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL)); // 挂载sysfs文件系统
CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11))); // 创建kmsg设备节点文件,用于输出log信息
if constexpr (WORLD_WRITABLE_KMSG) {
CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
}
CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
// This is needed for log wrapper, which gets called before ueventd runs.
CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
// These below mounts are done in first stage init so that first stage mount can mount
// subdirectories of /mnt/{vendor,product}/. Other mounts, not required by first stage mount,
// should be done in rc files.
// Mount staging areas for devices managed by vold
// See storage config details at http://source.android.com/devices/storage/
CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000"));
// /mnt/vendor is used to mount vendor-specific partitions that can not be
// part of the vendor partition, e.g. because they are mounted read-write.
CHECKCALL(mkdir("/mnt/vendor", 0755));
// /mnt/product is used to mount product-specific partitions that can not be
// part of the product partition, e.g. because they are mounted read-write.
CHECKCALL(mkdir("/mnt/product", 0755));
// /debug_ramdisk is used to preserve additional files from the debug ramdisk
CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
#undef CHECKCALL
这部分内容主要是挂载文件系统、创建设备节点和一些关键目录。
挂载的文件系统主要有4类:
- tmpfs: 一种虚拟内存文件系统,它会将所有的文件存储在内存中。由于 tmpfs 是驻留在 RAM 的,因此它的内容是不持久的。断电后,tmpfs 的内容就消失了,这也是被称作 tmpfs 的根本原因。
- devpts: 为伪终端提供了一个标准接口,它的标准挂接点是
/dev/pts。只要 pty(pseudo-tty, 虚拟终端)的主复合设备 /dev/ptmx 被打开,就会在 /dev/pts 下动态的创建一个新的 pty 设备文件。 - proc: 也是一个虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。
- sysfs: 与 proc 文件系统类似,也是一个不占有任何磁盘空间的虚拟文件系统。它通常被挂接在 /sys 目录下。
FirstStageMain 函数第三部分
cpp
SetStdioToDevNull(argv);
其实现如下:
cpp
// SetStdioToDevNull 函数的作用是将标准输入(stdin)、标准输出(stdout)
// 和标准错误(stderr)重定向到特殊的设备文件 "/dev/null"
void SetStdioToDevNull(char** argv) {
// Make stdin/stdout/stderr all point to /dev/null.
int fd = open("/dev/null", O_RDWR); // NOLINT(android-cloexec-open)
if (fd == -1) {
int saved_errno = errno;
android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
errno = saved_errno;
PLOG(FATAL) << "Couldn't open /dev/null";
}
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO) close(fd);
}
这里的主要作用是将标准输入(stdin)、标准输出(stdout)和标准错误(stderr)重定向到特殊的设备文件/dev/null,也就是说 print 函数是看不到输出的。
FirstStageMain 函数第四部分
cpp
// Android系统 init 进程启动的时候,log 子系统没有启动起来,但是我们仍然可以
// 使用 logcat -b kernel 看到 init 进程的日志,这是怎么做到的呢?其实是通过日志重定向来完成的
InitKernelLogging(argv);
Android 系统 init 进程启动的时候,log 子系统没有启动起来,但是我们仍然可以使用 logcat -b kernel 看到 init 进程的日志,这是怎么做到的呢?其实是通过日志重定向来完成的,其实现如下:
c
void InitKernelLogging(char** argv) {
// 空实现
SetFatalRebootTarget();
android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
}
接着调用了 InitLogging() 方法:
cpp
void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
SetLogger(std::forward<LogFunction>(logger));
SetAborter(std::forward<AbortFunction>(aborter));
if (gInitialized) {
return;
}
gInitialized = true;
// Stash the command line for later use. We can use /proc/self/cmdline on
// Linux to recover this, but we don't have that luxury on the Mac/Windows,
// and there are a couple of argv[0] variants that are commonly used.
if (argv != nullptr) {
SetDefaultTag(basename(argv[0]));
}
const char* tags = getenv("ANDROID_LOG_TAGS");
if (tags == nullptr) {
return;
}
// SetMinimumLogSeverity
std::vector<std::string> specs = Split(tags, " ");
for (size_t i = 0; i < specs.size(); ++i) {
// "tag-pattern:[vdiwefs]"
std::string spec(specs[i]);
if (spec.size() == 3 && StartsWith(spec, "*:")) {
switch (spec[2]) {
case 'v':
SetMinimumLogSeverity(VERBOSE);
continue;
case 'd':
SetMinimumLogSeverity(DEBUG);
continue;
case 'i':
SetMinimumLogSeverity(INFO);
continue;
case 'w':
SetMinimumLogSeverity(WARNING);
continue;
case 'e':
SetMinimumLogSeverity(ERROR);
continue;
case 'f':
SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
continue;
// liblog will even suppress FATAL if you say 's' for silent, but that's
// crazy!
case 's':
SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
continue;
}
}
LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
<< ")";
}
}
里面调用了 SetLogger() 和 SetAborter() :
cpp
void SetLogger(LogFunction&& logger) {
Logger() = std::move(logger);
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
liblog_functions->__android_log_set_logger([](const struct __android_log_message* log_message) {
auto log_id = log_id_tToLogId(log_message->buffer_id);
auto severity = PriorityToLogSeverity(log_message->priority);
Logger()(log_id, severity, log_message->tag, log_message->file, log_message->line,
log_message->message);
});
}
}
void SetAborter(AbortFunction&& aborter) {
Aborter() = std::move(aborter);
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
liblog_functions->__android_log_set_aborter(
[](const char* abort_message) { Aborter()(abort_message); });
}
}
static LogFunction& Logger() {
#ifdef __ANDROID__
static auto& logger = *new LogFunction(LogdLogger());
#else
static auto& logger = *new LogFunction(StderrLogger);
#endif
return logger;
}
static AbortFunction& Aborter() {
static auto& aborter = *new AbortFunction(DefaultAborter);
return aborter;
}
可以看出,这里就是给两个静态变量赋值。
我们重点关注下 SetLogger 函数设置的 KernelLogger:
cpp
void KernelLogger(android::base::LogId, android::base::LogSeverity severity, const char* tag,
const char*, unsigned int, const char* full_message) {
SplitByLines(full_message, KernelLogLine, severity, tag);
}
里面调用了 SplitByLines() 方法:
cpp
static void SplitByLines(const char* msg, const F& log_function, Args&&... args) {
const char* newline = strchr(msg, '\n');
while (newline != nullptr) {
log_function(msg, newline - msg, args...);
msg = newline + 1;
newline = strchr(msg, '\n');
}
log_function(msg, -1, args...);
}
里面又调用了 log_function() ,log_function() 即为 KernelLogLine() :
cpp
static void KernelLogLine(const char* msg, int length, android::base::LogSeverity severity,
const char* tag) {
// clang-format off
static constexpr int kLogSeverityToKernelLogLevel[] = {
[android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
// level)
[android::base::DEBUG] = 7, // KERN_DEBUG
[android::base::INFO] = 6, // KERN_INFO
[android::base::WARNING] = 4, // KERN_WARNING
[android::base::ERROR] = 3, // KERN_ERROR
[android::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT
[android::base::FATAL] = 2, // KERN_CRIT
};
// clang-format on
static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1,
"Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity");
static int klog_fd = OpenKmsg();
if (klog_fd == -1) return;
int level = kLogSeverityToKernelLogLevel[severity];
// The kernel's printk buffer is only 1024 bytes.
// TODO: should we automatically break up long lines into multiple lines?
// Or we could log but with something like "..." at the end?
char buf[1024] __attribute__((__uninitialized__));
size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %.*s\n", level, tag, length, msg);
if (size > sizeof(buf)) {
size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
level, tag, size);
}
iovec iov[1];
iov[0].iov_base = buf;
iov[0].iov_len = size;
TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
}
其内部实现就是打开/dev/kmsg,拿到 fd,然后把日志信息格式化一下,接着把日志 write 到前面拿到的 fd 中。
FirstStageMain 函数第五部分
cpp
...
if (!DoFirstStageMount()) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
DoFirstStageMount() 方法内部实现如下:
cpp
bool DoFirstStageMount() {
// Skips first stage mount if we're in recovery mode.
if (IsRecoveryMode()) {
LOG(INFO) << "First stage mount skipped (recovery mode)";
return true;
}
// AVB校验
std::unique_ptr<FirstStageMount> handle = FirstStageMount::Create();
if (!handle) {
LOG(ERROR) << "Failed to create FirstStageMount";
return false;
}
return handle->DoFirstStageMount();
}
先来看看 FirstStageMount::Create(),该方法主要用于 AVB 校验,AVB 校验可以去看看 Google 文档:
cpp
std::unique_ptr<FirstStageMount> FirstStageMount::Create() {
// 读取 fstab, file system table,里面包含了要挂载的逻辑分区
auto fstab = ReadFirstStageFstab();
// 判断 device tree(fstabl)中是否有 vbmeta/compatible 结构,值是 android,vbmeta
// 创建 FirstStageMountVBootV1 或者 FirstStageMountVBootV2 实例,取决于
// IsDtVbmetaCompatible(fstab) 的返回值,如果支持 vbmeta,则使用 FirstStageMountVBootV2,
// 反之 FirstStageMountVBootV1
if (IsDtVbmetaCompatible(fstab)) {
return std::make_unique<FirstStageMountVBootV2>(std::move(fstab));
} else {
return std::make_unique<FirstStageMountVBootV1>(std::move(fstab));
}
}
以上主要是创建 V1 或者 V2 版本的 AVB 校验,AVB 校验主要是针对分区进行校验,对于要启动的 Android 版本中包含的所有可执行代码和数据,启动时验证均要求在使用前以加密形式对其进行验证。包括内核(从 boot 分区加载)、设备树(从 dtbo 分区加载)、system 分区和 vendor 分区等
- 对于 boot 和 dtbo 这类仅读取一次的小分区,通常是通过将整个内容加载到内存中,然后计算其哈希值来进行验证;
- 内存装不下的较大分区(如文件系统)可能会使用哈希树;
- 如果在某个时刻计算出的根哈希值与预期根哈希值不一致,系统便不会使用相应数据,无法启动 Android 系统;
我们继续看 handle->DoFirstStageMount() 的实现:
cpp
bool FirstStageMount::DoFirstStageMount() {
if (!IsDmLinearEnabled() && fstab_.empty()) {
// Nothing to mount.
LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
return true;
}
if (!InitDevices()) return false;
if (!MountPartitions()) return false;
return true;
}
这里的逻辑很清晰,初始化设备,创建逻辑分区,最后挂载分区。
FirstStageMain 函数第六部分
cpp
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);
// 启动 init 进程,参数为 selinux_setup
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;
最后通过 selinux_setup 参数,调用 execv() 方法再次执行 init 可执行文件,进入 init 过程的第二阶段。
小结
小结一下,FirstStageMain() 主要做了这些事情:
- init crash 时重启引导程序;
- 创建和挂载启动所需的文件目录;
- 输入输出重定向到 dev/null;
- 初始化日志;
- 启动 init 进程,参数为 selinux_setup ;
2.2 SetupSelinux
在第6步时参数为 selinux_setup ,此时开始执行 SetupSelinux() 方法:
cpp
// system/core/init/selinux.cpp
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
// 初始化内核日志
InitKernelLogging(argv);
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
MountMissingSystemPartitions();
// 初始化 SELinux,加载 SELinux 策略
SelinuxSetupKernelLogging();
// 通过 security_setenforce() 方法设置默认 selinux 模式
SelinuxInitialize();
// We're in the kernel domain and want to transition to the init domain. File systems that
// store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,
// but other file systems do. In particular, this is needed for ramdisks such as the
// recovery image for A/B devices.
if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
}
setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
// execv() only returns if an error happened, in which case we
// panic and never return from this function.
PLOG(FATAL) << "execv(\"" << path << "\") failed";
return 1;
}
先调用InitKernelLogging(argv)初始化内核日志,再调用InstallRebootSignalHandlers()注册需要处理的信号,这个和第一阶段是一样的。接着调用 SelinuxSetupKernelLogging() 将 selinux 日志写入/dev/kmsg中,再调用 SelinuxInitialize() 加载 selinux_policy 策略文件,设置默认 selinux 模式。最后调用 selinux_android_restorecon() 来设置 init 的安全上下文,接着通过 execv() 传入参数second_stage,跳转到 SecondStageMain()。
SelinuxSetupKernelLogging() 方法实现如下:
cpp
void SelinuxSetupKernelLogging() {
selinux_callback cb;
cb.func_log = SelinuxKlogCallback;
selinux_set_callback(SELINUX_CB_LOG, cb);
}
这里调用了selinux_set_callback() 方法,第一个参数 SELINUX_CB_LOG 表示要设置 Selinux 日志的回调,第二个参数 cb 的成员 func_log 就是具体的回调函数:
cpp
int SelinuxKlogCallback(int type, const char* fmt, ...) {
android::base::LogSeverity severity = android::base::ERROR;
if (type == SELINUX_WARNING) {
severity = android::base::WARNING;
} else if (type == SELINUX_INFO) {
severity = android::base::INFO;
}
char buf[kKlogMessageSize];
va_list ap;
va_start(ap, fmt);
int length_written = vsnprintf(buf, sizeof(buf), fmt, ap);
va_end(ap);
if (length_written <= 0) {
return 0;
}
if (type == SELINUX_AVC) {
SelinuxAvcLog(buf, sizeof(buf));
} else {
android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
}
return 0;
}
这里会调用 KernelLogger 将 selinux 日志写入 /dev/kmsg 中,和 FirstStageMain 类似。
SelinuxInitialize() 方法实现如下:
cpp
void SelinuxInitialize() {
LOG(INFO) << "Loading SELinux policy";
// 加载 selinux_policy 策略文件
if (!LoadPolicy()) {
LOG(FATAL) << "Unable to load SELinux policy";
}
bool kernel_enforcing = (security_getenforce() == 1);
bool is_enforcing = IsEnforcing();
if (kernel_enforcing != is_enforcing) {
// 设置默认 selinux 模式
if (security_setenforce(is_enforcing)) {
PLOG(FATAL) << "security_setenforce(" << (is_enforcing ? "true" : "false")
<< ") failed";
}
}
if (auto result = WriteFile("/sys/fs/selinux/checkreqprot", "0"); !result.ok()) {
LOG(FATAL) << "Unable to write to /sys/fs/selinux/checkreqprot: " << result.error();
}
}
2.3 SecondStageMain
下面进入 SecondStageMain(),代码如下:
cpp
//system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {
...
PropertyInit(); // 1.初始化属性系统,并从指定文件读取属性
...
StartPropertyService(&property_fd); // 2.设置其他系统属性并开启系统属性服务
...
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
LoadBootScripts(am, sm);// 3.解析init.rc等文件,建立rc文件的action 、service,启动其他进程
...
return 0;
}
这里调用 LoadBootScripts() 方法来解析 init.rc 文件,我们先了解一下init.rc文件的规则。
init.rc简介
init.rc 是一个配置文件,内部由 Android 初始化语言(Android Init Language)编写,init.rc 文件是以块 (section)为单位的,一块可以包含多行。
块分成两大类:
- 动作(action):以关键字 on 开头,表示在某个时候执行一堆命令;
- 服务(service):以关键字 service 开头,表示启动某个进程的方式和参数;
块以关键字 on 或 service 开始,直到下一个 on 或 service 结束,中间所有行都属于这个块。
接着我们看两个示例:
bash
on init
sysclktz 0
symlink /system/etc /etc
symlink /sys/kernel/debug /d
这个块表示在系统启动(init)时,执行一堆命令。
sql
service adbd /sbin/adbd
user adb
group adb
这里定义了一个服务 adbd,对应的执行文件是 /sbin/adbd,接着定义了服务的两个属性 user 和 group。
动作(action) 的一般格式如下:
bash
on <trigger> # 触发条件
<command> # 执行命令
<command1> # 可以执行多个命令
on 后面跟着的字符串是触发器(trigger),trigger 是一个用于匹配某种事件类型的字符串,当事件发生时,就会执行 trigger 下面的 command。
trigger 有几种格式,最简单的一种是一个单纯的字符串。比如on boot,表示系统启动时。还有一种常见的格式是 on property:<属性>=<值>,表示属性在设成了指定的值时,action 中的命令列表就会执行。
常见的 trigger 有以下几种:
- on early-init:在初始化早期阶段触发;
- on init:在初始化阶段触发;
- on late-init:在初始化晚期阶段触发;
- on boot/charger:当系统启动/充电时触发;
- on property:当属性值满足条件时触发;
常见命令有以下这些:
xml
exec <path> [ <argument> ]*:运行指定路径下的程序,并传递参数
export <name> <value>:设置全局环境参数。此参数被设置后对全部进程都有效
ifup <interface>:使指定的网络接口"上线",相当激活指定的网络接口
import <filename>:导入一个额外的 rc 配置文件
hostname <name>:设置主机名
chdir <directory>:改变工作文件夹
chmod <octal-mode> <path>:设置指定文件的读取权限
chown <owner> <group> <path>:设置文件所有者和文件关联组
chroot <directory>:设置根文件夹
class_start <serviceclass>:启动指定类属的全部服务,假设服务已经启动,则不再反复启动
class_stop <serviceclass>:停止指定类属的全部服务
domainname <name>:设置域名
insmod <path>:安装模块到指定路径
mkdir <path> [mode] [owner] [group]:用指定参数创建一个文件夹
mount <type> <device> <dir> [ <mountoption> ]*:类似于linux的mount指令
setprop <name> <value>:设置属性及相应的值
setrlimit <resource> <cur> <max>:设置资源的rlimit
start <service>:假设指定的服务未启动,则启动它
stop <service>:假设指定的服务当前正在执行。则停止它
symlink <target> <path>:创建一个符号链接
sysclktz <mins_west_of_gmt>:设置系统基准时间
trigger <event>:触发另一个时间
write <path> <string> [ <string> ]*:往指定的文件写字符串
服务(service) 的一般格式如下:
xml
service <name> <pathname> [ <argument> ]*
<option>
<option>
- name:表示此服务的名称;
- pathname:可执行文件对应的路径;
- argument:执行可执行文件时传入的参数;
- option:服务的选项;
option主要有:
- class <name>:为服务指定 class 名字。 同一个 class 名字的服务会被一起启动或退出,默认值是 default。
- console <console>:这个选项表明服务需要一个控制台。 第二个参数 console 的意思是可以设置你想要的控制台类型,默认控制台是 /dev/console, /dev 这个前缀通常是被省略的, 比如你要设置控制台 /dev/tty0, 那么只需要设置为 console tty0 即可。
- critical:表示服务是严格模式。如果这个服务在4分钟内或者启动完成前退出超过4次,那么设备将重启进入 bootloader 模式。
- disabled:这个服务不会随着 class 一起启动。只能通过服务名来显式启动。比如 foobar 服务的 class 是 core, 且是 disabled 的,当执行 class_start core 时,foobar 服务是不会被启动的。 foobar 服务只能通过 start foobar 这种方法来启动。
- file <path> <type>:根据文件路径 path 来打开文件,然后把文件描述符 fd 传递给服务进程。type 表示打工文件的方式,只有三种取值 r, w, rw。
- group <groupname>:在启动 Service 前,将 Service 的用户组改为第一个 groupname,第一个 groupname 是必须有的, 第二个 groupname 可以不设置,用于追加组(通过setgroups)。目前默认的用户组是 root 组。
- oneshot:当服务退出的时候,不自动重启,适用于那些开机只运行一次的服务。
- onrestart:在服务重启的时候执行一个命令。
- seclabel <seclabel>:在启动 Service 前设置指定的 seclabel,默认使用 init 的安全策略。 主要用于在 rootfs 上启动的 service,比如 ueventd, adbd。 在系统分区上运行的 service 有自己的 SELinux安全策略。
- setenv <name> <value>:设置进程的环境变量。
- socket <name> <type> <perm> [<user> [<group> [<seclabel> ] ] ]:创建一个 unix domain socket,路径为/dev/socket/name ,并将 fd 返回给 Service。 type 只能是 dgram, stream or seqpacket。user 和 group 默认值是 0。 seclabel 是这个 socket 的 SELinux security context, 它的默认值是 service 的 security context 或者基于其可执行文件的 security context。
- user <username>:在启动 Service 前修改进程的所属用户,默认启动时 user 为 root
最后,我们看个例子:
kotlin
service bootanim /system/bin/bootanimation
class core //给服务指定一个类属,这样方便操作多个服务同时启动或停止
user graphics //在执行此服务之前先切换用户名
group graphics audio
disabled //这个服务不会随着 class 一起启动
oneshot //当此服务退出时不会自动重启
注释里面做了解释,日常工作中遇到不懂的配置,可以查阅源码中的文档,路径为: /system/core/init/README.md
回到前面的 LoadBootScripts() 方法,代码如下:
cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);// 构建解析器
std::string bootscript = GetProperty("ro.boot.init_rc", "");
// 若 ro.boot.init_rc 属性没有设置具体的 init.rc 文件,则进入
if (bootscript.empty()) {
// 依次解析不同路径下谷歌 system、soc 厂商 vendor、odm 厂商的 init.rc 文件并启动相关进程
// system/etc/init/hw/是手机设备上的路径,和源码中 system/core/rootdir/init.rc 是一个文件
parser.ParseConfig("/system/etc/init/hw/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
这里先调用 CreateParser() 方法创建 Parser 对象,代码如下:
cpp
Parser CreateParser(ActionManager &action_manager, ServiceList &service_list) {
Parser parser;
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));
return parser;
}
里面给 parser 添加了 ServiceParser、ActionParser、ImportParser 三个成员:
- ServiceParser 用于解析 init.rc 中定义的 Service
- ActionParser 用于解析 init.rc 中定义的 Action
- ImportParser 用于解析 init.rc 中的 import 语句
3. Zygote进程
启动模拟器,用 adb 进入/system/etc/init/hw/目录下,可以找到 init.rc 文件,对应源码中的 init.rc 路径为system/core/rootdir/init.rc:
sql
# 通过 ${ro.zygote} 属性的值来决定引入的Zygote配置文件
import /system/etc/init/hw/init.${ro.zygote}.rc
...
on late-init
...
# Now we can start zygote for devices with file based encryption
trigger zygote-start // 1. 触发启动zygote
...
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start statsd
start netd
start zygote // 2.启动zygote服务
start zygote_secondary
${ro.zygote}是一个变量,取值范围有 zygote32、zygote64、zygote32_64、zygote64_32,会根据系统是 32 位还是 64 位进行选择。
可以使用命令:getprop ro.zygote来查询设备的 zygote 位数,比如我的手机查询出来结果如下:

在源码的/system/core/rootdir/目录下,有4个对应的配置文件:
- init.zygote32.rc,Zygote进程的执行程序路径为 /system/bin/app_process;
- init.zygote64.rc,Zygote进程的执行程序路径为 /system/bin/app_process64;
- init.zygote32_64.rc,会启动两个Zygote进程,有两个执行程序,32为主模式;
- init.zygote64_32.rc,会启动两个Zygote进程,有两个执行程序,64为主模式(系统兼容运行64位和32位的应用);
目前最新的设备中普遍使用的是 init.zygote64_32.rc 这个配置文件:
perl
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh MaxPerformance
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
class main
priority -20
user root
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
socket usap_pool_secondary stream 660 root system
onrestart restart zygote
task_profiles ProcessCapacityHigh MaxPerformance
可以看到定义了2个 Zygote 服务:zygote 和 zygote_secondary,其中 zygote 是 32 位的,zygote_secondary 是 64位的。第一行service表示 Zygote 进程以服务的形式来启动,zygote是进程的名字, /system/bin/app_process64是执行程序的路径。
这里调用 app_process 启动 zygote 进程,app_process 是一个 cpp 的命令行程序,该命令的格式如下:
css
app_process [java-options] cmd-dir start-class-name [options]
上面的配置文件中-Xzygote 属于 [java-options],这些参数会传给虚拟机,并且参数必须以 - 开头,一旦遇到非 - 或者 --,表示 [java-options] 结束。/system/bin 属于 cmd-dir,程序的运行目录,随便指定即可,文件操作时会以此为当前路径,大多运行在 /system/bin 下。--zygote --start-system-server --socket-name=zygote 都属于 [options],这些参数都以符号--开头。参数 --zygote 表示要启动 Zygote 进程,--start-system-server 表示要启动 System Server,--socket-name=zygote 用于指定 Zygote 中 Socket 的名字。660 表示访问权限rw-rw----,表示文件拥有者和同一群组用户具有读写权限。
3.1 Native 层的 app_process
这里/system/bin/app_process64是设备上的路径,对应的源码路径为:frameworks/base/cmds/app_process/,其 Android.bp 文件代码如下:
cpp
cc_binary {
name: "app_process",
srcs: ["app_main.cpp"], //是同目录的app_main.cpp
...
}
对应的源码为 /frameworks/base/cmds/app_process/app_main.cpp:
cpp
int main(int argc, char* const argv[])
{
...
// 创建 AppRuntime 对象
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
...
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
...
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true; // 参数标识启动 Zygote 进程
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true; //参数标识启动 System Server 进程
}
...
}
...
if (!niceName.isEmpty()) {
// 进程名 app_process 修改为 zygote
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
if (zygote) {
// 启动 Zygote,执行 AndroidRuntime 的 start() 函数
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
...
}
}
这里先初始化了一个 AppRuntime 实例,然后通过该实例的 start() 方法来启动ZygoteInit。
AppRuntime 直译为应用运行时,开发时,我们写好 Java/Kotlin 代码,通过对应的编译器将代码编译为字节码,AppRuntime 的作用就是创建一个可以执行字节码的环境,这个环境主要由两部分内容构成:
- 一部分负责对象的创建与回收,譬如类的加载器、垃圾回收器等;
- 一部分负责程序逻辑的运行,譬如即时编译系统、解释器等;
AppRuntime 继承自 AndroidRuntime,其 start() 方法如下:
cpp
void AndroidRuntime::start(const char *className, const Vector <String8> &options, bool zygote)
{
...
/* start the virtual machine */
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv *env;
// 1. 启动虚拟机
if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {
return;
}
onVmCreated(env);
// 2. 注册JNI函数到虚拟机,我们在 Java 层调用的很多 Native 方法就是在这里注册的
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
...
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
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 {
// 3. 调用 ZygoteInit 的 main() 函数
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件事:
- 启动进程的 VM 虚拟机;
- 注册 JNI 函数到虚拟机,我们在 Java 层调用的很多 Native 方法就是在这里注册的 3. 调用 ZygoteInit 的 main() 函数
3.2 Java 层的 ZygoteInit
接下来就会执行 ZygoteInit.java 的 main() 函数,源码路径为frameworks/base/core/java/com/android/internal/os/ZygoteInit.java:
cpp
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 {
...
for (int i = 1; i < argv.length; i++) {
// 有 start-system-server 参数,表示要创建 SystemServer
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]);
}
}
...
if (!enableLazyPreload) {
...
// 1. 预加载资源
preload(bootTimingsTraceLog);
...
}
...
zygoteServer = new ZygoteServer(isPrimaryZygote); // 2. 创建 Socket 服务端 if (startSystemServer) {
// 3. fork 启动 System Server进程
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
...
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.
// 4. Sokcet 服务端等待 AMS 请求(AMS 会通过 Socket 请求 Zygote 来创建应用程序进程)
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
if (zygoteServer != null) {
zygoteServer.closeServerSocket();
}
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {
caller.run();
}
}
static void preload(TimingsTraceLog bootTimingsTraceLog) {
...
preloadClasses(); // 1.加载system/etc/preloaded-classes文件中定义的各个系统类 ...
preloadResources(); // 2.加载系统中定义的各个drawables、color资源
...
}
这里主要做了这些事情:
- Zygote进程中预加载类、主题资源、字体资源等,基于Linux的copy-on-write机制,从而加速后续Zygote fork创建出的应用进程的启动速度;
- 创建 Socket 服务端,用于跨进程通信;
- fork 创建启动系统 System Server 进程;
- Sokcet服务端进入循环监听等待,等待后续 AMS 请求(AMS 会通过 Socket 请求 Zygote 来创建应用程序进程)。
4. System Server进程的启动
接下来看看 ZygoteInit 的 forkSystemServer() :
java
private static Runnable forkSystemServer(String abiList, String socketName, ZygoteServer zygoteServer) {
...
/* Hardcoded command line to start the system server */
// 1. 构建启动 System Server 相关的参数
String args[] = { "--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
+ "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};
...
int pid;
try {
...
// 2. fork 创建 System Server 进程,具体实现在 native 层
pid = Zygote.forkSystemServer( parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids, parsedArgs.mRuntimeFlags, null, parsedArgs.mPermittedCapabilities, parsedArgs.mEffectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}
zygoteServer.closeServerSocket();
// 3. pid 为 0 代表 System Server 进程,继续通过 handleSystemServerProcess() 进一步处理
return handleSystemServerProcess(parsedArgs);
}
return null;
}
接下来进入 handleSystemServerProcess() :
java
private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {
...
if (parsedArgs.mInvokeWith != null) {
...
} else {
...
/*
* Pass the remaining arguments to SystemServer.
*/
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion, parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, cl);
}
/* should never reach here */
}
接下来调用 ZygoteInit 的 zygoteInit() 方法:
java
public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges, String[] argv, ClassLoader classLoader) {
...
// 1. RuntimeInit 初始化,通过 setDefaultUncaughtExceptionHandler 设置默认的异常处理机制
RuntimeInit.commonInit();
// 2. 触发启动进程的 Binder 线程池
ZygoteInit.nativeZygoteInit();
// 3. 内部经过层层调用,最终通过反射创建 "com.android.server.SystemServer" 类对象并执行其 main() 函数
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv, classLoader);
}
继续看看 RuntimeInit 的 applicationInit() 方法:
java
public class RuntimeInit {
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges, String[] argv, ClassLoader classLoader) {
...
// Remaining arguments are passed to the start class's static main
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
protected static Runnable findStaticMain(String className, String[] argv, ClassLoader classLoader) {
Class<?> cl;
try {
// 1. 通过反射加载构建"com.android.server.SystemServer"类对象
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException("Missing class when invoking static main " + className, ex);
}
Method m;
try {
// 2. 获取其 main() 方法
m = cl.getMethod("main", new Class[]{String[].class});
} catch (NoSuchMethodException ex) {
throw new RuntimeException("Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException("Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (!(Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException("Main method is not public and static on " + className);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
// 3.调用 main() 方法
return new MethodAndArgsCaller(m, argv);
}
static class MethodAndArgsCaller implements Runnable {
/** method to call */
private final Method mMethod;
/** argument array */
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
// 通过反射调用 main() 方法
mMethod.invoke(null, new Object[]{mArgs});
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}
}
里面通过反射调用了 com.android.server.SystemServer 类的 main() 方法:
java
public final class SystemServer {
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
...
// 1. 创建主线程 Looper
Looper.prepareMainLooper();
// 2. 初始化系统上下文
createSystemContext();
...
// 3. 创建 SystemServiceManager,用于后续系统服务(AMS、WMS等)的创建、启动和生命周期管理
mSystemServiceManager = new SystemServiceManager(mSystemContext);
mSystemServiceManager.setStartInfo(mRuntimeRestart, mRuntimeStartElapsedTime, mRuntimeStartUptime);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
...
// 4.服务根据优先级被分成3批来启动:
try {
t.traceBegin("StartServices");
// 启动引导服务,如 AMS、PMS 等
startBootstrapServices(t);
// 启动核心服务
startCoreServices(t);
// 启动其他服务
startOtherServices(t);
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
t.traceEnd(); // StartServices
}
...
// 5.开启looper循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
总结一下上述流程,System Server进程创建之后,主要做了下面几件事情:
- 初始化 Looper;
- 初始化 SystemContext;
- 初始化 SystemServiceManager,用于后续系统服务(AMS、WMS等)的创建、启动和生命周期管理;
- 执行 startBootstrapServices、startCoreServices、startOtherServices 启动所有的系统服务;
- 调用 Looper.loop() 循环处理消息;
5. Launcher的启动
上面的代码中会调用 startBootstrapServices() 启动核心服务:
java
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
ActivityTaskManagerService atm = mSystemServiceManager.startService(
ActivityTaskManagerService.Lifecycle.class).getService();
// 1. SystemServiceManager 启动 AMS 服务
mActivityManagerService = ActivityManagerService.Lifecycle.startService(
mSystemServiceManager, atm);
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer);
mWindowManagerGlobalLock = atm.getGlobalLock();
t.traceEnd();
...
}
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
// 2.从这里开始便可以开始启动三方 APP 应用(如Launcher)
mActivityManagerService.systemReady(() -> {
...
}, t);
}
从上面的代码可以看出,AMS 调用 systemReady() 后便可以开始启动第三方 App 应用(如Launcher),继续往下:
java
/*frameworks/base/services/java/com/android/server/am/ActivitymanagerService.java*/
public void systemReady(final Runnable goingCallback, TimingsTraceAndSlog t){
...
if (bootingSystemUser) {
...
// 由于 Android 系统支持多用户和多显示,调用 startHomeOnAllDisplays()
// 启动每个 display 上的 HomeActivity
mAtmInternal.startHomeOnAllDisplays(currentUserId,"systemReady"); t.traceEnd();
...
}
}
startHomeOnAllDisplays() 经过层层调用最终会调用到 startHomeActivity() 启动 Category 类型为CATEGORY_HOME 的应用,也就是 Launcher:
java
/*frameworks/base/services/java/com/android/server/wm/RootWindowContainer.java*/
boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea, boolean allowinstrumenting, boolean fromHomeKey){
...
// 最终调用 startHomeActivity() 启动 Category 类型
// 为 CATEGORY_HOME 的应用,也就是Launcher 桌面
mService.startActivityStartController().startHomeActivity(homeIntent, aInfo, myReason, taskDisplayArea);
return true;
}