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
一图胜千文
项目
android_startup: 提供一种在应用启动时能够更加简单、高效的方式来初始化组件。开发人员可以使用android-startup
来简化启动序列,并显式地设置初始化顺序与组件之间的依赖关系。 与此同时android-startup
支持同步与异步等待,并通过有向无环图拓扑排序的方式来保证内部依赖组件的初始化顺序。
AwesomeGithub: 基于Github
客户端,纯练习项目,支持组件化开发,支持账户密码与认证登陆。使用Kotlin
语言进行开发,项目架构是基于Jetpack&DataBinding
的MVVM
;项目中使用了Arouter
、Retrofit
、Coroutine
、Glide
、Dagger
与Hilt
等流行开源技术。
flutter_github: 基于Flutter
的跨平台版本Github
客户端,与AwesomeGithub
相对应。
android-api-analysis: 结合详细的Demo
来全面解析Android
相关的知识点, 帮助读者能够更快的掌握与理解所阐述的要点。
daily_algorithm: 每日一算法,由浅入深,欢迎加入一起共勉。