We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本文基于 Android 14.0.0_r2 的系统启动流程分析。
init 进程属于一个守护进程,准确的说,它是 Linux 系统中用户控制的第一个进程,它的进程号为 1,它的生命周期贯穿整个 Linux 内核运行的始终。Android 中所有其它的进程共同的鼻祖均为 init 进程。
可以通过 adb shell ps | grep init 命令来查看 init 的进程号:
adb shell ps | grep init
wutianhao@wutianhao-Ubuntu:~$ adb shell ps | grep init root 1 0 10858080 932 0 0 S init
init 入口函数是 main.cpp,它把各个阶段的操作分离开来,使代码更加简洁:
/system/core/init/main.cpp int main(int argc, char** argv) { ... // 设置进程最高优先级 -20最高,20最低 setpriority(PRIO_PROCESS, 0, -20); // 当 argv[0] 的内容为 ueventd 时,strcmp的值为 0,!strcmp 为 1; // 1 表示 true,也就执行 ueventd_main; // ueventd 主要是负责设备节点的创建、权限设定等一些列工作。 if (!strcmp(basename(argv[0]), "ueventd")) { return ueventd_main(argc, argv); } if (argc > 1) { // 参数为 subcontext,初始化日志系统。 if (!strcmp(argv[1], "subcontext")) { android::base::InitLogging(argv, &android::base::KernelLogger); const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap(); return SubcontextMain(argc, argv, &function_map); } // 参数为 selinux_setup,启动 SELinux 安全策略 if (!strcmp(argv[1], "selinux_setup")) { return SetupSelinux(argv); } // 参数为 second_stage,启动 init 进程第二阶段 if (!strcmp(argv[1], "second_stage")) { return SecondStageMain(argc, argv); } } // 默认启动 init 进程第一阶段 return FirstStageMain(argc, argv); }
main 函数有四个参数入口:
main 函数的执行顺序:
ueventd_main 函数是 Android 系统中 ueventd 服务的主入口函数,负责处理和响应来自 Linux 内核的 uevents(设备事件)。
ueventd
源码路径:/system/core/init/ueventd.cpp
初始化
int ueventd_main(int argc, char** argv) { umask(000); android::base::InitLogging(argv, &android::base::KernelLogger); ... }
SELinux 设置
int ueventd_main(int argc, char** argv) { ... SelinuxSetupKernelLogging(); SelabelInitialize(); ... }
SelinuxSetupKernelLogging()
SelabelInitialize()
创建 UeventHandler 对象
int ueventd_main(int argc, char** argv) { ... std::vector<std::unique_ptr<UeventHandler>> uevent_handlers; auto ueventd_configuration = GetConfiguration(); uevent_handlers.emplace_back(std::make_unique<DeviceHandler>( std::move(ueventd_configuration.dev_permissions), std::move(ueventd_configuration.sysfs_permissions), std::move(ueventd_configuration.subsystems), android::fs_mgr::GetBootDevices(), true)); uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>( std::move(ueventd_configuration.firmware_directories), std::move(ueventd_configuration.external_firmware_handlers))); if (ueventd_configuration.enable_modalias_handling) { std::vector<std::string> base_paths = {"/odm/lib/modules", "/vendor/lib/modules"}; uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>(base_paths)); } ... }
初始化 UeventListener
int ueventd_main(int argc, char** argv) { ... UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size); ... }
冷启动处理
int ueventd_main(int argc, char** argv) { ... if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) { ColdBoot cold_boot(uevent_listener, uevent_handlers, ueventd_configuration.enable_parallel_restorecon, ueventd_configuration.parallel_restorecon_dirs); cold_boot.Run(); } ... }
冷启动完成通知
int ueventd_main(int argc, char** argv) { ... for (auto& uevent_handler : uevent_handlers) { uevent_handler->ColdbootDone(); } ... }
信号处理
int ueventd_main(int argc, char** argv) { ... signal(SIGCHLD, SIG_IGN); while (waitpid(-1, nullptr, WNOHANG) > 0) { } ... }
恢复优先级
int ueventd_main(int argc, char** argv) { ... setpriority(PRIO_PROCESS, 0, 0); ... }
setpriority(PRIO_PROCESS, 0, 0)
主循环
int ueventd_main(int argc, char** argv) { ... uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) { for (auto& uevent_handler : uevent_handlers) { uevent_handler->HandleUevent(uevent); } return ListenerAction::kContinue; }); ... }
总结:ueventd_main 函数在 Android 启动过程中扮演着核心角色,它负责监听和处理与硬件设备状态变化相关的事件,确保系统能够正确识别并响应设备添加、移除或属性更改等操作,从而使得设备驱动和用户空间能够有效地交互。
ueventd_main
FirstStageMain 是 Android 系统启动流程中第一阶段初始化的主入口函数,它负责在系统启动早期进行一系列关键的系统设置和挂载操作。
源码路径:/system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) { if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } ... }
REBOOT_BOOTLOADER_ON_PANIC
时间戳记录与错误检查宏
int FirstStageMain(int argc, char** argv) { ... 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); ... }
CHECKCALL(x)
x
设置文件或目录的默认权限
int FirstStageMain(int argc, char** argv) { ... umask(0); ... }
环境清理与基本文件系统准备
int FirstStageMain(int argc, char** argv) { ... CHECKCALL(clearenv()); CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1)); 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) CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC))); #undef MAKE_STR CHECKCALL(chmod("/proc/cmdline", 0440)); std::string cmdline; android::base::ReadFileToString("/proc/cmdline", &cmdline); chmod("/proc/bootconfig", 0440); std::string bootconfig; android::base::ReadFileToString("/proc/bootconfig", &bootconfig); 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)); ... }
/dev
/dev/pts
/dev/socket
/dev/dm-user
特殊设备节点创建
int FirstStageMain(int argc, char** argv) { ... 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))); CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2))); CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3))); CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, "mode=0755,uid=0,gid=1000")); CHECKCALL(mkdir("/mnt/vendor", 0755)); CHECKCALL(mkdir("/mnt/product", 0755)); CHECKCALL(mount("tmpfs", "/debug_ramdisk", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, "mode=0755,uid=0,gid=0")); CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, "mode=0755,uid=0,gid=0")) #undef CHECKCALL ... }
日志初始化与权限控制
int FirstStageMain(int argc, char** argv) { ... SetStdioToDevNull(argv); InitKernelLogging(argv); ... }
挂载特定临时文件系统
int FirstStageMain(int argc, char** argv) { ... 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(); } ... }
/mnt
/mnt/vendor
/mnt/product
/debug_ramdisk
模块加载及计时
int FirstStageMain(int argc, char** argv) { ... auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0; auto want_parallel = bootconfig.find("androidboot.load_modules_parallel = \"true\"") != std::string::npos; boot_clock::time_point module_start_time = boot_clock::now(); int module_count = 0; if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console, want_parallel, module_count)) { if (want_console != FirstStageConsoleParam::DISABLED) { LOG(ERROR) << "Failed to load kernel modules, starting console"; } else { LOG(FATAL) << "Failed to load kernel modules"; } } if (module_count > 0) { auto module_elapse_time = std::chrono::duration_cast<std::chrono::milliseconds>( boot_clock::now() - module_start_time); setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1); LOG(INFO) << "Loaded " << module_count << " kernel modules took " << module_elapse_time.count() << " ms"; } ... }
创建设备节点与控制台启动
int FirstStageMain(int argc, char** argv) { ... bool created_devices = false; if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) { if (!IsRecoveryMode()) { created_devices = DoCreateDevices(); if (!created_devices) { LOG(ERROR) << "Failed to create device nodes early"; } } StartConsole(cmdline); } ... }
ramdisk 属性复制
int FirstStageMain(int argc, char** argv) { ... if (access(kBootImageRamdiskProp, F_OK) == 0) { std::string dest = GetRamdiskPropForSecondStage(); std::string dir = android::base::Dirname(dest); std::error_code ec; if (!fs::create_directories(dir, ec) && !!ec) { LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message(); } if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) { LOG(FATAL) << "Can't copy " << kBootImageRamdiskProp << " to " << dest << ": " << ec.message(); } LOG(INFO) << "Copied ramdisk prop to " << dest; } ... }
调试模式支持
int FirstStageMain(int argc, char** argv) { ... 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; 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("INIT_FORCE_DEBUGGABLE", "true", 1); } ... }
切换根文件系统
int FirstStageMain(int argc, char** argv) { ... if (ForceNormalBoot(cmdline, bootconfig)) { mkdir("/first_stage_ramdisk", 0755); PrepareSwitchRoot(); if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) { PLOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself"; } SwitchRoot("/first_stage_ramdisk"); } ... }
SwitchRoot()
完成第一阶段挂载
int FirstStageMain(int argc, char** argv) { ... if (!DoFirstStageMount(!created_devices)) { LOG(FATAL) << "Failed to mount required partitions early ..."; } ... }
DoFirstStageMount()
释放旧 ramdisk 资源
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); } ... }
其他系统设置
int FirstStageMain(int argc, char** argv) { ... SetInitAvbVersionInRecovery(); setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
进入 SetupSelinux
int FirstStageMain(int argc, char** argv) { ... 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(path, const_cast<char**>(args)); PLOG(FATAL) << "execv(\"" << path << "\") failed"; return 1; }
总结:整个 FirstStageMain 函数确保了 Android 系统在初始启动阶段能够正确地设置文件系统结构、加载必需的内核模块、建立基础设备节点以及安全地切换到下一阶段的初始化流程。
FirstStageMain
SetupSelinux 是 Android 系统启动流程中用于设置和初始化 SELinux 环境的关键函数。
源码路径:/system/core/init/selinux.cpp
标准输入输出重定向与内核日志初始化
int SetupSelinux(char** argv) { SetStdioToDevNull(argv); InitKernelLogging(argv); ... }
SetStdioToDevNull(argv)
/dev/null
InitKernelLogging(argv)
int SetupSelinux(char** argv) { ... if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } ... }
计时并挂载分区
int SetupSelinux(char** argv) { ... boot_clock::time_point start_time = boot_clock::now(); MountMissingSystemPartitions(); ... }
MountMissingSystemPartitions()
SELinux 内核日志设置
int SetupSelinux(char** argv) { ... SelinuxSetupKernelLogging(); ... }
准备和读取SELinux策略
int SetupSelinux(char** argv) { ... PrepareApexSepolicy(); std::string policy; ReadPolicy(&policy); CleanupApexSepolicy(); ... }
PrepareApexSepolicy
管理 snapuserd 守护进程
int SetupSelinux(char** argv) { ... auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded(); if (snapuserd_helper) { snapuserd_helper->StartTransition(); } ... }
加载 SELinux 策略
int SetupSelinux(char** argv) { ... LoadSelinuxPolicy(policy); ... }
完成 snapuserd 的 SELinux 上下文转换
int SetupSelinux(char** argv) { ... if (snapuserd_helper) { snapuserd_helper->FinishTransition(); snapuserd_helper = nullptr; } ... }
恢复上下文与设置强制执行模式
int SetupSelinux(char** argv) { ... if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) { PLOG(FATAL) << "restorecon failed of /dev/selinux failed"; } SelinuxSetEnforcement(); ... }
/dev/selinux/
SelinuxSetEnforcement
针对 init 进程进行额外的上下文恢复
int SetupSelinux(char** argv) { ... if (selinux_android_restorecon("/system/bin/init", 0) == -1) { PLOG(FATAL) << "restorecon failed of /system/bin/init failed"; } ... }
设置环境变量
int SetupSelinux(char** argv) { ... setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1); ... }
执行第二阶段 init 进程
int SetupSelinux(char** argv) { ... const char* path = "/system/bin/init"; const char* args[] = {path, "second_stage", nullptr}; execv(path, const_cast<char**>(args)); PLOG(FATAL) << "execv(\"" << path << "\") failed"; return 1; }
总结:通过 SetupSelinux 函数的执行,Android 系统能够在启动时正确地建立和启用 SELinux 安全机制,为后续系统的运行提供安全保障。
SecondStageMain 函数是 Android 系统启动过程中的第二个阶段,主要负责更深层次的系统初始化工作。
源码路径:/system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) { if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } boot_clock::time_point start_time = boot_clock::now(); trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); }; SetStdioToDevNull(argv); InitKernelLogging(argv); LOG(INFO) << "init second stage started!"; SelinuxSetupKernelLogging(); ... }
资源准备与清理
int SecondStageMain(int argc, char** argv) { ... if (setenv("PATH", _PATH_DEFPATH, 1) != 0) { PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage"; } { struct sigaction action = {.sa_flags = SA_RESTART}; action.sa_handler = [](int) {}; sigaction(SIGPIPE, &action, nullptr); } 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); close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000)); 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"); if (!load_debug_prop) { UmountDebugRamdisk(); } ... }
/dev/.booting
系统服务和属性初始化
int SecondStageMain(int argc, char** argv) { ... PropertyInit(); UmountSecondStageRes(); if (load_debug_prop) { UmountDebugRamdisk(); } MountExtraFilesystems(); SelabelInitialize(); SelinuxRestoreContext(); ... }
事件循环与回调设置
int SecondStageMain(int argc, char** argv) { ... Epoll epoll; if (auto result = epoll.Open(); !result.ok()) { PLOG(FATAL) << result.error(); } epoll.SetFirstCallback(ReapAnyOutstandingChildren); InstallSignalFdHandler(&epoll); InstallInitNotifier(&epoll); StartPropertyService(&property_fd); ... }
关键数据记录与设置
int SecondStageMain(int argc, char** argv) { ... RecordStageBoottimes(start_time); if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) { SetProperty("ro.boot.avb_version", avb_version); } unsetenv("INIT_AVB_VERSION"); ... }
系统特定功能
int SecondStageMain(int argc, char** argv) { ... fs_mgr_vendor_overlay_mount_all(); export_oem_lock_status(); MountHandler mount_handler(&epoll); SetUsbController(); SetKernelVersion(); 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(); LoadBootScripts(am, sm); ... }
初始化命名空间与控制组
int SecondStageMain(int argc, char** argv) { ... if (false) DumpState(); 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); ... }
调度内置动作
int SecondStageMain(int argc, char** argv) { ... am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups"); am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict"); am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux"); am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd"); am.QueueEventTrigger("early-init"); am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done"); 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"); am.QueueEventTrigger("init"); ... }
启动与触发事件
int SecondStageMain(int argc, char** argv) { ... 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"); ... }
int SecondStageMain(int argc, char** argv) { ... while (true) { const boot_clock::time_point far_future = boot_clock::time_point::max(); boot_clock::time_point next_action_time = far_future; auto shutdown_command = shutdown_state.CheckShutdown(); if (shutdown_command) { LOG(INFO) << "Got shutdown_command '" << *shutdown_command << "' Calling HandlePowerctlMessage()"; HandlePowerctlMessage(*shutdown_command); } if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) { am.ExecuteOneCommand(); if (am.HasMoreCommands()) { next_action_time = boot_clock::now(); } } if (!IsShuttingDown()) { auto next_process_action_time = HandleProcessActions(); if (next_process_action_time) { next_action_time = std::min(next_action_time, *next_process_action_time); } } std::optional<std::chrono::milliseconds> epoll_timeout; if (next_action_time != far_future) { epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>( std::max(next_action_time - boot_clock::now(), 0ns)); } auto epoll_result = epoll.Wait(epoll_timeout); if (!epoll_result.ok()) { LOG(ERROR) << epoll_result.error(); } if (!IsShuttingDown()) { HandleControlMessages(); SetUsbController(); } } ... }
总结:SecondStageMain 函数对 Android 系统进行全面深入的初始化,包括设置系统资源、挂载文件系统、启动关键服务、初始化安全性组件以及执行启动脚本等。整个函数通过事件循环持续监控并响应系统内部及外部事件,直至系统完全启动就绪。
SecondStageMain
init 是一个守护进程,为了防止 init 的子进程成为僵尸进程(zombie process),需要 init 在子进程在结束时获取子进程的结束码,通过结束码将程序表中的子进程移除,防止成为僵尸进程的子进程占用程序表的空间(程序表的空间达到上限时,系统就不能再启动新的进程了,会引起严重的系统问题)。
子进程重启流程如下图所示:
信号处理主要工作:
注意:EPOLL 类似于 POLL,是 Linux 中用来做事件触发的,跟 EventBus 功能差不多。Linux 很长的时间都在使用 select 来做事件触发,它是通过轮询来处理的,轮询的 fd 数目越多,自然耗时越多,对于大量的描述符处理,EPOLL 更有优势。
InstallSignalFdHandler
InstallSignalFdHandler 用于在 Linux 系统中设置信号处理。
static void InstallSignalFdHandler(Epoll* epoll) { // 设置一个默认的 SIGCHLD 信号处理器,并使用 sigaction 函数应用了 SA_NOCLDSTOP 标志。这可以防止当子进程停止或继续时,signalfd 接收到 SIGCHLD 信号。 const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP }; sigaction(SIGCHLD, &act, nullptr); // 创建一个信号集,将 SIGCHLD 信号添加到该集中。 sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); // 如果当前进程没有重启能力(可能是在容器中运行),那么它还会将 SIGTERM 信号添加到信号集中。这是因为在容器环境中,接收到 SIGTERM 信号通常会导致系统关闭。 if (!IsRebootCapable()) { sigaddset(&mask, SIGTERM); } // 使用 sigprocmask 函数阻塞这些信号。如果阻塞失败,它将记录一条致命错误日志。 if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) { PLOG(FATAL) << "failed to block signals"; } // 使用 pthread_atfork 函数注册一个 fork 处理器,该处理器在子进程中解除信号阻塞。如果注册失败,它将记录一条致命错误日志。 const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals); if (result != 0) { LOG(FATAL) << "Failed to register a fork handler: " << strerror(result); } // 使用 signalfd 函数创建一个信号文件描述符,用于接收信号。如果创建失败,它将记录一条致命错误日志。 signal_fd = signalfd(-1, &mask, SFD_CLOEXEC); if (signal_fd == -1) { PLOG(FATAL) << "failed to create signalfd"; } // 使用 epoll->RegisterHandler 函数注册一个处理器,用于处理从信号文件描述符接收到的信号。如果注册失败,它将记录一条致命错误日志。 constexpr int flags = EPOLLIN | EPOLLPRI; if (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd, flags); !result.ok()) { LOG(FATAL) << result.error(); } }
InstallSignalFdHandler 函数的主要目的是设置一个系统,使得当特定的信号发生时,可以通过文件描述符来接收和处理这些信号。
RegisterHandler
RegisterHandler 在 Epoll 类中定义。这个函数的目的是注册一个处理器(Handler)来处理特定文件描述符(fd)的事件。这是通过 Linux 的 epoll 机制实现的,epoll 是一种 I/O 多路复用技术。
源码路径:/system/core/init/epoll.cpp
Result<void> Epoll::RegisterHandler(int fd, Handler handler, uint32_t events) { // 检查是否指定了事件(events)。如果没有指定任何事件,函数将返回一个错误。 if (!events) { return Error() << "Must specify events"; } // 尝试将文件描述符(fd)和一个包含事件和处理器的 Info 对象插入到 epoll_handlers_ 映射中。 auto [it, inserted] = epoll_handlers_.emplace( fd, Info{ .events = events, .handler = std::move(handler), }); // 如果插入失败(也就是说,给定的文件描述符已经有一个处理器),函数将返回一个错误。 if (!inserted) { return Error() << "Cannot specify two epoll handlers for a given FD"; } // 创建一个 epoll_event 结构体,该结构体包含了事件和文件描述符。 epoll_event ev = { .events = events, .data.fd = fd, }; // 使用 epoll_ctl 函数将文件描述符添加到 epoll 实例中。如果这个操作失败,它将从 epoll_handlers_ 映射中删除文件描述符,并返回一个错误。 if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, fd, &ev) == -1) { Result<void> result = ErrnoError() << "epoll_ctl failed to add fd"; epoll_handlers_.erase(fd); return result; } // 如果所有操作都成功,函数将返回一个空的 Result 对象,表示操作成功。 return {}; }
RegisterHandler 函数的主要用途是设置 epoll,使得当文件描述符上发生指定的事件时,可以调用相应的处理器来处理这些事件。
HandleSignalFd
HandleSignalFd 用于处理通过 signal_fd 接收到的信号。这个函数使用了 Linux 的 signalfd 机制,该机制允许通过文件描述符接收信号。
static void HandleSignalFd() { // 定义了一个 signalfd_siginfo 结构体,用于存储从 signal_fd 读取的信号信息。 signalfd_siginfo siginfo; // 尝试从 signal_fd 读取信号信息。如果读取失败,将记录一条错误日志并返回。 ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo))); // 如果成功读取到信号信息,将检查读取的字节数是否等于 signalfd_siginfo 的大小。如果不等,将记录一条错误日志并返回。 if (bytes_read != sizeof(siginfo)) { PLOG(ERROR) << "Failed to read siginfo from signal_fd"; return; } switch (siginfo.ssi_signo) { case SIGCHLD: ReapAnyOutstandingChildren(); break; case SIGTERM: HandleSigtermSignal(siginfo); break; default: LOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo; break; } }
HandleSignalFd 函数的主要用途是处理通过 signal_fd 接收到的信号,以便在接收到特定的信号时执行相应的操作。
ReapOneProcess
ReapOneProcess 用于处理子进程的结束。它使用了 Linux 的 waitid 和 waitpid 系统调用来收集子进程的退出状态,防止子进程变成僵尸进程。
源码路径:/system/core/init/sigchld_handler.cpp
void ReapAnyOutstandingChildren() { while (ReapOneProcess() != 0) { } } static pid_t ReapOneProcess() { // 定义一个 siginfo_t 结构体,用于存储从 waitid 系统调用中获取的信息。 siginfo_t siginfo = {}; // 调用 waitid 系统调用来获取一个僵尸进程的 PID,或者得知没有更多的僵尸进程需要处理。注意,这个调用并不实际收集僵尸进程的退出状态,这个操作在后面进行。 if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) { PLOG(ERROR) << "waitid failed"; return 0; } // 如果 waitid 调用失败,它将记录一条错误日志并返回 0。 const pid_t pid = siginfo.si_pid; if (pid == 0) { DCHECK_EQ(siginfo.si_signo, 0); return 0; } // 如果 waitid 调用成功,它将检查返回的 PID 是否为 0。如果是,它将确保信号编号为 0,然后返回0。如果 PID 不为 0,它将确保信号编号为SIGCHLD。 DCHECK_EQ(siginfo.si_signo, SIGCHLD); // 创建一个作用域保护器(scope guard)。这个保护器在函数返回时会自动调用 waitpid 系统调用来收集僵尸进程的退出状态。 auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); }); std::string name; std::string wait_string; Service* service = nullptr; // 尝试找到与 PID 对应的服务。如果找到,将记录服务的名称和 PID,以及服务的执行时间。如果没有找到,将记录 PID。 if (SubcontextChildReap(pid)) { name = "Subcontext"; } else { service = ServiceList::GetInstance().FindService(pid, &Service::pid); if (service) { name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid); if (service->flags() & SVC_EXEC) { auto exec_duration = boot_clock::now() - service->time_started(); auto exec_duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count(); wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f); } else if (service->flags() & SVC_ONESHOT) { auto exec_duration = boot_clock::now() - service->time_started(); auto exec_duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration) .count(); wait_string = StringPrintf(" oneshot service took %f seconds in background", exec_duration_ms / 1000.0f); } } else { name = StringPrintf("Untracked pid %d", pid); } } // 检查子进程是正常退出还是因为接收到信号而退出,并记录相应的日志。 if (siginfo.si_code == CLD_EXITED) { LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string; } else { LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string; } // 如果没有找到与 PID 对应的服务,将记录一条日志,并返回 PID。 if (!service) { LOG(INFO) << name << " did not have an associated service entry and will not be reaped"; return pid; } // 如果找到了服务,将调用服务的 Reap 方法来处理服务的结束。 service->Reap(siginfo); // 如果服务是临时的,将从服务列表中移除服务。 if (service->flags() & SVC_TEMPORARY) { ServiceList::GetInstance().RemoveService(*service); } // 返回 PID return pid; }
ReapOneProcess 函数的主要用途是处理子进程的结束,收集子进程的退出状态,防止子进程变成僵尸进程,并处理与子进程相关的服务。
我们在开发和调试过程中看到通过 property_set 可以轻松设置系统属性,那干嘛这里还要启动一个属性服务呢?这里其实涉及到一些权限的问题,不是所有进程都可以随意修改任何的系统属性,Android 将属性的设置统一交由 init 进程管理,其他进程不能直接修改属性,而只能通知 init 进程来修改,而在这过程中,init 进程可以进行权限控制,我们来看看具体的流程是什么:
property_set
PropertyInit
PropertyInit 用于初始化 Android 系统的属性服务。这个服务用于管理系统的属性,这些属性是一些键值对,可以被系统的各个部分用来配置和通信。
源码路径:/system/core/init/property_service.cpp
void PropertyInit() { // 设置一个 SELinux 回调函数 PropertyAuditCallback,用于处理 SELinux 的审计事件。 selinux_callback cb; cb.func_audit = PropertyAuditCallback; selinux_set_callback(SELINUX_CB_AUDIT, cb); // 创建一个名为 /dev/__properties__ 的目录,这个目录用于存储系统属性的信息。 mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); // 调用 CreateSerializedPropertyInfo 函数来创建序列化的属性信息。 CreateSerializedPropertyInfo(); // 调用 __system_property_area_init 函数来初始化属性区域。如果初始化失败,它将记录一条致命错误日志并退出。 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"; } // 处理内核设备树(DT)和内核命令行中的参数。如果这两种方式都提供了参数,设备树中的属性将优先于命令行中的属性。 ProcessKernelDt(); ProcessKernelCmdline(); // 处理启动配置。 ProcessBootconfig(); // 将内核变量传播到 init 使用的内部变量以及当前需要的属性。 ExportKernelBootProps(); // 加载启动默认属性。 PropertyLoadBootDefaults(); }
PropertyInit 函数的主要用途是初始化系统属性服务,以便系统的各个部分可以使用系统属性进行配置和通信。
StartPropertyService
StartPropertyService 函数用于启动属性服务。
void StartPropertyService(int* epoll_socket) { // 调用 InitPropertySet 函数来初始化一个名为 ro.property_service.version 的系统属性,其值为"2"。 InitPropertySet("ro.property_service.version", "2"); // 创建一个 UNIX 域套接字对,用于 property_service 和 init 之间的通信。如果套接字对的创建失败,它将记录一条致命错误日志并退出。 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,另一个端点赋值给 init_socket。 *epoll_socket = from_init_socket = sockets[0]; init_socket = sockets[1]; // 调用 StartSendingMessages 函数来开始发送消息。 StartSendingMessages(); // 创建一个名为 PROP_SERVICE_NAME 的套接字,用于处理属性设置请求。如果套接字的创建失败,它将记录一条致命错误日志并退出。 if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, false, false, 0666, 0, 0, {}); result.ok()) { property_set_fd = *result; } else { LOG(FATAL) << "start_property_service socket creation failed: " << result.error(); } // 调用 listen 函数来开始监听属性设置请求。 listen(property_set_fd, 8); // 创建一个新的线程来运行 PropertyServiceThread 函数。这个函数用于处理属性设置请求。 auto new_thread = std::thread{PropertyServiceThread}; property_service_thread.swap(new_thread); // 检查 ro.property_service.async_persist_writes 系统属性的值。 auto async_persist_writes = android::base::GetBoolProperty("ro.property_service.async_persist_writes", false); // 如果 async_persist_writes 为 true,将创建一个 PersistWriteThread 对象来异步写入持久化的属性。 if (async_persist_writes) { persist_write_thread = std::make_unique<PersistWriteThread>(); } }
StartPropertyService 函数的主要用途是启动属性服务,以便系统的各个部分可以使用系统属性进行配置和通信。
handle_property_set_fd
handle_property_set_fd 函数用于处理来自客户端的属性设置请求。这个函数通过接收和处理来自 UNIX 域套接字的消息来完成这个任务。
static void handle_property_set_fd() { static constexpr uint32_t kDefaultSocketTimeout = 2000; // 调用 accept4 函数来接收新的连接请求。如果接收失败,函数会直接返回。 int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC); if (s == -1) { return; } // 获取连接的对端的凭据(包括用户ID,组ID和进程ID)。 ucred cr; socklen_t cr_size = sizeof(cr); if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED"; return; } // 创建一个 SocketConnection 对象,用于处理和这个连接相关的操作。 SocketConnection socket(s, cr); uint32_t timeout_ms = kDefaultSocketTimeout; // 从套接字中读取一个 32 位的命令。如果读取失败,函数会发送一个错误码并返回。 uint32_t cmd = 0; if (!socket.RecvUint32(&cmd, &timeout_ms)) { PLOG(ERROR) << "sys_prop: error while reading command from the socket"; socket.SendUint32(PROP_ERROR_READ_CMD); return; } switch (cmd) { // 如果命令是 PROP_MSG_SETPROP,函数会从套接字中读取属性的名字和值,然后调用 HandlePropertySetNoSocket 函数来设置属性。 case PROP_MSG_SETPROP: { char prop_name[PROP_NAME_MAX]; char prop_value[PROP_VALUE_MAX]; if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) || !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) { PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket"; return; } prop_name[PROP_NAME_MAX-1] = 0; prop_value[PROP_VALUE_MAX-1] = 0; std::string source_context; if (!socket.GetSourceContext(&source_context)) { PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed"; return; } const auto& cr = socket.cred(); std::string error; auto result = HandlePropertySetNoSocket(prop_name, prop_value, source_context, cr, &error); if (result != PROP_SUCCESS) { LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; } break; } // 如果命令是 PROP_MSG_SETPROP2,函数会从套接字中读取属性的名字和值,然后调用 HandlePropertySet 函数来设置属性。这个函数会处理异步属性设置的情况。 case PROP_MSG_SETPROP2: { std::string name; std::string value; if (!socket.RecvString(&name, &timeout_ms) || !socket.RecvString(&value, &timeout_ms)) { PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket"; socket.SendUint32(PROP_ERROR_READ_DATA); return; } std::string source_context; if (!socket.GetSourceContext(&source_context)) { PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed"; socket.SendUint32(PROP_ERROR_PERMISSION_DENIED); return; } const auto& cr = socket.cred(); std::string error; auto result = HandlePropertySet(name, value, source_context, cr, &socket, &error); if (!result) { return; } if (*result != PROP_SUCCESS) { LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; } socket.SendUint32(*result); break; } // 如果命令是其他值,函数会记录一个错误日志并发送一个错误码。 default: LOG(ERROR) << "sys_prop: invalid command " << cmd; socket.SendUint32(PROP_ERROR_INVALID_CMD); break; } }
handle_property_set_fd 函数的主要用途是处理属性设置请求,以便系统的各个部分可以使用系统属性进行配置和通信。
当属性服务建立完成后,init 的自身功能基本就告一段落,接下来需要来启动其他的进程。但是 init 进程如何启动其他进程呢?其他进程都是一个二进制文件,我们可以直接通过 exec 的命令方式来启动,例如 ./system/bin/init second_stage,来启动 init 进程的第二阶段。但是 Android 系统有那么多的 Native 进程,如果都通过传 exec 在代码中一个个的来执行进程,那无疑是一个灾难性的设计。在这个基础上 Android 推出了一个 init.rc 的机制,即类似通过读取配置文件的方式,来启动不同的进程。接下来我们就来看看 init.rc 是如何工作的。
init.rc 是一个配置文件,内部由 Android 初始化语言编写(Android Init Language)编写的脚本。init.rc 在 Android 设备的目录:./init.rc。init.rc 主要包含五种类型语句:Action、Command、Service、Option、Import。
./init.rc
Action
Command
Service
Option
Import
Action 表示了一组命令(commands)组成.动作包括一个触发器,决定了何时运行这个 Action。Action 通过触发器(trigger),即以 on 开头的语句来决定执行相应的 service 的时机,具体有如下时机:
on early-init
on init
on late-init
on boot/charger
on property:<key>=<value>
Command 是 Action 的命令列表中的命令,或者是 Service 中的选项 onrestart 的参数命令,命令将在所属事件发生时被一个个地执行。
下面列举常用的命令:
class_start <service_class_name>
class_stop <service_class_name>
start <service_name>
stop <service_name>
setprop <name> <value>
mkdir <path>
symlink <target> <sym_link>
<target>
<sym_link>
write <path> <string>
exec
exprot <name> <name>
loglevel <level>
hostname <name>
import <filename>
服务 Service,以 service 开头,由 init 进程启动,一般运行在 init 的一个子进程,所以启动 service 前需要判断对应的可执行文件是否存在。
命令:service <name><pathname> [ <argument> ]* <option> <option>
service <name><pathname> [ <argument> ]* <option> <option>
<name>
<pathname>
<argument>
<option>
init 生成的子进程,定义在 rc 文件,其中每一个 service 在启动时会通过 fork 方式生成子进程。
例如:service servicemanager /system/bin/servicemanager 代表的是服务名为 servicemanager,服务执行的路径为 /system/bin/servicemanager。
service servicemanager /system/bin/servicemanager
servicemanager
/system/bin/servicemanager
Options
Options 是 Service 的可选项,与 service 配合使用:
disabled
oneshot
user/group
class
onrestart
socket
/dev/socket/<name>
critical
default:意味着 disabled=false,oneshot=false,critical=false。
用来导入其他的 rc 文件。
命令:import <filename>
init.rc 解析过程 - LoadBootScripts
LoadBootScripts 的主要任务是加载启动脚本。
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) { // 创建一个Parser对象,该对象用于解析启动脚本。 Parser parser = CreateParser(action_manager, service_list); // 尝试获取名为"ro.boot.init_rc"的属性,该属性的值应该是一个启动脚本的路径。 std::string bootscript = GetProperty("ro.boot.init_rc", ""); // 如果该属性不存在或者为空,那么它会尝试解析一系列默认的启动脚本。 if (bootscript.empty()) { parser.ParseConfig("/system/etc/init/hw/init.rc"); if (!parser.ParseConfig("/system/etc/init")) { // 如果在尝试解析时失败,那么它会将该路径添加到 late_import_paths 列表中,这意味着这些路径将在稍后再次尝试解析。 late_import_paths.emplace_back("/system/etc/init"); } parser.ParseConfig("/system_ext/etc/init"); if (!parser.ParseConfig("/vendor/etc/init")) { // 如果在尝试解析时失败,那么它会将该路径添加到 late_import_paths 列表中,这意味着这些路径将在稍后再次尝试解析。 late_import_paths.emplace_back("/vendor/etc/init"); } if (!parser.ParseConfig("/odm/etc/init")) { // 如果在尝试解析时失败,那么它会将该路径添加到 late_import_paths 列表中,这意味着这些路径将在稍后再次尝试解析。 late_import_paths.emplace_back("/odm/etc/init"); } if (!parser.ParseConfig("/product/etc/init")) { // 如果在尝试解析时失败,那么它会将该路径添加到 late_import_paths 列表中,这意味着这些路径将在稍后再次尝试解析。 late_import_paths.emplace_back("/product/etc/init"); } } else { // 如果"ro.boot.init_rc"属性存在并且不为空,那么它会尝试解析该属性指定的启动脚本。 parser.ParseConfig(bootscript); } }
总的来说,LoadBootScripts 函数的目的是尽可能地加载和解析所有可用的启动脚本。
LoadBootScripts
init.rc 解析过程 - CreateParser
CreateParser 的主要任务是创建并配置一个Parser对象。它接受两个参数,一个是ActionManager类型的action_manager,另一个是ServiceList类型的service_list。
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) { // 创建一个 Parser 对象。 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)); // 返回配置好的 Parser 对象。 return parser; }
总的来说,这个函数的目的是创建一个能够解析启动脚本中的"service"、"on"和"import"部分的Parser对象。
init.rc 解析过程 - 执行 Action 动作
按顺序把相关 Action 加入触发器队列,按顺序为 early-init -> init -> late-init。然后在循环中,执行所有触发器队列中 Action 带 Command 的执行函数。
am.QueueEventTrigger("early-init"); am.QueueEventTrigger("init"); am.QueueEventTrigger("late-init"); ... while (true) { ... if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) { am.ExecuteOneCommand(); ... } ... } ...
init.rc 解析过程 - Zygote 启动
Android 支持 64 位的编译,因此 zygote 本身也支持 32 位和 64 位。通过属性 ro.zygote 来控制不同版本的 zygote 进程启动。在 init.rc 的 import 段我们看到如下代码:
import /system/etc/init/hw/init.${ro.zygote}.rc
init.rc 位于 /system/core/rootdir/ 下。在这个路径下还包括三个关于 zygote 的 rc 文件。分别是 init.zygote32.rc、init.zygote64.rc、init.zygote64_32.rc,由硬件决定调用哪个文件。
/system/core/rootdir/
init.zygote32.rc
init.zygote64.rc
init.zygote64_32.rc
这里拿 64 位处理器为例,init.zygote64.rc 的代码如下所示:
// 定义了一个名为"zygote"的服务,它使用/system/bin/app_process64程序,并传递了一些参数来启动zygote进程。 service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote // 将此服务归类为主服务。 class main // 设置此服务的优先级为-20,这是最高优先级,意味着这个服务将优先于其他服务运行。 priority -20 // 设置此服务的用户和组为root,同时给予了读取进程信息和访问保留磁盘的权限。 user root group root readproc reserved_disk // 创建了两个名为"zygote"和"usap_pool_primary"的socket,权限为660,所有者和组都是root和system。 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 # NOTE: If the wakelock name here is changed, then also # update it in SystemSuspend.cpp onrestart write /sys/power/wake_lock zygote_kwl onrestart restart audioserver onrestart restart cameraserver onrestart restart media onrestart restart media.tuner onrestart restart netd onrestart restart wificond // 设置了任务的性能配置文件。 task_profiles ProcessCapacityHigh MaxPerformance // 设置了一个名为"zygote-fatal"的目标,当zygote进程在定义的窗口时间内崩溃时,将会触发这个目标。 critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
总的来说,这个脚本定义了 zygote 服务的行为和属性,包括它如何启动,它的权限,它的优先级,以及当它重启时应该执行的操作。
init 进程启动过程分为三个阶段:
第一阶段:主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,并启用 SELinux 安全策略。
第二阶段:主要工作是初始化属性系统,解析 SELinux 的策略文件,处理子进程终止信号,启动系统属性服务。每一项都是关键的。如果说第一阶段是为属性系统和 SELinux 做准备,那么第二阶段就是真正去实现这些功能。
第三阶段:主要是解析 init.rc 文件来启动其他进程,并进入一个无限循环,进行子进程的实时监控。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
一、概述
init 进程属于一个守护进程,准确的说,它是 Linux 系统中用户控制的第一个进程,它的进程号为 1,它的生命周期贯穿整个 Linux 内核运行的始终。Android 中所有其它的进程共同的鼻祖均为 init 进程。
可以通过
adb shell ps | grep init
命令来查看 init 的进程号:二、init 进程入口
init 入口函数是 main.cpp,它把各个阶段的操作分离开来,使代码更加简洁:
main 函数有四个参数入口:
main 函数的执行顺序:
三、ueventd_main
ueventd_main 函数是 Android 系统中
ueventd
服务的主入口函数,负责处理和响应来自 Linux 内核的 uevents(设备事件)。初始化
SELinux 设置
SelinuxSetupKernelLogging()
和SelabelInitialize()
函数来配置 SELinux 相关的日志以及标签库初始化。创建 UeventHandler 对象
初始化 UeventListener
冷启动处理
冷启动完成通知
信号处理
恢复优先级
setpriority(PRIO_PROCESS, 0, 0)
来恢复进程的默认优先级。主循环
总结:
ueventd_main
函数在 Android 启动过程中扮演着核心角色,它负责监听和处理与硬件设备状态变化相关的事件,确保系统能够正确识别并响应设备添加、移除或属性更改等操作,从而使得设备驱动和用户空间能够有效地交互。四、FirstStageMain
FirstStageMain 是 Android 系统启动流程中第一阶段初始化的主入口函数,它负责在系统启动早期进行一系列关键的系统设置和挂载操作。
信号处理
REBOOT_BOOTLOADER_ON_PANIC
,则安装重启到引导加载器的信号处理程序,在系统出现 panic 时执行。时间戳记录与错误检查宏
CHECKCALL(x)
,用于调用函数x
并检查其返回值是否为0(表示成功),若非0,则将错误信息添加至错误列表中。设置文件或目录的默认权限
环境清理与基本文件系统准备
/dev
目录下,并创建必要的子目录如/dev/pts
、/dev/socket
和/dev/dm-user
。特殊设备节点创建
日志初始化与权限控制
挂载特定临时文件系统
/mnt
、/mnt/vendor
和/mnt/product
下挂载临时文件系统,为后续挂载分区做准备。/debug_ramdisk
和第二阶段资源存储目录,并挂载临时文件系统。模块加载及计时
创建设备节点与控制台启动
ramdisk 属性复制
调试模式支持
切换根文件系统
SwitchRoot()
函数。完成第一阶段挂载
DoFirstStageMount()
函数来挂载启动过程中所需的必要分区。释放旧 ramdisk 资源
其他系统设置
进入 SetupSelinux
总结:整个
FirstStageMain
函数确保了 Android 系统在初始启动阶段能够正确地设置文件系统结构、加载必需的内核模块、建立基础设备节点以及安全地切换到下一阶段的初始化流程。五、SetupSelinux
SetupSelinux 是 Android 系统启动流程中用于设置和初始化 SELinux 环境的关键函数。
标准输入输出重定向与内核日志初始化
SetStdioToDevNull(argv)
将标准输入、输出和错误重定向至/dev/null
,防止无用的日志输出。InitKernelLogging(argv)
初始化内核日志功能。信号处理
REBOOT_BOOTLOADER_ON_PANIC
,则安装重启到引导加载器的信号处理程序。计时并挂载分区
MountMissingSystemPartitions()
挂载必要的系统分区。SELinux 内核日志设置
SelinuxSetupKernelLogging()
来设置SELinux相关的内核日志参数。准备和读取SELinux策略
PrepareApexSepolicy
)。管理 snapuserd 守护进程
加载 SELinux 策略
完成 snapuserd 的 SELinux 上下文转换
恢复上下文与设置强制执行模式
/dev/selinux/
目录及其子目录下的文件到正确的SELinux上下文。SelinuxSetEnforcement
)。针对 init 进程进行额外的上下文恢复
设置环境变量
执行第二阶段 init 进程
总结:通过 SetupSelinux 函数的执行,Android 系统能够在启动时正确地建立和启用 SELinux 安全机制,为后续系统的运行提供安全保障。
六、SecondStageMain
SecondStageMain 函数是 Android 系统启动过程中的第二个阶段,主要负责更深层次的系统初始化工作。
信号处理
资源准备与清理
/dev/.booting
文件。系统服务和属性初始化
事件循环与回调设置
关键数据记录与设置
系统特定功能
初始化命名空间与控制组
调度内置动作
启动与触发事件
主循环
总结:
SecondStageMain
函数对 Android 系统进行全面深入的初始化,包括设置系统资源、挂载文件系统、启动关键服务、初始化安全性组件以及执行启动脚本等。整个函数通过事件循环持续监控并响应系统内部及外部事件,直至系统完全启动就绪。七、信号处理
init 是一个守护进程,为了防止 init 的子进程成为僵尸进程(zombie process),需要 init 在子进程在结束时获取子进程的结束码,通过结束码将程序表中的子进程移除,防止成为僵尸进程的子进程占用程序表的空间(程序表的空间达到上限时,系统就不能再启动新的进程了,会引起严重的系统问题)。
子进程重启流程如下图所示:
信号处理主要工作:
InstallSignalFdHandler
InstallSignalFdHandler 用于在 Linux 系统中设置信号处理。
InstallSignalFdHandler
函数的主要目的是设置一个系统,使得当特定的信号发生时,可以通过文件描述符来接收和处理这些信号。RegisterHandler
RegisterHandler 在 Epoll 类中定义。这个函数的目的是注册一个处理器(Handler)来处理特定文件描述符(fd)的事件。这是通过 Linux 的 epoll 机制实现的,epoll 是一种 I/O 多路复用技术。
RegisterHandler
函数的主要用途是设置 epoll,使得当文件描述符上发生指定的事件时,可以调用相应的处理器来处理这些事件。HandleSignalFd
HandleSignalFd 用于处理通过 signal_fd 接收到的信号。这个函数使用了 Linux 的 signalfd 机制,该机制允许通过文件描述符接收信号。
HandleSignalFd
函数的主要用途是处理通过 signal_fd 接收到的信号,以便在接收到特定的信号时执行相应的操作。ReapOneProcess
ReapOneProcess 用于处理子进程的结束。它使用了 Linux 的 waitid 和 waitpid 系统调用来收集子进程的退出状态,防止子进程变成僵尸进程。
ReapOneProcess
函数的主要用途是处理子进程的结束,收集子进程的退出状态,防止子进程变成僵尸进程,并处理与子进程相关的服务。八、属性服务
我们在开发和调试过程中看到通过
property_set
可以轻松设置系统属性,那干嘛这里还要启动一个属性服务呢?这里其实涉及到一些权限的问题,不是所有进程都可以随意修改任何的系统属性,Android 将属性的设置统一交由 init 进程管理,其他进程不能直接修改属性,而只能通知 init 进程来修改,而在这过程中,init 进程可以进行权限控制,我们来看看具体的流程是什么:PropertyInit
PropertyInit 用于初始化 Android 系统的属性服务。这个服务用于管理系统的属性,这些属性是一些键值对,可以被系统的各个部分用来配置和通信。
PropertyInit
函数的主要用途是初始化系统属性服务,以便系统的各个部分可以使用系统属性进行配置和通信。StartPropertyService
StartPropertyService 函数用于启动属性服务。
StartPropertyService
函数的主要用途是启动属性服务,以便系统的各个部分可以使用系统属性进行配置和通信。handle_property_set_fd
handle_property_set_fd 函数用于处理来自客户端的属性设置请求。这个函数通过接收和处理来自 UNIX 域套接字的消息来完成这个任务。
handle_property_set_fd
函数的主要用途是处理属性设置请求,以便系统的各个部分可以使用系统属性进行配置和通信。九、init.rc
当属性服务建立完成后,init 的自身功能基本就告一段落,接下来需要来启动其他的进程。但是 init 进程如何启动其他进程呢?其他进程都是一个二进制文件,我们可以直接通过 exec 的命令方式来启动,例如 ./system/bin/init second_stage,来启动 init 进程的第二阶段。但是 Android 系统有那么多的 Native 进程,如果都通过传 exec 在代码中一个个的来执行进程,那无疑是一个灾难性的设计。在这个基础上 Android 推出了一个 init.rc 的机制,即类似通过读取配置文件的方式,来启动不同的进程。接下来我们就来看看 init.rc 是如何工作的。
init.rc 是一个配置文件,内部由 Android 初始化语言编写(Android Init Language)编写的脚本。init.rc 在 Android 设备的目录:
./init.rc
。init.rc 主要包含五种类型语句:Action
、Command
、Service
、Option
、Import
。Action
Action 表示了一组命令(commands)组成.动作包括一个触发器,决定了何时运行这个 Action。Action 通过触发器(trigger),即以 on 开头的语句来决定执行相应的 service 的时机,具体有如下时机:
on early-init
:在初始化早期阶段触发on init
:在初始化阶段触发on late-init
:在初始化晚期阶段触发on boot/charger
:当系统启动/充电时触发on property:<key>=<value>
:当属性值满足条件时触发Command
Command 是 Action 的命令列表中的命令,或者是 Service 中的选项 onrestart 的参数命令,命令将在所属事件发生时被一个个地执行。
下面列举常用的命令:
class_start <service_class_name>
:启动属于同一个 class 的所有服务class_stop <service_class_name>
:停止指定类的服务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 级别hostname <name>
:设置主机名import <filename>
:导入一个额外的 init 配置文件Service
服务 Service,以 service 开头,由 init 进程启动,一般运行在 init 的一个子进程,所以启动 service 前需要判断对应的可执行文件是否存在。
命令:
service <name><pathname> [ <argument> ]* <option> <option>
<name>
<pathname>
<argument>
<option>
init 生成的子进程,定义在 rc 文件,其中每一个 service 在启动时会通过 fork 方式生成子进程。
例如:
service servicemanager /system/bin/servicemanager
代表的是服务名为servicemanager
,服务执行的路径为/system/bin/servicemanager
。Options
Options 是 Service 的可选项,与 service 配合使用:
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。
Import
用来导入其他的 rc 文件。
命令:
import <filename>
init.rc 解析过程 - LoadBootScripts
LoadBootScripts 的主要任务是加载启动脚本。
总的来说,
LoadBootScripts
函数的目的是尽可能地加载和解析所有可用的启动脚本。init.rc 解析过程 - CreateParser
CreateParser 的主要任务是创建并配置一个Parser对象。它接受两个参数,一个是ActionManager类型的action_manager,另一个是ServiceList类型的service_list。
总的来说,这个函数的目的是创建一个能够解析启动脚本中的"service"、"on"和"import"部分的Parser对象。
init.rc 解析过程 - 执行 Action 动作
按顺序把相关 Action 加入触发器队列,按顺序为 early-init -> init -> late-init。然后在循环中,执行所有触发器队列中 Action 带 Command 的执行函数。
init.rc 解析过程 - Zygote 启动
Android 支持 64 位的编译,因此 zygote 本身也支持 32 位和 64 位。通过属性 ro.zygote 来控制不同版本的 zygote 进程启动。在 init.rc 的 import 段我们看到如下代码:
init.rc 位于
/system/core/rootdir/
下。在这个路径下还包括三个关于 zygote 的 rc 文件。分别是init.zygote32.rc
、init.zygote64.rc
、init.zygote64_32.rc
,由硬件决定调用哪个文件。这里拿 64 位处理器为例,init.zygote64.rc 的代码如下所示:
总的来说,这个脚本定义了 zygote 服务的行为和属性,包括它如何启动,它的权限,它的优先级,以及当它重启时应该执行的操作。
十、总结
init 进程启动过程分为三个阶段:
第一阶段:主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,并启用 SELinux 安全策略。
第二阶段:主要工作是初始化属性系统,解析 SELinux 的策略文件,处理子进程终止信号,启动系统属性服务。每一项都是关键的。如果说第一阶段是为属性系统和 SELinux 做准备,那么第二阶段就是真正去实现这些功能。
第三阶段:主要是解析 init.rc 文件来启动其他进程,并进入一个无限循环,进行子进程的实时监控。
参考
The text was updated successfully, but these errors were encountered: