启动概述#
Android 的第一个启动进程,init 的 PID 为 1,这个进程会解析 init.rc 来建构出系统的初始运作型态,其他的系统才会相继启动
启动电源&系统启动
当电源启动后,会从预定的地方(PC 读取 ROM),开始加载程序(BootLoader)到 RAM 中,并开始执行
引导程序 BootLoader
引导程序 BootLoader 是在 Android 系统开始运行前的一个小程序,主要功能是把系统 OS 拉起并运行
Linux 内核启动
当内核启动时,设定缓存、保护储存器、计划列表、加载驱动… 在内核完成系统设置后,它会在系统中找 init.rc 文件,并启动 init 进程
init 进程启动
该进程主要是作初始化&启动系统属性服务,也用来启动 Zygote 进程
Launcher App 应用(桌面应用程序)
init 进程概述#
init 作为 Android 系统启动的第一个进程,通过解析 init.rc 来陆续启动关键的系统服务进程,其有三个重点服务进程
ServiceManager(类似 DNS 功能,用来查找 SystemServer 中的服务句柄)
Zygote(初始进程,加载必要 Anroid resource、启动虚拟机… 等等)
SystemServer(启动系统级别服务,像是 AMS、WMS… 服务)
init 进程会启动 Zygote、SystemServer 两个进程,而 Zygote 使用 Socket 监听 AMS、ServiceManager 使用 Binder 通信
创建、挂载启动所需要的文件目录
初始化、启动性服务
解析 init.rc 配置文件并启动 Zygote 进程
init 进程 - main.cpp 分析#
// /init/main.cpp
#include "builtins.h"
#include "first_stage_init.h"
#include "init.h"
#include "selinux.h"
#include "subcontext.h"
#include "ueventd.h"
using namespace android::init;
... 省略部分
int main(int argc, char** argv) {
... 省略部分
// Boost prio which will be restored later
setpriority(PRIO_PROCESS, 0, -20);
if (!strcmp(basename(argv[0]), "ueventd")) { // ueventd 负责创建设备节点、权限、匿名共享內存... 等等
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
// 初始化 Logger
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
// Selinux 安全相关
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
// init 进程 (第二阶段)
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
// 启动 init 进程 (第一阶段)
return FirstStageMain(argc, argv);
}
init 进程第一阶段 - first_stage_init#FirstStageMain
文件挂载#
第一阶段主要在 1. 创建并挂载相关的文件,这里列出几个较为重要的文件系统,详细功能可看注释、2. 设定传入参数、3 执行 execv 函数(再次加载 main.cpp,但带入不同参数)
挂载的文件都在內存中(RAM),并实体文件
使用指令 | 挂载目录 / 文件名、档案管理 | 特性 | 说明 |
---|---|---|---|
mount | tmpfs | 存在 RAM 中,并不持久 (tmp 的取名) | 虚拟内存文件系统,它将所有的 文件存在虚拟内存中,将 tmpfs 卸载后,内部的资料就会全部消失 |
mount | /dev/devpts | 动态挂载 devpts | 为伪终端提供了一个标准,只要 pty 的主复合设备 /dev/ptmx 被开启,就会去/dev/pts 下动态创建一个新的 pty 设备文件 |
mount | proc | 可在运行时修改内核参数 | 虚拟文件系统,可以看做内核内部数据结构的接口 |
mount | sysfs | Linux 2.6 内核引入(主要针对 硬件相关参数,这就有关于 devpts 设备) | 虚拟文件系统,通常被挂载在 /sys 目录下 |
// first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers(); // init 错误时重启 Boot Loader
}
// 记录启动时间
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);
// 清除 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.
// mount 用來挂载 tmpfs 文件系统
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
// 创建资料夹,并赋予权限
CHECKCALL(mkdir("/dev/pts", 0755));
CHECKCALL(mkdir("/dev/socket", 0755));
CHECKCALL(mkdir("/dev/dm-user", 0755));
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
// 挂载proc
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);
// Don't expose the raw bootconfig to unprivileged processes.
chmod("/proc/bootconfig", 0440);
std::string bootconfig;
android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
// 8.0 增加了用户群组
gid_t groups[] = {AID_READPROC};
// 将群组加入到目前进程的设备中
CHECKCALL(setgroups(arraysize(groups), groups));
// 挂载 sys,使用sysfs 管理文档
CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
// 创建节点 (mknod) 用于输出 Log
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)));
// See storage config details athttp://source.android.com/devices/storage/
// 挂载 /mnt/{vendor,product},使用 tmpfs 管理
CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000"));
// /mnt/vendor 用于挂载vendor
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"));
// /second_stage_resources is used to preserve files from first to second
// stage init
CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"))
#undef CHECKCALL
SetStdioToDevNull(argv);
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";
}
... 省略部分
return 1;
}
挂载特定分区设备
// first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
... 省略部分
// 挂载特定分区设备
if (!DoFirstStageMount(!created_devices)) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
... 省略部分
}
SELinux 相关工作
// first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
... 省略部分
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);
}
// 初始化安全框架: Android Verified Boot & AVB 主要用于防止系统文件被篡改
// 也防止系统回滚的功能
SetInitAvbVersionInRecovery();
setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
1);
... 省略部分
}
透过 execv 启动 init 进程的 SecondStageMain 方法
// first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
... 省略部分
// 设定 init 进程参数
const char* path = "/system/bin/init";
const char* args[] = {path, "selinux_setup", nullptr};
// Binder open (log 讯息)
auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
// 传入上面的参数 args
// 启动 init 进程,传入参数为 "selinux_setup"
execv(path, const_cast<char**>(args)); // 通过 execv 启动 init 进程 的 SecondStageMain 方法
}
init 进程插曲 SE#
selinux.cpp#SetupSelinux
在第二阶段之前会先进行 SE 设定
在经过first_stage_init#FirstStageMain
文件挂载后,会透过 execv 函数再次启动 main.cpp 行程,再次进入main.cpp#main
方法
// /init/main.cpp 档案
#include "builtins.h"
#include "first_stage_init.h"
#include "init.h"
#include "selinux.h"
#include "subcontext.h"
#include "ueventd.h"
using namespace android::init;
... 省略部分
// 再次进入后,argv 就是带 selinux_setup 字串
int main(int argc, char** argv) {
... 省略部分
// Boost prio which will be restored later
setpriority(PRIO_PROCESS, 0, -20);
// 判断传入参数
if (!strcmp(basename(argv[0]), "ueventd")) { // ueventd 负责创建设备节点、权限、匿名共享内存等
return ueventd_main(argc, argv);
}
// 当参数数量 > 1 时
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
// 初始化 Log
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
// Selinux 安全相关
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
// init 进程 (第二阶段)
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
// 启动 init 进程 (第一阶段)
return FirstStageMain(argc, argv);
}
SetupSelinux 主要是在加载 SE 策略,加载 SE 完成后,就会设定新参数,再次透过 execv 启动 main.cpp(如上一小节说明)
// /init/selinux.cpp
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
// 1. 初始化核心的 Log
InitKernelLogging(argv);
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
// 2. 挂载遗失的系统区块
MountMissingSystemPartitions();
SelinuxSetupKernelLogging();
LOG(INFO) << "Opening SELinux policy";
PrepareApexSepolicy();
// Read the policy before potentially killing snapuserd.
std::string policy;
// 读取策略
ReadPolicy(&policy);
CleanupApexSepolicy();
auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();
if (snapuserd_helper) {
// Kill the old snapused to avoid audit messages. After this we cannot
// read from /system (or other dynamic partitions) until we call
// FinishTransition().
snapuserd_helper->StartTransition();
}
//Load SE 策略
LoadSelinuxPolicy(policy);
... 省略部分
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
// 再次启动该进程
execv(path, const_cast<char**>(args));
return 1;
}
init 进程第二阶段#
init.cpp#SecondStageMain 解析 init.rc
经过 first_stage_init 的初始化挂载设备后,就会执行到 main.cpp 类的 SecondStageMain 函数(可以看做 init 进城的第二阶段)
// /init/main.cpp 档案
#include "builtins.h"
#include "first_stage_init.h"
#include "init.h"
#include "selinux.h"
#include "subcontext.h"
#include "ueventd.h"
using namespace android::init;
... 省略部分
// 再次进入后,argv 就是带 second_stage 字串
int main(int argc, char** argv) {
... 省略部分
// Boost prio which will be restored later
setpriority(PRIO_PROCESS, 0, -20);
// 判断传入参数
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
// 初始化 Log
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
// Selinux 安全相关
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv);
}
// init 进程 (第二阶段)
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
// 启动 init 进程 (第一阶段段)
return FirstStageMain(argc, argv);
}
开始分析init.cpp#SecondStageMai
n,分为 10 个阶段,详细请看注释
初始化属性域,主要就是做 PropertyInit 函数
// init/init.cpp
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers(); // init 失败时重新启动 Boot Loader
}
// 启动时间
boot_clock::time_point start_time = boot_clock::now();
trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
// 把标准输入、标准输出、错误重新定向到空设备文件中 `/dev/null`
SetStdioToDevNull(argv);
// 初始化 kernel log 系统
InitKernelLogging(argv);
// 初始化系统 $PATH 路径
if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
}
// init 进程并不依赖其他进程,所以不会 Crush,所以不该收到信号
// 所以这边重新设定收到信号时要处理的函数为null
{
// sigaction 就是信号
struct sigaction action = {.sa_flags = SA_RESTART};
action.sa_handler = [](int) {};
// 忽略 SIGPIPE 信号
sigaction(SIGPIPE, &action, nullptr); // 将 SIGPIPE 置 为 null
}
// 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();
}
// 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.
// 调用 syscall 设定相关参数
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
// Indicate that booting is in progress to background fw loaders, etc.
// 创建 /dev/.booting 文件,标记 booting 正在进行中
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
... 省略部分
PropertyInit(); // 实现在 init/property_service.cpp
... 省略部分
return 0;
}
清空环境变量:清除之前写到系统属性中的环境变数
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// 清除之前写到系统属性中的环境变量
// Clean up our environment.
unsetenv("INIT_AVB_VERSION");
unsetenv("INIT_FORCE_DEBUGGABLE");
... 省略部分
}
SELinux 相关工作
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging(); // 完成第二阶段的 selinux 工作
SelabelInitialize(); // 注册部分处理器
SelinuxRestoreContext(); // SELinux存储的 Context
... 省略部分
}
创建 epoll 事件通知
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
Epoll epoll;
// epoll.Open() 创建子进程 I/O 监听
if (auto result = epoll.Open(); !result.ok()) {
PLOG(FATAL) << result.error();
}
... 省略部分
}
装载子进程信号处理器:init 是一个守护进程(可理解为守护线程),以该进程为主,若该 init 进程结束,其他子进程都会结束
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// 创建 handler 处理子进程终止信号,注册一个信号到 epoll 监听(若有所改变就会 epoll)
// epoll 会先收到通知,再用 socket 读取资料
// 为了防止 init 进程的子进程变成僵尸进程
InstallSignalFdHandler(&epoll);
InstallInitNotifier(&epoll);
... 省略部分
}
启动属性服务:主要函数 StartPropertyService,启动一个 socket 监听属性更改
// /init/init.cpp
static int property_fd = -1;
int SecondStageMain(int argc, char** argv) {
... 省略部分
// 启动其他属性服务
StartPropertyService(&property_fd);
yiiu
unsetenv("INIT_AVB_VERSION"); // 环境变量移除IT_AVB_VERSION
fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status(); // 最终决定"ro.boot.flash.locked" 的值
MountHandler mount_handler(&epoll);
// 为 USB 存储设置 UDC Controller (sys/class/udc)
SetUsbController();
... 省略部分
}
匹配命令 & 函数之间对应关系:Cmd & Function Map,从这可以看出系统支持哪些 cmd 指令,还有指令实际运作的方法
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// 创建 Function & Cmd 的 Map
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
// 在 Action 中保存 function_map 对象
// Action::set_function_map,`::` 相当于 static Function
Action::set_function_map(&function_map);
if (!SetupMountNamespaces()) {
PLOG(FATAL) << "SetupMountNamespaces failed";
}
... 省略部分
}
LoadBootScripts: 解析 init.rc 档案
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// 之后会使用该对象取得分析结果
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
// 解析脚本
LoadBootScripts(am, sm);
... 省略部分
}
// 解析 .rc 脚本
static void LoadBootScripts(ActionManager& action_manager,
ServiceList& service_list) {
// 创建解析器
Parser parser = CreateParser(action_manager, service_list);
// 预设解析 ro.boot.init_rc
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
// 解析指定 init.rc 档案
parser.ParseConfig("/system/etc/init/hw/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
添加触发事件:这边可以看到 rc 档的一些设定,其中常见的指令包括,1 early-init、2 init、3 late-init
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
// ActionManager 解析完成
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
// 触发标签 early-init
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(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");
// 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");
... 省略部分
}
进入循环:不断处理接收到的命令 & 执行 Service
// /init/init.cpp
int SecondStageMain(int argc, char** argv) {
... 省略部分
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);
shutdown_state.set_do_shutdown(false);
}
... 省略部分
// 不必等待 && Service 没有正在 running || Service 是运行完毕
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
am.ExecuteOneCommand(); // 执行一个 cmd
}
... 省略部分
}
}