[TOC]

文章参考:http://kmanong.top/kmn/qxw/form/article?id=14312&cate=45

文章参考:https://juejin.cn/post/6844903965688250382

源码分析基于Android12

概述

我们先来看一下Android系统启动流程的全过程。

  1. 按电源键
  2. 进入开机动画
  3. 经过漫长的等待
  4. 开机动画结束
  5. 正式开机,进入设置画面
  6. 进入系统桌面(Launcher)

1.BootLoader

刷过机的朋友大概都知道,Android可以通过某个组合按键进入BootLoader页面,这个也就是上图中的最底层,在Android系统,甚至于它的内核还未加载时的一个引导程序,主要负责对kenel进行解压和初始化的工作

2.idle进程

kernel中的idle进程是0号进程,由内核中启动,并始终执行在内核态,由内核态的idle进程开启我们常提的1号进程,init(对应源码,system/core/init/init.cpp

3.init进程

init.cpp中做的事情其实不只是开启init进程,这个后面分析具体源码时再做详细介绍

init进程负责的事情主要是对init.rc这个系统启动脚本文件进行解析(loadBootScripts()),经过对ro.zygote对应的具体的架构的脚本文件进行解析,进行到第四步

4.zygote进程

zygote的进程的启动入口。我们找到对应的源码位置在framework/base/cmds/app_process/app_main

app_process中把app_main运行的进程设置process_name为zygote,这也就是我们在执行ps指令时看到的zygote进程了,此时zygote还处于native层,通过jni调用zygoteInit.java中的main函数,正式进入Java世界,zygoteInit中又开启了SystemServer,进入第四步

5.SystemServer进程

这个是Android中最核心的进程。SystemServer进程主要用于创建系统服务,我们熟悉的AMS、WMS、PMS都是由SystemServer创建的。这个我们后面会一一学习到。

6.Launcher

Launcher已经是我们具体的App应用了,这个是由zygote进程fork出来的进程

init进程启动

init 进程是第一个进程,或者我们可以说它是所有进程的父进程或者父父进程。

init 进程有两个职责:

  1. 挂载/sys,/dev或/proc之类的目录

  2. 运行/init.rc脚本。init.rc负责系统的初始化设置

init的源码位于system/core/init包下,我们先从入口类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
/**
* 源码位于:system/core/init/main.cpp
**/
int main(int argc, char **argv)
{
if (!strcmp(basename(argv[0]), "ueventd"))
{
return ueventd_main(argc, argv);
}

if (argc > 1)
{
//......

if (!strcmp(argv[1], "selinux_setup"))
{
// 对SELinux进行初始化,并通过execs的系统调用开启init进程
return SetupSelinux(argv);
}

if (!strcmp(argv[1], "second_stage"))
{
//第二阶段
return SecondStageMain(argc, argv);
}
}
//第一阶段
return FirstStageMain(argc, argv);
}

main.cpp的函数跟之前版本有了很大的区别。

SetupSelinux

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
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
InitKernelLogging(argv);

if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}

boot_clock::time_point start_time = boot_clock::now();

MountMissingSystemPartitions();

// Set up SELinux, loading the SELinux policy.
SelinuxSetupKernelLogging();
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);
//init二进制文件的目录
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
//调用execv开启init进程
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;
}

我们可以看到在SetupSelinux的函数里面调用execv开启init进程‘

FirstStageMain函数

我们再来看下第一阶段的函数FirstStageMain做了哪些工作

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/**
* 代码位于:system/core/first_stage_init.cpp
**/
int FirstStageMain(int argc, char** argv) {
//是否定义由init.mk决定
if (REBOOT_BOOTLOADER_ON_PANIC) {
//处理init挂掉的情况,会重启bootloader
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);

// 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"));
CHECKCALL(mkdir("/dev/pts", 0755));
CHECKCALL(mkdir("/dev/socket", 0755));
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#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));
CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));

CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));

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

SetStdioToDevNull(argv);
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
InitKernelLogging(argv);

if (!errors.empty()) {
for (const auto& [error_string, error_errno] : errors) {
LOG(ERROR) << error_string << " " << strerror(error_errno);
}
LOG(FATAL) << "Init encountered errors starting first stage, aborting";
}

LOG(INFO) << "init first stage started!";

auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
if (!old_root_dir) {
PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
}

struct stat old_root_info;
if (stat("/", &old_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
}

auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline) : 0;

if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline), want_console)) {
if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
} else {
LOG(FATAL) << "Failed to load kernel modules";
}
}

if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {
StartConsole();
}

if (ForceNormalBoot(cmdline)) {
mkdir("/first_stage_ramdisk", 0755);
// SwitchRoot() must be called with a mount point as the target, so we bind mount the
// target directory to itself here.
if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
}
SwitchRoot("/first_stage_ramdisk");
}

// If this file is present, the second-stage init will use a userdebug sepolicy
// and load adb_debug.prop to allow adb root, if the device is unlocked.
//如果该文件存在且已经解锁bootbloade r,则允许调用adb root指令(userdebug sepolicy)
if (access("/force_debuggable", F_OK) == 0) {
std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||
!fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {
LOG(ERROR) << "Failed to setup debug ramdisk";
} else {
// setenv for second-stage init to read above kDebugRamdisk* files.
setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
}
}

if (!DoFirstStageMount()) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}

struct stat new_root_info;
if (stat("/", &new_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
old_root_dir.reset();
}

if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
}

SetInitAvbVersionInRecovery();

setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
1);
//找到init的二进制文件目录
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);
//通过execv来启动init进程
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;
}

主要代码处已经做处了注释,现在来总结下FirstStageMain的工作:

  1. 处理init进程挂掉的情况
  2. 设置用户组,挂载相关系统文件
  3. 根据/force_debuggable文件来判断是否允许adb root指令
  4. 找到init的二进制文件目录,通过execv来启动init进程

至此init进程就被启动起来了,我们在回来看看SecondStageMain函数接下来做了哪些工作

SecondStageMain函数

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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/**
* 代码位于:system/core/init/init.cpp
**/
int SecondStageMain(int argc, char** argv) {
//是否定义由init.mk决定
if (REBOOT_BOOTLOADER_ON_PANIC) {
//处理init挂掉的情况,会重启bootloader
InstallRebootSignalHandlers();
}

// ......
LOG(INFO) << "init second stage started!";
{
struct sigaction action = {.sa_flags = SA_RESTART};
action.sa_handler = [](int) {};
sigaction(SIGPIPE, &action, nullptr);
}

// Set init and its forked children's oom_adj.
if (auto result =
WriteFile("/proc/1/oom_score_adj", StringPrintf("%d", DEFAULT_OOM_SCORE_ADJUST));
!result.ok()) {
LOG(ERROR) << "Unable to write " << DEFAULT_OOM_SCORE_ADJUST
<< " to /proc/1/oom_score_adj: " << result.error();
}

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));

// See if need to load debug props to allow adb root, when the device is unlocked.
const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
bool load_debug_prop = false;
if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
load_debug_prop = "true"s == force_debuggable_env;
}
unsetenv("INIT_FORCE_DEBUGGABLE");

// Umount the debug ramdisk so property service doesn't read .prop files from there, when it
// is not meant to.
if (!load_debug_prop) {
UmountDebugRamdisk();
}
//初始化系统属性,使用mmap共享内存,"/dev/__properties__/property_info"
PropertyInit();

// Umount the debug ramdisk after property service has read the .prop files when it means to.
if (load_debug_prop) {
UmountDebugRamdisk();
}

// Mount extra filesystems required during second stage init
MountExtraFilesystems();

// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();
//使用IO复用机制,epoll,即 event poll,是poll机制的升级版
Epoll epoll;
if (auto result = epoll.Open(); !result.ok()) {
PLOG(FATAL) << result.error();
}
// 使用epoll对init子进程的信号进行监听
InstallSignalFdHandler(&epoll);
InstallInitNotifier(&epoll);
//开启属性服务,并注册到epoll中
StartPropertyService(&property_fd);

// Make the time that init stages started available for bootstat to log.
RecordStageBoottimes(start_time);

// Set libavb version for Framework-only OTA match in Treble build.
if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) {
SetProperty("ro.boot.avb_version", avb_version);
}
unsetenv("INIT_AVB_VERSION");

fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
MountHandler mount_handler(&epoll);
SetUsbController();

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

if (!SetupMountNamespaces()) {
PLOG(FATAL) << "SetupMountNamespaces failed";
}

InitializeSubcontext();

ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
//加载系统启动脚本"/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();

// Make the GSI status available before scripts start running.
auto is_running = android::gsi::IsGsiRunning() ? "1" : "0";
SetProperty(gsi::kGsiBootedProp, is_running);
auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
SetProperty(gsi::kGsiInstalledProp, is_installed);

am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
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");
Keychords keychords;
am.QueueBuiltinAction(
[&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {
for (const auto& svc : ServiceList::GetInstance()) {
keychords.Register(svc->keycodes());
}
keychords.Start(&epoll, HandleKeychord);
return {};
},
"KeychordInit");

// 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.
auto epoll_timeout = std::optional<std::chrono::milliseconds>{};

auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
HandlePowerctlMessage(*shutdown_command);
}

if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!IsShuttingDown()) {
auto next_process_action_time = HandleProcessActions();

// If there's a process that needs restarting, wake up in time for that.
if (next_process_action_time) {
epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_action_time - boot_clock::now());
if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
}
}

if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout = 0ms;
}

auto pending_functions = epoll.Wait(epoll_timeout);
if (!pending_functions.ok()) {
LOG(ERROR) << pending_functions.error();
} else if (!pending_functions->empty()) {
// We always reap children before responding to the other pending functions. This is to
// prevent a race where other daemons see that a service has exited and ask init to
// start it again via ctl.start before init has reaped it.
ReapAnyOutstandingChildren();
for (const auto& function : *pending_functions) {
(*function)();
}
}
if (!IsShuttingDown()) {
HandleControlMessages();
SetUsbController();
}
}

return 0;
}

}

SecondStageMain的主要工作总结

在Android12的版本中,该类的入口函数为main,代码中的关键部分我已经做了注释,现在来我们来总结一下SecondStageMain的主要工作:

  1. 使用epoll对init子进程的信号进行监听

  2. 初始化系统属性,使用mmap共享内存,”/dev/properties/property_info” (重要)

  3. 开启属性服务,并注册到epoll中(重要)

  4. 加载系统启动脚本”/init.rc”

  5. 解析启动脚本,启动相关服务

属性服务

什么是属性服务,我觉得它更像关于这台手机的各种系统信息,通过 key / value 的形式供我们所有程序使用,下面内容就是我的模拟器进入 adb shell 后获取到的属性值,下面我从输出结果里面保留的一部分:

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
generic_x86:/ $ getprop
...
[dalvik.vm.heapsize]: [512m]
...
[dalvik.vm.usejit]: [true]
[dalvik.vm.usejitprofiles]: [true]
...
[init.svc.adbd]: [running]
...
[init.svc.gpu]: [running]
...
[init.svc.surfaceflinger]: [running]
...
[init.svc.zygote]: [running]
...
[ro.product.brand]: [google]
[ro.product.cpu.abi]: [x86]
...
[ro.serialno]: [EMULATOR29X2X1X0]
[ro.setupwizard.mode]: [DISABLED]
[ro.system.build.date]: [Sat Sep 21 05:19:49 UTC 2019]
...
// zygote 启动该启动哪个
[ro.zygote]: [zygote32]
[ro.zygote.disable_gl_preload]: [1]
[security.perf_harden]: [1]
[selinux.restorecon_recursive]: [/data/misc_ce/0]
...
[wifi.interface]: [wlan0]

属性服务相关代码在 SecondStageMain 阶段其实主要做了三件事:创建共享内存、加载各种属性值以及创建属性服务的 Socket。下面是这关于这几部分的片段:

PropertyInit初始化

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
/**
* 代码位于:system/core/init/property_service.cpp
**/
void PropertyInit() {
selinux_callback cb;
cb.func_audit = PropertyAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
//创建系统属性文件夹
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();
// 初始化system_property内存区域
// __system_property_area_init()这个函数,这里已经到了bionic包中.
// 我们就不再往下跟进了。
if (__system_property_area_init()) {
LOG(FATAL) << "Failed to initialize property area";
}
if (!property_info_area.LoadDefaultPath()) {
LOG(FATAL) << "Failed to load serialized property info file";
}

// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
ProcessKernelDt();
ProcessKernelCmdline();

// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
ExportKernelBootProps();

PropertyLoadBootDefaults();
}

分析到这里,系统属性的初始化终于分析完了,现在来总结下,系统属性初始化是使用了mmap的内存共享机制,来让其他进程来获取系统属性的。

那既然可以通过共享内存来访问,为什么还需要开启一个属性服务呢?直接通过共享内存来设置系统属性,不就好了么?

这里其实就涉及到安全性的问题了,如果所有进程都可以自由的修改系统属性,那系统属性,还能被成为系统属性吗?所以Android设计为,其他进程只能通过共享内存来获取系统属性,而“修改”的权限则统一收拢到init进程中,开启一个属性服务。

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
void StartPropertyService(int* epoll_socket) {
InitPropertySet("ro.property_service.version", "2");

int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
PLOG(FATAL) << "Failed to socketpair() between property_service and init";
}
*epoll_socket = from_init_socket = sockets[0];
init_socket = sockets[1];
StartSendingMessages();
// 创建socket通信
// 这个socket 就是用来处理系统属性。所有进程都通过它来修改共享内存里面的系统属性
if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, {});
result.ok()) {
property_set_fd = *result;
} else {
LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
}
// 开始注册监听 监听sokcet服务,最大并发数是8
listen(property_set_fd, 8);

auto new_thread = std::thread{PropertyServiceThread};
property_service_thread.swap(new_thread);
}

开启属性服务的重要步骤已经注释说明了,现在再来总结下:

系统服务开启流程

  1. 创建socket服务端
  2. 监听sokcet服务,最大并发数是8
  3. 注册到epoll的handler中进行IO优化处理

至此,init进程的主要工作流程和重要原理已分析完成

init.rc 解析

init.rc 是什么?它是非常重要的配置文件,而且众多 rc 文件中 init.rc 是最主要的文件,不过这里我不会讲 rc 文件的语法是怎么样的,因为 system/core/init/README.md 中已经写的很清楚了,init.rc 会根据 on 分成不同阶段,并且由 trigger 进行不同阶段的触发,而每个阶段里面就是一条条要执行指令,比如 start 后面跟的就是要启动的服务,mkdir 就是创建目录。

既然分成了多个阶段,那先来看看触发阶段是怎么样的: