【Android关键流程】Init启动
Init 进程是 Android 系统中用户空间的第一个进程(pid=1),它是用户进程的鼻祖,负责孵化各种属性服务、守护进程也包括 Zygote。Init 是由多个源文件共同组成的,这些文件位于。ueventd:实际上就是 Init 程序的软链接,在 init.rc 的 early-init 阶段启动selinux_setup:FitstStageMain 中启动subcontext:Secon
文章目录
Init 介绍
Init 进程是 Android 系统中用户空间的第一个进程(pid=1),它是用户进程的鼻祖,负责孵化各种属性服务、守护进程也包括 Zygote。Init 是由多个源文件共同组成的,这些文件位于 /system/core/init。
启动过程
Kernel 启动找到 Init 进程后,进程入口为源码 init 目录下的 main.cpp 的 main()。
/system/core/init/main.cpp
int main(int argc, char** argv) {
...
// init进程创建子进程ueventd,负责设备节点的创建、权限设定等
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
// 初始化日志系统
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
return SetupSelinux(argv); // Step2,对Selinux初始化
}
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv); // Step3,解析init.rc文件、提供服务、创建epoll与处理子进程的终止等
}
}
return FirstStageMain(argc, argv); // Step1,挂载相关文件系统
}
FirstStageMain
/system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {
// 将各种信号量,如SIGABRT,SIGBUS等的行为设置为SA_RESTART,
// 一旦监听到这些信号,即执行重启系统
if (REBOOT_BOOTLOADER_ON_PANIC) {
// 处理Init进程挂掉的情况,会重启bootloader
InstallRebootSignalHandlers();
}
...
// 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.
// 挂载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));
// 挂载devpts文件系统
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
// 不要将原始命令行公开给非特权进程,root也只有只读权限
CHECKCALL(chmod("/proc/cmdline", 0440));
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
// 不要将原始bootconfig公开给非特权进程
chmod("/proc/bootconfig", 0440);
std::string bootconfig;
android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
gid_t groups[] = {AID_READPROC};
CHECKCALL(setgroups(arraysize(groups), groups)); // 设置用户组
// 挂载sysfs文件系统
CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
// 提前创建了kmsg设备节点文件,用于输出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)));
}
// 创建Linux伪随机设备
CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
// log wrapper所必需的,需要在ueventd运行之前被调用
CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
// 在第一阶段挂tmpfs、mnt/vendor、mount/product分区。
// 其他的分区在第二阶段通过rc文件解析来加载
CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000"));
// 创建可供读写的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"))
// First stage init stores Mainline sepolicy here.
CHECKCALL(mkdir("/dev/selinux", 0744));
#undef CHECKCALL
// 将内核的stdin/stdout/stderr全部重定向/dev/null,关闭默认控制台输出
SetStdioToDevNull(argv);
// tmpfs已经挂载到/dev上,同时也挂载了/dev/kmsg,可以和外界沟通了
// 初始化日志系统
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!";
...
// 如果该文件存在且设备已经解锁,则允许adb root指令(userdebug sepolicy)
if (access("/force_debuggable", F_OK) == 0) {
constexpr const char adb_debug_prop_src[] = "/adb_debug.prop";
constexpr const char userdebug_plat_sepolicy_cil_src[] = "/userdebug_plat_sepolicy.cil";
std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
if (access(adb_debug_prop_src, F_OK) == 0 &&
!fs::copy_file(adb_debug_prop_src, kDebugRamdiskProp, ec)) {
LOG(WARNING) << "Can't copy " << adb_debug_prop_src << " to " << kDebugRamdiskProp
<< ": " << ec.message();
}
if (access(userdebug_plat_sepolicy_cil_src, F_OK) == 0 &&
!fs::copy_file(userdebug_plat_sepolicy_cil_src, kDebugRamdiskSEPolicy, ec)) {
LOG(WARNING) << "Can't copy " << userdebug_plat_sepolicy_cil_src << " to "
<< kDebugRamdiskSEPolicy << ": " << ec.message();
}
// setenv for second-stage init to read above kDebugRamdisk* files.
setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
}
...
// 挂载system、cache、data等系统分区
if (!DoFirstStageMount(!created_devices)) {
LOG(FATAL) << "Failed to mount required partitions early ...";
}
...
// 进入下一步,SetupSelinux
const char* path = "/system/bin/init"; // 找到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(path, const_cast<char**>(args)); // 通过execv来启动Init进程
// 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;
}
主要通过 mount 挂载对应的文件系统,mkdir 创建对应的文件目录,并配置相应的访问权限。
这些文件只是在应用运行的时候存在,一旦应用运行结束就会随着应用一起消失。
挂载的文件系统主要有四类:
tmpfs:一种虚拟内存文件系统,它会将所有的文件存储在虚拟内存中。由于 tmpfs 是驻留在 RAM 的,因此它的内容是不持久的。断电后,tmpfs 的内容就消失了。devpts:为伪终端提供了一个标准接口,它的标准挂接点是/dev/pts。只要 pty 的主复合设备/dev/ptmx被打开,就会在/dev/pts下动态地创建一个新的 pty 设备文件。proc:也是一个虚拟文件系统,它可以看作是内核内部数据结构的接口,通过它我们可以获得系统的信息,同时也能够在运行时修改特定的内核参数。sysfs:与 proc 文件系统类似,也是一个不占有任何磁盘空间的虚拟文件系统。它通常被挂接在/sys目录下。作用是把系统的设备和总线按层次组织起来,使得它们可以在用户空间读取,用来向用户空间导出内核的数据结构和属性。
在 FirstStageMain 还会通过 InitKernelLogging(argv) 来初始化 log 日志系统。此时 Android 还没有自己的系统日志,采用 kernel 的 log 系统,打开的设备节点 /dev/kmsg, 可通过 cat /dev/kmsg 来获取内核 log。
最后会通过 execv 方法传递对应的 path 与下一阶段的参数 selinux_setup。
SetupSelinux
/system/core/init/selinux.cpp
int SetupSelinux(char** argv) {
...
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
execv(path, const_cast<char**>(args));
return 1;
}
SetupSelinux 方法中加载了 selinux 的策略并启动 selinux 的强制模式,然后启动了 Init 进程。Init 的二进制文件存放在机器的 /system/bin/init,然后通过 execv 启动 Init 进程第二阶段。
SecondStageMain
int SecondStageMain(int argc, char** argv) {
...
SetStdioToDevNull(argv);
// 初始化本阶段内核日志
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
...
// 禁止OOM Killer杀死该进程以及它的子进程
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);
// 创建/dev/.booting文件,一个标记,表示booting进行中
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
...
// 初始化属性服务,并从指定文件读取属性
// 使用mmap共享内存,/dev/__properties__/property_info
PropertyInit();
...
// 进行Selinux第二阶段,并恢复一些文件安全上下文
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();
// 初始化epoll,android这里对epoll做了一层封装
Epoll epoll;
if (auto result = epoll.Open(); !result.ok()) {
PLOG(FATAL) << result.error();
}
// 使用epoll对Init子进程的信号进行监听
// epoll中注册signalfd,主要是为了创建handler处理子进程终止信号
InstallSignalFdHandler(&epoll);
InstallInitNotifier(&epoll);
// 开启属性服务,并注册到epoll中
StartPropertyService(&property_fd);
...
// 会执行/system/bin/init subcontext
InitializeSubcontext();
// 为解析init.rc中的action和service做准备
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
// 加载系统启动脚本init.rc
LoadBootScripts(am, sm);
...
// cgroups用于控制资源,cpuset相关
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
// 执行rc文件中触发器为on early-init的语句
am.QueueEventTrigger("early-init");
// 等冷插拔设备初始化完成
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");
// 执行rc文件中触发器为on init的语句
am.QueueEventTrigger("init");
// 当设备处于充电模式时,不需要mount文件系统或者启动系统服务。
// 充电模式下,将charger设为执行队列,否则把late-init设为执行队列
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// 基于属性当前状态,运行所有的属性触发器
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
// Restore prio before main loop
setpriority(PRIO_PROCESS, 0, 0);
while (true) {
// 进入死循环状态
auto epoll_timeout = std::optional<std::chrono::milliseconds>{kDiagnosticTimeout};
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
LOG(INFO) << "Got shutdown_command '" << *shutdown_command
<< "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
shutdown_state.set_do_shutdown(false);
}
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()) {
...
}
}
return 0;
}
LoadBootScripts() 会加载 init.rc 配置文件,之后加载 /{system, vendor, odm}/etc/init/ 下(Android 设备中的目录)的所有 rc 配置文件。
LoadBootScripts
/system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
// 初始化ServiceParse、ActionParser、ImportParser三个解析器
Parser parser = CreateParser(action_manager, service_list);
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");
}
// late_import is available only in Q and earlier release. As we don't
// have system_ext in those versions, skip late_import for system_ext.
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);
}
}
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
Parser parser;
// 加载解析Service语句的解析器
parser.AddSectionParser("service", std::make_unique<ServiceParser>(
&service_list, GetSubcontext(), std::nullopt));
// 加载解析on语句的解析器
parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
// 加载解析import语句的解析器
parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));
return parser;
}
init.rc
init.rc 有两个,分别位于:
/system/core/rootdir/init.rc,正常启动
/bootable/recovery/etc/init.rc,刷机
import
import /init.environ.rc # 导入全局环境变量
import /system/etc/init/hw/init.usb.rc # adb服务、USB相关内容的定义
import /init.${ro.hardware}.rc # 硬件相关的初始化,一般是厂商定制
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /system/etc/init/hw/init.usb.configfs.rc
import /system/etc/init/hw/init.${ro.zygote}.rc # 定义Zygote服务
init.rc 中会根据系统的不同属性来引入不同的 zygote 脚本。
- init.zygote32.rc:zygote 进程对应的执行程序是 app_process(纯32bit模式)
- init.zygote64.rc:zygote 进程对应的执行程序是 app_process(纯64bit模式)
- init.zygote64_32.rc:启动两个 zygote 进程(zygote 和 zygote_secondary),对应的执行程序分别是 app_process64(主模式),app_process32
on early-init
on early-init
...
start ueventd
exec_start apexd-bootstrap
...
early-init 中启动了 ueventd 服务和 apex 相关服务。
-
ueventd服务
service ueventd # ueventd服务的可执行文件的路径为/system/bin/ueventd class core # ueventd归属于core class,同样归属于core class的还有adbd、console等服务 critical # 表明这个Service对设备至关重要,如果Service在四分钟内退出超过4次,则设备将重启进入恢复模式。 seclabel u:r:ueventd:s0 # selinux相关的配置 shutdown critical # ueventd服务关闭行为 -
early-init触发时机
/system/core/init.init.cpp$SecondStageMain am.QueueEventTrigger("early-init");
on init
on init
...
start logd. # 用于保存Android运行期间的日志
...
start servicemanager # Android系统服务管理者,负责查询和注册服务
...
on late-init
on late-init
# 启动vold服务(管理和控制Android平台外部存储设备,包括SD插拔、挂载、卸载、格式化等)
trigger early-fs
trigger factory-fs
trigger fs
trigger post-fs
trigger late-fs
# 挂载/data,启动apexd服务
trigger post-fs-data
# Should be before netd, but after apex, properties and logging is available.
trigger load_bpf_programs
# 启动zygote服务,在启动zygote服务之前会先启动netd服务(专门负责网络管理和控制的后台守护进程)
trigger zygote-start
# 移除/dev/.booting文件
trigger firmware_mounts_complete
trigger early-boot
trigger boot
trigger mmi
-
zygote
/system/core/rootdir/init.zygote64.rc service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server class main priority -20 user root group root readproc reserved_disk socket zygote stream 660 root system socket usap_pool_primary stream 660 root system onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse onrestart write /sys/power/state on onrestart restart audioserver onrestart restart cameraserver onrestart restart media onrestart restart media.tuner onrestart restart netd onrestart restart wificond task_profiles ProcessCapacityHigh critical window=${zygote.critical_window.minute:-off} target=zygote-fatal该文件通过 service 语句来创建 zygote 进程,该进程的代码位于
/system/bin/app_process目录下。当相关的触发器被触发后,便会启动 zygote 进程。
总结
-
Init 入口中包含5个分支:
- ueventd:实际上就是 Init 程序的软链接,在 init.rc 的 early-init 阶段启动
- selinux_setup:FitstStageMain 中启动
- subcontext:SecondStageMain 中启动
- second_stage:在 selinux_setup 启动完之后执行
- first_stage:默认首先执行
-
Init 进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用 SELinux 安全策略。
Init 进程第二阶段的主要工作是初始化属性系统,解析 SELinux 的匹配规则,处理子进程终止信号,启动系统属性服务。
Init 进程第三阶段主要是解析 init.rc 来启动其他进程,进入死循环,进行子进程实时监控。
-
在 SecondStage,首先加载 init.rc 配置,然后再依次在 Android 设备中查找以下三种配置,并加载:
/system/etc/init//vendor/etc/init//odm/etc/init/
-
Android 根文件系统的镜像中不存在
/dev目录,该目录是 Init 进程启动后动态创建的。为此,Init 进程创建子进程 ueventd,并将创建设备节点文件的工作托付给 ueventd。ueventd 通过两种方式创建设备节点文件:
-
冷插拔(Cold Plug)
以预先定义的设备信息为基础,当 ueventd 启动后,统一创建设备节点文件。这一类设备节点文件也被称为静态节点文件。
-
热插拔(Hot Plug)
在系统运行中,当有设备插入 USB 端口时,ueventd 就会接收到这一事件,为插入的设备动态创建设备节点文件。这一类设备节点文件也被称为动态节点文件。
-
-
signal:每个进程在处理其他进程发送的 signal 信号时都需要先注册,当进程的运行状态改变或终止时会产生某种 signal 信号,Init 进程是所有用户空间进程的父进程,当其子进程终止时产生 signal 信号,以便父进程进行处理,主要是为了防止子进程成为僵尸进程。
僵尸进程:父进程使用 fork 创建子进程,子进程终止后,如果父进程不知道子进程已经终止的话,这时子进程虽然已经退出,但是在系统进程表中还为它保留了一些信息(如进程号、运行时间、退出状态等),这个子进程就是僵尸进程。系统进程表是一项有限的资源,如果它被僵尸进程耗尽的话,系统可能会无法创建新的进程。
更多推荐


所有评论(0)