Code analysis is based onandroid-12.0.0_r28
early stage
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.");
}
First, the kernel startup command is judged, and if there is a command input corresponding to the path, the corresponding user space process under the path is executed. execute_command
The value of is passed through, and the value can be passed to him uboot
when used bootargs
in , for example , it means that in the root file system is the user space program to be executed."init=xxxx"
"init=/linuxrc"
linuxrc
init
If there is no path to the user-space process, linux
the path to the user-space process specified by default in the kernel is executed:
/sbin/init 、/etc/init 、/bin/init、/bin/sh
If linux
there is no corresponding program under the default user space process path specified by the above kernel, linux
the kernel will output panic
information.
For normal startup, system/core/init/main.cpp
the parameters passed by the kernel layer: argc:1,argv:init
. So, the first stage callsFirstStageMain
Linux Kernel
After the startup is complete, then find init
the program to start and start init
the process. In Android
, init
the entry of the process is:
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为0
setpriority(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")) {
// 创建Selinux
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
return FirstStageMain(argc, argv);
}
setpriority
: is a method that can be used to set the priority of a process. The priority of a process determines how the system allocates resources to the process under limited resources. By setting the priority, you can specify the level of importance of the process in the system so that the system can allocate appropriate resources to it.- The header file is
#include <sys/time.h>
,#include <sys/resource.h>
- Function prototype:
int setpriority(int which, id_t who, int prio);
which
:PRIO_PROCESS
: means to set the priority of the specified process;PRIO_PGRP
: means to set the priority of all the processes of the specified process group;PRIO_USER
: means to set the priority of all the processes of the specified user.who
: The parameter specifies the ID of the process, process group, or user whose priority is to be setprio
: The parameter specifies the priority value to set, ranging from-20
(highest priority) to19
(lowest priority)
- The header file is
The first stage calls 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=1
if (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-002
umask(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/pts
CHECKCALL(mkdir("/dev/pts", 0755));
// 创建权限值为0755的目录/dev/socket
CHECKCALL(mkdir("/dev/socket", 0755));
// 创建权限值为0755的目录/dev/dm-user
CHECKCALL(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的权限为0440
CHECKCALL(chmod("/proc/cmdline", 0440));
std::string cmdline;
// 读取文件/proc/cmdline的信息到cmdline中
android::base::ReadFileToString("/proc/cmdline", &cmdline);
// 禁止非特权进程(即非root进程)读取文件内容
// 设置文件/proc/bootconfig的权限为0440
chmod("/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/vendor
CHECKCALL(mkdir("/mnt/vendor", 0755));
// 创建权限值为0755的目录/mnt/product
CHECKCALL(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 code
if (!errors.empty()) {
for (const auto& [error_string, error_errno] : errors) {
LOG(ERROR) << error_string << " " << strerror(error_errno);
}
LOG(FATAL) << "Init encountered errors starting first stage, aborting";
}
LOG(INFO) << "init first stage started!";
// 打开根目录"/"
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::DISABLED
auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
// 记录当前系统时间到module_start_time
boot_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_ramdisk
SwitchRoot("/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/kmsg
dup2(fd, STDOUT_FILENO);
// 将标准错误重定向到/dev/kmsg
dup2(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";
// 调用内核空间执行reboot
syscall(__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();
}
The second stage calls SetupSelinux
system/core/init/main.cpp
if (!strcmp(argv[1], "selinux_setup")) {
// 创建Selinux
return 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=1
if (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);
}
}
In fact, the above code is read from " /proc/cmdline
", " ":/proc/bootimage
- contains "
androidboot.init_fatal_panic=true
" to determineinit_fatal_panic
whethertrue
or notfalse
- Read to "
androidboot.init_fatal_reboot_target
", then assign toinit_fatal_reboot_target
variable (初始化值为bootloader
) - In summary, the code will pass these two values to set the behavior (such as whether to print) when a fatal error occurs in kernel logging
The third stage calls 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=1
if (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_env
const 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_ramdisk
if (!load_debug_prop) {
UmountDebugRamdisk();
}
// 初始化属性服务相关的,包括属性的selinux上下主,属性的key-value通过MMAP映射到全局,供所有进程使用
PropertyInit();
// 在属性服务读取.prop文件后,将卸载/second_stage_resources
UmountSecondStageRes();
// 在属性服务读取.prop文件后,如果load_debug_prop=true,则卸载/debug_ramdisk
if (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的fd
if (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-init
write /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 property
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
// Restore prio before main loop
setpriority(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=xxx
ro.boot.mode=xxx
ro.boot.serialno=xxx
*/
ProcessKernelDt();
// 从/proc/cmdline读取是否含有androidboot=xxx,如有,刚设置ro.boot.androidboot=xxx
ProcessKernelCmdline();
// 从/proc/bootconfig读取是否含有androidboot=xxx,如有,刚设置ro.boot.androidboot=xxx
ProcessBootconfig();
/*
将下面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.prop
2、/system_ext/etc/build.prop
如上失败,android R(30)设备及以下,则会解析如下两:
2.1、/system_ext/default.prop
2.2、/system_ext/build.prop
3、/vendor/default.prop
4、/vendor/build.prop
5、/vendor_dlkm/etc/build.prop
6、/odm_dlkm/etc/build.prop
7、/odm/etc/build.prop
如上失败,android P(28)设备及以下,则会解析如下两
7.1、/odm/default.prop
7.2、/odm/build.prop
8、/product/etc/build.prop
如上失败,android R(30)设备及以下,则会解析如下两
8.1、/product/default.prop
8.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_contexts
if (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_contexts
if (!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_contexts
if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
&property_infos);
}
// 解析/odm/etc/selinux/odm_property_contexts
if (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_contexts
if (!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_`;
-
Read the following files, which describe
selinux
the correspondence between attributes and contexts:
/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
The read content is parsed into a PropertyInfoEntry array, and each PropertyInfoEntry object corresponds to the name of the property, the selinux context, the type of the property and whether it is an exact_match property.
-
Call
BuildTrie
the function toPropertyInfoEntry
parse the array intoTrie
a tree (dictionary tree), the treeTrieBuilder
is represented by a class, the node of the treeTrieBuilderNode
is represented by a class, the root node object of the tree isTrieBuilder
a member variable of the class, andTrieBuilderNode builder_root_
TrieBuilder
there are two other member variables of the class:std::set<std::string> contexts_
andstd::set<std::string> types_
respectively represent all parsedselinux
A list of contexts and a list of types for all properties.
Next, the serialization will be implemented through the functionsTrieSerializer
of the class, the object will be serialized into a string, and the string will be written to the file: , the context of the file itself is: .SerializeTrie()
TrieBuilder
/dev/__properties__/property_info
selinux
u:object_r:property_info:s0
-
The call
selinux_android_restorecon
sets the context/dev/__properties__/property_info
of the fileselinux
tou: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一般为true
bool 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;
}
// 创建存储属性信息的结构体PropertyInfoEntry
auto property_info_entry = PropertyInfoEntry{
};
auto parse_error = std::string{
};
// 真正解析xxx_property_contexts文件每一行数据的方法ParsePropertyInfoLine
if (!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
First understand some knowledge points:
trie
A tree is a data structure for efficiently storing and looking up strings.
Android
TrieBuilder
A class named named is defined in , which is used to build trie
the structure of the tree
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
The root node of the tree, which is anTrieBuilderNode
object of type -
std::set<std::string> contexts_
: String container for storing security context, suchu:object_r:default_prop:s0
as -
std::set<std::string> types_
: String container of storage type, such asstring
,int
etc.
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
A structure represents Trie
a node in the tree, and it contains the following members:
-
PropertyEntryBuilder property_entry_
: Indicates the attribute information corresponding to the node, which is aPropertyEntryBuilder
type of object. -
std::vector<TrieBuilderNode> children_
: Store the child nodes of this node. -
std::vector<PropertyEntryBuilder> prefixes_
: Used to store attribute information matching the string prefix of the node -
std::vector<PropertyEntryBuilder> exact_matches_
: Used to store attribute information that exactly matches the string of the node.
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;
};
A structure describing attribute information:
name
: The name used to store the attribute, such as:fastbootd_protocol
etc.context
: The context used to store attributes, such as:u:object_r:fastbootd_protocol_prop:s0
etc.type
: The type used to store attributes, such as:string
,boot
,int
etc.
Their relationship is as follows:
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());
}
// 根据匹配的类型来存储上下文context
if (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);
}
To illustrate with an example:
For example, std::vector<PropertyInfoEntry>& property_info
the data is:
("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)
Then its tree structure diagram:
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=xxx
ro.boot.mode=xxx
ro.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> temp
load_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> properties
load_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_contents
auto 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_contents
LoadProperties(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__ = 28
if (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赋值给key
key = sol;
// 此时eol指向的是换行符'\n',然后把换行符'\n'改成0,即字符串结束标志,
// 然后eol++,为下面sol行头赋值
*eol++ = 0;
// 上面行尾eol++,即是行头了sol
sol = eol;
// 获取到key,遇到非空格字符停止,即行头有可能以空格符开始的,则除去空格符,再获取key
while (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_file
load_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()一般为true
if (!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_fd
if (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;
}
}
Step 1 : Set the signal processing function, SIGCHLD
and SIGTERM
add the and signals to the signal mask set, so as to prevent these two signals from interrupting the execution of the program during the operation of the signal processing function. This setting is often used in multi-process programming to prevent child processes from being terminated or interrupted unexpectedly, causing the parent process to fail to work properly . Then by pthread_atfork
setting the child process can correctly receive SIGCHLD
the and SIGTERM
signals, that is, the setting does not inherit the blocking of these signals by the parent process
Step 2 : Create signal_fd
a file descriptor, and Epoll
asynchronously receive SIGCHLD,SIGTERM
signals
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_fd
if (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的socket
if (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_fd
auto 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_service
snprintf(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上下文,存放到secontext
if (SelabelLookupFileContext(addr.sun_path, S_IFSOCK, &secontext) && !secontext.empty()) {
// 设置文件系统创建的默认安全上下文为secontext
setfscreatecon(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 << "'";
}
}
// 绑定该fd
int 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 fd
if (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));
}
}
The fourth stage calls init.rc
When the attribute service is established, init
its own functions basically come to an end, and then
Needed to start other processes. But init
what about other processes? other
The process is a binary file, we can directly use exec
the command method to
Start, for example ./system/bin/init second_stage
, to start the first
second stage. But Android
the system has so many Native
processes, if all pass
exec
Executing processes one by one in the code is undoubtedly a disastrous design
count.
On this basis, Android
a mechanism is introduced init.rc
, which is similar to reading
Configuration files to start different processes.
init.rc
is a configuration file, Android
written internally by the initialization language
( Android Init Language
) script written.
init.rc
It mainly contains five types of statements:
-
Action
-
Command
-
Service
-
Option
-
Import
Action
An action represents a group of commands ( commands
). An action includes a trigger, which determines
When to run this action
Action
: Through the trigger trigger
, that is, on
the statement beginning with the decision to execute the corresponding
The specific service
timing is as follows:
on early-init: Triggered in the early stages of initialization;
on init: Triggered during the initialization phase;
on late-init: triggered in the late stage of initialization;
on boot/charger: triggered when the system starts/charges;
on property: Triggered when the property value meets the condition;
Command
command
is action
a command in the command list, or service
an option in
onrestart
The parameter command, the command will be executed one by one when the event occurs.
Commonly used commands are listed below
class_start <service_class_name>
class
: start all of the same
Serve;
class_stop <service_class_name>
: Stop the service of the specified class
start <service_name>
: Start the specified service, if it is already started, skip it;
stop <service_name>
: Stop the running service
setprop
: set attribute value
mkdir
: Create the specified directory
symlink <sym_link>
<sym_link>
: create a symbolic link to connect to ;
write
: Write a string to the file path;
exec
: fork and execute, it will block the init process until the program is completed;
exprot
: Set environment variables;
loglevel
: set log
level
hostname
: set the hostname
import
: import an additional init
configuration file
####Service
Services Service
, service
starting with , init
are started by processes and generally run init
on
A child process, so service
it is necessary to determine whether the corresponding executable file is
exist.
parameter | meaning |
---|---|
Indicates the name of this service | |
Because the path where this service is located is an executable file, there must be a storage path. | |
Parameters to start the service | |
Constraint options for this service |
init
Spawned subprocesses, defined in rc
the file, each of which service
at startup will
Spawn a child process by fork
way.
For example: service servicemanager /system/bin/servicemanager
generation
The table is the service name servicemanager
, the path of service execution
for /system/bin/servicemanager
.
Options
Options
Yes Service
optional, service
use with
disabled
: Do not class
start automatically, only service
start according to the name;
oneshot
: service
No restart after exiting;
user/group
: Set the user/user group to execute the service, the default is root;
class
: Set the name of the class to which it belongs. When the class starts/exits, the service also starts/stops
stop, the default is default
;
onrestart
: Execute the corresponding command when the service is restarted;
socket
: Create a /dev/socket/
namedsocket
critical
service
: The system will restart and enter the recovery mode if it should restart continuously within the specified time
complex pattern
default: means disabled=false,oneshot=false,critical=false
.
#####import
to import other rc
files
Order:import
Call uevent_main to start
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/init
symlinks: ["ueventd"],
....
Create an /system/bin/ueventd
l soft link to/system/bin/init
SeondStateMain->LoadBootScripts(am, sm)>am.QueueEventTrigger("early-init")
:
on early-init
# ...
start ueventd
# ...
service ueventd /system/bin/ueventd
class core
critical
seclabel u:r:ueventd:s0
shutdown critical
service ueventd /system/bin/ueventd
, execution /system/bin/ueventd
is actually execution system/bin/init
, so it is passed to system/core/init/main.cpp
, but [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);
}
...
int ueventd_main(int argc, char** argv) {
// umask(0)用于设置当前进程的文件模式创建屏蔽字
// 用户创建文件夹权限值=初始创建文件夹默认值-umask的预设值
// 如:775=777-002
// 用户创建文件权限值=初始创建文件默认值-umask的预设值
// 如:664=666-002
umask(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 loop
setpriority(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 start
#...
import /system/etc/init/hw/init.${ro.zygote}.rc
#....
init.rc
located /system/core/rootdir
below. This path also includes four levels
to zygote
the rc
file.
They are init.zygote32.rc ,init.zygote32_64.rc
, init.zygote64.rc,
init.zygote64_32.rc
, which file to call is determined by the hardware.
Here take 64
the bit processor as an example, init.zygote64.rc
the code is as follows:
system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
# class是一个option,指定zygote服务的类型为main
class main
priority -20
user root
group root readproc reserved_disk
# socket关键字表示一个option,创建一个名为dev/socket/zygote,
# 类型为stream,权限为660的socket
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart是一个option,说明在zygote重启时需要执行的command
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
Parse:
service zygote
: init.zygote64.rc
defines a zygote
service. The process is created init
by this nameservice
zygote
/system/bin/app_process64 -Xzygote /system/bin --zygote --
start-system-server
Parse:
zygote
This service, performed by performing /system/bin/app_process64
and
Pass in 4 parameters to run:
-
Parameter 1:
-Xzygote
This parameter will be used as the parameter required when the virtual machine starts -
Parameter 2:
/system/bin
Represents the directory where the virtual machine program is located -
Parameter 3:
--zygote
Indicates that the functionZygoteInit.java
in the class is used as the entry point for the virtual machine to executemain
-
Parameter 4: -
-start-system-server
tellsZygote
the process to startsystemServer
the process
Summarize
-
init
The main work done in the first stage of the process is to mount the partition, create device nodes and some key directories, initialize the log output system, and enableSELinux
security policies. -
init
The main work of the second stage of the process is to initialize the attribute system, analyzeSELinux
the matching rules, process the subprocess termination signal, and start the system attribute service. It can be said that each item is very important. If the first stage is to prepare for the attribute system,SELinux
then The second stage is to actually implement these functions. -
init
The third stage is mainly to analyzeinit.rc
to start other processes, enter an infinite loop, and monitor sub-processes in real time.