如何应对Android面试官 -> Android 系统启动流程浅析
前言
本章节主要分析 Android 系统启动流程;
Android系统启动的大概流程
当我们按下电源键的时候,就会从 Boot Rom 执行代码,就会加载一个 Boot loader 引导程序「此引导程序就会引导用户选择哪个系统启动,如果只有一个系统则直接命中」,Boot loader 就会拉起 Linux Kernel,Linux Kernel 就会从固定位置读取设置的一些参数,根据这些参数来加载驱动。Linux Kernel 通过 syscall 系统调用拉起用户态的第一个进程「init 进程,这个 init 进程会:初始化 Android 的一些属性服务,启动一系列的守护进程,启动 zygote 进程,启动 media server(多媒体相关的服务)」,当 zygote 进程启动之后会孵化出 system_server 进程,这个进程就会开启一系列的服务例如 ActivityManagerService,WindowManagerService,PackageManagerService,PowerManagerService 等等大概80多个服务;当我们通过 launcher 进程启动一个 app 的时候,就会通过zygote 进程 fork 出一个子进程,这个子进程就是我们 app 所在进程;
看到上面的流程,可能大家会有疑问了,为什么不 fork init 进程,不 fork SystemServer 进程呢?带着这些疑问我们继续往下分析;
init 进程分析
我们先来看下 init 进程的启动流程,进入 init.cpp 文件的 main 函数看起;
/*
*
* 1.C++中主函数有两个参数,第一个参数argc表示参数个数,第二个参数是参数列表,也就是具体的参数
*
* 2.init的main函数有两个其它入口,一是参数中有ueventd,进入ueventd_main,二是参数中有watchdogd,进入watchdogd_main
*
*/
int main(int argc, char** argv) {
/*
* 1.strcmp是String的一个函数,比较字符串,相等返回0
* 2.C++中0也可以表示false
* 3.basename是C库中的一个函数,得到特定的路径中的最后一个'/'后面的内容,
* 比如/sdcard/miui_recovery/backup,得到的结果是backup
*/
if (!strcmp(basename(argv[0]), "ueventd")) { //当argv[0]的内容为ueventd时,strcmp的值为0,!strcmp为1, 1表示true,也就执行ueventd_main,ueventd主要是负责设备节点的创建、权限设定等一
些列工作
return ueventd_main(argc, argv);
}
// watchdogd俗称看门狗,用于系统出问题时重启系统
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
if (argc > 1 && !strcmp(argv[1], "subcontext")) {
InitKernelLogging(argv);
const BuiltinFunctionMap function_map;
return SubcontextMain(argc, argv, &function_map);
}
// 初始化重启系统的处理信号,内部通过 sigaction 注册信号,当监听到该信号时重启系统
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
// 查看是否有环境变量INIT_SECOND_STAGE
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
/*
* 1.init的main方法会执行两次,由is_first_stage控制,first_stage就是第一阶段要做的事
*/
if (is_first_stage) {
boot_clock::time_point start_time = boot_clock::now();
// Clear the umask. 清空文件权限
umask(0);
clearenv();
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.
// mount 用来挂载文件系统,mount 属于 linux 系统调用
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
// 创建目录,第一个表示目录路径,第二个表示读写权限
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
#define MAKE_STR(x) __STRING(x)
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
// Don't expose the raw commandline to unprivileged processes.
// 用于修改文件/目录的读写权限
chmod("/proc/cmdline", 0440);
gid_t groups[] = { AID_READPROC };
setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
// mknod用于创建Linux中的设备文件
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
if constexpr (WORLD_WRITABLE_KMSG) {
mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11));
}
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
// Mount staging areas for devices managed by vold
// See storage config details at http://source.android.com/devices/storage/
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.
mkdir("/mnt/vendor", 0755);
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
InitKernelLogging(argv);
LOG(INFO) << "init first stage started!";
if (!DoFirstStageMount()) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
// Avb即Android Verfied boot,功能包括Secure Boot, verfying boot 和 dmverity,
// 原理都是对二进制文件进行签名,在系统启动时进行认证,确保系统运行的是合法的二进制镜像文件。
// 其中认证的范围涵盖:bootloader,boot.img,system.img
SetInitAvbVersionInRecovery(); // 在刷机模式下初始化avb的版本,不是刷机模式直接跳过
// Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
global_seccomp();
// Set up SELinux, loading the SELinux policy.
SelinuxSetupKernelLogging();
SelinuxInitialize(); // 加载SELinux policy,也就是安全策略
// We're in the kernel domain, so re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
/*
* 1.这句英文大概意思是,我们执行第一遍时是在kernel domain,所以要重新执行init文件,切换到init domain,
* 这样SELinux policy才已经加载进来了
* 2.后面的security_failure函数会调用panic重启系统
*/
if (selinux_android_restorecon("/init", 0) == -1) {
PLOG(FATAL) << "restorecon failed of /init failed";
}
setenv("INIT_SECOND_STAGE", "true", 1);
static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);
char* path = argv[0];
char* args[] = { path, nullptr };
execv(path, args);
// execv() only returns if an error happened, in which case we
// panic and never fall through this conditional.
PLOG(FATAL) << "execv(\"" << path << "\") failed";
}
// At this point we're in the second stage of init.
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
// Set up a session keyring that all processes will have access to. It
// will hold things like FBE encryption keys. No process should override
// its session keyring.
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
// 初始化属性系统,并从指定文件读取属性
property_init();
// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
//接下来的一系列操作都是从各个文件读取一些属性,然后通过property_set设置系统属性
// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
/*
* 1.这句英文的大概意思是,如果参数同时从命令行和DT传过来,DT的优先级总是大于命令行的
* 2.DT即device-tree,中文意思是设备树,这里面记录自己的硬件配置和系统运行参数,参考http://www.wowotech.net/linux_kenrel/why-dt.html
*/
process_kernel_dt(); // 处理DT属性
process_kernel_cmdline(); // 处理命令行属性
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props(); // 处理其他的一些属性
// Make the time that init started available for bootstat to log.
property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));
// Set libavb version for Framework-only OTA match in Treble build.
const char* avb_version = getenv("INIT_AVB_VERSION");
if (avb_version) property_set("ro.boot.avb_version", avb_version);
// Clean up our environment.
// 清空这些环境变量,因为之前都已经存入到系统属性中去了
unsetenv("INIT_SECOND_STAGE");
unsetenv("INIT_STARTED_AT");
unsetenv("INIT_SELINUX_TOOK");
unsetenv("INIT_AVB_VERSION");
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();
// 创建epoll实例,并返回epoll的文件描述符
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
PLOG(FATAL) << "epoll_create1 failed";
}
// 主要是创建handler处理子进程终止信号,创建一个匿名socket并注册到epoll进行监听
sigchld_handler_init();
if (!IsRebootCapable()) {
// If init does not have the CAP_SYS_BOOT capability, it is running in a container.
// In that case, receiving SIGTERM will cause the system to shut down.
InstallSigtermHandler();
}
// 从文件中加载一些属性,读取usb配置
property_load_boot_defaults();
// 设置ro.boot.flash.locked 属性
export_oem_lock_status();
// 开启一个socket监听系统属性的设置
start_property_service();
// 设置sys.usb.controller 属性
set_usb_controller();
//方法映射 “class_start” -> "do_class_start"
const BuiltinFunctionMap function_map;
/*
* 1.C++中::表示静态方法调用,相当于java中static的方法
*/
Action::set_function_map(&function_map); // 将function_map存放到Action中作为成员属性
subcontexts = InitializeSubcontexts();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
// 解析 init.rc 配置文件,然后会把 zygote 进程拉起来;我们来看下 init.rc 是如何解析的
LoadBootScripts(am, sm);
// Turning this on and letting the INFO logging be discarded adds 0.2s to
// Nexus 9 boot time, so it's disabled by default.
if (false) DumpState();
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init");
// Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");
// Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
// wasn't ready immediately after wait_for_coldboot_done
am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
// 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");
}
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) {
shutting_down = true;
}
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!(waiting_for_prop || Service::is_exec_service_running())) {
if (!shutting_down) {
auto next_process_restart_time = RestartProcesses();
// If there's a process that needs restarting, wake up in time for that.
if (next_process_restart_time) {
epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_restart_time - boot_clock::now())
.count();
if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
}
}
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout_ms = 0;
}
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
if (nr == -1) {
PLOG(ERROR) << "epoll_wait failed";
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
最核心的是这里 LoadBootScripts(am, sm); 这个函数会解析 init.rc 配置文件,然后会把 zygote 进程拉起来;我们来看下 init.rc 是如何解析的;
init.rc 解析流程
init.rc是一个非常重要的配置文件,它是由Android初始化语言(Android Init Language)编写的脚本,它主要包含五种类型语句:Action(Action中包含了一系列的 Command )、Commands( init 语言中的命令)、Services(由 init 进程启动的服务)、Options(对服务进行配置的选项)和 Import(引入其他配置文件)
init.rc 这个配置文件在 4.3 以后,zygote 进程的来就不在这里执行了,而是改成了 init.zygote32.rc 和 init.zygote64.rc 这两个文件;以及 init.zygote32_64.rc 和 init.zygote64_32.rc
这几个文件的区别就是启动 32 还是 64,而 32_64 表示一个主从模式,优先看 32 位能不能启动,不能启动则启动 64 位,反之则是优先启动 64 位;
我们来看下这个 init.rc 中的一些命令
on init # L41
sysclktz 0
# Mix device-specific information into the entropy pool
copy /proc/cmdline /dev/urandom
copy /default.prop /dev/urandom
on <trigger> [&& <trigger>]* //设置触发器
<command>
<command> //动作触发之后要执行的命令
service <name> <pathname> [ <argument> ]* //<service的名字><执行程序路径><传递参数>
<option> //Options是Services的参数配置. 它们影响Service如何运行及运行时机
group <groupname> [ <groupname>\* ] //在启动Service前将group改为第一个groupname,第一个groupname是必须有的,默认值为root(或许默认值是无),第二个groupname可以不设置,用于追加组(通过setgroups)
priority <priority> //设置进程优先级. 在-20~19之间,默认值是0,能过setpriority实现
socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]//创建一个unix域的socket,名字叫/dev/socket/name , 并将fd返回给Service. type 只能是"dgram", "stream" or "seqpacket"
我们接着进入 init.cpp 中看下 init.rc 具体是如何解析的
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
首先是调用 CreateParser 创建一个解析器
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
Parser parser;
parser.AddSectionParser("service", std::make_unique<ServiceParser>(&service_list, subcontexts));
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, subcontexts));
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
}
解析的时候是分三种类型的,分别是 service 类型、on 类型、import 类型,然后对应使用不同类型的解析器;解析器创建出来之后,调用 ParseConfig 进行解析;
bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
*parse_errors = 0;
if (is_dir(path.c_str())) {
return ParseConfigDir(path, parse_errors);
}
return ParseConfigFile(path, parse_errors);
}
ParseConfig 最终调用到 ParseConfigFile 进行解析;
bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
LOG(INFO) << "Parsing file " << path << "...";
android::base::Timer t;
auto config_contents = ReadFile(path);
if (!config_contents) {
LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
return false;
}
config_contents->push_back('\n'); // TODO: fix parse_config.
ParseData(path, *config_contents, parse_errors);
for (const auto& [section_name, section_parser] : section_parsers_) {
section_parser->EndFile();
}
LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
return true;
}
ParseConfigFile 最终调用到 ParseData 函数进行解析,这个函数就相当于把 init.rc 文件读取到内存中了;
void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
// TODO: Use a parser with const input and remove this copy
std::vector<char> data_copy(data.begin(), data.end());
data_copy.push_back('\0');
parse_state state;
state.line = 0;
state.ptr = &data_copy[0];
state.nexttoken = 0;
SectionParser* section_parser = nullptr;
int section_start_line = -1;
std::vector<std::string> args;
auto end_section = [&] {
if (section_parser == nullptr) return;
if (auto result = section_parser->EndSection(); !result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
}
section_parser = nullptr;
section_start_line = -1;
};
for (;;) {
switch (next_token(&state)) {
case T_EOF:
end_section();
return;
case T_NEWLINE:
state.line++;
if (args.empty()) break;
// If we have a line matching a prefix we recognize, call its callback and unset any
// current section parsers. This is meant for /sys/ and /dev/ line entries for
// uevent.
for (const auto& [prefix, callback] : line_callbacks_) {
if (android::base::StartsWith(args[0], prefix)) {
end_section();
if (auto result = callback(std::move(args)); !result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
break;
}
}
if (section_parsers_.count(args[0])) {
end_section();
section_parser = section_parsers_[args[0]].get();
section_start_line = state.line;
if (auto result =
// 这是一个抽象方向,具体实现在不同类型的解析器中(service、on、import)
section_parser->ParseSection(std::move(args), filename, state.line);
!result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
section_parser = nullptr;
}
} else if (section_parser) {
// 一行一行的进行解析
if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
!result) {
(*parse_errors)++;
LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
}
}
args.clear();
break;
case T_TEXT:
args.emplace_back(state.text);
break;
}
}
}
读取 init.zygote64.rc 配置文件中的内容,进行语法分析;
// 这是一个抽象方向,具体实现在不同类型的解析器中(service、on、import)
section_parser->ParseSection(std::move(args), filename, state.line);
service 解析流程
我们来看下 Service 中的 ParseSection 的具体实现;
Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
const std::string& filename, int line) {
if (args.size() < 3) {
return Error() << "services must have a name and a program";
}
// 读取文件名称
const std::string& name = args[1];
if (!IsValidName(name)) {
return Error() << "invalid service name '" << name << "'";
}
Subcontext* restart_action_subcontext = nullptr;
if (subcontexts_) {
for (auto& subcontext : *subcontexts_) {
if (StartsWith(filename, subcontext.path_prefix())) {
restart_action_subcontext = &subcontext;
break;
}
}
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
// 读取之后,创建了一个 service 对象;
service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
return Success();
}
创建 service 对象之后,通过 ParseLineSection 一行一行的进行内容的解析
Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
return service_ ? service_->ParseLine(std::move(args)) : Success();
}
解析完成之后,会调用 end_section() 函数,把解析的 service 添加到 vector 列表中
Result<Success> ServiceParser::EndSection() {
if (service_) {
Service* old_service = service_list_->FindService(service_->name());
if (old_service) {
if (!service_->is_override()) {
return Error() << "ignored duplicate definition of service '" << service_->name()
<< "'";
}
service_list_->RemoveService(*old_service);
old_service = nullptr;
}
// 把解析的 service 添加到 vector 列表中,这个 service_list 就是一个列表,里面使用的是 vector
service_list_->AddService(std::move(service_));
}
return Success();
}
所以 init 的整体流程总结如下:
- 创建和挂载启动所需要的文件目录;
- 初始化和启动属性服务;
- 解析init.rc配置文件并启动zygote进程;
Zygote 进程
我们接下来分析下 zygote 进程
Zygote 概述
Zygote 中文翻译为『受精卵』,正如其名,它主要用于孵化子进程。在 Android 系统中有以下两种程序:Java 应用程序,主要基于 ART 虚拟机,所有的应用程序 Apk 都属于这类 Native 程序,也就是利用 C 或 C++ 语言开发的程序,如 bootanimation。所有的 Java 应用程序进程及系统服务 SystemServer 进程都由 Zygote 进程通过 Linux 的 fork() 函数孵化出来的,这也就是为什么把它称为 Zygote 的原因,因为他就像一个受精卵,孵化出无数子进程,而 native 程序则由 Init 程序创建启动。Zygote 进程最初的名字不是『Zygote』而是『app_process』,这个名字是在 Android.mk 文件中定义的 Zygote 是 Android 中的第一个 ART 虚拟机,它通过 Socket 的方式与其他进程进行通信。这里的『其他进程』其实主要是系统进程——SystemServer
Zygote 触发过程
init.rc 会解析 zygote 下的 service,解析出来之后,最终是要执行里面的命令,在它准备执行里面的命令的时候,每个命令执行哪个函数,它都有一个映射表,这个映射表就存放在 builtins.cpp 的 map 函数中;
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map builtin_functions = {
{"bootchart", {1, 1, {false, do_bootchart}}},
{"chmod", {2, 2, {true, do_chmod}}},
{"chown", {2, 3, {true, do_chown}}},
{"class_reset", {1, 1, {false, do_class_reset}}},
{"class_restart", {1, 1, {false, do_class_restart}}},
// 这个命令,执行的就是 do_class_start 函数
{"class_start", {1, 1, {false, do_class_start}}},
{"class_stop", {1, 1, {false, do_class_stop}}},
{"copy", {2, 2, {true, do_copy}}},
{"domainname", {1, 1, {true, do_domainname}}},
{"enable", {1, 1, {false, do_enable}}},
{"exec", {1, kMax, {false, do_exec}}},
{"exec_background", {1, kMax, {false, do_exec_background}}},
{"exec_start", {1, 1, {false, do_exec_start}}},
{"export", {2, 2, {false, do_export}}},
{"hostname", {1, 1, {true, do_hostname}}},
{"ifup", {1, 1, {true, do_ifup}}},
{"init_user0", {0, 0, {false, do_init_user0}}},
{"insmod", {1, kMax, {true, do_insmod}}},
{"installkey", {1, 1, {false, do_installkey}}},
{"load_persist_props", {0, 0, {false, do_load_persist_props}}},
{"load_system_props", {0, 0, {false, do_load_system_props}}},
{"loglevel", {1, 1, {false, do_loglevel}}},
{"mkdir", {1, 4, {true, do_mkdir}}},
// TODO: Do mount operations in vendor_init.
// mount_all is currently too complex to run in vendor_init as it queues action triggers,
// imports rc scripts, etc. It should be simplified and run in vendor_init context.
// mount and umount are run in the same context as mount_all for symmetry.
{"mount_all", {1, kMax, {false, do_mount_all}}},
{"mount", {3, kMax, {false, do_mount}}},
{"umount", {1, 1, {false, do_umount}}},
{"readahead", {1, 2, {true, do_readahead}}},
{"restart", {1, 1, {false, do_restart}}},
{"restorecon", {1, kMax, {true, do_restorecon}}},
{"restorecon_recursive", {1, kMax, {true, do_restorecon_recursive}}},
{"rm", {1, 1, {true, do_rm}}},
{"rmdir", {1, 1, {true, do_rmdir}}},
{"setprop", {2, 2, {true, do_setprop}}},
{"setrlimit", {3, 3, {false, do_setrlimit}}},
{"start", {1, 1, {false, do_start}}},
{"stop", {1, 1, {false, do_stop}}},
{"swapon_all", {1, 1, {false, do_swapon_all}}},
{"symlink", {2, 2, {true, do_symlink}}},
{"sysclktz", {1, 1, {false, do_sysclktz}}},
{"trigger", {1, 1, {false, do_trigger}}},
{"verity_load_state", {0, 0, {false, do_verity_load_state}}},
{"verity_update_state", {0, 0, {false, do_verity_update_state}}},
{"wait", {1, 2, {true, do_wait}}},
{"wait_for_prop", {2, 2, {false, do_wait_for_prop}}},
{"write", {2, 2, {true, do_write}}},
};
// clang-format on
return builtin_functions;
}
// 这个命令,执行的就是 do_class_start 函数
{"class_start", {1, 1, {false, do_class_start}}}
这个函数,我们来看下干了什么
static Result<Success> do_class_start(const BuiltinArguments& args) {
// Starting a class does not start services which are explicitly disabled.
// They must be started individually.
for (const auto& service : ServiceList::GetInstance()) {
if (service->classnames().count(args[1])) {
if (auto result = service->StartIfNotDisabled(); !result) {
LOG(ERROR) << "Could not start service '" << service->name()
<< "' as part of class '" << args[1] << "': " << result.error();
}
}
}
return Success();
}
通过一个 for 循环,遍历前面说到的 ServiceList,这个 vector 类型的 list,就是存放的前面创建的各种服务;
取出所有的服务,调用 StartIfNotDisabled 函数进行启动,如果这个服务没有被禁用;
Result<Success> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
return Success();
}
这个函数会调用 Start 函数进行启动,这个 Start 函数中就调用了 fork 函数,创建了一个子进程;
Result<Success> Service::Start() {
// 省略部分代码
pid_t pid = -1;
if (namespace_flags_) {
pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
} else {
pid = fork(); // 创建子进程
}
// if 中的逻辑都是在子进程中执行的;
if (pid == 0) {
// 省略部分代码
// 启动 app_main.cpp 中 main 函数
if (!ExpandArgsAndExecv(args_)) {
PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
}
}
}
ExpandArgsAndExecv(args_) 这个函数会通过 execv 函数启动 app_main.cpp 中 main 函数;
static bool ExpandArgsAndExecv(const std::vector<std::string>& args) {
std::vector<std::string> expanded_args;
std::vector<char*> c_strings;
expanded_args.resize(args.size());
c_strings.push_back(const_cast<char*>(args[0].data()));
for (std::size_t i = 1; i < args.size(); ++i) {
if (!expand_props(args[i], &expanded_args[i])) {
LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
}
c_strings.push_back(expanded_args[i].data());
}
c_strings.push_back(nullptr);
return execv(c_strings[0], c_strings.data()) == 0;
}
我们来看下 main 中做了什么;
int main(int argc, char* const argv[])
{
if (!LOG_NDEBUG) {
String8 argv_String;
for (int i = 0; i < argc; ++i) {
argv_String.append(""");
argv_String.append(argv[i]);
argv_String.append("" ");
}
ALOGV("app_process main with argv: %s", argv_String.string());
}
// 启动 Zygote
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// 省略部分代码
...
//
}
整体流程大致如下:
Zygote 启动过程
在app_main.cpp的main函数中,主要做的事情就是参数解析. 这个函数有两种启动模式:
- 一种是 zygote 模式,也就是初始化 zygote 进程,传递的参数有--start-system-server --socketname=zygote,前者表示启动 SystemServer,后者指定 socket 的名称;
- 一种是 application 模式,也就是启动普通应用程序,传递的参数有 class 名字以及 class 带的参数;
两者最终都是调用 AppRuntime 对象的 start 函数,加载 ZygoteInit 或 RuntimeInit 两个 Java 类,并将之前整理的参数传入进去
int main(int argc, char* const argv[]) {
++i; // Skip unused "parent dir" argument.
// 启动不同的进程;
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
// zygote 类型的
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
// application 类型的
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg);
break;
} else {
--i;
break;
}
}
}
经过 while 循环的判断之后,如果是 zygote 进程,就会直接调用 start 函数,传递 com.android.internal.os.ZygoteInit 进行 zygote 的启动;
if (zygote) {
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}
我们接着来看下这个 start 函数中都做了什么
void AndroidRuntime::start(const char* className, const Vector<String8>&options, bool zygote)
{
...
JNIEnv* env;
// JNI_CreateJavaVM L1015
// 启动虚拟机
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);
// 进行注册
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
}
start 函数中主要是 startVm 函数启动了我们的虚拟机;startReg 函数映射表的注册;
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
// 省略部分代码
...
//
/*
* Initialize the VM.
*
* The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
* If this call succeeds, the VM is ready, and we can start issuing
* JNI calls.
*/
// 创建虚拟机,然后启动它
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
ALOGE("JNI_CreateJavaVM failed\n");
return -1;
}
}
我们来看下是如何创建并启动的;
bool Runtime::Create(RuntimeArgumentMap&& runtime_options) {
if (Runtime::instance_ != nullptr) {
return false;
}
// 创建 Runtime 并调用 Init 进行初始化
instance_ = new Runtime;
Locks::SetClientCallback(IsSafeToCallAbort);
// 初始化
if (!instance_->Init(std::move(runtime_options))) {
instance_ = nullptr;
return false;
}
return true;
}
// 创建 Runtime 并调用 Init 进行初始化
Init中主要做了如下几件事情;
- new gc::Heap // 创建堆管理对象
- JavaVMExt::Create //创建java虚拟机对象
- Thread::Attach //连接主线程
- new ClassLinker //创建类连接器
- class_linker_->InitFromBootImage //初始化类连接器
初始化成功之后,就会调用到 Java 层的 ZygoteInit.java 的 main 函数;
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();
// 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);
}
final Runnable caller;
try {
// Report Zygote start time to tron unless it is a runtime restart
if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
MetricsLogger.histogram(null, "boot_zygote_init",
(int) SystemClock.elapsedRealtime());
}
String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
Trace.TRACE_TAG_DALVIK);
bootTimingsTraceLog.traceBegin("ZygoteInit");
RuntimeInit.enableDdms();
boolean startSystemServer = false;
String socketName = "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)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
zygoteServer.registerServerSocketFromEnv(socketName);
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else {
Zygote.resetNicePriority();
}
// Do an initial gc to clean up after startup
bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
gcAndFinalize();
bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
bootTimingsTraceLog.traceEnd(); // ZygoteInit
// Disable tracing so that forked processes do not inherit stale tracing tags from
// Zygote.
Trace.setTracingEnabled(false, 0);
Zygote.nativeSecurityInit();
// Zygote process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
ZygoteHooks.stopZygoteNoThreadCreation();
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, 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 exception", ex);
throw ex;
} finally {
zygoteServer.closeServerSocket();
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {
caller.run();
}
}
Java 层的 main 函数主要做了以下事情;
- new ZygoteServer(); //新建Zygote服务器端
- registerServerSocketFromEnv(socketName);//注册Socket
- preload 这里做了大量的耗时操作,这也是系统开机耗时的主要原因;
- gcAndFinalize //主动进行一次资源GC
- forkSystemServer
- zygoteServer.runSelectLoop
- caller.run()
fork 函数
我们接下来讲解下 fork 函数;
在 Linux 中是没有线程和进程的概念的,它们都是使用的 thread_task 这样的一个结构体,区别在于线程是资源共享的;
当我们由一个进程创建另一个进程的时候,就是使用的 fork 函数;这里要注意的是 fork 函数会返回两次,一次是 pid = 0,表示子进程创建成功,接下来进入子进程执行流程,一次是 pid > 0 成功创建子进程,并且继续执行父进程流程代码;返回非正数(<0),创建子进程失败,失败原因主要有:进程数超过系统所能创建的上限,errno会被设置为EAGAIN系统内存不足,errno会被设置为ENOMEM;
使用 fork() 函数得到的子进程是父进程的一个复制品,如果什么都不做,它们两个是一模一样的,它从父进程处继承了整个进程的地址空间:包括进程上下文(进程执行活动全过程的静态描述)、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。子进程所独有的只有它的进程号,计时器等(只有小量信息)。因此,使用 fork() 函数的代价是很大的;
子进程与父进程的区别
- 除了文件锁以外,其他的锁都会被继承;
- 各自的进程ID和父进程ID不同;
- 子进程的未决告警被清除;
- 子进程的未决信号集设置为空集;
写时拷贝 (copy-on-write)
Linux 的 fork() 使用是通过写时拷贝 (copy- on-write) 实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只有在需要写入的时候才会复制地址空间,从而使各个进行拥有各自的地址空间。也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享;
多线程进程的 fork 调用
在 POSIX 标准中,fork 的行为是这样的:复制整个用户空间的数据(通常使用 copy-on-write 的策略,所以可以实现的速度很快)以及所有系统对象,然后仅复制当前线程到子进程。这里:所有父进程中别的线程,到了子进程中都是突然蒸发掉的;
假设这么一个环境,在 fork 之前,有一个子线程 lock 了某个锁,获得了对锁的所有权。fork 以后,在子进程中,所有的额外线程都人间蒸发了。而锁却被正常复制了,在子进程看来,这个锁没有主人,所以没有任何人可以对它解锁。当子进程想 lock 这个锁时,不再有任何手段可以解开了。程序发生死锁
常见问题
Q:为什么要专门使用 Zygote 进程去孵化应用进程,而不是让 system_server 去孵化呢?
A:为什么要专门使用 Zygote 进程去孵化应用进程,而不是让 system_server 去孵化呢?首先 system_server 相比 Zygote 多运行了 AMS、WMS 等服务,这些对一个应用程序来说是不需要的。另外进程的 fork() 对多线程不友好,仅会将发起调用的线程拷贝到子进程,这可能会导致死锁,而 system_server 中肯定是有很多线程的。
Q:System_server 为什么要在 Zygote 中启动,而不是由 init 直接启动?
A:Zygote 作为一个孵化器,可以提前加载一些资源,这样 fork() 时基于 Copy-On-Write 机制创建的其 他进程就能直接使用这些资源,而不用重新加载。比如 system_server 就可以直接使用 Zygote 中的 JNI 函数、共享库、常用的类、以及主题资源;
Q:Zygote 为什么不采用 Binder 机制进行 IPC 通信?
A:Binder 机制中存在 Binder 线程池,是多线程的,如果 Zygote 采用 Binder 的话就存在上面说的 fork() 与 多线程的问题了。其实严格来说,Binder 机制不一定要多线程,所谓的 Binder 线程只不过是 在循环读取 Binder 驱动的消息而已,只注册一个 Binder 线程也是可以工作的,比如 service manager 就是这样的。实际上 Zygote 尽管没有采取 Binder 机制,它也不是单线程的,但它在 fork() 前主动停止 了其他线程,fork() 后重新启动了;
下一章预告
Dex格式分析
欢迎三连
来都来了,点个关注,点个赞吧,你的支持是我最大的动力~~~
转载自:https://juejin.cn/post/7352939767719493683