Android Linux Zygote启动
source link: https://mp.weixin.qq.com/s?__biz=MzIzNTc5NDY4Nw%3D%3D&%3Bmid=2247484903&%3Bidx=1&%3Bsn=b55b5bd01ad292256364ecd965ca2288
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Android
启动的第一个进程是由 Linux Kernel
启动的 init
进程。在 init
进程启动的过程中,会通过解析 init.rc
文件来启动 zygote
进程。
zygote
进程是 Android
所在 Java
层的第一个进程,接下来我们一起来看一下 zygote
的启动过程。
.rc文件语法
如果你直接打开 init.rc
文件看它的内容,你大概率会一脸懵逼。因为它使用了一套独有的语法。
首先来了解一下 .rc
语法。它内部使用的是 Android Init Language
,俗称 Android初始化语言 。
在上篇文章中其实已经提及过,它的语法主要分为五类,分别为: Actions
、 Commands
、 Services
、 Options
与 Imports
。
Actions
Actions
称为命令序列。
它结合 trigger
触发器一起使用。主要用于确定该 Actions
作用的时机,当发生时机与某个 Actions
的 trigger
相匹配时,这个 Actions
将会添加到执行的队列中,队列中的每个 Actions
都按顺序出队,并且该 Actions
中的每个命令都按顺序执行。
Actions
的格式如下:
on <trigger> [&& <trigger>]* <command> <command> <command>
以 on
开头,配合对应的 trigger
,一旦 trigger
触发,将会执行下面的 command
。
trigger
主要包含以下几种类型:
-
early-init
: 在初始化早期阶段触发 -
init
: 在初始化阶段触发 -
late-init
: 在初始化晚期阶段触发 -
boot/charger
: 当系统启动/充电时触发,还包含其他情况,此处不一一列举 -
property:<key>=<value>
: 当属性值满足条件时触发
来看一个示例:
on boot setprop a 1 setprop b 2 on boot && property:true=true setprop c 1 setprop d 2 on boot setprop e 1 setprop f 2
当 boot
触发并假设属性 true
等于 true
时,将触发上面的 Actions
, 执行的命令顺序为:
setprop a 1 setprop b 2 setprop c 1 setprop d 2 setprop e 1 setprop f 2
Commands
Commands
俗称指令。
就是 Linux
需要执行的具体内容。
Commands
包含的指令还是很多的,这里列举一些常用的指令。
-
class_start <service_class_name>
:启动属于同一个class
的所有服务 -
start <service_name>
:启动指定的服务,若已启动则跳过 -
stop <service_name>
:停止正在运行的服务 -
setprop <name> <value>
:设置属性值 -
mkdir <path>
:创建指定目录 -
symlink <target> <sym_link> <target> <sym_link>
-
write <path> <string>
:向文件path
中写入字符串 -
exec fork init
-
exprot <name> <name>
:设定环境变量 -
loglevel <level>
:设置log
级别
这个很简单,遇到就直接对照查看就可以了,例如上面提到的 setprop
,它就是用来对属性赋值的。
Services
Services
俗称服务。
它由 init
进程启动,一般运行在 init
的子进程中,启动服务时首先会判断该服务对应的文件是否存在,而服务定义在 .rc
文件中,会通过 init
进程 fork
出子进程来启动对应的 Service
。
具体的定义格式如下:
service <name> <pathname> [ <argument> ]* <option> <option> ...
例如:
service servicemanager /system/bin/servicemanager
代表的意义是:该服务的名称是 servicemanager
,对应的执行路径为 /system/bin/servicemanager
。
Options
Options
俗称可选项。
它都作用于 Services
,改变服务的运行方式与对应的时间等。
它的内容与 Commands
一样有很多,这里列举一些常用的,遇到直接查看就可以了。
-
disabled class service
-
oneshot
:service
退出后不再重启 -
user/group
:设置执行服务的用户/用户组,默认都是root
-
class
:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为default
-
onrestart
:当服务重启时执行相应命令 -
socket /dev/socket/<name> socket
-
critical
: 在规定时间内该service
不断重启,则系统会重启并进入恢复模式
default
: 意味着 disabled=false
, oneshot=false
, critical=false
Imports
Imports
俗称导入。
用途是用来导入对应的 .rc
文件。
具体格式为:
import <path>
例如
import /init.environ.rc import /init.usb.rc import /init.${ro.hardware}.rc
Zygote
现在已经对 .rc
语法有了一个初步的了解,我们再来看 init.rc
的源文件,来分析一下它是如何启动 Zygote
进程的。
on early-init # Set init and its forked children's oom_adj. write /proc/1/oom_score_adj -1000 # Apply strict SELinux checking of PROT_EXEC on mmap/mprotect calls. write /sys/fs/selinux/checkreqprot 0 # Set the security context for the init process. # This should occur before anything else (e.g. ueventd) is started. setcon u:r:init:s0 # Set the security context of /adb_keys if present. restorecon /adb_keys start ueventd # create mountpoints mkdir /mnt 0775 root system on init sysclktz 0 loglevel 3 ... ... on late-init ... ... on post-fs //挂载文件系统 ... ... on post-fs-data //挂载data ... ... on boot //启动核心服务 ... ... class_start very-first on property:androVM.inited=1 class_start core class_start main on property:genymotion.local_opengl.started=1 start surfaceflinger on nonencrypted class_start main class_start late_start
以上是各个触发器的执行顺序。满足条件会执行 class_start
来启动名称为 main
的类,并启动它关联的 service
。
其中 class_start
对应的就是 builtins.cpp
中的 do_class_start
。它们之间的映射关系是由 BuiltinFunctionMap
维护的
const BuiltinFunctionMap& GetBuiltinFunctionMap() { constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max(); // clang-format off static const BuiltinFunctionMap 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_reset_post_data", {1, 1, {false, do_class_reset_post_data}}}, {"class_restart", {1, 1, {false, do_class_restart}}}, {"class_start", {1, 1, {false, do_class_start}}}, {"class_start_post_data", {1, 1, {false, do_class_start_post_data}}}, ... ... } }
它与对应的 Actions
绑定是在 init.cpp
中的 SecondStageMain
方法里面。
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap(); Action::set_function_map(&function_map);
system/core/init/builtins.cpp
所以我们直接看 do_class_start
。
static Result<void> do_class_start(const BuiltinArguments& args) { // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1. if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false)) return {}; // 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.ok()) { LOG(ERROR) << "Could not start service '" << service->name() << "' as part of class '" << args[1] << "': " << result.error(); } } } return {}; }
在这方法中会通过 ServiceList::GetInstance
来获取解析 .rc
文件中的 Service
链表,通过匹配对应的 classnames
来找到对应的 Service
。
根据上面的 class_start main
,说明要找的是 class
名称为 main
这个关联的 Service
。
这个 main service
位于 init.zygote.rc
中,内部源码如下:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart media onrestart restart netd
它只注册了一个 service
,名称为 zygote
,对应的执行文件为 /system/bin/app_process
, 后面的 --zygote --start-system-serve
都是它的启动参数。
通过 class main
设置它的启动类为 main
,通过它来启动这个 service
。
这里正好对应的上面需要寻找的类名为 main
的启动类来启动 service
,也就是说启动的这个 service
就是 zygote
。
至于 service
的解析过程在之前的文章 init
启动有提及过,它是通过 LoadBootScripts(am, sm)
进入解析 .rc
文件,然后内部会通过 service_parser.cpp
来解析对应的 service
语句,最后将解析的 service
信息添加到 service vector
链表中。
service_list_->AddService(std::move(service_)); vector<_Tp, _Allocator>::__emplace_back_slow_path(_Args&&... __args)
找到匹配的 main class
之后,会进入 StartIfNotDisabled
system/core/init/service.cpp
Result<void> Service::StartIfNotDisabled() { if (!(flags_ & SVC_DISABLED)) { return Start(); } else { flags_ |= SVC_DISABLED_START; } return {}; }
如果它不是 disabled
就是调用 Start
,通过上面的 service
注册信息是没有设置 disabled
,所以为 false
。即进入 Start
启动 service
Result<void> Service::Start() { ... ... if (flags_ & SVC_RUNNING) { if ((flags_ & SVC_ONESHOT) && disabled) { flags_ |= SVC_RESTART; } // 如果没有错误就启动之前启动的service reboot_on_failure.Disable(); return {}; } 。。。 // 判断该service对于的执行文件是否存在 struct stat sb; if (stat(args_[0].c_str(), &sb) == -1) { flags_ |= SVC_DISABLED; return ErrnoError() << "Cannot find '" << args_[0] << "'"; } ... pid_t pid = -1; if (namespaces_.flags) { pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr); } else { // fork()创建子进程 pid = fork(); } if (pid == 0) { // fork的子进程 umask(077); ... ... if (!ExpandArgsAndExecv(args_, sigstop_)) { PLOG(ERROR) << "cannot execv('" << args_[0] << "'). See the 'Debugging init' section of init's README.md for tips"; } _exit(127); } ... NotifyStateChange("running"); reboot_on_failure.Disable(); return {}; }
在 Start
中首先会判断该 service
是否已经启动,若没有,然后去验证该 service
是否能够找到对应的可执行文件,这里对应的就是 /system/bin/app_process
。最后会通过 fork()
来创建子进程,并返回 pid = 0
。
所以当 pid === 0
时,说明运行在子进程中,然后进入 ExpandArgsAndExecv
方法。
static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) { 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) { auto expanded_arg = ExpandProps(args[i]); if (!expanded_arg.ok()) { LOG(FATAL) << args[0] << ": cannot expand arguments': " << expanded_arg.error(); } expanded_args[i] = *expanded_arg; c_strings.push_back(expanded_args[i].data()); } c_strings.push_back(nullptr); if (sigstop) { kill(getpid(), SIGSTOP); } // 启动zygote return execv(c_strings[0], c_strings.data()) == 0; }
在 ExpandArgsAndExecv
方法中最后会通过 execv
来启动子进程,对应的是进入 app_main.cpp
并调用它的 main
方法。
即 zygote
是通过 fork
和 execv
共同创建的。
再来看 app_main.cpp
做了什么
frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[]) { ... ... // --zygote : Start in zygote mode // --start-system-server : Start the system server. // --application : Start in application (stand alone, non zygote) mode. // --nice-name : The nice name for this process. ... bool zygote = false; bool startSystemServer = false; bool application = false; String8 niceName; String8 className; ++i; // Skip unused "parent dir" argument. while (i < argc) { const char* arg = argv[i++]; if (strcmp(arg, "--zygote") == 0) { // 启动的是zygote zygote = true; // 设置进程名称,这里为zygote niceName = ZYGOTE_NICE_NAME; } else if (strcmp(arg, "--start-system-server") == 0) { // 启动完zygote之后,需要开启system service startSystemServer = true; } else if (strcmp(arg, "--application") == 0) { 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; } } ... if (zygote) { // 启动java ZygoteInit 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."); } }
核心部分就两步,第一步是解析 service
中的参数,这个参数是在 .rc
中注册时的参数,第二步通过 runtime.start
来启动 java
中的 ZygoteInit
。
这一步是 Linux
首次进入到 Java
层。
为了避免单篇幅度过长,关于 Linux
如何启动 Java
中的 ZygoteInit
与 ZygoteInit
的内部启动过程,这块的具体内容将会在下篇文章继续分析。
最后总结一下 zygote
在 Linux
中的创建过程:
-
通过
LoadBootScripts(am, sm)
解析.rc
文件 -
解析完之后,根据
Actions
事件触发时机找到类名为main
的启动类 -
根据
main
启动类来启动对应的zygote
服务 -
fork zygote pid = 0
-
execv zygote app_main.cpp main
-
service runtime.start java ZygoteInit
一图胜千文
推荐阅读
你的点赞我将开心一整天
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK