Android Init(后期还会更加完善)
代码分析基于android-12.0.0_r28
前期阶段
kernel/init/main.c
:
static int __ref kernel_init(void *unused)
{// ...省略一堆代码if (execute_command) {ret = run_init_process(execute_command);if (!ret)return 0;panic("Requested init %s failed (error %d).",execute_command, ret);}if (!try_to_run_init_process("/sbin/init") ||!try_to_run_init_process("/etc/init") ||!try_to_run_init_process("/bin/init") ||!try_to_run_init_process("/bin/sh"))return 0;panic("No working init found. Try passing init= option to kernel. ""See Linux Documentation/admin-guide/init.rst for guidance.");
}
首先对内核启动命令进行判断,如果有路径对应的命令输入,则执行路径下对应的用户空间进程。execute_command
的值是通过uboot
传递的,在 bootargs
中使用"init=xxxx"
就可以将值传给他,比如"init=/linuxrc"
表示根文件系统中的 linuxrc
就是要执行的用户空间 init
程序。
如果没有用户空间进程的路径,则执行linux
内核中默认指定的用户空间进程路径:
/sbin/init 、/etc/init 、/bin/init、/bin/sh
如果以上linux
内核指定的默认用户空间进程路径下都不存在对应的程序,linux
内核将输出panic
信息。
正常启动来说,内核层传给system/core/init/main.cpp
的参数:argc:1,argv:init
。所以,第一阶段调用了FirstStageMain
Linux Kernel
在启动完成之后,随后是找到要启动的init
程序并启动init
进程,在Android
中,init
进程的入口是在:
android-12.0.0_r28/system/core/init/main.cpp
int main(int argc, char** argv) {#if __has_feature(address_sanitizer)__asan_set_error_report_callback(AsanReportCallback);
#endif// Boost prio which will be restored later// 设置当前进程的优先级, init的进程ID为0setpriority(PRIO_PROCESS, 0, -20);if (!strcmp(basename(argv[0]), "ueventd")) {// 初始化设备,监听uevent事件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")) {// 创建Selinuxreturn SetupSelinux(argv);}if (!strcmp(argv[1], "second_stage")) {return SecondStageMain(argc, argv);}}return FirstStageMain(argc, argv);
}
setpriority
:是一个方法,它可以用于设置进程的优先级。进程的优先级决定了系统在有限的资源下如何为进程分配资源。通过设置优先级,您可以指定进程在系统中的重要性级别,以便系统可以为其分配适当的资源。- 头文件为
#include <sys/time.h>
,#include <sys/resource.h>
- 函数原型:
int setpriority(int which, id_t who, int prio);
which
:PRIO_PROCESS
:表示设置指定进程的优先级;PRIO_PGRP
:表示设置指定进程组的所有进程的优先级;PRIO_USER
:表示设置指定用户的所有进程的优先级。who
:参数指定要设置优先级的进程、进程组或用户的 IDprio
:参数指定要设置的优先级值,范围从-20
(最高优先级)到19
(最低优先级)
- 头文件为
第一阶段调用FirstStageMain
android-12.0.0_r28/system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {//该宏REBOOT_BOOTLOADER_ON_PANIC由system/core/init/Android.mk中定义,//只有user,eng版本,REBOOT_BOOTLOADER_ON_PANIC=1if (REBOOT_BOOTLOADER_ON_PANIC) {// init信号处理器,当init crash,打印当前进程的回溯信息对象(调用栈信息),// 并重启到 bootLoader,可以看1.1、InstallRebootSignalHandlers分析InstallRebootSignalHandlers();}//记录当前时间start_time boot_clock::time_point start_time = boot_clock::now();// 用来存放执行的命令失败时的error code的std::vector窗口std::vector<std::pair<std::string, int>> errors;
#define CHECKCALL(x) \if ((x) != 0) errors.emplace_back(#x " failed", errno);// umask(0)用于设置当前进程的文件模式创建屏蔽字// 用户创建文件夹权限值=初始创建文件夹默认值-umask的预设值// 如:775=777-002// 用户创建文件权限值=初始创建文件默认值-umask的预设值// 如:664=666-002umask(0);// clearenv是一个 C 标准库函数,它的作用是清除当前进程环境中所有的环境变量CHECKCALL(clearenv());// 将默认的 PATH 环境变量设置到进程环境中CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));/*initramfs(Initial RAM File System)是一个临时的文件系统,它存在于内存中,在 Linux 内核引导过程中使用。Android 启动过程中,initramfs中的文件会被挂载为根文件系统,然后init进程会按照 /init.rc 文件中的配置启动其他服务和进程,最终构建出完整的 Android 系统*/// 将文件系统tmpfs挂载到/dev目录下CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));// 创建权限值为0755的目录/dev/ptsCHECKCALL(mkdir("/dev/pts", 0755));// 创建权限值为0755的目录/dev/socketCHECKCALL(mkdir("/dev/socket", 0755));// 创建权限值为0755的目录/dev/dm-userCHECKCALL(mkdir("/dev/dm-user", 0755));// 将文件系统devpts挂载/dev/pts目录下CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)// 将文件系统proc挂载proc目录下CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR// 禁止非特权进程(即非root进程)读取文件内容// 设置文件/proc/cmdline的权限为0440CHECKCALL(chmod("/proc/cmdline", 0440));std::string cmdline;// 读取文件/proc/cmdline的信息到cmdline中android::base::ReadFileToString("/proc/cmdline", &cmdline);// 禁止非特权进程(即非root进程)读取文件内容// 设置文件/proc/bootconfig的权限为0440chmod("/proc/bootconfig", 0440);std::string bootconfig;// 读取文件/proc/bootconfig的信息到bootconfig中android::base::ReadFileToString("/proc/bootconfig", &bootconfig);gid_t groups[] = {AID_READPROC};/* 设置进程的附属组为AID_READPROC(3009)通过将init进程的附属组设置为 AID_READPROC 组,init进程就具有了读取 /proc 目录下文件的权限*/ CHECKCALL(setgroups(arraysize(groups), groups));// 将文件系统sysfs挂载到/sys目录下CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));// 将文件系统selinuxfs挂载到/sys/fs/selinux目录下CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));// 创建字符设备节点/dev/kmsg,主设备号为1,次设备号为11,权限cr--------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)));}// 创建字符设备节点/dev/random,主设备号为1,次设备号为8,权限crw-rw-rw-CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));// 创建字符设备节点/dev/urandom,主设备号为1,次设备号为9,权限crw-rw-rw-CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));// 日志包装器(log wrapper)需要在 ueventd 运行之前运行,因此需要进行一些初始化操作// 创建字符设备节点/dev/ptmx,主设备号为5,次设备号为2,权限crw-rw-rw-CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));// 创建字符设备节点/dev/null,主设备号为1,次设备号为3,权限crw-rw-rw-CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));/* 挂载一个 tmpfs 文件系统到 /mnt 目录,以便第一阶段启动过程中挂载 /mnt/{vendor,product}/ 目录的子目录。其他的,放在第二阶段通过rc文件解析来加载*/CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,"mode=0755,uid=0,gid=1000"));// 创建权限值为0755的目录/mnt/vendorCHECKCALL(mkdir("/mnt/vendor", 0755));// 创建权限值为0755的目录/mnt/productCHECKCALL(mkdir("/mnt/product", 0755));// /debug_ramdisk目录的作用,即用于保存来自debug ramdisk// 文件系统中的额外文件。这些文件通常用于调试和故障排除目的// 将文件系统tmpfs挂载到/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// 将文件系统tmpfs挂载到/second_stage_resources目录下CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,"mode=0755,uid=0,gid=0"))
#undef CHECKCALL// 标准输入、标准输出和标准错误输出重定向到/dev/null设备文件中SetStdioToDevNull(argv);// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually// talk to the outside world...// 初始化kernel的日志InitKernelLogging(argv);// 打印上述使用CHECKCALL宏定义执行出错的命令与error codeif (!errors.empty()) {for (const auto& [error_string, error_errno] : errors) {LOG(ERROR) << error_string << " " << strerror(error_errno);}LOG(FATAL) << "Init encountered errors starting first stage, aborting";}LOG(INFO) << "init first stage started!";// 打开根目录"/"auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};if (!old_root_dir) {PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";}struct stat old_root_info;// 用stat函数获取根目录"/"的文件信息if (stat("/", &old_root_info) != 0) {PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";old_root_dir.reset();}// 该宏ALLOW_FIRST_STAGE_CONSOLE 由system/core/init/Android.mk中定义,// 只有user,eng版本,ALLOW_FIRST_STAGE_CONSOLE =1// FirstStageConsole:从cmdline,boot获取androidboot.first_stage_console=xx的值,// 如果没有获取到,直接返回FirstStageConsoleParam::DISABLEDauto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;// 记录当前系统时间到module_start_timeboot_clock::time_point module_start_time = boot_clock::now();int module_count = 0;// ForceNormalBoot:从cmdline,bootconfig是否有androidboot.force_normal_boot=1,// LoadKernelModules:从/lib/modules insmod内核模块,并把加载到的内核模块数量保存到module_count if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,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);//设置INIT_MODULE_DURATION_MS=module_elapse_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";}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);}// 判断/system/etc/ramdisk/build.prop文件是否存在if (access(kBootImageRamdiskProp, F_OK) == 0) {// dest = "/second_stage_resources/system/etc/ramdisk/build.prop"std::string dest = GetRamdiskPropForSecondStage();// dir = "/second_stage_resources/system/etc/ramdisk/" std::string dir = android::base::Dirname(dest);std::error_code ec;// 创建dir目录if (!fs::create_directories(dir, ec) && !!ec) {LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();}// 复制/system/etc/ramdisk/build.prop文件到/second_stage_resources/system/etc/ramdisk/目录中if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) {LOG(FATAL) << "Can't copy " << kBootImageRamdiskProp << " to " << dest << ": "<< ec.message();}LOG(INFO) << "Copied ramdisk prop to " << dest;}/* 如果设备的 /force_debuggable 文件存在,则第二阶段调用SecondStageMain的进程会使用userdebug版本的SELinux Policy,同时还会解析adb_debug.prop属性文件,以便在设备未锁定的情况下(bootloader解锁)允许使用adb root权限。*/// 判断/force_debuggable文件是否存在if (access("/force_debuggable", F_OK) == 0) {std::error_code ec; // to invoke the overloaded copy_file() that won't throw.// 复制文件/adb_debug.prop到/debug_ramdisk/adb_debug.prop文件// 复制文件/userdebug_plat_sepolicy.cil到/debug_ramdisk/userdebug_plat_sepolicy.cil文件if (!fs::copy_file("/adb_debug.prop", kDebugRamdiskProp, ec) ||!fs::copy_file("/userdebug_plat_sepolicy.cil", kDebugRamdiskSEPolicy, ec)) {LOG(ERROR) << "Failed to setup debug ramdisk";} else {// setenv for second-stage init to read above kDebugRamdisk* files./*复制成功后设置INIT_FORCE_DEBUGGABLE的环境变量为1,会用于第二阶段调用SecondStageMain的init读取kDebugRamdiskRrop(/debug_ramdisk/adb_debug.prop)文件:SecondStageMain->PropertyInit->PropertyLoadBootDefaults。*/ setenv("INIT_FORCE_DEBUGGABLE", "true", 1);}}// ForceNormalBoot:从cmdline,bootconfig是否有androidboot.force_normal_boot=1,if (ForceNormalBoot(cmdline, bootconfig)) {mkdir("/first_stage_ramdisk", 0755);// SwitchRoot() must be called with a mount point as the target, so we bind mount the// target directory to itself here.// 重新挂载/first_stage_ramdisk目录if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";}// 将根目录由"/"切换到/first_stage_ramdiskSwitchRoot("/first_stage_ramdisk");}// 挂载 system、vendor 、product等系统分区if (!DoFirstStageMount(!created_devices)) {LOG(FATAL) << "Failed to mount required partitions early ...";}struct stat new_root_info;if (stat("/", &new_root_info) != 0) {PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";old_root_dir.reset();}if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);}// 初始化安全框架 Android Verified Boot,用于防止系统文件本身被篡改、防止系统回滚,以免回滚系统利用以前的漏洞。// 包括Secure Boot, verified boot 和 dm-verity(会校验只读分区大小,若只读分区二进制改变则可能上被串改了,例如 user强制root),// 原理都是对二进制文件进行签名,在系统启动时进行认证,确保系统运行的是合法的二进制镜像文件。其中认证的范围涵盖:bootloader,boot.img,system.img。// 此处是在recovery模式下初始化avb的版本,不是recovery模式直接跳过SetInitAvbVersionInRecovery();// 设置环境变量FIRST_STAGE_STARTED_AT=start_time,即FirstStageMain方法启动时的时间setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),1);const char* path = "/system/bin/init";const char* args[] = {path, "selinux_setup", nullptr};auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);// 将标准输出重定向到/dev/kmsgdup2(fd, STDOUT_FILENO);// 将标准错误重定向到/dev/kmsgdup2(fd, STDERR_FILENO);close(fd);// execv()函数不会创建新的进程,而是将当前进程替换为新的程序// path为执行程序的路径:/system/bin/init// args为传递的参数:selinux_setup// 执行,即重新回到system/core/init/main.cpp,执行第二阶段execv(path, const_cast<char**>(args));// execv() only returns if an error happened, in which case we// panic and never fall through this conditional.PLOG(FATAL) << "execv(\"" << path << "\") failed";return 1;
}
1.1、InstallRebootSignalHandlers
void InstallRebootSignalHandlers() {// Linux panic 状态:内核会终止所有正在运行的进程,并输出一些有关错误的诊断信息,最终系统会被强制关机或者重启// 当init崩溃时,我们在开发构建(userdebug,eng版本)中更倾向于重启到bootloader,而不是像linux中进入panic状态,// 因为这将防止boot循环错误的配置,这同时也允许开发人员和测试场轻松恢复 struct sigaction action;memset(&action, 0, sizeof(action));sigfillset(&action.sa_mask);// 信号处理函数action.sa_handler = [](int signal) {// These signal handlers are also caught for processes forked from init, however we do not// want them to trigger reboot, so we directly call _exit() for children processes here.if (getpid() != 1) {_exit(signal);}// Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.// RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option// and probably good enough given this is already an error case and only enabled for// development builds.InitFatalReboot(signal);};// SA_RESTART 表示如果系统调用被中断,自动重新启动该系统调用。action.sa_flags = SA_RESTART;// SIGABRT:中止信号,通常是由程序调用abort函数发送的sigaction(SIGABRT, &action, nullptr);// SIGBUS:总线错误信号,通常由进程对不能执行操作的地址进行访问时产生,例如非对齐访问或虚拟地址在物理内存上没有映射等情况sigaction(SIGBUS, &action, nullptr);// SIGFPE:浮点异常信号,通常由浮点运算错误产生sigaction(SIGFPE, &action, nullptr);// SIGILL:非法指令信号,通常由进程试图执行未定义的指令或数据错误引起sigaction(SIGILL, &action, nullptr);// SIGSEGV:段错误信号,通常由进程对非法的地址空间进行访问时产生sigaction(SIGSEGV, &action, nullptr);
#if defined(SIGSTKFLT)// SIGSTKFLT:协处理器栈故障sigaction(SIGSTKFLT, &action, nullptr);
#endif// SIGSYS:系统调用错误信号。当一个进程调用一个不存在的系统调用时,内核会发送 SIGSYS 信号给该进程,告知它系统调用发生错误。sigaction(SIGSYS, &action, nullptr);// SIGTRAP:调试器或进程自身触发的信号,用于暂停进程并允许调试器执行某些操作。通常,调试器会使用SIGTRAP在进程中设置断点或跟踪执行路径。sigaction(SIGTRAP, &action, nullptr);
}
android-12.0.0_r28/system/core/init/reboot_utils.cpp
void __attribute__((noreturn)) InitFatalReboot(int signal_number) {auto pid = fork();if (pid == -1) {// fork失败,重启到bootloader(init_fatal_reboot_target = "bootloader")RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);} else if (pid == 0) {// 尽管父进程负责打印当前进程与当前线程的堆栈信息,也负责关机重启的操作。// 为了安全起见,fork一个子进程也要为我们关机重启操作,就是确保关机重启操作。sleep(5);RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);}// In the parent, let's try to get a backtrace then shutdown.LOG(ERROR) << __FUNCTION__ << ": signal " << signal_number;// 创建当前进程与当前线程的堆栈信息类std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));// 用于获取堆栈信息,并将结果保存在backtrace对象中if (!backtrace->Unwind(0)) {LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";}// backtrace->->NumFrames():backtrace保存的堆栈帧数量for (size_t i = 0; i < backtrace->->NumFrames(); i++) {// backtrace->FormatFrameData(i):将指定位置的堆栈帧信息格式化为一段字符串// 然后通过LOG输出LOG(ERROR) << backtrace->FormatFrameData(i);}// 读取/proc/cmdline或者/proc/bootconifg,是否含有roidboot.init_fatal_panic=true,来判断init_fatal_panic的bool值if (init_fatal_panic) {LOG(ERROR) << __FUNCTION__ << ": Trigger crash";//对/proc/sysrq-trigger写入字符‘c’android::base::WriteStringToFile("c", PROC_SYSRQ);LOG(ERROR) << __FUNCTION__ << ": Sys-Rq failed to crash the system; fallback to exit().";// 退出当前进程_exit(signal_number);}// 重启方法调用RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
}void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {LOG(INFO) << "Reboot ending, jumping to kernel";// 判断init进程是否有重启能力,没有则直接退出if (!IsRebootCapable()) {// On systems where init does not have the capability of rebooting the// device, just exit cleanly.exit(0);}switch (cmd) {case ANDROID_RB_POWEROFF:// linux系统中一个标准的用户空间命令reboot,用户空间执行reboot(RB_POWER_OFF);break;case ANDROID_RB_RESTART2:// syscall函数是一种特殊的函数调用,用于从用户空间向内核空间请求服务// 下面语句用于在内核中执行reboot操作,内核空间执行syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());break;case ANDROID_RB_THERMOFF:// 当设备温度过高时,系统会尝试通过降低 CPU 频率等方式来降低温度。// 但如果这些措施都无法降低温度,那么系统就会执行热重启操作,这是一种比正常重启更快的方式,以防止硬件过热而导致设备损坏。// "ro.thermal_warmreset" 这个属性控制热重启功能的开启和关闭if (android::base::GetBoolProperty("ro.thermal_warmreset", false)) {LOG(INFO) << "Try to trigger a warm reset for thermal shutdown";static constexpr const char kThermalShutdownTarget[] = "shutdown,thermal";// 调用内核空间执行rebootsyscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);} else {// 调用用户空间的reboot方法reboot(RB_POWER_OFF);}break;}// In normal case, reboot should not return.PLOG(ERROR) << "reboot call returned";// 向进程发送一个SIGABRT信号,使进程终止abort();
}
第二阶段调用SetupSelinux
system/core/init/main.cpp
if (!strcmp(argv[1], "selinux_setup")) {// 创建Selinuxreturn SetupSelinux(argv);}
system/core/init/selinux.cpp
// The SELinux setup process is carefully orchestrated around snapuserd. Policy
// must be loaded off dynamic partitions, and during an OTA, those partitions
// cannot be read without snapuserd. But, with kernel-privileged snapuserd
// running, loading the policy will immediately trigger audits.
//
// We use a five-step process to address this:
// (1) Read the policy into a string, with snapuserd running.
// (2) Rewrite the snapshot device-mapper tables, to generate new dm-user
// devices and to flush I/O.
// (3) Kill snapuserd, which no longer has any dm-user devices to attach to.
// (4) Load the sepolicy and issue critical restorecons in /dev, carefully
// avoiding anything that would read from /system.
// (5) Re-launch snapuserd and attach it to the dm-user devices from step (2).
//
// After this sequence, it is safe to enable enforcing mode and continue booting.
int SetupSelinux(char** argv) {// 将标准输入、标准输出和标准错误输出重定向到/dev/null设备文件,// 在运行时避免产生不必要的输出或接收用户的输入,从而避免产生垃圾信息和消耗系统资源。SetStdioToDevNull(argv);// 初始化kernel的日志InitKernelLogging(argv);// 该宏REBOOT_BOOTLOADER_ON_PANIC由system/core/init/Android.mk中定义,// 只有user,eng版本,REBOOT_BOOTLOADER_ON_PANIC=1if (REBOOT_BOOTLOADER_ON_PANIC) {// init信号处理器,当init crash,打印当前进程的回溯信息对象(调用栈信息),并重启到 bootLoader,InstallRebootSignalHandlers();}// 记录当前时间start_time boot_clock::time_point start_time = boot_clock::now();// 用于检查并挂载缺失的系统分区。具体来说,// 该函数会依次检查 /system、/vendor、/odm 这些分区是否挂载成功,// 如果没有挂载成功,则会尝试挂载它们。MountMissingSystemPartitions();// 注册回调,用来设置需要写入kmsg的selinux日志// 对于安全性和调试都非常重要,可以帮助管理员及时发现和解决 SELinux 相关的安全问题。SelinuxSetupKernelLogging();LOG(INFO) << "Opening SELinux policy";// Read the policy before potentially killing snapuserd.std::string policy;// snapuserd 是 Android 操作系统中的一个进程,它负责管理 Snapshots 镜像的创建、合并和恢复等操作。// Snapshots 是一种存储系统状态的技术,它可以让系统在崩溃或其他异常情况下快速恢复到之前的状态,从而提高系统的可靠性和可用性。// 在 Android 系统中,snapuserd 进程通常在系统启动时就会自动启动,并一直运行在后台。// 在杀死snapuserd前,读取 SELinux 策略ReadPolicy(&policy);// 1.在 Android 系统中,通常会使用 SELinux 上下文转换来提高系统的安全性。// 2.具体而言,当进程需要执行某些特权操作时,完成 Snapuserd 进程的 SELinux 上下文转换过程// 3.可以将其当前的 SELinux 上下文转换为具有更高权限的上下文,以完成相应的操作。// 4.转换完成后,系统会自动将进程的 SELinux 上下文转换回原始的上下文,以保证系统的安全性。// 创建 Snapuserd 进程所需的 SELinux 上下文auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();if (snapuserd_helper) {// Kill the old snapused to avoid audit messages. After this we cannot// read from /system (or other dynamic partitions) until we call// FinishTransition().// 启动 Snapuserd 进程的 SELinux 上下文转换过程,以授予其必要的特权权限,// 并确保其能够正常运行。snapuserd_helper->StartTransition();}// 加载 SELinux 策略LoadSelinuxPolicy(policy);if (snapuserd_helper) {// Before enforcing, finish the pending snapuserd transition.// 完成 Snapuserd 进程的 SELinux 上下文转换过程,// 转换回原始的SELinux上下文snapuserd_helper->FinishTransition();snapuserd_helper = nullptr;}// 设置 SELinux 系统的执行模式SelinuxSetEnforcement();// We're in the kernel domain and want to transition to the init domain. File systems that// store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,// but other file systems do. In particular, this is needed for ramdisks such as the// recovery image for A/B devices.// 该方法用于恢复 /system/bin/init 文件的 SELinux 安全上下文。// 在 SELinux 系统中,每个文件都有一个对应的安全上下文,用于控制文件的访问权限。// 如果文件的安全上下文被修改或者损坏,// selinux_android_restorecon() 函数来重新设置安全上下文。if (selinux_android_restorecon("/system/bin/init", 0) == -1) {PLOG(FATAL) << "restorecon failed of /system/bin/init failed";}// 设置环境变量SELINUX_STARTED_AT=start_time,即SetupSelinux方法启动时的时间setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);const char* path = "/system/bin/init";const char* args[] = {path, "second_stage", nullptr};// execv()函数不会创建新的进程,而是将当前进程替换为新的程序// path为执行程序的路径:/system/bin/init// args为传递的参数:second_stage// 执行,即重新回到system/core/init/main.cpp,执行第三阶段execv(path, const_cast<char**>(args));// execv() only returns if an error happened, in which case we// panic and never return from this function.PLOG(FATAL) << "execv(\"" << path << "\") failed";return 1;
}
2.1、SetStdioToDevNull
android-12.0.0_r28/system/core/init/util.cpp
void SetStdioToDevNull(char** argv) {int fd = open("/dev/null", O_RDWR); // NOLINT(android-cloexec-open)if (fd == -1) {int saved_errno = errno;android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);errno = saved_errno;PLOG(FATAL) << "Couldn't open /dev/null";}// 将标准输入、标准输出和标准错误输出重定向到/dev/null设备文件,dup2(fd, STDIN_FILENO);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);if (fd > STDERR_FILENO) close(fd);
}
2.2、InitKernelLogging
android-12.0.0_r28/system/core/init/util.cpp
void InitKernelLogging(char** argv) {SetFatalRebootTarget();android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);
}
android-12.0.0_r28/system/core/init/reboot_utils.cpp
void SetFatalRebootTarget(const std::optional<std::string>& reboot_target) {std::string cmdline;android::base::ReadFileToString("/proc/cmdline", &cmdline);cmdline = android::base::Trim(cmdline);const std::string kInitFatalPanicParamString = "androidboot.init_fatal_panic";if (cmdline.find(kInitFatalPanicParamString) == std::string::npos) {init_fatal_panic = false;ImportBootconfig([kInitFatalPanicParamString](const std::string& key, const std::string& value) {if (key == kInitFatalPanicParamString && value == "true") {init_fatal_panic = true;}});} else {const std::string kInitFatalPanicString = kInitFatalPanicParamString + "=true";init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;}if (reboot_target) {init_fatal_reboot_target = *reboot_target;return;}const std::string kRebootTargetString = "androidboot.init_fatal_reboot_target";auto start_pos = cmdline.find(kRebootTargetString);if (start_pos == std::string::npos) {ImportBootconfig([kRebootTargetString](const std::string& key, const std::string& value) {if (key == kRebootTargetString) {init_fatal_reboot_target = value;}});// We already default to bootloader if no setting is provided.} else {const std::string kRebootTargetStringPattern = kRebootTargetString + "=";start_pos += sizeof(kRebootTargetStringPattern) - 1;auto end_pos = cmdline.find(' ', start_pos);// if end_pos isn't found, then we've run off the end, but this is okay as this is the last// entry, and -1 is a valid size for string::substr();auto size = end_pos == std::string::npos ? -1 : end_pos - start_pos;init_fatal_reboot_target = cmdline.substr(start_pos, size);}
}
上面的代码其实,就是从"/proc/cmdline
","/proc/bootimage
"的读取:
- 是否含有”
androidboot.init_fatal_panic=true
“来确定init_fatal_panic
是true
还是false
- 读取到"
androidboot.init_fatal_reboot_target
",然后赋值到init_fatal_reboot_target
变量(初始化值为bootloader
) - 总结来说,代码会通过这两个值,用来设置当内核日志记录发生致命错误时的行为(比如是否打印)
第三阶段调用SecondStageMain
system/core/init/main.cpp
if (!strcmp(argv[1], "second_stage")) {return SecondStageMain(argc, argv);}
system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {// 该宏REBOOT_BOOTLOADER_ON_PANIC由system/core/init/Android.mk中定义,// 只有user,eng版本,REBOOT_BOOTLOADER_ON_PANIC=1if (REBOOT_BOOTLOADER_ON_PANIC) {// init信号处理器,当init crash,打印当前进程的回溯信息对象(调用栈信息),并重启到 bootLoader,InstallRebootSignalHandlers();}// 记录当前系统时间到module_start_time }boot_clock::time_point start_time = boot_clock::now();trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };// 将标准输入、标准输出和标准错误输出重定向到/dev/null设备文件,// 在运行时避免产生不必要的输出或接收用户的输入,从而避免产生垃圾信息和消耗系统资源。SetStdioToDevNull(argv);// 初始化kernel的日志InitKernelLogging(argv);LOG(INFO) << "init second stage started!";// 更新环境变量$PATH,第二阶段 init 比第一阶段 init 更新,因为第一阶段 init 是第一次设置的。if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";}/* SA_RESTART :标志表示如果某个系统调用因为接收到信号而被中断,那么该系统调用将会被自动重启。SIGPIPE 信号:在 Linux 中表示管道或者套接字已经被关闭,但是程序还在试图向其写入数据;如果不处理这个信号,程序会立即退出,因为默认情况下 Linux 系统会向进程发送 SIGPIPE 信号并终止进程。由于 Init 进程不能因为与其他进程的依赖关系而崩溃,因此它会忽略 SIGPIPE 信号,并在调用处直接处理 EPIPE 错误。需要注意的是,进程如将信号设置为SIG_IGN,在调用 exec后会被继承,但自定义的信号处理程序(action.sa_handler = [](int) {})不会。由于我们不希望子进程忽略 SIGPIPE 信号,所以在信号处理函数中设置一个空函数。这样,对于 Init 进程,SIGPIPE 信号被忽略,而对于子进程,SIGPIPE 信号会按照默认方式处理。*/{struct sigaction action = {.sa_flags = SA_RESTART};action.sa_handler = [](int) {};// 设置进程在接收到 SIGPIPE 信号时的处理方式。sigaction(SIGPIPE, &action, nullptr);}/* 设置 init 进程及其衍生的子进程中设置 oom_adj 属性 oom_adj 是一个用于控制进程被内核杀死的顺序的属性。oom_adj 值越小的进程越不容易被杀死,而oom_adj 值较高的进程则更容易被杀死DEFAULT_OOM_SCORE_ADJUST=-1000,将/proc/1/oom_score_adj写值-1000,设置oom_adj,其中1是init的PID*/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();}/* 是用来获取进程的会话密钥环的标识符:会话密钥环是 Linux 内核中的一种机制,用于存储进程会话中使用的密钥。Android 也使用了这种机制,用于存储应用程序的加密密钥等敏感信息。通过调用 keyctl_get_keyring_ID() 函数获取会话密钥环的标识符,Android 系统可以在应用程序运行时访问会话密钥环中存储的密钥,从而保护应用程序中的敏感数据不受未授权的访问。*/keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);/* 这个文件描述符的创建是为了让其他进程(如后台固件加载程序)E.g:system/core/init/firmware_handler.cpp(负责解析里固件配置)static bool IsBooting() {return access("/dev/.booting", F_OK) == 0;}能够检查 /dev/.booting 文件的存在来确定系统是否正在启动,是第四阶段调用ueventd_main里面使用到*/close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));// 当设备的bootloader已解锁时,查看是否需要加载一些调试的系统属性去允许adb root。// 获取环境变量$INIT_FORCE_DEBUGGABLE的,再赋值到force_debuggable_envconst char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");bool load_debug_prop = false;// AvbHandle::IsDeviceUnlocked():设备的bootloader是否解锁if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {load_debug_prop = "true"s == force_debuggable_env;}// 清除环境变量$INIT_FORCE_DEBUGGABLE的值unsetenv("INIT_FORCE_DEBUGGABLE");// 卸载/debug_ramdisk,让下面的PropertyInit中不会加载它里面的xxx.prop文件// 如果load_debug_prop=false,则卸载/debug_ramdiskif (!load_debug_prop) {UmountDebugRamdisk();}// 初始化属性服务相关的,包括属性的selinux上下主,属性的key-value通过MMAP映射到全局,供所有进程使用PropertyInit();// 在属性服务读取.prop文件后,将卸载/second_stage_resourcesUmountSecondStageRes();// 在属性服务读取.prop文件后,如果load_debug_prop=true,则卸载/debug_ramdiskif (load_debug_prop) {UmountDebugRamdisk();}// 将一个 tmpfs 文件系统挂载到 /apex 目录和/linkerconfig下。// 具体来说,为应用程序提供一个可写的目录,用于存储应用程序的数据和配置信息MountExtraFilesystems();// 设置内核日志记录的SELinux标签,以确保内核记录的日志中包含SELinux标签信息// 对于安全性和调试都非常重要,可以帮助管理员及时发现和解决 SELinux 相关的安全问题。SelinuxSetupKernelLogging();// 初始化SELinux标签库,以便可以将SELinux标签应用于文件和目录。SelabelInitialize();// 根据文件上下文信息,恢复系统中文件和目录的SELinux安全上下文。SelinuxRestoreContext();// 这里的Epoll是在system/core/init/epoll.cpp对<sys/epoll>的封装的一个类Epoll epoll;// 创建一个epoll的fdif (auto result = epoll.Open(); !result.ok()) {PLOG(FATAL) << result.error();}// 防止子进程意外终止或被中断,从而导致父进程无法正常工作;监听子进程终止或者中断事件,回收子进程 InstallSignalFdHandler(&epoll);/* Init进程轮询各种fd以等待各种输入。以前它使用一个阻塞套接字来等待属性更改,该套接字包含与更改相关的信息,然而,很容易填充该套接字并使系统死锁。现在我们使用锁直接在属性线程中处理属性更改,但是我们仍然必须唤醒epoll来通知init有更改要处理,因此我们使用此FD。它是非阻塞的,不管WakeMainInitThread()被调用了多少次,只关心epoll会被唤醒。*/InstallInitNotifier(&epoll);/*之前PropertyInit()初始化了属性服务,这里将开始属性服务,其实它就是创建socketpair(一对一传递信息)与socket(一对8个连接),去处理客户端发来的请求,决定是更新属性值还是新增属性值*/StartPropertyService(&property_fd);/*设置:ro.boottime.init:linux forc出init进程当时的时间,ro.boottime.init.first_stage:第一阶段调用FirstStageMain所用的时间ro.boottime.init.selinux:第二阶段调用SetupSelinux所用的时间ro.boottime.init.modules:第一阶段调用FirstStageMain时,加载内核模块所用的时间*/RecordStageBoottimes(start_time);// Set libavb version for Framework-only OTA match in Treble build.// 在升级过程中使用的一种“框架级别”(Framework-only)OTA匹配方式需要,// 使用 libavb 库的版本信息来进行匹配,而某些较旧的设备没有在sysfs中// 报告AVB 版本,因此 init 进程需要在这些设备上设置 ro.boot.avb_version 属性,以便进行正确的 OTA 匹配。// 环境变量INIT_AVB_VERSION,是在FistStageMain->DoFirstStageMount(!created_devices) 可能会设置的if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) {SetProperty("ro.boot.avb_version", avb_version);}unsetenv("INIT_AVB_VERSION");// 挂载/(system|product)/vendor_overlay/<ver>到 /vendor分区上fs_mgr_vendor_overlay_mount_all();// 检测设备是否支持 OEM 解锁功能,并根据 Verified Boot 状态来设置 OEM 锁定状态的属性值。export_oem_lock_status();MountHandler mount_handler(&epoll);SetUsbController();SetKernelVersion();// 内置函数的映射表,这个映射表是由 /init.rc 文件中的 service 和 on 命令生成的可调用对象的集合。const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();/* 将内置函数映射表存储到 Action 类中的function_map_,以便后续的操作可以使用这些内置函数。这些内置函数可以在 /init.rc 文件中定义,并且可以在系统启动过程中被 init 进程调用。这些内置函数包括一些基本操作,比如创建目录mkdir、启动服务start等,它们为系统启动过程提供了基础支持。eg:system/core/roodir/init.rc中有,如下on early-initwrite /proc/sys/kernel/sysrq 0意义:当early-init 触发器被触发,会调用内置函数write,Action类就会从 function_map 中查找该函数(映射->>do_write),并调用它来执行相应的操作。*/Action::set_function_map(&function_map);if (!SetupMountNamespaces()) {PLOG(FATAL) << "SetupMountNamespaces failed";}// 在 Android 中,每个应用程序和系统组件都被分配了一个特定的安全上下文,该上下文指定了它们可以访问的资源和执行的操作。// Subcontext 类就是用来管理这些安全上下文的,它可以在初始化过程中为子进程创建特定的安全上下文,以确保它们在访问系统资源时具有适当的权限。// 为/vendor,/odm ,设置u:r:vendor_init:s0的子上下文InitializeSubcontext();// ActionManager 类负责管理系统启动时执行的所有操作(Action)。// 在启动过程中,init 进程会解析 init.rc 脚本,从中读取各个 Service 的定义,// 然后构建对应的 Action 并加入到 ActionManager 中。// ActionManager 会按照事先定义好的顺序执行这些 Action,以启动系统中所有的服务。ActionManager& am = ActionManager::GetInstance();// ServiceList 类则是用来管理系统中所有的服务(Service)的。// 在系统启动时,init 进程会读取 init.rc 中定义的 Service,并创建对应的 Service 实例。// 这些 Service 实例会被 ServiceList 管理起来,以便其他组件可以方便地查询和控制这些服务。// 例如,init 进程需要知道哪些服务已经启动成功、哪些服务启动失败等等,都需要通过 ServiceList 来查询。ServiceList& sm = ServiceList::GetInstance();// 解析/system/etc/init/hw/init.rc,/system/etc/init目录// 解析/system_ext/etc/init,/vendor/etc/init,/odm/etc/init,/product/etc/init目录所以*.rc的信息到ActionManager与ServiceList中LoadBootScripts(am, sm);// Turning this on and letting the INFO logging be discarded adds 0.2s to// Nexus 9 boot time, so it's disabled by default.if (false) DumpState();// Make the GSI status available before scripts start running.// 根据/metadata/gsi/dsu/booted文件是否存在,设置系统属性ro.gsid.image_running的值auto is_running = android::gsi::IsGsiRunning() ? "1" : "0";SetProperty(gsi::kGsiBootedProp, is_running);// 根据/metadata/gsi/dsu/install_status文件是否存在,设置系统属性gsid.image_installed的值auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";SetProperty(gsi::kGsiInstalledProp, is_installed);// 用于设置 cgroup 的相关属性,这是 Android 系统中进行资源隔离和限制的一种手段。am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");// 用于设置内核指针泄漏保护机制,它可以限制用户空间程序访问内核空间的信息,从而增强系统的安全性。am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");// 用于测试 PerfEvent 与 SELinux 之间的交互是否正常。am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");// 执行.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");// Trigger all the boot actions to get us started.// 执行.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");}// 基于属性当前状态 运行所有的属性触发器.// 运行所有属性触发器(action),例如 on propertyam.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");// Restore prio before main loopsetpriority(PRIO_PROCESS, 0, 0);while (true) {// By default, sleep until something happens.// 定义 epoll 超时变量,默认为无限等待auto epoll_timeout = std::optional<std::chrono::milliseconds>{};// 检查系统是否有关机的命令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);}// 依次执行每个action中携带command对应的执行函数// 如果没有属性等待或执行服务运行,则执行一条命令action命令if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {am.ExecuteOneCommand();}// 如果系统没有在关机状态,那么检查是否有需要重新启动的进程,// 如果有,则计算下一次执行该进程的时间并设置 epoll 等待时间为该时间。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;}}// 如果没有属性等待或执行服务运行,并且还有更多的命令需要执行,则立即唤醒 epoll。if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {if (am.HasMoreCommands()) epoll_timeout = 0ms;}// 循环等待事件发生,等待 epoll 唤醒,执行在 epoll 中注册的回调函数auto pending_functions = epoll.Wait(epoll_timeout);if (!pending_functions.ok()) {LOG(ERROR) << pending_functions.error();} else if (!pending_functions->empty()) {// We always reap children before responding to the other pending functions. This is to// prevent a race where other daemons see that a service has exited and ask init to// start it again via ctl.start before init has reaped it.ReapAnyOutstandingChildren();for (const auto& function : *pending_functions) {(*function)();}}if (!IsShuttingDown()) {HandleControlMessages();SetUsbController();}}return 0;
}
PropertyInit
android-12.0.0_r28/system/core/init/property_service.cpp
void PropertyInit() {// 设置 SELinux 回调函数(PropertyAuditCallback),以便在属性服务修改属性值时进行安全审计audit。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);// 读plat_property_contexts文件,初始化到propertyInfo数据结构中,// 然后创建trie数据结构,最后将trie数据结构写入到/dev/__properties__/property_info文件CreateSerializedPropertyInfo();if (__system_property_area_init()) {LOG(FATAL) << "Failed to initialize property area";}if (!property_info_area.LoadDefaultPath()) {LOG(FATAL) << "Failed to load serialized property info file";}// If arguments are passed both on the command line and in DT,// properties set in DT always have priority over the command-line ones./*设置属性值:ro.boot.hardware=xxxro.boot.mode=xxxro.boot.serialno=xxx*/ProcessKernelDt();// 从/proc/cmdline读取是否含有androidboot=xxx,如有,刚设置ro.boot.androidboot=xxxProcessKernelCmdline();// 从/proc/bootconfig读取是否含有androidboot=xxx,如有,刚设置ro.boot.androidboot=xxxProcessBootconfig();/*将下面ro.boot.xx的属性值复制给ro.xxx,即初始化ro.xxx的值{ "ro.boot.serialno", "ro.serialno", UNSET, },{ "ro.boot.mode", "ro.bootmode", "unknown", },{ "ro.boot.baseband", "ro.baseband", "unknown", },{ "ro.boot.bootloader", "ro.bootloader", "unknown", },{ "ro.boot.hardware", "ro.hardware", "unknown", },{ "ro.boot.revision", "ro.revision", "0", },*/ExportKernelBootProps();/*读取1、/system/build.prop2、/system_ext/etc/build.prop如上失败,android R(30)设备及以下,则会解析如下两:2.1、/system_ext/default.prop2.2、/system_ext/build.prop3、/vendor/default.prop4、/vendor/build.prop5、/vendor_dlkm/etc/build.prop6、/odm_dlkm/etc/build.prop7、/odm/etc/build.prop如上失败,android P(28)设备及以下,则会解析如下两7.1、/odm/default.prop7.2、/odm/build.prop8、/product/etc/build.prop如上失败,android R(30)设备及以下,则会解析如下两8.1、/product/default.prop8.2、/product/build.prop 越是靠后读取的key-value,优先级越高,因为读取后会覆盖原来的值,优先级:product > odm > odm_dlkm > vendor > system_ext > system,最后将上面读取的数据通过MMAP映射到全局内存中,供所有进程访问 */PropertyLoadBootDefaults();
}//...
CreateSerializedPropertyInfo
android-12.0.0_r28/system/core/init/property_service.cpp
void CreateSerializedPropertyInfo() {// PropertyInfoEntry 是一个结构体,用于存储属性信息,解析下面的xxx_property_contexts文件用的auto property_infos = std::vector<PropertyInfoEntry>();// 判断/system/etc/selinux/plat_property_contexts文件是否可读if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {// 解析/system/etc/selinux/plat_property_contexts,该文件内容配置的是属性值的selinux上下文,selinux权限相关if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",&property_infos)) {return;}// 解析/system_ext/etc/selinux/system_ext_property_contextsif (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",&property_infos);}// 解析/vendor/etc/selinux/vendor_property_contextsif (!LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",&property_infos)) {// Fallback to nonplat_* if vendor_* doesn't exist.LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",&property_infos);}// 解析/product/etc/selinux/product_property_contextsif (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",&property_infos);}// 解析/odm/etc/selinux/odm_property_contextsif (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);}} else {// 解析/plat_property_contexts,if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {return;}// 解析/system_ext_property_contexts,LoadPropertyInfoFromFile("/system_ext_property_contexts", &property_infos);// 解析/vendor_property_contexts,if (!LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos)) {// Fallback to nonplat_* if vendor_* doesn't exist.// 解析/nonplat_property_contexts,LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);}// 解析/product_property_contexts,LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);// 解析/odm_property_contexts,LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);}auto serialized_contexts = std::string();auto error = std::string();// 1、将property_infos转化成Trie树(字典树,方便高效查询)// 2、Trie树序列化成字符串serialized_contextsif (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,&error)) {LOG(ERROR) << "Unable to serialize property contexts: " << error;return;}// 3、将字符串serialized_contexts写进/dev/__properties__/property_info中constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {PLOG(ERROR) << "Unable to write serialized property infos to file";}// 将dev/__properties__/property_info文件的`selinux上下文设置为`u:object_r:property_info:s0`selinux_android_restorecon(kPropertyInfosPath, 0);
}
// ... `std::set<std::string> contexts_`;
读取以下文件,这些文件描述了属性和
selinux
上下文的对应关系:
/system/etc/selinux/plat_property_contexts
,/system_ext/etc/selinux/system_ext_property_contexts
,/vendor/etc/selinux/vendor_property_contexts
,/product/etc/selinux/product_property_contexts
,/odm/etc/selinux/odm_property_contexts
读取到的内容解析成PropertyInfoEntry数组,每个PropertyInfoEntry对象对应着属性的名称,selinux上下文,属性的类型以及是否为exact_match属性。
调用
BuildTrie
函数将PropertyInfoEntry
数组解析Trie
树(字典树),树由TrieBuilder
类表示,树的结点由TrieBuilderNode
类表示,树的根结点对象为TrieBuilder
类的成员变量TrieBuilderNode builder_root_
TrieBuilder
类另有两个成员变量:std::set<std::string> contexts_
和std::set<std::string> types_
分别表示解析到的所有selinux
上下文列表以及所有属性的类型列表。
接下来将通过类TrieSerializer
的SerializeTrie()
函数来实现序列化,将TrieBuilder
对象序列化为一个字符串,并且将字符串写入文件:/dev/__properties__/property_info
,此文件本身的selinux
上下文为:u:object_r:property_info:s0
。调用
selinux_android_restorecon
将/dev/__properties__/property_info
文件的selinux
上下文设置为u:object_r:property_info:s0
。
LoadPropertyInfoFromFile
android-12.0.0_r28/system/core/init/property_service.cpp
bool LoadPropertyInfoFromFile(const std::string& filename,std::vector<PropertyInfoEntry>* property_infos) {auto file_contents = std::string();// 读取filename的文件到file_contents中if (!ReadFileToString(filename, &file_contents)) {PLOG(ERROR) << "Could not read properties from '" << filename << "'";return false;}auto errors = std::vector<std::string>{};// SelinuxGetVendorAndroidVersion:读取/vendor/etc/selinux/plat_sepolicy_vers.txt,获取SELinux厂商映射的Android版本号// require_prefix_or_exact:决定是否要求系统属性名必须以特定的前缀开头,或者完全匹配才能被访问;// Android R(30) 以上的设备,require_prefix_or_exact一般为truebool require_prefix_or_exact = SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__;// 解析xxx_property_contexts文件的每一行数据,// 然后将每一行的数据读取后转化成存储属性信息的结构体PropertyInfoEntry,最后每一行的PropertyInfoEntry添加到列表property_infos中ParsePropertyInfoFile(file_contents, require_prefix_or_exact, property_infos, &errors);// Individual parsing errors are reported but do not cause a failed boot, which is what// returning false would do here.for (const auto& error : errors) {LOG(ERROR) << "Could not read line from '" << filename << "': " << error;}return true;
}
ParsePropertyInfoFile
android-12.0.0_r28/system/core/property_service/libpropertyinfoserializer/property_info_file.cpp
void ParsePropertyInfoFile(const std::string& file_contents, bool require_prefix_or_exact,std::vector<PropertyInfoEntry>* property_infos,std::vector<std::string>* errors) {errors->clear();// 每行进行解析for (const auto& line : Split(file_contents, "\n")) {auto trimmed_line = Trim(line);// 空行和以#开头注释不解析if (trimmed_line.empty() || StartsWith(trimmed_line, "#")) {continue;}// 创建存储属性信息的结构体PropertyInfoEntryauto property_info_entry = PropertyInfoEntry{};auto parse_error = std::string{};// 真正解析xxx_property_contexts文件每一行数据的方法ParsePropertyInfoLineif (!ParsePropertyInfoLine(trimmed_line, require_prefix_or_exact, &property_info_entry,&parse_error)) {errors->emplace_back(parse_error);continue;}property_infos->emplace_back(property_info_entry);}
}// 真正解析xxx_property_contexts文件每一行数据的方法ParsePropertyInfoLine
bool ParsePropertyInfoLine(const std::string& line, bool require_prefix_or_exact,PropertyInfoEntry* out, std::string* error) {// SpaceTokenizer是解析字符串的类auto tokenizer = SpaceTokenizer(line);// GetNext():从第0个字符开始,存取遍历的字符串line的每个字符,// 如遇到空白字符则停止获取,并保存当前的迭代器,并下次调用GetNext()时,从当前的当前的迭代器遍历字符串Line/* E.g: /system/etc/selinux/plat_property_contexts中有fastbootd.protocol u:object_r:fastbootd_protocol_prop:s0 exact enum usb tcp */// 第一次,GetNext(),则返回:fastbootd.protocol,// 第二次,GetNext(),则返回:u:object_r:fastbootd_protocol_prop:s0// 第三次,GetNext(),则返回:exact// 第四次,GetNext(),则返回:enum// 第五次,GetNext(),则返回:usb// 第六次,GetNext(),则返回:tcp// 用上面的例子,property就是fastbootd.protocol,匹配属性名的字符串(用正则表达式写的字符串)auto property = tokenizer.GetNext();if (property.empty()) {*error = "Did not find a property entry in '" + line + "'";return false;}// 用上面的例子,context就是u:object_r:fastbootd_protocol_prop:s0,表示该属性property(fastbootd.protocol)的安全上下文auto context = tokenizer.GetNext();if (context.empty()) {*error = "Did not find a context entry in '" + line + "'";return false;}// 用上面的例子,match_operation就是exact,完全匹配auto match_operation = tokenizer.GetNext();auto type_strings = std::vector<std::string>{};// 用上面的例子,std::vector类型的type_strings包括了[enum,usb,tcp]auto type = tokenizer.GetNext();while (!type.empty()) {//type_strings.emplace_back(type);type = tokenizer.GetNext();}// match_operation只能是"exact"或者是"prefix"或者是空字符bool exact_match = false;if (match_operation == "exact") {exact_match = true;} else if (match_operation != "prefix" && match_operation != "" && require_prefix_or_exact) {*error = "Match operation '" + match_operation +"' is not valid: must be either 'prefix' or 'exact'";return false;}// 判断type_strings的内容是否合法if (!type_strings.empty() && !IsTypeValid(type_strings)) {*error = "Type '" + Join(type_strings, " ") + "' is not valid";return false;}// 对PropertyInfoEntry的name(property),context(context),// type(Join(type_strings, " ")),exact_match(exact_match)进行赋值/* 用上面的例子,out其实就是:注:enum 后面会接它的至少一个枚举成员,这个比较特殊,除了"enum",还有"string", "bool", "int", "uint", "double", "size"。out = {.name = "fastbootd.protocol",.context = "u:object_r:fastbootd_protocol_prop:s0",.type = "enum usb tcp",.exact_match = true,}*/*out = {property, context, Join(type_strings, " "), exact_match};return true;
}
BuildTrie
先了解一些知识点:
trie
树是一种数据结构,用于高效地存储和查找字符串。
Android
中定义了一个名为TrieBuilder
的类,用于构建trie
树的结构
android-12.0.0_r28/system/core/property_service/libpropertyinfoserializer/trie_builder.h
class TrieBuilder {//...省略TrieBuilderNode builder_root_;std::set<std::string> contexts_;std::set<std::string> types_;
};
TrieBuilderNode builder_root_
:Trie
树的根节点,是一个TrieBuilderNode
类型的对象std::set<std::string> contexts_
:存放安全上下文的字符串容器,比如u:object_r:default_prop:s0
等std::set<std::string> types_
:存放类型的字符串容器,比如string
,int
等
android-12.0.0_r28/system/core/property_service/libpropertyinfoserializer/trie_builder.h
class TrieBuilderNode {//...省略PropertyEntryBuilder property_entry_;std::vector<TrieBuilderNode> children_;std::vector<PropertyEntryBuilder> prefixes_;std::vector<PropertyEntryBuilder> exact_matches_;
};
TrieBuilderNode
结构体表示Trie
树中的一个节点,它包含了以下成员:
PropertyEntryBuilder property_entry_
:表示该节点对应的属性信息,是一个PropertyEntryBuilder
类型的对象。std::vector<TrieBuilderNode> children_
:存储该节点的子节点。std::vector<PropertyEntryBuilder> prefixes_
:用于存储与该节点的字符串前缀匹配的属性信息std::vector<PropertyEntryBuilder> exact_matches_
:用于存储与该节点的字符串完全匹配的属性信息。
android-12.0.0_r28/system/core/property_service/libpropertyinfoserializer/trie_builder.h
struct PropertyEntryBuilder {PropertyEntryBuilder() : context(nullptr), type(nullptr) {}PropertyEntryBuilder(const std::string& name, const std::string* context, const std::string* type): name(name), context(context), type(type) {}std::string name;const std::string* context;const std::string* type;
};
描述属性信息的结构体:
name
:用于存储属性的名字,比如:fastbootd_protocol
等context
:用于存储属性的上下文,比如:u:object_r:fastbootd_protocol_prop:s0
等type
:用于存储属性的类型,比如:string
,boot
,int
等
它们的关系如下图:
android-12.0.0_r28/system/core/property_service/libpropertyinfoserializer/property_info_serializer.cpp
bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,const std::string& default_context, const std::string& default_type,std::string* serialized_trie, std::string* error) {// 创建TrieBuilder,初始化转化上下文context:u:object_r:default_prop:s0,default_type:string,树auto trie_builder = TrieBuilder(default_context, default_type);for (const auto& [name, context, type, is_exact] : property_info) {// 把 property_info 的每一项添加trie_builder中if (!trie_builder.AddToTrie(name, context, type, is_exact, error)) {return false;}}// 使用TrieSerializer auto trie_serializer = TrieSerializer(); *serialized_trie = trie_serializer.SerializeTrie(trie_builder);return true;
}
system/core/property_service/libpropertyinfoserializer/trie_builder.cpp
TrieBuilder::TrieBuilder(const std::string& default_context, const std::string& default_type): builder_root_("root") {// 构建树的根节点root的数据 auto* context_pointer = StringPointerFromContainer(default_context, &contexts_);builder_root_.set_context(context_pointer);auto* type_pointer = StringPointerFromContainer(default_type, &types_);builder_root_.set_type(type_pointer);
}bool TrieBuilder::AddToTrie(const std::string& name, const std::string& context,const std::string& type, bool exact, std::string* error) {auto* context_pointer = StringPointerFromContainer(context, &contexts_);auto* type_pointer = StringPointerFromContainer(type, &types_);return AddToTrie(name, context_pointer, type_pointer, exact, error);
}// 添加树的节点
bool TrieBuilder::AddToTrie(const std::string& name, const std::string* context,const std::string* type, bool exact, std::string* error) {// 设置当前节点为树的根节点。 TrieBuilderNode* current_node = &builder_root_;// 以"."作为分隔, auto name_pieces = Split(name, ".");bool ends_with_dot = false;if (name_pieces.back().empty()) {ends_with_dot = true;// 如果以点为结尾,则将分隔得到的std::vector窗口的最后一个元素去掉(空字符)name_pieces.pop_back();}// 如果name_pieces的vector容器数据个数大于1,比如,name_pieces = {"log","tag"}while (name_pieces.size() > 1) {// 从当前节点找去子节点, auto child = current_node->FindChild(name_pieces.front());if (child == nullptr) {// 找不着,则创建一个叶节点TrieBuilderNode(name:log,context:null,tpye:null)child = current_node->AddChild(name_pieces.front());}if (child == nullptr) {*error = "Unable to allocate Trie node";return false;}// 将当前节点设置为上面创建的叶节点current_node = child;// 清除 name_pieces = {"log","tag"}的"log",则变成{"tag"}name_pieces.erase(name_pieces.begin());}// 根据匹配的类型来存储上下文contextif (exact) {// 存储与当前节点的字符串完全匹配的属性信息。if (!current_node->AddExactMatchContext(name_pieces.front(), context, type)) {*error = "Duplicate exact match detected for '" + name + "'";return false;}} else if (!ends_with_dot) {// 存储与当前节点的字符串前缀匹配的属性信息。 if (!current_node->AddPrefixContext(name_pieces.front(), context, type)) {*error = "Duplicate prefix match detected for '" + name + "'";return false;}} else {auto child = current_node->FindChild(name_pieces.front());if (child == nullptr) {child = current_node->AddChild(name_pieces.front());}if (child == nullptr) {*error = "Unable to allocate Trie node";return false;}if (child->context() != nullptr || child->type() != nullptr) {*error = "Duplicate prefix match detected for '" + name + "'";return false;}// 存储当前节点属性描述的上下文child->set_context(context);// 存储当前节点属性描述的类型 child->set_type(type);}return true;
}const std::string* TrieBuilder::StringPointerFromContainer(const std::string& string,std::set<std::string>* container) {// 从容器std::set<std::string> contexts_中插入string,并返回该string在容器的指针,auto [iterator, _] = container->emplace(string);return &(*iterator);
}
用例子说明:
比如,std::vector<PropertyInfoEntry>& property_info
的数据为:
("log.", "u:object_r:log_prop:s0", "", false),
("log.tag", "u:object_r:log_tag_prop:s0", "", false),
("log.tag.WifiHAL", "u:object_r:wifi_log_prop:s0", "", false),
("lmkd.reinit", "u:object_r:lmkd_prop:s0", "int", true)
则它的树结构图:
ProcessKernelDt
android-12.0.0_r28/system/core/init/property_service.cpp
static void ProcessKernelDt() {// 读取/proc/device-tree/firmware/android/compatible的值是不是和"android,firmware"相等if (!is_android_dt_value_expected("compatible", "android,firmware")) {return;}std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);if (!dir) return;std::string dt_file;struct dirent* dp;while ((dp = readdir(dir.get())) != NULL) {if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") ||!strcmp(dp->d_name, "name")) {continue;}// 读取/proc/device-tree/firmware/android/文件下的xxxx文件的值(除了compatible与name文件)/* 一般有:/proc/device-tree/firmware/android/hardware/proc/device-tree/firmware/android/mode/proc/device-tree/firmware/android/serialno*/std::string file_name = get_android_dt_dir() + dp->d_name;// 读取hardware,mode,serailno文件的值android::base::ReadFileToString(file_name, &dt_file);std::replace(dt_file.begin(), dt_file.end(), ',', '.');/* 设置属性值:ro.boot.hardware=xxxro.boot.mode=xxxro.boot.serialno=xxx*/InitPropertySet("ro.boot."s + dp->d_name, dt_file);}
}
PropertyLoadBootDefaults
void PropertyLoadBootDefaults() {std::map<std::string, std::string> properties;// 如果是恢复模式则解析/prop.default文件if (IsRecoveryMode()) {load_properties_from_file("/prop.default", nullptr, &properties);}// 对新的android 版本与旧的android 版本的xxx.prop的路径支持问题,从而判断是否加载const auto load_properties_from_partition = [&properties](const std::string& partition,int support_legacy_path_until) {auto path = "/" + partition + "/etc/build.prop";// 先解析/${partition}/etc/build.prop文件,如果失败,就走下面代码旧的路径if (load_properties_from_file(path.c_str(), nullptr, &properties)) {return;}// 下面通过读取 ro.<partition>.build.version.sdk 的值,并且判断该值是否需要使用旧的路径来读取std::map<std::string, std::string> temp;auto legacy_path1 = "/" + partition + "/default.prop";auto legacy_path2 = "/" + partition + "/build.prop";// 解析/${partition}/default.prop与/${partition}/build.prop,// 并所有key-value存到std::map<std::string, std::string> tempload_properties_from_file(legacy_path1.c_str(), nullptr, &temp);load_properties_from_file(legacy_path2.c_str(), nullptr, &temp);bool support_legacy_path = false;auto version_prop_name = "ro." + partition + ".build.version.sdk";auto it = temp.find(version_prop_name);// 在我的android 12的手机上,获取:getprop | grep -E ro.*.build.version.sdk/*[ro.bootimage.build.version.sdk]: [31][ro.build.version.sdk]: [31][ro.odm.build.version.sdk]: [31][ro.product.build.version.sdk]: [31][ro.system.build.version.sdk]: [31][ro.system_ext.build.version.sdk]: [31][ro.vendor.build.version.sdk]: [31][ro.vendor_dlkm.build.version.sdk]: [31]*/if (it == temp.end()) {// 没有找到ro.<partition>.build.version.sdk,则支持旧的路径support_legacy_path = true;} // 如果找到,并且获取到的值小于等于传进来的support_legacy_path_until版本号,else if (int value;ParseInt(it->second.c_str(), &value) && value <= support_legacy_path_until) {support_legacy_path = true;}if (support_legacy_path) {// 如果支持,则解析/${partition}/default.prop与/${partition}/build.prop,// 并所有key-value存到std::map<std::string, std::string> propertiesload_properties_from_file(legacy_path1.c_str(), nullptr, &properties);load_properties_from_file(legacy_path2.c_str(), nullptr, &properties);} else {LOG(FATAL) << legacy_path1 << " and " << legacy_path2 << " were not loaded "<< "because " << version_prop_name << "(" << it->second << ") is newer "<< "than " << support_legacy_path_until;}};// 如/second_stage_resources/system/etc/ramdisk/build.prop存在,则解析这个文件,// 并所有key-value存到std::map<std::string, std::string> properties中LoadPropertiesFromSecondStageRes(&properties);// 解析/system/build.prop文件,并所有key-value存到std::map<std::string, std::string> properties中load_properties_from_file("/system/build.prop", nullptr, &properties);/* 解析/system_ext/(|ect)/(build.prop|default.prop)文件,如/system_ext/ect/build.prop有,则解析,否则就判断ro.system_ext.build.version.sdk的值是否小于等于30或者没有读取到值,则解析/system_ext/default.prop与/system_ext/build.prop*/ load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);// 解析/vendor/default.prop文件,并所有key-value存到std::map<std::string, std::string> properties中load_properties_from_file("/vendor/default.prop", nullptr, &properties);// 解析/vendor/build.prop文件,并所有key-value存到std::map<std::string, std::string> properties中load_properties_from_file("/vendor/build.prop", nullptr, &properties);// 解析/vendor_dlkm/etc/build.prop文件,并所有key-value存到std::map<std::string, std::string> properties中load_properties_from_file("/vendor_dlkm/etc/build.prop", nullptr, &properties);// 解析/odm_dlkm/etc/build.prop文件,并所有key-value存到std::map<std::string, std::string> properties中load_properties_from_file("/odm_dlkm/etc/build.prop", nullptr, &properties);/* 解析/odm/(|ect)/(build.prop|default.prop)文件,如/odm/ect/build.prop有,则解析,否则就判断ro.odm.build.version.sdk的值是否小于等于28或者没有读取到值,则解析/odm/default.prop与/odm/build.prop*/ load_properties_from_partition("odm", /* support_legacy_path_until */ 28);/* 解析/product/(|ect)/(build.prop|default.prop)文件,如/product/ect/build.prop有,则解析,否则就判断ro.product.build.version.sdk的值是否小于等于30或者没有读取到值,则解析/product/default.prop与/product/build.prop*/load_properties_from_partition("product", /* support_legacy_path_until */ 30);// 判断/debug_ramdisk/adb_debug.prop是否可以读,可读则解析这个文件if (access(kDebugRamdiskProp, R_OK) == 0) {LOG(INFO) << "Loading " << kDebugRamdiskProp;load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);}// 遍历std::map<std::string, std::string> properties,// 设置系统属性值key-value通过MMAP映射到全局内存中,供所有进程访问for (const auto& [name, value] : properties) {std::string error;if (PropertySet(name, value, &error) != PROP_SUCCESS) {LOG(ERROR) << "Could not set '" << name << "' to '" << value<< "' while loading .prop files" << error;}}/* 功能:设置ro.product.{brand | device | manufacturer | model | name }的值。是通过读取ro.product.<partition>.{brand | device | manufacturer | model | name },来设置它们的值(如果有值,就不会再赋值),所以,默认读取的优先级是product,odm,vendor,system_ext,system,可以通过ro.product.property_source_order来设置这5个字符串的摆放的数组位置来设置这个优先级。*/ property_initialize_ro_product_props();/* 功能:设置ro.build.id的值如果读取ro.build.id的值为空,则通过读取ro.build.legacy.id和ro.boot.vbmeta.digest的值设置,如果ro.boot.vbmeta.digest读取的字符串大小小于8,则设置ro.build.id为读取ro.build.legacy.id的值,否则,设置ro.build.id = getprop(ro.build.legacy.id) + [getprop(ro.boot.vbmeta.digest)的前8个字符]*/property_initialize_build_id();/*功能:设置ro.build.fingerprint如果读取ro.build.fingerprint的值为空,则ro.build.fingerprint = getprop(ro.product.brand) + "/" + getprop(ro.product.name) + "/" + getprop(ro.product.device) + "/" + getprop(ro.build.version.release_or_codename) + "/" + getprop(ro.build.id) + "/" + getprop(ro.build.version.incremental) + "/" + getprop(ro.build.type) + "/" + getprop(ro.build.tags) + "/" + */property_derive_build_fingerprint();/*功能:设置ro.build.legacy.fingerprint如果读取ro.build.legacy.fingerprint的值为空,则ro.build.fingerprint = getprop(ro.product.brand) + "/" + getprop(ro.product.name) + "/" + getprop(ro.product.device) + "/" + getprop(ro.build.version.release_or_codename) + "/" + getprop(ro.build.legacy.id) + "/" + getprop(ro.build.version.incremental) + "/" + getprop(ro.build.type) + "/" + getprop(ro.build.tags) + "/" + */property_derive_legacy_build_fingerprint();/*功能:设置ro.product.cpu.abilist,ro.product.cpu.abilist32,ro.product.cpu.abilist64的值如果读取ro.product.cpu.abilis的值为空,则通过读取ro.{product|odm|vendor|system}.product.cpu.abilist32与ro.{product|dom|vendor|system}.product.cpu.abilist64的值,分别来设置ro.product.cpu.abilist32与product.cpu.abilist64的值,其中{product|dom|vendor|system},优先级最高是product>odm>vendor>system,然后再将两者的值ro.xxx.product.cpu.abilist32与ro.xxx.product.cpu.abilist64拼接起来,再设置ro.product.cpu.abilist的值*/property_initialize_ro_cpu_abilist();/*功能:设置persist.sys.usb.config的值通过读取ro.debuggable的值来设置*/update_sys_usb_config();
}
// ...
static Result<void> load_properties_from_file(const char* filename, const char* filter,std::map<std::string, std::string>* properties) {Timer t;//读取xxx.prop文件内容到file_contentsauto file_contents = ReadFile(filename);if (!file_contents.ok()) {return Error() << "Couldn't load property file '" << filename<< "': " << file_contents.error();}file_contents->push_back('\n');// 解析从xxx.prop的内容file_contentsLoadProperties(file_contents->data(), filter, filename, properties);LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";return {};
}
LoadProperties
static void LoadProperties(char* data, const char* filter, const char* filename,std::map<std::string, std::string>* properties) {char *key, *value, *eol, *sol, *tmp, *fn;size_t flen = 0;static constexpr const char* const kVendorPathPrefixes[4] = {"/vendor","/odm","/vendor_dlkm","/odm_dlkm",};// kInitContext = "u:r:init:s0";const char* context = kInitContext;// __ANDROID_API_P__ = 28if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {for (const auto& vendor_path_prefix : kVendorPathPrefixes) {// filename如果是以上面/vendor,/odm,/vendor_dlkm,/odm_dlkm开头的if (StartsWith(filename, vendor_path_prefix)) {// kVendorContext[] = "u:r:vendor_init:s0";// context 就更换成"" context = kVendorContext;}}}// 如果有过滤器,则获取其长度if (filter) {flen = strlen(filter);}//sol:当前行头sol = data;// strchr读取字符指针sol第一个出现'\n'换行符// eol指向字符串中第一个出现字符'\n'的地址while ((eol = strchr(sol, '\n'))) {// 将当前行头的sol赋值给keykey = sol;// 此时eol指向的是换行符'\n',然后把换行符'\n'改成0,即字符串结束标志,// 然后eol++,为下面sol行头赋值*eol++ = 0;// 上面行尾eol++,即是行头了solsol = eol;// 获取到key,遇到非空格字符停止,即行头有可能以空格符开始的,则除去空格符,再获取keywhile (isspace(*key)) key++;// 以#的注释行跳过if (*key == '#') continue;// eol目前,指向的是下一行的行头,此时eol-1,即是指向当前行的字符串结束标志'\0',// eol-2,tmp即是当前的行尾tmp = eol - 2;// 如果当前的行尾tmp还有多余的空格的,将全部转化成字符串结束标志'\0'while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;// 如果当前key是import,且没有过滤器if (!strncmp(key, "import ", 7) && flen == 0) {fn = key + 7;// 找到不是以空格符的fn文件名(包含路径),即import /system/etc/aaa.prop,// import与/system/etc/aaa.prop之前存在多个空格符while (isspace(*fn)) fn++;// 查看文件名后面是否有空格符号,如有,将全部转化成字符串结束标志'\0',则此时key为nullptr/*E.g."import /system/etc/aaa.prop ",这一行数据中,要把"/system/etc/aaa.prop"后面的空格符全部转化成字符串结束标志'\0',直接key没有指向任何数据nullptr*/key = strchr(fn, ' ');if (key) {*key++ = 0;while (isspace(*key)) key++;}std::string raw_filename(fn);// ExpandProp主要是拓展了文件的灵活性/*1、变量的格式可以是 $x.y 或者 ${x.y},前者适用于变量名是字符串的一部分的情况。2、双美元符号 ($$) 会被解释为一个普通的美元符号 ($)3、不支持嵌套的属性扩展,例如 ${foo.${bar}} 不受支持。4、如果变量为空,则 ${x.y:-default} 将返回默认值 default。*/auto expanded_filename = ExpandProps(raw_filename);if (!expanded_filename.ok()) {LOG(ERROR) << "Could not expand filename ': " << expanded_filename.error();continue;}// 递归执行load_properties_from_fileload_properties_from_file(expanded_filename->c_str(), key, properties);} else {value = strchr(key, '=');if (!value) continue;// 此时value指向'=',然后把符号'='改成0,即字符串结束标志,value++,即是'='的下一个符号的位置,即真正的value值/* E.g:ro.build.type=user,value此时向的是"user",*/ *value++ = 0;// value - 1:'\0'的位置,value - 2:'\0'的前一个位置,即是tmp:指向的是key字符串的尾部,tmp = value - 2;// 将key后面的空格符,全部转化成字符串结束标志'\0'while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;// 获取value不是以空格符开头的值while (isspace(*value)) value++;if (flen > 0) {if (filter[flen - 1] == '*') {if (strncmp(key, filter, flen - 1) != 0) continue;} else {if (strcmp(key, filter) != 0) continue;}}// kRestoreconProperty = "selinux.restorecon_recursive"// 如果key是以ctlg开头,或者key=sys.powerctl,key=selinux.restorecon_recursive,不做处理if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||std::string{key} == kRestoreconProperty) {LOG(ERROR) << "Ignoring disallowed property '" << key<< "' with special meaning in prop file '" << filename << "'";continue;}ucred cr = {.pid = 1, .uid = 0, .gid = 0};std::string error;// 检查当前进程是否有权限修改指定的属性值if (CheckPermissions(key, value, context, cr, &error) == PROP_SUCCESS) {auto it = properties->find(key);if (it == properties->end()) {// std::map<std::string, std::string> properties中不存在key,则添加(*properties)[key] = value;} else if (it->second != value) {LOG(WARNING) << "Overriding previous property '" << key << "':'" << it->second<< "' with new value '" << value << "'";// std::map<std::string, std::string> properties中存在key,且值不相同,就重新赋值覆盖掉原来的值it->second = value;}} else {LOG(ERROR) << "Do not have permissions to set '" << key << "' to '" << value<< "' in property file '" << filename << "': " << error;}}}
}
InstallSignalFdHandler
static void InstallSignalFdHandler(Epoll* epoll) {// 步骤1 start// .sa_handler = SIG_DFL:表示使用默认的信号处理函数// .sa_flags = SA_NOCLDSTOP:表示不将停止子进程的信号发送给当前进程const struct sigaction act { .sa_handler = SIG_DFL, .sa_flags = SA_NOCLDSTOP };// SIGCHLD 是一个信号名,它表示子进程状态改变信号(Child status changed);// 当一个子进程终止或停止时,会向其父进程发送该信号,以通知父进程子进程的状态发生了变化。// 将 act 对象与 SIGCHLD 信号关联,这样当进程收到 SIGCHLD 信号时,就会使用默认的信号处理函数。sigaction(SIGCHLD, &act, nullptr);sigset_t mask;sigemptyset(&mask);sigaddset(&mask, SIGCHLD);// 判断init进程是否有重启设备能力,IsRebootCapable()一般为trueif (!IsRebootCapable()) {// If init does not have the CAP_SYS_BOOT capability, it is running in a container.// In that case, receiving SIGTERM will cause the system to shut down.sigaddset(&mask, SIGTERM);}// 设置屏蔽信号集合(SIGCHLD,SIGTERM)if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {PLOG(FATAL) << "failed to block signals";}// 设置子进程可以正确地接收 SIGCHLD 和 SIGTERM 信号,并且不会继承父进程对这些信号的阻塞const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);if (result != 0) {LOG(FATAL) << "Failed to register a fork handler: " << strerror(result);}// 步骤1 end// 步骤2 start// 创建信号集(SIGINT,SIGTERM信号)的文件描述符signal_fd,通过Epoll监听signal_fd可将异步接收SIGCHLD,SIGTERM信号。signal_fd = signalfd(-1, &mask, SFD_CLOEXEC);if (signal_fd == -1) {PLOG(FATAL) << "failed to create signalfd";}// 使用Epoll监听信号集为SIGINT,SIGTERM的文件描述符signal_fd,处理函数为HandleSignalFd// 这里的Epoll是在system/core/init/epoll.cpp对<sys/epoll>的封装的一个类,// 通过RegisterHandler监听这个signal_fdif (auto result = epoll->RegisterHandler(signal_fd, HandleSignalFd); !result.ok()) {LOG(FATAL) << result.error();}// 步骤2 end
}static void UnblockSignals() {const struct sigaction act { .sa_handler = SIG_DFL };sigaction(SIGCHLD, &act, nullptr);sigset_t mask;sigemptyset(&mask);sigaddset(&mask, SIGCHLD);sigaddset(&mask, SIGTERM);// 设置解除屏蔽信号集合(SIGCHLD,SIGTERM)if (sigprocmask(SIG_UNBLOCK, &mask, nullptr) == -1) {PLOG(FATAL) << "failed to unblock signals for PID " << getpid();}
}
// 监听信号集(SIGINT,SIGTERM信号)的文件描述符signal_fd的处理函数
static void HandleSignalFd() {signalfd_siginfo siginfo;ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));if (bytes_read != sizeof(siginfo)) {PLOG(ERROR) << "Failed to read siginfo from signal_fd";return;}switch (siginfo.ssi_signo) {// 子进程停止信号case SIGCHLD:/*终止出现问题的子进程,里面使用ReapOneProcess调用waitpid找出挂掉进程的pid,然后根据pid找到对应Service,最后调用Service的Reap方法清除资源,根据进程对应的类型,决定是否重启机器或重启进程*/ReapAnyOutstandingChildren();break;case SIGTERM:// 会发送一个广播通知系统组件和应用程序进行清理工作,最终执行关机操作。HandleSigtermSignal(siginfo);break;default:PLOG(ERROR) << "signal_fd: received unexpected signal " << siginfo.ssi_signo;break;}
}
步骤1:设置信号处理函数,并将 SIGCHLD
和 SIGTERM
信号加入到信号屏蔽集中,从而防止这两个信号在信号处理函数运行期间中断程序的执行。这种设置通常用于多进程编程中,以防止子进程意外终止或被中断,从而导致父进程无法正常工作。然后通过pthread_atfork
设置子进程可以正确地接收 SIGCHLD
和 SIGTERM
信号,即设置不继承父进程对这些信号的阻塞
步骤2:创建signal_fd
文件描述符,通过Epoll
监听该文件描述符来达到异步接收 SIGCHLD,SIGTERM
信号
InstallInitNotifier
static int wake_main_thread_fd = -1;
static void InstallInitNotifier(Epoll* epoll) {/* 创建事件通知的文件描述符当进程需要等待某个事件发生时,可以通过 eventfd 创建一个eventfd 对象,并使用 read 系统调用阻塞等待该事件的发生。当事件发生时,通过 write 系统调用向 eventfd 对象写入一个计数值,唤醒正在等待该事件的进程。*/wake_main_thread_fd = eventfd(0, EFD_CLOEXEC);if (wake_main_thread_fd == -1) {PLOG(FATAL) << "Failed to create eventfd for waking init";}auto clear_eventfd = [] {uint64_t counter;// 事件通知eventfd的读事件,与WakeMainInitThread配合使用,写完才能读,读完才能写。TEMP_FAILURE_RETRY(read(wake_main_thread_fd, &counter, sizeof(counter)));};// 使用Epoll监听事件通知的文件描述符wake_main_thread_fd,// 处理函数为:read(wake_main_thread_fd, &counter, sizeof(counter))// 通过RegisterHandler监听这个wake_main_thread_fdif (auto result = epoll->RegisterHandler(wake_main_thread_fd, clear_eventfd); !result.ok()) {LOG(FATAL) << result.error();}
}static void WakeMainInitThread() {uint64_t counter = 1;// 事件通知eventfd的写事件,与上面clear_eventfd方法配合使用,写完才能读,读完才能写。TEMP_FAILURE_RETRY(write(wake_main_thread_fd, &counter, sizeof(counter)));
}
StartPropertyService
void StartPropertyService(int* epoll_socket) {InitPropertySet("ro.property_service.version", "2");int sockets[2];// 创建Linux的socketpair通讯if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {PLOG(FATAL) << "Failed to socketpair() between property_service and init";}*epoll_socket = from_init_socket = sockets[0];init_socket = sockets[1];StartSendingMessages();// PROP_SERVICE_NAME = "property_service"// 创建一个指向/dev/socket/property_service的PF_UNIX的socketif (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,false, 0666, 0, 0, {});result.ok()) {property_set_fd = *result;} else {LOG(FATAL) << "start_property_service socket creation failed: " << result.error();}// 监听该fd,最大8个连接listen(property_set_fd, 8);// 通过epoll监听from_init_socket与property_fdauto new_thread = std::thread{PropertyServiceThread};property_service_thread.swap(new_thread);
}
CreateSocket
android-12.0.0_r28/system/core/init/util.cpp
Result<int> CreateSocket(const std::string& name, int type, bool passcred, mode_t perm, uid_t uid,gid_t gid, const std::string& socketcon) {if (!socketcon.empty()) {if (setsockcreatecon(socketcon.c_str()) == -1) {return ErrnoError() << "setsockcreatecon(\"" << socketcon << "\") failed";}}// 创建socket,协议域:PF_UNIX,与AF_UNIX两者等价,可以互换android::base::unique_fd fd(socket(PF_UNIX, type, 0));if (fd < 0) {return ErrnoError() << "Failed to open socket '" << name << "'";}if (!socketcon.empty()) setsockcreatecon(nullptr);struct sockaddr_un addr;memset(&addr, 0 , sizeof(addr));addr.sun_family = AF_UNIX;// 指定AF_UNIX协议的本地套接字的地址// ANDROID_SOCKET_DIR = "/dev/socket"// addr.sun_path = /dev/socket/property_servicesnprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR "/%s", name.c_str());if ((unlink(addr.sun_path) != 0) && (errno != ENOENT)) {return ErrnoError() << "Failed to unlink old socket '" << name << "'";}std::string secontext;// 查找/dev/socket/property_service的selinux上下文,存放到secontextif (SelabelLookupFileContext(addr.sun_path, S_IFSOCK, &secontext) && !secontext.empty()) {// 设置文件系统创建的默认安全上下文为secontextsetfscreatecon(secontext.c_str());}if (passcred) {int on = 1;if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {return ErrnoError() << "Failed to set SO_PASSCRED '" << name << "'";}}// 绑定该fdint ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));int savederrno = errno;// /dev/socket/property_service的selinux上下文,不为空,刚if (!secontext.empty()) {// 恢复文件系统创建的默认安全上下文setfscreatecon(nullptr);}auto guard = android::base::make_scope_guard([&addr] { unlink(addr.sun_path); });if (ret) {errno = savederrno;return ErrnoError() << "Failed to bind socket '" << name << "'";}// 设置/dev/socket/property_service文件的属主if (lchown(addr.sun_path, uid, gid)) {return ErrnoError() << "Failed to lchown socket '" << addr.sun_path << "'";}// 设置/dev/socket/property_service的文件权限if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {return ErrnoError() << "Failed to fchmodat socket '" << addr.sun_path << "'";}LOG(INFO) << "Created socket '" << addr.sun_path << "'"<< ", mode " << std::oct << perm << std::dec<< ", user " << uid<< ", group " << gid;guard.Disable();return fd.release();
}
PropertyServiceThread
system/core/init/property_service.cpp
static void PropertyServiceThread() {Epoll epoll;if (auto result = epoll.Open(); !result.ok()) {LOG(FATAL) << result.error();}// 监听/dev/socket/property_service的sokcet fd,if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);!result.ok()) {LOG(FATAL) << result.error();}// 监听socketpair的read fdif (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {LOG(FATAL) << result.error();}while (true) {// 等待消息auto pending_functions = epoll.Wait(std::nullopt);if (!pending_functions.ok()) {LOG(ERROR) << pending_functions.error();} else {for (const auto& function : *pending_functions) {// 回调上面的epoll.RegisterHandler的中设置的函数(*function)();}}}
}
fs_mgr_vendor_overlay_mount_all
system/core/fs_mgr/fs_mgr_vendor_overlay.cpp
bool fs_mgr_vendor_overlay_mount_all() {// kVndkVersionPropertyName = "ro.vndk.version"static const auto vndk_version = android::base::GetProperty(kVndkVersionPropertyName, "");if (vndk_version.empty()) {LINFO << "vendor overlay: vndk version not defined";return false;}// 获取 "/system/vendor_overlay/<ver>",// "/product/vendor_overlay/<ver>"下的所有子目录const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(vndk_version);if (vendor_overlay_dirs.empty()) return true;if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {LINFO << "vendor overlay: kernel does not support overlayfs";return false;}// Mount each directory in /(system|product)/vendor_overlay/<ver> on /vendor// 挂载/(system|product)/vendor_overlay/<ver>到 /vendor分区上auto ret = true;for (const auto& vendor_overlay_dir : vendor_overlay_dirs) {if (!fs_mgr_vendor_overlay_mount(vendor_overlay_dir)) {ret = false;}}return ret;
}
export_oem_lock_status
static void export_oem_lock_status() {if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {return;}SetProperty("ro.boot.flash.locked",android::base::GetProperty("ro.boot.verifiedbootstate", "") == "orange" ? "0" : "1");
}
system/core/init/mount_handler.cpp
MountHandler
// 监听/proc/mounts 文件fd,有变化就执行MountHandlerFunction方法去解析这个文件内容
MountHandler::MountHandler(Epoll* epoll) : epoll_(epoll), fp_(fopen("/proc/mounts", "re"), fclose) {if (!fp_) PLOG(FATAL) << "Could not open /proc/mounts";auto result = epoll->RegisterHandler(fileno(fp_.get()), [this]() { this->MountHandlerFunction(); }, EPOLLERR | EPOLLPRI);if (!result.ok()) LOG(FATAL) << result.error();
}
// ...
void MountHandler::MountHandlerFunction() {rewind(fp_.get());std::vector<MountHandlerEntry> touched;auto untouched = mounts_;char* buf = nullptr;size_t len = 0;// 读取文件内容中的每一行while (getline(&buf, &len, fp_.get()) != -1) {auto buf_string = std::string(buf);// 若读取的有"/emulated则跳过"if (buf_string.find("/emulated") != std::string::npos) {continue;}/* 取每行的前三个字段挂载的块设备,(以空格" "分隔,再取,不足则用""空字符补全)blk_device: 挂载的块设备mount_point:挂载的路径或者点fs_type: 挂载的文件系统类型*/ auto entry = ParseMount(buf_string);auto match = untouched.find(entry);if (match == untouched.end()) {touched.emplace_back(std::move(entry));} else {untouched.erase(match);}}free(buf);// 将匹配到的entry进行移除,并记录Mount属性值for (auto& entry : untouched) {// 设置一些dev.mnt.xxx属性SetMountProperty(entry, false);mounts_.erase(entry);}// 将未匹配到的entry追加到mounts_,并记录Mount属性值for (auto& entry : touched) {// 设置一些dev.mnt.xxx属性SetMountProperty(entry, true);mounts_.emplace(std::move(entry));}
}
第四阶段调用init.rc
当属性服务建立完成后,init
的自身功能基本就告一段落,接下来
需要来启动其他的进程。但是init
进程如何其他其他进程呢?其他
进程都是一个二进制文件,我们可以直接通过exec
的命令方式来
启动,例如 ./system/bin/init second_stage
,来启动init进程的第
二阶段。但是Android
系统有那么多的Native
进程,如果都通过传
exec
在代码中一个个的来执行进程,那无疑是一个灾难性的设
计。
在这个基础上Android
推出了一个init.rc
的机制,即类似通过读取
配置文件的方式,来启动不同的进程。
init.rc
是一个配置文件,内部由Android
初始化语言编写
(Android Init Language
)编写的脚本。
init.rc
主要包含五种类型语句:
Action
Command
Service
Option
Import
Action
动作表示了一组命令(commands
)组成.动作包括一个触发器,决
定了何时运行这个动作
Action
: 通过触发器trigger
,即以on
开头的语句来决定执行相应
的service
的时机,具体有如下时机:
on early-init:在初始化早期阶段触发;
on init:在初始化阶段触发;
on late-init:在初始化晚期阶段触发;
on boot/charger: 当系统启动/充电时触发;
on property: 当属性值满足条件时触发;
Command
command
是action
的命令列表中的命令,或者是service
中的选项
onrestart
的参数命令,命令将在所属事件发生时被一个个地执行.
下面列举常用的命令
class_start <service_class_name>
: 启动属于同一个class
的所有
服务;
class_stop <service_class_name>
: 停止指定类的服务
start <service_name>
: 启动指定的服务,若已启动则跳过;
stop <service_name>
: 停止正在运行的服务
setprop
:设置属性值
mkdir
:创建指定目录
symlink <sym_link>
: 创建连接到的<sym_link>
符号链接;
write
: 向文件path中写入字符串;
exec
: fork并执行,会阻塞init进程直到程序完毕;
exprot
:设定环境变量;
loglevel
:设置log
级别
hostname
: 设置主机名
import
:导入一个额外的init
配置文件
####Service
服务Service
,以 service
开头,由init
进程启动,一般运行在init
的
一个子进程,所以启动service
前需要判断对应的可执行文件是否
存在。
参数 | 含义 |
---|---|
表示此服务的名称 | |
此服务所在路径因为是可执行文件,所以一定有存储路径。 | |
启动服务所带的参数 | |
对此服务的约束选项 |
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/
的socket
critical
: 在规定时间内该service
不断重启,则系统会重启并进入恢
复模式
default: 意味着disabled=false,oneshot=false,critical=false
。
#####import
用来导入其他的rc
文件
命令:import
调用uevent_main启动
system/core/init/Android.bp
cc_binary {name: "init_second_stage",recovery_available: true,// 编译成init模块名字stem: "init", defaults: ["init_defaults"],static_libs: ["libinit"],required: ["e2fsdroid","init.rc","mke2fs","sload_f2fs","make_f2fs","ueventd.rc",],srcs: ["main.cpp"],// 创建一个/system/bin/ueventd 链接到/system/bin/initsymlinks: ["ueventd"], ....
创建一个/system/bin/ueventd
l 软链接到/system/bin/init
system/core/rootdir/init.rc
SeondStateMain->LoadBootScripts(am, sm)>am.QueueEventTrigger("early-init")
:
on early-init
# ...start ueventd
# ...
service ueventd /system/bin/ueventdclass corecriticalseclabel u:r:ueventd:s0shutdown critical
service ueventd /system/bin/ueventd
,执行/system/bin/ueventd
,其实就是执行system/bin/init
,所以传递给system/core/init/main.cpp
,只不过[argv]
=“/system/bin/ueventd
”
android-12.0.0_r28/system/core/init/main.cpp
...
if (!strcmp(basename(argv[0]), "ueventd")) {// 初始化设备,监听uevent事件return ueventd_main(argc, argv);}
...
system/core/init/ueventd.cpp
int ueventd_main(int argc, char** argv) {// umask(0)用于设置当前进程的文件模式创建屏蔽字// 用户创建文件夹权限值=初始创建文件夹默认值-umask的预设值// 如:775=777-002// 用户创建文件权限值=初始创建文件默认值-umask的预设值// 如:664=666-002umask(000);// 初始化内核日志,位于节点/dev/kmsg, 此时logd、logcat进程还没有起来,// 采用kernel的log系统,打开的设备节点/dev/kmsg, 那么可cat /dev/kmsg来获取内核log。android::base::InitLogging(argv, &android::base::KernelLogger);LOG(INFO) << "ueventd started!";// 注册selinux相关的用于打印log的回调函数SelinuxSetupKernelLogging();// SelabelInitialize() 函数的作用是初始化 libselinux 库,// 加载 SELinux 的策略文件和文件上下文文件,以确保在 Android 系统运行过程中,能够正确地运行 SELinux 策略。SelabelInitialize();std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;// 解析"/system/etc/ueventd.rc", /vendor/ueventd.rc", "/odm/ueventd.rc"," /ueventd.${getprop(ro.hardware)}.rc"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 uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);// 冷启动if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {ColdBoot cold_boot(uevent_listener, uevent_handlers,ueventd_configuration.enable_parallel_restorecon);cold_boot.Run();}for (auto& uevent_handler : uevent_handlers) {uevent_handler->ColdbootDone();}// 忽略子进程终止信号signal(SIGCHLD, SIG_IGN);// 在上一次调用 waitpid() 和设置 SIGCHLD 信号的 SIG_IGN 之间退出的子进程需要被回收while (waitpid(-1, nullptr, WNOHANG) > 0) {}// Restore prio before main loopsetpriority(PRIO_PROCESS, 0, 0);// 监听来自驱动的uevent,进行“热插拔”处理uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {for (auto& uevent_handler : uevent_handlers) {uevent_handler->HandleUevent(uevent);}return ListenerAction::kContinue;});return 0;
}
Zygote启动
system/core/rootdir/init.rc
#...
import /system/etc/init/hw/init.${ro.zygote}.rc
#....
init.rc
位于/system/core/rootdir
下。在这个路径下还包括四个关
于zygote
的rc
文件。
分别是init.zygote32.rc,init.zygote32_64.rc
,init.zygote64.rc,
init.zygote64_32.rc
,由硬件决定调用哪个文件。
这里拿64
位处理器为例,init.zygote64.rc
的代码如下所示:
system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server# class是一个option,指定zygote服务的类型为mainclass main priority -20user rootgroup root readproc reserved_disk# socket关键字表示一个option,创建一个名为dev/socket/zygote,# 类型为stream,权限为660的socketsocket zygote stream 660 root systemsocket usap_pool_primary stream 660 root systemonrestart是一个option,说明在zygote重启时需要执行的commandonrestart exec_background - system system -- /system/bin/vdc volume abort_fuseonrestart write /sys/power/state ononrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart netdonrestart restart wificondwritepid /dev/cpuset/foreground/taskscritical window=${zygote.critical_window.minute:-off} target=zygote-fatal
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
解析:
service zygote
:init.zygote64.rc
中定义了一个zygote
服务。 init
进程就是通过这个service
名称来创建zygote
进程
/system/bin/app_process64 -Xzygote /system/bin --zygote --
start-system-server
解析:
zygote
这个服务,通过执行进行/system/bin/app_process64
并
传入4个参数进行运行:
参数1:
-Xzygote
该参数将作为虚拟机启动时所需的参数参数2:
/system/bin
代表虚拟机程序所在目录参数3:
--zygote
指明以ZygoteInit.java
类中的main
函数作为虚拟机执行入口参数4:-
-start-system-server
告诉Zygote
进程启动systemServer
进程
总结
init
进程第一阶段做的主要工作是挂载分区,创建设备节点和一些关键目录,初始化日志输出系统,启用SELinux
安全策略。init
进程第二阶段主要工作是初始化属性系统,解析SELinux
的匹配规则,处理子进程终止信号,启动系统属性服务,可以说每一项都很关键,如果说第一阶段是为属性系统,SELinux
做准备,那么第二阶段就是真正去把这些功能落实。init
进行第三阶段主要是解析init.rc
来启动其他进程,进入无限循环,进行子进程实时监控。
Android Init(后期还会更加完善)相关推荐
- 安卓 linux init.rc,[原创]Android init.rc文件解析过程详解(二)
Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...
- Android init.rc文件解析过程详解(二)
Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...
- Android init.rc分析
1 前言 什么是init.rc文件? import /init.usb.rc import /init.${ro.hardware}.rc import /init.trace.rcon early- ...
- 介绍一款比Android原生模拟器还要快的模拟器Genymotion(转)
源:介绍一款比Android原生模拟器还要快的模拟器Genymotion
- Android init.rc文件格式解析
/****************************************************************************** Android init.rc文件格式解 ...
- Android init.rc文件解析过程详解(三)
Android init.rc文件解析过程详解(三) 三.相关结构体 1.listnode listnode结构体用于建立双向链表,这种结构广泛用于kernel代码中, android源代码中定义了l ...
- Android init.rc文件解析过程详解(一)
Android init.rc文件解析过程详解(一) 一.init.rc文件结构介绍 init.rc文件基本组成单位是section, section分为三种类型,分别由三个关键字(所谓关键字 ...
- android init.rc中启动的service 默认是disable的,后续如何启动此服务
如果 android init.rc中启动的service 默认是disable的,如何才能启动此服务呢? init.rc中可以直接启动service 附带的参数决定启动程序的状态,例如数据业务中配置 ...
- android 初始化语言,Android Init Language(安卓初始化语言)
android源码学习目录 了解Android init进程的都知道,启动 init进程之前会解析init.rc文件,init.rc是Android的配置文件脚本,它由一种被称为"Andro ...
最新文章
- [JLOI2013]地形生成
- HBase建表高级属性,hbase应用案例看行键设计,HBase和mapreduce结合,从Hbase中读取数据、分析,写入hdfs,从hdfs中读取数据写入Hbase,协处理器和二级索引
- CSDN博客搬家到WordPress
- 在一个字符串中找到第一个只出现一次的字符。
- 032-IDUtils 工具类模板
- windows 安装使用luarocks
- html5%3chr%3e的样式,Vbs脚本编程简明教程
- 非功能需求分析--web开发课内实例
- FTM的PWM、输入捕获、正交解码
- Twilight暮光之城。。。暮色。。。
- 思维导图带你了解22个职场学习网站!亲测好用
- peek java linkedlist_Java LinkedList peek()方法
- 【寒假每日一题】剪绳子(个人练习)详细题解+推导证明(第六天)
- 苹果设备尺寸和控件尺寸
- 无人驾驶学习---第一课
- 与“你”相关,中国移动数据语音与天猫精灵携手打造打造优质智能生活
- 智慧高速再“提速”,数字化+智能化成建设焦点
- 如何给普通人解释机器学习是什么
- 私有化DNS服务器搭建
- 怎样“管理”你的上司?