盒子
盒子
文章目录
  1. .rc文件语法
    1. Actions
    2. Commands
    3. Services
    4. Options
    5. Imports
  2. Zygote
  3. 项目

Android Linux Zygote启动

Android init 启动

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的格式如下:

1
2
3
4
on <trigger> [&& <trigger>]*
<command>
<command>
<command>

以on开头,配合对应的trigger,一旦trigger触发,将会执行下面的command。

trigger主要包含以下几种类型:

  1. early-init: 在初始化早期阶段触发
  2. init: 在初始化阶段触发
  3. late-init: 在初始化晚期阶段触发
  4. boot/charger: 当系统启动/充电时触发,还包含其他情况,此处不一一列举
  5. property:=: 当属性值满足条件时触发

来看一个示例:

1
2
3
4
5
6
7
8
9
10
11
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, 执行的命令顺序为:

1
2
3
4
5
6
setprop a 1
setprop b 2
setprop c 1
setprop d 2
setprop e 1
setprop f 2

Commands

Commands俗称指令。

就是Linux需要执行的具体内容。

Commands包含的指令还是很多的,这里列举一些常用的指令。

  1. class_start <service_class_name>: 启动属于同一个class的所有服务
  2. start <service_name>: 启动指定的服务,若已启动则跳过
  3. stop <service_name>: 停止正在运行的服务
  4. setprop :设置属性值
  5. mkdir :创建指定目录
  6. symlink <sym_link>: 创建连接到的<sym_link>符号链接
  7. write : 向文件path中写入字符串
  8. exec: fork并执行,会阻塞init进程直到程序完毕
  9. exprot :设定环境变量
  10. loglevel :设置log级别

这个很简单,遇到就直接对照查看就可以了,例如上面提到的setprop,它就是用来对属性赋值的。

Services

Services俗称服务。

它由init进程启动,一般运行在init的子进程中,启动服务时首先会判断该服务对应的文件是否存在,而服务定义在.rc文件中,会通过init进程fork出子进程来启动对应的Service。

具体的定义格式如下:

1
2
3
4
service <name> <pathname> [ <argument> ]*
<option>
<option>
...

例如:

1
service servicemanager /system/bin/servicemanager

代表的意义是:该服务的名称是servicemanager,对应的执行路径为/system/bin/servicemanager。

Options

Options俗称可选项。

它都作用于Services,改变服务的运行方式与对应的时间等。

它的内容与Commands一样有很多,这里列举一些常用的,遇到直接查看就可以了。

  1. disabled: 不随class自动启动,只有根据service名才启动
  2. oneshot: service退出后不再重启
  3. user/group: 设置执行服务的用户/用户组,默认都是root
  4. class:设置所属的类名,当所属类启动/退出时,服务也启动/停止,默认为default
  5. onrestart:当服务重启时执行相应命令
  6. socket: 创建名为/dev/socket/的socket
  7. critical: 在规定时间内该service不断重启,则系统会重启并进入恢复模式

default: 意味着disabled=false,oneshot=false,critical=false

Imports

Imports俗称导入。

用途是用来导入对应的.rc文件。

具体格式为:

1
import <path>

例如

1
2
3
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc

Zygote

现在已经对.rc语法有了一个初步的了解,我们再来看init.rc的源文件,来分析一下它是如何启动Zygote进程的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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维护的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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方法里面。

1
2
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);

system/core/init/builtins.cpp

所以我们直接看do_class_start。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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中,内部源码如下:

1
2
3
4
5
6
7
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链表中。

1
2
3
service_list_->AddService(std::move(service_));

vector<_Tp, _Allocator>::__emplace_back_slow_path(_Args&&... __args)

找到匹配的main class之后,会进入StartIfNotDisabled

system/core/init/service.cpp

1
2
3
4
5
6
7
8
Result<void> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
return {};
}

如果它不是disabled就是调用Start,通过上面的service注册信息是没有设置disabled,所以为false。即进入Start启动service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
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中的创建过程:

  1. 通过LoadBootScripts(am, sm)解析.rc文件
  2. 解析完之后,根据Actions事件触发时机找到类名为main的启动类
  3. 根据main启动类来启动对应的zygote服务
  4. 然后通过fork来创建zygote子进程,并返回pid = 0
  5. 通过execv来启动zygote子进程,进入app_main.cpp的main方法
    方法
  6. 最后再解析注册表中service的参数并通过runtime.start启动java层的ZygoteInit

一图胜千文

项目

android_startup: 提供一种在应用启动时能够更加简单、高效的方式来初始化组件,优化启动速度。不仅支持Jetpack App 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: 每日一算法,由浅入深,欢迎加入一起共勉。

支持一下
赞赏是一门艺术