当前位置: 首页 > news >正文

网站开发企业部门阿里巴巴有几个网站是做外贸的

网站开发企业部门,阿里巴巴有几个网站是做外贸的,免费注册企业邮箱域名,网站代码优化方案本文基于 Android 14.0.0_r2 的系统启动流程分析。 一、概述 init 进程属于一个守护进程#xff0c;准确的说#xff0c;它是 Linux 系统中用户控制的第一个进程#xff0c;它的进程号为 1#xff0c;它的生命周期贯穿整个 Linux 内核运行的始终。Android 中所有其它的进程… 本文基于 Android 14.0.0_r2 的系统启动流程分析。 一、概述 init 进程属于一个守护进程准确的说它是 Linux 系统中用户控制的第一个进程它的进程号为 1它的生命周期贯穿整个 Linux 内核运行的始终。Android 中所有其它的进程共同的鼻祖均为 init 进程。 可以通过 adb shell ps | grep init 命令来查看 init 的进程号 wutianhaowutianhao-Ubuntu:~$ adb shell ps | grep init root 1 0 10858080 932 0 0 S init二、init 进程入口 init 入口函数是 main.cpp它把各个阶段的操作分离开来使代码更加简洁 /system/core/init/main.cppint 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 函数有四个参数入口 参数中有 ueventd进入 ueventd_main。参数中有 subcontext进入 InitLogging 和 SubcontextMain。参数中有 selinux_setup进入 SetupSelinux。参数中有 second_stage进入 SecondStageMain。 main 函数的执行顺序 ueventd_maininit 进程创建子进程 ueventd并将创建设备节点文件的工作托付给 ueventdueventd 通过两种方式创建设备节点文件。FirstStageMain启动第一阶段。SetupSelinux加载 selinux 规则并设置 selinux 日志,完成 SELinux 相关工作。SecondStageMain启动第二阶段。 三、ueventd_main ueventd_main 函数是 Android 系统中 ueventd 服务的主入口函数负责处理和响应来自 Linux 内核的 uevents设备事件。 源码路径/system/core/init/ueventd.cpp 初始化 int ueventd_main(int argc, char** argv) {umask(000);android::base::InitLogging(argv, android::base::KernelLogger);... }首先调用 umask(000) 来设置进程创建文件时不受 umask 影响确保新创建的文件具有指定的精确权限。接着初始化日志系统以便记录相关信息。 SELinux 设置 int ueventd_main(int argc, char** argv) {...SelinuxSetupKernelLogging();SelabelInitialize();... }调用 SelinuxSetupKernelLogging() 和 SelabelInitialize() 函数来配置 SELinux 相关的日志以及标签库初始化。 创建 UeventHandler 对象 int ueventd_main(int argc, char** argv) {...std::vectorstd::unique_ptrUeventHandler uevent_handlers;auto ueventd_configuration GetConfiguration();uevent_handlers.emplace_back(std::make_uniqueDeviceHandler(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_uniqueFirmwareHandler(std::move(ueventd_configuration.firmware_directories),std::move(ueventd_configuration.external_firmware_handlers)));if (ueventd_configuration.enable_modalias_handling) {std::vectorstd::string base_paths {/odm/lib/modules, /vendor/lib/modules};uevent_handlers.emplace_back(std::make_uniqueModaliasHandler(base_paths));}... }根据获取到的配置信息创建并存储多个不同类型的 UeventHandler 子类实例如 DeviceHandler、FirmwareHandler 和可能的 ModaliasHandler。这些处理器分别负责特定类型的设备事件处理如挂载设备节点、管理固件目录等。 初始化 UeventListener int ueventd_main(int argc, char** argv) {...UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);... }创建一个 UeventListener 对象用于监听内核通过 uevent socket 发送的 uevents并配置接收缓冲区大小。 冷启动处理 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();}... }检查系统是否完成冷启动android::base::GetBoolProperty(kColdBootDoneProp, false)如果尚未完成则执行 ColdBoot 类的 Run() 方法进行冷启动相关的设备事件处理和权限恢复。 冷启动完成通知 int ueventd_main(int argc, char** argv) {...for (auto uevent_handler : uevent_handlers) {uevent_handler-ColdbootDone();}... }所有 UeventHandler 对象调用 ColdbootDone() 方法以表明冷启动阶段已完成。 信号处理 int ueventd_main(int argc, char** argv) {...signal(SIGCHLD, SIG_IGN);while (waitpid(-1, nullptr, WNOHANG) 0) {}... }忽略子进程结束信号 SIGCHLD并清理任何已退出但未被收集的子进程。 恢复优先级 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;});... }进入主循环使用 UeventListener 的 Poll() 方法监听 uevents。当接收到 uevent 时遍历所有 UeventHandler 对象并调用它们的 HandleUevent() 方法来处理相应的事件。 总结ueventd_main 函数在 Android 启动过程中扮演着核心角色它负责监听和处理与硬件设备状态变化相关的事件确保系统能够正确识别并响应设备添加、移除或属性更改等操作从而使得设备驱动和用户空间能够有效地交互。 四、FirstStageMain 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则安装重启到引导加载器的信号处理程序在系统出现 panic 时执行。 时间戳记录与错误检查宏 int FirstStageMain(int argc, char** argv) {...boot_clock::time_point start_time boot_clock::now();std::vectorstd::pairstd::string, int errors; #define CHECKCALL(x) \if ((x) ! 0) errors.emplace_back(#x failed, errno);... }记录启动时间点。定义一个宏 CHECKCALL(x)用于调用函数 x 并检查其返回值是否为0表示成功若非0则将错误信息添加至错误列表中。 设置文件或目录的默认权限 int FirstStageMain(int argc, char** argv) {...umask(0);... }当 umask 值为 0 时意味着新建的文件或目录将具有最大权限即对于文件来说是 666rw-rw-rw-对于目录来说是 777rwxrwxrwx。 环境清理与基本文件系统准备 int FirstStageMain(int argc, char** argv) {...CHECKCALL(clearenv());CHECKCALL(setenv(PATH, _PATH_DEFPATH, 1));CHECKCALL(mount(tmpfs, /dev, tmpfs, MS_NOSUID, mode0755));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, hidepid2,gid MAKE_STR(AID_READPROC))); #undef MAKE_STRCHECKCALL(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));... }清除当前进程的环境变量。设置 PATH 环境变量为默认值。挂载临时文件系统 tmpfs 到 /dev 目录下并创建必要的子目录如 /dev/pts、/dev/socket 和 /dev/dm-user。按需挂载 proc、sysfs、selinuxfs 文件系统并调整相关权限。 特殊设备节点创建 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,mode0755,uid0,gid1000));CHECKCALL(mkdir(/mnt/vendor, 0755));CHECKCALL(mkdir(/mnt/product, 0755));CHECKCALL(mount(tmpfs, /debug_ramdisk, tmpfs, MS_NOEXEC | MS_NOSUID | MS_NODEV,mode0755,uid0,gid0));CHECKCALL(mount(tmpfs, kSecondStageRes, tmpfs, MS_NOEXEC | MS_NOSUID | MS_NODEV,mode0755,uid0,gid0)) #undef CHECKCALL... }创建 kmsg、random、urandom、ptmx 和 null 等特殊设备节点。 日志初始化与权限控制 int FirstStageMain(int argc, char** argv) {...SetStdioToDevNull(argv);InitKernelLogging(argv);... }将标准输入输出重定向至 /dev/null以避免不必要的输出干扰。初始化内核日志功能。 挂载特定临时文件系统 int FirstStageMain(int argc, char** argv) {...LOG(INFO) init first stage started!;auto old_root_dir std::unique_ptrDIR, 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_caststd::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) Cant mkdir dir : ec.message();}if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) {LOG(FATAL) Cant copy kBootImageRamdiskProp to dest : ec.message();}LOG(INFO) Copied ramdisk prop to dest;}... }将 bootimage 中 ramdisk 的属性复制到指定位置以便于第二阶段使用。 调试模式支持 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) Cant 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) Cant copy userdebug_plat_sepolicy_cil_src to kDebugRamdiskSEPolicy : ec.message();}setenv(INIT_FORCE_DEBUGGABLE, true, 1);}... }当检测到 “/force_debuggable” 文件存在时会启用用户debug模式相关的设置例如允许adb root访问等。 切换根文件系统 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);}... }验证旧根文件系统是否已成功切换如果已切换则释放旧的 ramdisk 相关资源。 其他系统设置 int FirstStageMain(int argc, char** argv) {...SetInitAvbVersionInRecovery();setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),1);设置 AVB 版本信息等额外操作。 进入 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_castchar**(args));PLOG(FATAL) execv(\ path \) failed;return 1; }通过 execv() 函数执行 /system/bin/init 进程传入 “selinux_setup” 参数作为子进程的启动参数开始进入 SetupSelinux。 总结整个 FirstStageMain 函数确保了 Android 系统在初始启动阶段能够正确地设置文件系统结构、加载必需的内核模块、建立基础设备节点以及安全地切换到下一阶段的初始化流程。 五、SetupSelinux 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();}... }如果定义了 REBOOT_BOOTLOADER_ON_PANIC则安装重启到引导加载器的信号处理程序。 计时并挂载分区 int SetupSelinux(char** argv) {...boot_clock::time_point start_time boot_clock::now();MountMissingSystemPartitions();... }记录开始时间点并调用 MountMissingSystemPartitions() 挂载必要的系统分区。 SELinux 内核日志设置 int SetupSelinux(char** argv) {...SelinuxSetupKernelLogging();... }调用 SelinuxSetupKernelLogging() 来设置SELinux相关的内核日志参数。 准备和读取SELinux策略 int SetupSelinux(char** argv) {...PrepareApexSepolicy();std::string policy;ReadPolicy(policy);CleanupApexSepolicy();... }准备Apex SELinux策略文件 (PrepareApexSepolicy)。读取SELinux策略文件的内容并将它存储在字符串变量 policy 中。清理 Apex SELinux 策略文件相关资源。 管理 snapuserd 守护进程 int SetupSelinux(char** argv) {...auto snapuserd_helper SnapuserdSelinuxHelper::CreateIfNeeded();if (snapuserd_helper) {snapuserd_helper-StartTransition();}... }创建或获取一个 SnapuserdSelinuxHelper 对象来管理 snapuserd 守护进程的 SELinux 上下文转换。如果需要杀死旧的 snapuserd 进程以避免产生审计消息并开始转换过程。 加载 SELinux 策略 int SetupSelinux(char** argv) {...LoadSelinuxPolicy(policy);... }使用之前读取的 policy 字符串内容加载 SELinux 策略 (LoadSelinuxPolicy(policy))。 完成 snapuserd 的 SELinux 上下文转换 int SetupSelinux(char** argv) {...if (snapuserd_helper) {snapuserd_helper-FinishTransition();snapuserd_helper nullptr;}... }如果存在 snapuserd_helper完成其 SELinux 上下文转换过程并释放该对象。 恢复上下文与设置强制执行模式 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/ 目录及其子目录下的文件到正确的SELinux上下文。设置 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;}... }特别对 /system/bin/init 进行 SELinux 上下文恢复确保其拥有正确权限以便进行后续启动操作。 设置环境变量 int SetupSelinux(char** argv) {...setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);... }设置环境变量 kEnvSelinuxStartedAt 记录 SELinux 启动的时间点。 执行第二阶段 init 进程 int SetupSelinux(char** argv) {...const char* path /system/bin/init;const char* args[] {path, second_stage, nullptr};execv(path, const_castchar**(args));PLOG(FATAL) execv(\ path \) failed;return 1; }准备参数数组包含命令路径 “/system/bin/init” 和参数 “second_stage”。使用 execv 系统调用执行新的 init 进程进入系统的第二阶段初始化。如果 execv 函数返回通常表示出错会记录致命错误并退出程序。由于在成功执行 execv 后不会返回所以这里的 PLOG(FATAL) … 是一种异常情况处理。 总结通过 SetupSelinux 函数的执行Android 系统能够在启动时正确地建立和启用 SELinux 安全机制为后续系统的运行提供安全保障。 六、SecondStageMain 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();... }设置重启至引导加载器的信号处理程序并忽略 SIGPIPE 信号防止因管道破裂导致的进程崩溃。 资源准备与清理 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 trues force_debuggable_env;}unsetenv(INIT_FORCE_DEBUGGABLE);if (!load_debug_prop) {UmountDebugRamdisk();}... }设置 PATH 变量值。调整 init 进程及其子进程的内存管理器 OOM 分数调整值。设置会话密钥环保证进程间共享加密密钥的安全性。标记正在启动状态打开 /dev/.booting 文件。根据解锁状态和环境变量决定是否加载调试属性并卸载调试 ramdisk。 系统服务和属性初始化 int SecondStageMain(int argc, char** argv) {...PropertyInit();UmountSecondStageRes();if (load_debug_prop) {UmountDebugRamdisk();}MountExtraFilesystems();SelabelInitialize();SelinuxRestoreContext();... }初始化属性服务加载系统属性。卸载第二阶段资源。挂载额外的文件系统。初始化 SELinux 并恢复上下文。 事件循环与回调设置 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);... }使用 Epoll 实现 I/O 多路复用设置信号处理回调处理子进程退出事件。安装 Init 通知器启动属性服务。 关键数据记录与设置 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);... }记录启动阶段的时间点供 bootstat 使用。设置 libavb 版本属性。 系统特定功能 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);... }负责厂商层叠挂载、导出 OEM 锁定状态、挂载处理器、设置 USB 控制器、设置内核版本等。设置内置函数映射表加载启动脚本。 初始化命名空间与控制组 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) - Resultvoid {for (const auto svc : ServiceList::GetInstance()) {keychords.Register(svc-keycodes());}keychords.Start(epoll, HandleKeychord);return {};},KeychordInit);am.QueueEventTrigger(init);... }队列化一系列内置动作如设置 cgroups、设置 kptr_restrict 等安全选项。注册并启动按键组合Keychord处理。 启动与触发事件 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::optionalstd::chrono::milliseconds epoll_timeout;if (next_action_time ! far_future) {epoll_timeout std::chrono::ceilstd::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();}}... }在无限循环中根据队列中的动作计划执行相关命令。监听并处理控制消息如电源管理命令如关机、重启等。处理进程管理和重启操作。检查并更新 USB 控制器状态。 总结SecondStageMain 函数对 Android 系统进行全面深入的初始化包括设置系统资源、挂载文件系统、启动关键服务、初始化安全性组件以及执行启动脚本等。整个函数通过事件循环持续监控并响应系统内部及外部事件直至系统完全启动就绪。 七、信号处理 init 是一个守护进程为了防止 init 的子进程成为僵尸进程(zombie process)需要 init 在子进程在结束时获取子进程的结束码通过结束码将程序表中的子进程移除防止成为僵尸进程的子进程占用程序表的空间程序表的空间达到上限时系统就不能再启动新的进程了会引起严重的系统问题。 子进程重启流程如下图所示 信号处理主要工作 初始化信号 signal 句柄循环处理子进程注册 epoll 句柄处理子进程终止 注意EPOLL 类似于 POLL是 Linux 中用来做事件触发的跟 EventBus 功能差不多。Linux 很长的时间都在使用 select 来做事件触发它是通过轮询来处理的轮询的 fd 数目越多自然耗时越多对于大量的描述符处理EPOLL 更有优势。 InstallSignalFdHandler InstallSignalFdHandler 用于在 Linux 系统中设置信号处理。 源码路径/system/core/init/init.cpp 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 Resultvoid 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) {Resultvoid result ErrnoError() epoll_ctl failed to add fd;epoll_handlers_.erase(fd);return result;}// 如果所有操作都成功函数将返回一个空的 Result 对象表示操作成功。return {}; }RegisterHandler 函数的主要用途是设置 epoll使得当文件描述符上发生指定的事件时可以调用相应的处理器来处理这些事件。 HandleSignalFd HandleSignalFd 用于处理通过 signal_fd 接收到的信号。这个函数使用了 Linux 的 signalfd 机制该机制允许通过文件描述符接收信号。 源码路径/system/core/init/init.cpp 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_caststd::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_caststd::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);}// 返回 PIDreturn pid; }ReapOneProcess 函数的主要用途是处理子进程的结束收集子进程的退出状态防止子进程变成僵尸进程并处理与子进程相关的服务。 八、属性服务 我们在开发和调试过程中看到通过 property_set 可以轻松设置系统属性那干嘛这里还要启动一个属性服务呢这里其实涉及到一些权限的问题不是所有进程都可以随意修改任何的系统属性Android 将属性的设置统一交由 init 进程管理其他进程不能直接修改属性而只能通知 init 进程来修改而在这过程中init 进程可以进行权限控制我们来看看具体的流程是什么 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 函数用于启动属性服务。 源码路径/system/core/init/property_service.cpp 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_uniquePersistWriteThread();} }StartPropertyService 函数的主要用途是启动属性服务以便系统的各个部分可以使用系统属性进行配置和通信。 handle_property_set_fd handle_property_set_fd 函数用于处理来自客户端的属性设置请求。这个函数通过接收和处理来自 UNIX 域套接字的消息来完成这个任务。 源码路径/system/core/init/property_service.cpp 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.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:keyvalue当属性值满足条件时触发 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 中写入字符串execfork 并执行会阻塞 init 进程直到程序完毕exprot name name设定环境变量loglevel level设置 log 级别hostname name设置主机名import filename导入一个额外的 init 配置文件 Service 服务 Service以 service 开头由 init 进程启动一般运行在 init 的一个子进程所以启动 service 前需要判断对应的可执行文件是否存在。 命令service namepathname [ 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 名才启动。oneshotservice 退出后不再重启。user/group设置执行服务的用户/用户组默认都是 root。class设置所属的类名当所属类启动/退出时服务也启动/停止默认为 default。onrestart当服务重启时执行相应命令。socket创建名为 /dev/socket/name 的 socket。critical在规定时间内该 service 不断重启则系统会重启并进入恢复模式。 default意味着 disabledfalseoneshotfalsecriticalfalse。 Import 用来导入其他的 rc 文件。 命令import filename init.rc 解析过程 - LoadBootScripts LoadBootScripts 的主要任务是加载启动脚本。 源码路径/system/core/init/init.cpp 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 函数的目的是尽可能地加载和解析所有可用的启动脚本。 init.rc 解析过程 - CreateParser CreateParser 的主要任务是创建并配置一个Parser对象。它接受两个参数一个是ActionManager类型的action_manager另一个是ServiceList类型的service_list。 源码路径/system/core/init/init.cpp Parser CreateParser(ActionManager action_manager, ServiceList service_list) {// 创建一个 Parser 对象。Parser parser;// 解析启动脚本中的service部分。parser.AddSectionParser(service, std::make_uniqueServiceParser(service_list, GetSubcontext(), std::nullopt));// 解析启动脚本中的on部分。parser.AddSectionParser(on, std::make_uniqueActionParser(action_manager, GetSubcontext()));// 解析启动脚本中的import部分。parser.AddSectionParser(import, std::make_uniqueImportParser(parser));// 返回配置好的 Parser 对象。return parser; }总的来说这个函数的目的是创建一个能够解析启动脚本中的service、on和import部分的Parser对象。 init.rc 解析过程 - 执行 Action 动作 按顺序把相关 Action 加入触发器队列按顺序为 early-init - init - late-init。然后在循环中执行所有触发器队列中 Action 带 Command 的执行函数。 源码路径/system/core/init/init.cpp 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}.rcinit.rc 位于 /system/core/rootdir/ 下。在这个路径下还包括三个关于 zygote 的 rc 文件。分别是 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-namezygote// 将此服务归类为主服务。class main// 设置此服务的优先级为-20这是最高优先级意味着这个服务将优先于其他服务运行。priority -20// 设置此服务的用户和组为root同时给予了读取进程信息和访问保留磁盘的权限。user rootgroup root readproc reserved_disk// 创建了两个名为zygote和usap_pool_primary的socket权限为660所有者和组都是root和system。socket zygote stream 660 root systemsocket usap_pool_primary stream 660 root system// 定义了当服务重启时要执行的命令包括执行一些命令、写入一些系统文件、重启一些服务等。onrestart exec_background - system system -- /system/bin/vdc volume abort_fuseonrestart write /sys/power/state on# NOTE: If the wakelock name here is changed, then also# update it in SystemSuspend.cpponrestart write /sys/power/wake_lock zygote_kwlonrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart media.tuneronrestart restart netdonrestart restart wificond// 设置了任务的性能配置文件。task_profiles ProcessCapacityHigh MaxPerformance// 设置了一个名为zygote-fatal的目标当zygote进程在定义的窗口时间内崩溃时将会触发这个目标。critical window${zygote.critical_window.minute:-off} targetzygote-fatal总的来说这个脚本定义了 zygote 服务的行为和属性包括它如何启动它的权限它的优先级以及当它重启时应该执行的操作。 十、总结 init 进程启动过程分为三个阶段 第一阶段主要工作是挂载分区创建设备节点和一些关键目录初始化日志输出系统并启用 SELinux 安全策略。 第二阶段主要工作是初始化属性系统解析 SELinux 的策略文件处理子进程终止信号启动系统属性服务。每一项都是关键的。如果说第一阶段是为属性系统和 SELinux 做准备那么第二阶段就是真正去实现这些功能。 第三阶段主要是解析 init.rc 文件来启动其他进程并进入一个无限循环进行子进程的实时监控。 参考 Android 10.0系统启动之init进程-[Android取经之路]
http://www.tj-hxxt.cn/news/233265.html

相关文章:

  • vip视频解析网站怎么做的行业网站大全
  • 百度多久收录一次网站中国建行官网首页
  • 电商自建站wordpress用户名怎么起
  • 网站进入之前动态效果管局审核网站名称
  • 网站设计一般是什么专业建立网站需要哪些东西
  • 外贸公司网站如何免费推广域名注册在那个网站好
  • 四川省工程建设协会网站龙岩网站开发较好的公司
  • php 网站下载器苏州市吴江区建设局网站
  • 阿里网站如何做接入做网站的周记
  • 做中考试卷的网站东莞长安营销型网站建设
  • 网站开发建设合同模板桂林网站制作推荐
  • 黑龙江省建设集团网站设计本和游戏本的区别
  • 静态网页有哪些网站云典wordpress
  • 网站建设的能力第一媒体app最新版本
  • 广西网站建设教程用asp做网站span
  • qq音乐如何做mp3下载网站在哪里购买域名
  • 陕西交通建设集团蓝商分公司网站wordpress 手机看不了视频
  • 掼蛋网站建设深圳燃气公司招聘
  • 网站建设类书籍石家庄seo网站管理
  • 上海网站建设上海迈歌做网站发布信息
  • 江宁网站建设制作怎样宣传自己的品牌
  • 有什么类型的网站矿泉水瓶手工制作大全
  • 产权交易中心网站建设的原因广州企业网站seo
  • 国外网站顶部菜单设计网站描述怎样写
  • 传统pc网站云南省网站建设
  • 网站有几种语言开发的北京定制网站开发
  • html5网站开发需要框架吗网站js效果
  • 360网站认证域名p站代理网址
  • 曲阜市古建设计院网站百度电脑版入口
  • 淘宝客做网站需要那些条件wordpress点击量设置