1 init进程描述
Android系统实际上是运行于Linux内核之上的一系列"服务进程",并不算一个完成意义上的"操作系统";而这一系列进程是维持Android设备正常工作的关键,所以它们肯定有一个"根进程",这个"根进程"衍生出了这一系列进程。这个"根进程"就是init进程。
init进程是Android系统启动的第一个进程。它通过解析init.rc脚本来构建出系统的初始形态。其他的"一系列"Android系统进程大部分也是通过"init.rc"来启动的。因为要兼容不同的开发商,所以init.rc脚本的语法很简单,并且采用的是纯文本编辑的,这样导致它可读性就会很高
2 Init进程 main函数
init是Linux系统中用户空间的第一个进程(pid=1),Linux Kernel启动后,会调用/system/core/init/Init.cpp的main()方法
3 init进程 FirstStageMain函数
路径 first_stage_init.cpp
代码解析
int FirstStageMain(int argc, char** argv) {
//REBOOT_BOOTLOADER_ON_PANIC 编译时定义的宏 1 代表内核存在巨大错误
/**第一部分**/
if (REBOOT_BOOTLOADER_ON_PANIC) {
//1.1 重启bootLoader
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
std::vector<std::pair<std::string, int>> errors;
//定义宏函数 检查函数调用结果
#define CHECKCALL(x) \
if ((x) != 0) errors.emplace_back(#x " failed", errno);
// Clear the umask.
//这个调用设置了文件模式创建掩码为0,这意味着当进程创建新文件时,
//默认不会屏蔽任何权限。这通常是为了确保init进程创建的文件具有适当的权限,
//因为init作为系统启动的第一个进程,需要设置正确的环境供其他进程继承
umask(0);
//清除当前进程环境
CHECKCALL(clearenv());
//设置当前进程环境变量 系统在哪些目录下查找可执行文件
CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
// Get the basic filesystem setup we need put together in the initramdisk
// on / and then we'll let the rc file figure out the rest.
/**第二部分**/
//挂载/创建基础目录
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
//创建目录dev/pts dev/socket /dev/dm-user
CHECKCALL(mkdir("/dev/pts", 0755));
CHECKCALL(mkdir("/dev/socket", 0755));
CHECKCALL(mkdir("/dev/dm-user", 0755));
////挂载devpts目录到dev/pts路径
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
//定义了MAKE_STR 就挂载proc目录
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
#undef MAKE_STR
// Don't expose the raw commandline to unprivileged processes.
//修改/proc/cmdline 文件的权限 所有者读权限
//所属组读权限 其他用户无权限
CHECKCALL(chmod("/proc/cmdline", 0440));
std::string cmdline;
//读取/proc/cmdline文件 这里记录的linux内核启动时的命令行参数
android::base::ReadFileToString("/proc/cmdline", &cmdline);
// Don't expose the raw bootconfig to unprivileged processes.
//调整/proc/bootconfig权限
chmod("/proc/bootconfig", 0440);
std::string bootconfig;
//读取bootConfig文件 这里记录的bootLoader引导配置信息
android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
//创建gid_t 数组
gid_t groups[] = {AID_READPROC};
//设置组列表
CHECKCALL(setgroups(arraysize(groups), groups));
//挂载sysfs目录
CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
//挂载selinuxfs目录
CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
//创建一个名为"/dev/kmsg"的字符设备节点
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设备节点
CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
//创建/dev/urandom设备节点
CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
// This is needed for log wrapper, which gets called before ueventd runs.
//创建/dev/ptmx设备节点
CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
//创建/dev/null设备节点
CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
// These below mounts are done in first stage init so that first stage mount can mount
// subdirectories of /mnt/{vendor,product}/. Other mounts, not required by first stage mount,
// should be done in rc files.
// Mount staging areas for devices managed by vold
// See storage config details at http://source.android.com/devices/storage/
//tmpfs挂载mnt
CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000"));
// /mnt/vendor is used to mount vendor-specific partitions that can not be
// part of the vendor partition, e.g. because they are mounted read-write.
//创建vendor目录
CHECKCALL(mkdir("/mnt/vendor", 0755));
// /mnt/product is used to mount product-specific partitions that can not be
// part of the product partition, e.g. because they are mounted read-write.
创建product目录
CHECKCALL(mkdir("/mnt/product", 0755));
// /debug_ramdisk is used to preserve additional files from the 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到kSecondStageRes
CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"))
// First stage init stores Mainline sepolicy here.
//创建selinux目录
CHECKCALL(mkdir("/dev/selinux", 0744));
#undef CHECKCALL
SetStdioToDevNull(argv);
// Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
// talk to the outside world...
//初始化kenellog
InitKernelLogging(argv);
//检查到现在 函数操作是否存在error
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";
}
//第一节段init完成
LOG(INFO) << "init first stage started!";
//打开根目录/ unique_ptr析构时自动调用closedir函数关闭目
auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir("/"), closedir};
if (!old_root_dir) {
PLOG(ERROR) << "Could not opendir(\"/\"), not freeing ramdisk";
}
struct stat old_root_info;
//读取根目录信息
if (stat("/", &old_root_info) != 0) {
PLOG(ERROR) << "Could not stat(\"/\"), not freeing ramdisk";
//失败 重置unique_ptr指针
old_root_dir.reset();
}
//是否启用控制台
/**第三部分 加载内核模块**/
auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;
//查看boot引导的配置 是否加载模块
auto want_parallel =
bootconfig.find("androidboot.load_modules_parallel = \"true\"") != std::string::npos;
//设置当前加载模块的时间
boot_clock::time_point module_start_time = boot_clock::now();
int module_count = 0;
//加载kenel模块
if (!LoadKernelModules(IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig), want_console,
want_parallel, module_count)) {
//失败 控制台输出
if (want_console != FirstStageConsoleParam::DISABLED) {
LOG(ERROR) << "Failed to load kernel modules, starting console";
} else {
LOG(FATAL) << "Failed to load kernel modules";
}
}
//成功 记录加载的内核模块数量是花费时间
if (module_count > 0) {
auto module_elapse_time = std::chrono::duration_cast<std::chrono::milliseconds>(
boot_clock::now() - module_start_time);
setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1);
LOG(INFO) << "Loaded " << module_count << " kernel modules took "
<< module_elapse_time.count() << " ms";
}
bool created_devices = false;
//当前存在failure情况 启动控制台
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);
}
/**第四部分**/
//文件拷贝
//打开kBootImageRamdiskProp路径文件
///system/etc/ramdisk/build.prop
if (access(kBootImageRamdiskProp, F_OK) == 0) {
//获取第二阶段/second_stage_resources//system/etc/ramdisk/build.prop
std::string dest = GetRamdiskPropForSecondStage();
//父目录名ramdisk
std::string dir = android::base::Dirname(dest);
std::error_code ec;
//创建父目录ramdisk
if (!fs::create_directories(dir, ec) && !!ec) {
LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();
}
//将build.prop文件copy到 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;
}
// If "/force_debuggable" is present, the second-stage init will use a userdebug
// sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked.
//打开force_debuggable路径下的文件
//打开force_debuggable路径下的文件
if (access("/force_debuggable", F_OK) == 0) {
constexpr const char adb_debug_prop_src[] = "/adb_debug.prop";
constexpr const char userdebug_plat_sepolicy_cil_src[] = "/userdebug_plat_sepolicy.cil";
std::error_code ec; // to invoke the overloaded copy_file() that won't throw.
//打开adb_debug.prop文件
if (access(adb_debug_prop_src, F_OK) == 0 &&
//adb_debug.prop文件 copy到/debug_ramdisk/adb_debug.prop目录
!fs::copy_file(adb_debug_prop_src, kDebugRamdiskProp, ec)) {
LOG(WARNING) << "Can't copy " << adb_debug_prop_src << " to " << kDebugRamdiskProp
<< ": " << ec.message();
}
//打开userdebug_plat_sepolicy.cil文件 将文件拷贝到/debug_ramdisk目录下
if (access(userdebug_plat_sepolicy_cil_src, F_OK) == 0 &&
!fs::copy_file(userdebug_plat_sepolicy_cil_src, kDebugRamdiskSEPolicy, ec)) {
LOG(WARNING) << "Can't copy " << userdebug_plat_sepolicy_cil_src << " to "
<< kDebugRamdiskSEPolicy << ": " << ec.message();
}
// setenv for second-stage init to read above kDebugRamdisk* files.
//设置环境 强制DEBUG true
setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
}
// setenv for second-stage init to read above kDebugRamdisk* files.
//设置环境 强制DEBUG true
setenv("INIT_FORCE_DEBUGGABLE", "true", 1);
}
//第一阶段中强制进行正常启动,并进行根目录的切换操作
if (ForceNormalBoot(cmdline, bootconfig)) {
mkdir("/first_stage_ramdisk", 0755);
PrepareSwitchRoot();
// SwitchRoot() must be called with a mount point as the target, so we bind mount the
// target directory to itself here.
if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
PLOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
}
SwitchRoot("/first_stage_ramdisk");
}
/**第五部分 挂载分区**/
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();
}
//旧根目录存在 并且 比较旧的根目录和新的根目录的设备号(st_dev)。
//如果它们不相等,表示根目录切换失败或发生了异常情况
if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {
//可以释放旧的根目录所在的 RAM 磁盘
FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);
}
SetInitAvbVersionInRecovery();
setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
1);
//可执行文件路径
/**第六部分 重新执行init文件**/
const char* path = "/system/bin/init";
const char* args[] = {path, "selinux_setup", nullptr};
//打开内核日志节点
auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
//init 进程的输出重定向到 /dev/kmsg 设备。
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
//执行"/system/bin/init"文件 传入的参数数组
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;
}
第一部分 安装系统重启信号处理器
REBOOT_BOOTLOADER_ON_PANIC
系统编译时定义的宏 1 代表 系统发生崩溃或严重错误时将系统重启到引导加载程序(bootloader)。
InstallRebootSignalHandlers函数解析
代码路径
reboot_utils.cpp
void InstallRebootSignalHandlers() {
// Instead of panic'ing the kernel as is the default behavior when init crashes,
// we prefer to reboot to bootloader on development builds, as this will prevent
// boot looping bad configurations and allow both developers and test farms to easily
// recover.
//用于设置信号处理器,当特定信号发生时,根据进程的身份执行相应的操作。
//对于 init 进程以外的其他进程,直接终止;对于 init 进程本身,执行致命错误重启操作
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.
//不是init进程引起的reboot
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.
//1.2 重启bootLoader
InitFatalReboot(signal);
};
//设置信号处理标志 重启
action.sa_flags = SA_RESTART;
//针对几个常见的异常信号(SIGABRT、SIGBUS、SIGFPE、SIGILL、SIGSEGV)的信号处理器,
//将它们与先前定义的 action 关联起来
//程序abort函数
sigaction(SIGABRT, &action, nullptr);
//非法地址访问或内存对齐错误
sigaction(SIGBUS, &action, nullptr);
//浮点数异常,如除以零或溢出
sigaction(SIGFPE, &action, nullptr);
//非法指令
sigaction(SIGILL, &action, nullptr);
//无效的内存引用或段错误
sigaction(SIGSEGV, &action, nullptr);
#if defined(SIGSTKFLT)
sigaction(SIGSTKFLT, &action, nullptr);
#endif
sigaction(SIGSYS, &action, nullptr);
sigaction(SIGTRAP, &action, nullptr);
}
- 首先创建sigaction结构体 分配内存
- sigfillset掩码填充 保证同一时刻处理信号时堵塞其他信号
- 设置信号处理函数
- 设置信号标志位
- 将一些异常信号与sigaction关联
继续看如何触发系统重启
InitFatalReboot函数
代码路径 reboot_utils.cpp
void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
//fork子进程
//目的增加系统稳定性并尽可能获取导致致命错误的信息
auto pid = fork();
if (pid == -1) {
// Couldn't fork, don't even try to backtrace, just reboot.
//如果无法fork子进程,为防止存在再次fork子进程情况
//立刻重启系统
RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);
} else if (pid == 0) {
// Fork a child for safety, since we always want to shut down if something goes wrong, but
// its worth trying to get the backtrace, even in the signal handler, since typically it
// does work despite not being async-signal-safe.
//fork成功 当前处于子进程
//延时5S 获取致命错误backtrace信息
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;
//创建backTrace 记录当前进程和线程
std::unique_ptr<Backtrace> backtrace(
Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD));
//Unwind获取调用栈
if (!backtrace->Unwind(0)) {
LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack.";
}
//遍历调用栈信息 打印
for (size_t i = 0; i < backtrace->NumFrames(); i++) {
LOG(ERROR) << backtrace->FormatFrameData(i);
}
if (init_fatal_panic) {
LOG(ERROR) << __FUNCTION__ << ": Trigger crash";
//将特定字符写入PROC_SYSRQ文件 来出发崩溃
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);
}
尝试fork子进程来增加系统稳定性并且尽可能获取错误信息
rebootSystem函数
代码路径 reboot_utils.cpp
void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
LOG(INFO) << "Reboot ending, jumping to kernel";
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:
reboot(RB_POWER_OFF);
break;
case ANDROID_RB_RESTART2:
//系统调用函数 重启bootLoader
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
break;
case ANDROID_RB_THERMOFF:
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";
syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
LINUX_REBOOT_CMD_RESTART2, kThermalShutdownTarget);
} else {
reboot(RB_POWER_OFF);
}
break;
}
// In normal case, reboot should not return.
PLOG(ERROR) << "reboot call returned";
abort();
}
会根据cmd和target调用系统函数重启bootLoader加载程序
第二部分 挂载目录
这里提到了创建的目录和挂载的目录
1.tmpfs目录
tmpfs 是一种基于内存的文件系统,它使用系统 RAM 或交换空间来存储文件和目录
tmpfs通常用于以下目的:
临时文件存储:系统的 /tmp 目录经常被挂载为tmpfs,以便提供一个临时文件存储的位置,这些文件在重启后不需要保留。
缓存和工作目录:某些应用程序可能会在 /var/run 或 /var/cache 等目录下使用tmpfs,以提高访问速度和效率。
减少磁盘I/O:由于tmpfs基于内存,使用它可以减少对物理存储设备的读写操作,从而延长磁盘寿命,并提高系统性能。
2./dev/pts目录
/dev/pts 目录包含了伪终端设备。伪终端(Pseudo Terminals,PTYs)是一种特殊的软件驱动接口,用于模拟硬件终端(如控制台或串行端口),它们允许非交互式程序(比如脚本)表现得像是在一个终端上运行
3/dev/socket目录
/dev/socket 目录是用于存放UNIX域套接字(UNIX Domain Sockets)的特殊目录。UNIX域套接字是一种进程间通信(IPC)机制,允许运行在同一台机器上的进程通过它来进行双向通信。与网络套接字不同,UNIX域套接字不依赖于网络协议栈,因此它们提供了一种低延迟、高性能的通信方式。
ADB shell或者直接在设备上的终端查看/dev/socket目录
4 /dev/dm-user/目录
/dev/dm-user/ 目录在Android系统中通常与“Device Mapper”用户空间接口有关。Device Mapper是Linux内核的一个组件,它提供了一个通用的框架来映射一个块设备到另一个块设备。这种映射可以通过各种方式进行,如线性映射、加密、快照、条带化(striping)或镜像等。
#undef MAKE_STR
// Don't expose the raw commandline to unprivileged processes.
//修改/proc/cmdline 文件的权限 所有者读权限
//所属组读权限 其他用户无权限
CHECKCALL(chmod("/proc/cmdline", 0440));
std::string cmdline;
//读取/proc/cmdline文件 这里记录的linux内核启动时的命令行参数
android::base::ReadFileToString("/proc/cmdline", &cmdline);
// Don't expose the raw bootconfig to unprivileged processes.
//调整/proc/bootconfig权限
chmod("/proc/bootconfig", 0440);
std::string bootconfig;
//读取bootConfig文件 这里记录的bootLoader引导配置信息
android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
//创建gid_t 数组
gid_t groups[] = {AID_READPROC};
//设置组列表
CHECKCALL(setgroups(arraysize(groups), groups));
//挂载sysfs目录
CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
//挂载selinuxfs目录
CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));
首先读到这里我们可以看到第一阶段到目前为止所做的事情
1 配置正确的环境
2 创建/挂载 重要的目录
3 重要文件设置权限
提到的目录
sysfs目录
Linux内核提供的一种虚拟文件系统,用于向用户空间公开内核和设备相关的信息。它以文件和目录的形式呈现系统中的各种设备、驱动程序、总线和其他内核组件
selinuxfs目录
SELinux(Security-Enhanced Linux)是一种安全增强的Linux安全模块,用于提供更细粒度的访问控制和强化系统的安全性。在SELinux中,selinuxfs是一个虚拟文件系统,用于与SELinux子系统进行交互并获取相关信息。
selinuxfs位于/sys/fs/selinux目录下,它是selinuxfs文件系统的挂载点。该文件系统提供了许多文件和目录,用于控制和配置SELinux的各个方面。
以下是selinuxfs中一些重要文件和目录的说明:
enforce:该文件指示当前SELinux策略是否处于强制模式。如果内容为1,则表示强制模式;如果为0,则表示宽容模式。
booleans:此目录包含SELinux的布尔变量。布尔变量用于控制特定安全策略的开关。可以通过读取和写入这些文件来启用或禁用不同的SELinux功能。
contexts:此目录包含文件上下文的定义。文件上下文描述了文件、进程和其他系统资源的安全属性。通过该目录,可以查看和修改文件上下文。
policies:此目录包含当前加载的SELinux策略文件。可以通过检查该目录来获取有关当前正在使用的策略的信息。
status:该文件提供了SELinux子系统的状态信息,包括当前策略名称、模式、强制模式和策略版本等
后续我们可以了解一下Selinux 以及如何通过Selinuxfs配置
//创建一个名为"/dev/kmsg"的字符设备节点
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设备节点
CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
//创建/dev/urandom设备节点
CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));
// This is needed for log wrapper, which gets called before ueventd runs.
//创建/dev/ptmx设备节点
CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
//创建/dev/null设备节点
CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));
这里创建Linux系统常用设备节点
比如/dev/kmsg/ 内核缓冲区 用于写入kenel log
/dev/random:这是一个字符设备节点,用于生成随机数。该设备节点返回的数据源于硬件熵池,因此可以用于密码学、安全性等要求较高的应用程序
/dev/urandom:这也是一个字符设备节点,与/dev/random类似,但使用的是伪随机数生成器(PRNG)。该设备节点返回的数据不依赖于硬件熵池,因此可以用于生成大量随机数据
/dev/ptmx:这是一个字符设备节点,用于打开和管理伪终端(pty)设备。当用户在终端中使用SSH、telnet等远程登录协议时,通常会使用该设备节点来创建虚拟终端
/dev/null:这是一个字符设备节点,用于丢弃所有写入它的数据。该设备节点通常用于测试和调试,或者在脚本中将输出重定向到无效位置。
CHECKCALL(mount("tmpfs", "/mnt", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=1000"));
// /mnt/vendor is used to mount vendor-specific partitions that can not be
// part of the vendor partition, e.g. because they are mounted read-write.
//创建vendor目录
CHECKCALL(mkdir("/mnt/vendor", 0755));
// /mnt/product is used to mount product-specific partitions that can not be
// part of the product partition, e.g. because they are mounted read-write.
创建product目录
CHECKCALL(mkdir("/mnt/product", 0755));
// /debug_ramdisk is used to preserve additional files from the 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到kSecondStageRes
CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"))
// First stage init stores Mainline sepolicy here.
//创建selinux目录
CHECKCALL(mkdir("/dev/selinux", 0744));
InitKernelLogging(argv);
//检查到现在 函数操作是否存在error
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";
}
//第一节段init完成
LOG(INFO) << "init first stage started!";
第三部分 加载内核模块
bool LoadKernelModules(bool recovery, bool want_console, bool want_parallel, int& modules_loaded) {
struct utsname uts;
//获取内核版本
if (uname(&uts)) {
LOG(FATAL) << "Failed to get kernel version.";
}
int major, minor;
//用于从字符串中读取数据并根据指定的格式进行解析
//格式化解析内核版本
if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
LOG(FATAL) << "Failed to parse kernel version " << uts.release;
}
//#define MODULE_BASE_DIR "/lib/modules" 打开路径下目录
std::unique_ptr<DIR, decltype(&closedir)> base_dir(opendir(MODULE_BASE_DIR), closedir);
if (!base_dir) {
LOG(INFO) << "Unable to open /lib/modules, skipping module loading.";
return true;
}
dirent* entry;
std::vector<std::string> module_dirs;
//读取/lib/modules的目录
while ((entry = readdir(base_dir.get()))) {
if (entry->d_type != DT_DIR) {
//不是目录跳过
continue;
}
int dir_major, dir_minor;
//目录项的名称进行解析,并与预期的内核版本号进行比较
if (sscanf(entry->d_name, "%d.%d", &dir_major, &dir_minor) != 2 || dir_major != major ||
dir_minor != minor) {
//跳过
continue;
}
//内核模块列表记录加载列表
module_dirs.emplace_back(entry->d_name);
}
// Sort the directories so they are iterated over during module loading
// in a consistent order. Alphabetical sorting is fine here because the
// kernel version at the beginning of the directory name must match the
// current kernel version, so the sort only applies to a label that
// follows the kernel version, for example /lib/modules/5.4 vs.
// /lib/modules/5.4-gki.
//排序
std::sort(module_dirs.begin(), module_dirs.end());
//遍历/lib/modules下的目录
for (const auto& module_dir : module_dirs) {
std::string dir_path = MODULE_BASE_DIR "/";
dir_path.append(module_dir);
//加载目录下的内核模块
Modprobe m({dir_path}, GetModuleLoadList(recovery, dir_path));
bool retval = m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
//如果加载的目录下内核模块>0
if (modules_loaded > 0) {
return retval;
}
}
//获取/lib/modules下模块集合
Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(recovery, MODULE_BASE_DIR));
bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency())
: m.LoadListedModules(!want_console);
modules_loaded = m.GetModuleCount();
if (modules_loaded > 0) {
//加载模块数量 >0 返回
return retval;
}
return true;
}
读取lib/modules/下的目录 加载路径下的内核模块
如果lib/modules/下的目录下的路径都加载失败了
那就加载lib/modules/下的内核模块
第四部分 文件拷贝
- 将system/etc/ramdisk/build.prop文件拷贝到/second_stage_resources//system/etc/ramdisk/目录下
2.查看force_debuggable文件是否打开
adb_debug.prop文件 copy到/debug_ramdisk/adb_debug.prop目录
打开userdebug_plat_sepolicy.cil文件 将文件拷贝到/debug_ramdisk目录下
第五部分 挂载基础分区
函数 DoFirstStageMount
文件路径 first_stage_mount.cpp
bool FirstStageMount::DoFirstStageMount() {
if (!IsDmLinearEnabled() && fstab_.empty()) {
// Nothing to mount.
LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
return true;
}
if (!MountPartitions()) return false;
return true;
}
后续补充
第六部分 执行init文件
定义参数数组
const char* args[] = {path, "selinux_setup", nullptr};
执行init文件
4 SetupSelinux函数
由于在FirstStageMain函数重新执行了init文件,并传入参数selinux_setup 接下来重新执行init文件
函数 main函数
文件 main.cpp
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
__asan_set_error_report_callback(AsanReportCallback);
#elif __has_feature(hwaddress_sanitizer)
__hwasan_set_error_report_callback(AsanReportCallback);
#endif
// Boost prio which will be restored later
setpriority(PRIO_PROCESS, 0, -20);
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (argc > 1) {
if (!strcmp(argv[1], "subcontext")) {
android::base::InitLogging(argv, &android::base::KernelLogger);
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
return SubcontextMain(argc, argv, &function_map);
}
if (!strcmp(argv[1], "selinux_setup")) {
//执行selinux初始化
return SetupSelinux(argv);
}
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
return FirstStageMain(argc, argv);
}
接下来看SetupSelinux函数
函数 SetupSelinux
路径 selinux.cpp
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
InitKernelLogging(argv);
//加载重启信号处理器
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
boot_clock::time_point start_time = boot_clock::now();
MountMissingSystemPartitions();
//设置selinux 内核log记录
SelinuxSetupKernelLogging();
LOG(INFO) << "Opening SELinux policy";
PrepareApexSepolicy();
// Read the policy before potentially killing snapuserd.
//****第一部分****
//涉及到将预编译文件编译
//读取selinux策略文件
std::string policy;
ReadPolicy(&policy);
CleanupApexSepolicy();
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_helper->StartTransition();
}
//加载策略文件
LoadSelinuxPolicy(policy);
if (snapuserd_helper) {
// Before enforcing, finish the pending snapuserd transition.
snapuserd_helper->FinishTransition();
snapuserd_helper = nullptr;
}
// This restorecon is intentionally done before SelinuxSetEnforcement because the permissions
// needed to transition files from tmpfs to *_contexts_file context should not be granted to
// any process after selinux is set into enforcing mode.
//恢复/dev/selinux/文件的安全上下文。
if (selinux_android_restorecon("/dev/selinux/", SELINUX_ANDROID_RESTORECON_RECURSE) == -1) {
PLOG(FATAL) << "restorecon failed of /dev/selinux failed";
}
//设置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.
if (selinux_android_restorecon("/system/bin/init", 0) == -1) {
PLOG(FATAL) << "restorecon failed of /system/bin/init failed";
}
setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);
//设置执行参数 重新执行init文件
const char* path = "/system/bin/init";
const char* args[] = {path, "second_stage", nullptr};
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;
}
主要完成的事情
1 获取预编译文件 编译 读取selinux policy文件
2 加载policy文件
3 恢复/dev/selinux/路径下文件的上下文
4 设置selinux的启动状态 是强制 禁用 忽略
5 接下来设置main函数参数 重新执行init文件
5 SecondStageMain函数
因为SetupSelinux函数设置参数是second_stage
接下来init文件将执行SecondStageMain函数
疑问SecondStageMain函数做了什么
代码分析
int SecondStageMain(int argc, char** argv) {
if (REBOOT_BOOTLOADER_ON_PANIC) {
InstallRebootSignalHandlers();
}
// No threads should be spin up until signalfd
// is registered. If the threads are indeed required,
// each of these threads _should_ make sure SIGCHLD signal
// is blocked. See b/223076262
boot_clock::time_point start_time = boot_clock::now();
trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };
SetStdioToDevNull(argv);
InitKernelLogging(argv);
LOG(INFO) << "init second stage started!";
// Update $PATH in the case the second stage init is newer than first stage init, where it is
// first set.
//更新上下文环境
if (setenv("PATH", _PATH_DEFPATH, 1) != 0) {
PLOG(FATAL) << "Could not set $PATH to '" << _PATH_DEFPATH << "' in second stage";
}
// Init should not crash because of a dependence on any other process, therefore we ignore
// SIGPIPE and handle EPIPE at the call site directly. Note that setting a signal to SIG_IGN
// is inherited across exec, but custom signal handlers are not. Since we do not want to
// ignore SIGPIPE for child processes, we set a no-op function for the signal handler instead.
//这里提到SIGPIPE这个信号的处理函数为空
//这里提到的原因是Init进程作为第一个起来的进程 他的崩溃跟后续子进程没关系
{
struct sigaction action = {.sa_flags = SA_RESTART};
action.sa_handler = [](int) {};
sigaction(SIGPIPE, &action, nullptr);
}
// Set init and its forked children's oom_adj.
//DEFAULT_OOM_SCORE_ADJUST = -1000
//设置init进程在OOM下的kill进程优先级
///proc/1/oom_score_adj 这个文件保存的值是优先级值
//默认-1000是最小的
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();
}
// Set up a session keyring that all processes will have access to. It
// will hold things like FBE encryption keys. No process should override
// its session keyring.
keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 1);
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
// See if need to load debug props to allow adb root, when the device is unlocked.
const char* force_debuggable_env = getenv("INIT_FORCE_DEBUGGABLE");
bool load_debug_prop = false;
if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {
load_debug_prop = "true"s == force_debuggable_env;
}
unsetenv("INIT_FORCE_DEBUGGABLE");
// Umount the debug ramdisk so property service doesn't read .prop files from there, when it
// is not meant to.
if (!load_debug_prop) {
UmountDebugRamdisk();
}
//第一部分加载系统属性和对应的selinux安全上下文
//解析文件 设置文件默认属性
PropertyInit();
// Umount second stage resources after property service has read the .prop files.
UmountSecondStageRes();
// Umount the debug ramdisk after property service has read the .prop files when it means to.
if (load_debug_prop) {
UmountDebugRamdisk();
}
// Mount extra filesystems required during second stage init
MountExtraFilesystems();
// Now set up SELinux for second stage.
SelinuxSetupKernelLogging();
SelabelInitialize();
SelinuxRestoreContext();
Epoll epoll;
if (auto result = epoll.Open(); !result.ok()) {
PLOG(FATAL) << result.error();
}
InstallSignalFdHandler(&epoll);
InstallInitNotifier(&epoll);
//第二部分 打开属性服务
StartPropertyService(&property_fd);
// Make the time that init stages started available for bootstat to log.
RecordStageBoottimes(start_time);
// Set libavb version for Framework-only OTA match in Treble build.
if (const char* avb_version = getenv("INIT_AVB_VERSION"); avb_version != nullptr) {
SetProperty("ro.boot.avb_version", avb_version);
}
unsetenv("INIT_AVB_VERSION");
fs_mgr_vendor_overlay_mount_all();
export_oem_lock_status();
MountHandler mount_handler(&epoll);
SetUsbController();
SetKernelVersion();
const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
Action::set_function_map(&function_map);
if (!SetupMountNamespaces()) {
PLOG(FATAL) << "SetupMountNamespaces failed";
}
InitializeSubcontext();
ActionManager& am = ActionManager::GetInstance();
ServiceList& sm = ServiceList::GetInstance();
//加载解析init.rc脚本
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.
auto is_running = android::gsi::IsGsiRunning() ? "1" : "0";
SetProperty(gsi::kGsiBootedProp, is_running);
auto is_installed = android::gsi::IsGsiInstalled() ? "1" : "0";
SetProperty(gsi::kGsiInstalledProp, is_installed);
//将Action添加到行为队列中和集合中记录
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
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.
am.QueueEventTrigger("init");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
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.
//设置下次唤醒默认时间
auto epoll_timeout = std::optional<std::chrono::milliseconds>{kDiagnosticTimeout};
//检查系统是否需要关闭
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
LOG(INFO) << "Got shutdown_command '" << *shutdown_command
<< "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
shutdown_state.set_do_shutdown(false);
}
//当前没有要处理的service或者属性时
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
//执行命令队列
am.ExecuteOneCommand();
}
if (!IsShuttingDown()) {
//考虑重启进程相关
auto next_process_action_time = HandleProcessActions();
// If there's a process that needs restarting, wake up in time for that.
if (next_process_action_time) {
epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_action_time - boot_clock::now());
if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
}
}
//当前属性没发生改变或者没有正在执行的服务
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
// If there's more work to do, wake up again immediately.
//action队列存在命令 那么设置唤醒时间为当前
if (am.HasMoreCommands()) epoll_timeout = 0ms;
}
//如果epoll监听的fd未读取到事件则堵塞
auto pending_functions = epoll.Wait(epoll_timeout);
//处理读取到事件
//1 处理系统属性变化事件
//2 处理一种被称为"chorded keyboard"的键盘输入时间
//3 回收僵尸进程
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)();
}
} else if (Service::is_exec_service_running()) {
static bool dumped_diagnostics = false;
std::chrono::duration<double> waited =
std::chrono::steady_clock::now() - Service::exec_service_started();
if (waited >= kDiagnosticTimeout) {
LOG(ERROR) << "Exec service is hung? Waited " << waited.count()
<< " without SIGCHLD";
if (!dumped_diagnostics) {
DumpPidFds("exec service opened: ", Service::exec_service_pid());
std::string status_file =
"/proc/" + std::to_string(Service::exec_service_pid()) + "/status";
DumpFile("exec service: ", status_file);
dumped_diagnostics = true;
LOG(INFO) << "Attempting to handle any stuck SIGCHLDs...";
HandleSignalFd(true);
}
}
}
if (!IsShuttingDown()) {
HandleControlMessages();
SetUsbController();
}
}
return 0;
}
5.1 第一部分 加载系统属性
代码路径 property_service.cpp
void PropertyInit() {
//设置selinux回调
selinux_callback cb;
cb.func_audit = PropertyAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
//创建/dev/__properties__目录
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
//加载selinux目录下规定的系统属性安全上下文
CreateSerializedPropertyInfo();
//将/dev/_properties_/properties_serial 映射到内存
if (__system_property_area_init()) {
LOG(FATAL) << "Failed to initialize property area";
}
//将/dev/_properties_/properties_info 映射到内存
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.
//解析proc/device-tree/firmware/android
ProcessKernelDt();
//解析 /proc/cmdline/
ProcessKernelCmdline();
//解析/proc/config/
ProcessBootconfig();
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
//设置文件属性
ExportKernelBootProps();
//加载默认属性
PropertyLoadBootDefaults();
}
static void HandleInitSocket() {
auto message = ReadMessage(init_socket);
if (!message.ok()) {
LOG(ERROR) << "Could not read message from init_dedicated_recv_socket: " << message.error();
return;
}
这里主要加载默认属性
5.2 第二部分启动属性服务
void StartPropertyService(int* epoll_socket) {
//初始化 设置属性服务版本
InitPropertySet("ro.property_service.version", "2");
//设置socket数组
int sockets[2];
//创建socket对
if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
PLOG(FATAL) << "Failed to socketpair() between property_service and init";
}
//client端给到init进程
*epoll_socket = from_init_socket = sockets[0];
//服务端 属性服务
init_socket = sockets[1];
//设置标记为当前可接收信息
StartSendingMessages();
//创建property service 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();
}
listen(property_set_fd, 8);
//创建启动属性服务线程
auto new_thread = std::thread{PropertyServiceThread};
property_service_thread.swap(new_thread);
}
1 创建socket对保证init和propertyService之间的通信
2 创建socket监听属性变化事件
3 启动属性服务循环接收信息
5.3 第三部分 加载init.rc脚本
//加载解析init.rc脚本
LoadBootScripts(am, sm);
加载init.rc脚本 后续详细分析init.rc脚本
5.4 第四部分 创建行为事件添加到行为队列
//将Action添加到行为队列中和集合中记录
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
am.QueueBuiltinAction(TestPerfEventSelinuxAction, "TestPerfEventSelinux");
am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, "ConnectEarlyStageSnapuserd");
am.QueueEventTrigger("early-init");
// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
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.
am.QueueEventTrigger("init");
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
wait_for_coldboot_done_action()函数,等待冷插拔设备初始化完成
mix_hwrng_into_linux_rng_action()函数,从硬件PNG的设备文件/dev/hw_random中读取512字节并写到Linux RNG的设备文件/dev/urandom中
keychord_init_action()函数:初始化组合键监听模块
console_init_action()函数:在屏幕个上显示Android字样的Logo
property_service_init_action()函数:初始化属性服务,读取系统预制的属性值
singal_init_action()函数:初始化信号处理模块。
check_startup_action()函数:检查是否已经完成init进程初始化,如果完成则删除.booting文件。
queue_property_triggers_action()函数:检查Action列表中通过修改属性来触发的Action,查看相关属性值是否已经设置,如果已经设置,则将Action加入到执行列表中
5.5 第五部分 while无线循环处理
第一件事处理行为队列的命令
while (true) {
// By default, sleep until something happens.
//设置下次唤醒默认时间
auto epoll_timeout = std::optional<std::chrono::milliseconds>{kDiagnosticTimeout};
//检查系统是否需要关闭
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) {
LOG(INFO) << "Got shutdown_command '" << *shutdown_command
<< "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
shutdown_state.set_do_shutdown(false);
}
//当前没有要执行的service或者属性变化时
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
//执行命令队列
am.ExecuteOneCommand();
}
第二件事 重启服务进程
if (!IsShuttingDown()) {
//考虑重启服务进程相关 调整epoll唤醒时间
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;
}
}
看下HandleProcessActions函数
static std::optional<boot_clock::time_point> HandleProcessActions() {
std::optional<boot_clock::time_point> next_process_action_time;
//遍历服务列表,检查每个服务是否已经超时,如果超时则进行相应处理,
//并找到下一个需要处理的服务的超时时间点。这样可以及时处理服务的超时情况,
//并进行相应的调度和管理
for (const auto& s : ServiceList::GetInstance()) {
if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {
auto timeout_time = s->time_started() + *s->timeout_period();
if (boot_clock::now() > timeout_time) {
//超时处理
s->Timeout();
} else {
if (!next_process_action_time || timeout_time < *next_process_action_time) {
//如果next_process_action_time不存在或者超时时间<next_process_action_time
//设置next_process_action_time为服务超时时间
next_process_action_time = timeout_time;
}
}
}
//如果服务进程不需要重启
if (!(s->flags() & SVC_RESTARTING)) continue;
//计算重启时间
auto restart_time = s->time_started() + s->restart_period();
//如果当前时间超过重启的时间
if (boot_clock::now() > restart_time) {
//重启服务进程
if (auto result = s->Start(); !result.ok()) {
LOG(ERROR) << "Could not restart process '" << s->name() << "': " << result.error();
}
} else {
//下次进程动作时间>重启时间
if (!next_process_action_time || restart_time < *next_process_action_time) {
//调整下次进程动作时间为重启时间
next_process_action_time = restart_time;
}
}
}
return next_process_action_time;
}
第四件事epoll监听的fd是否有事件到来
//如果epoll监听的fd未读取到事件则堵塞
auto pending_functions = epoll.Wait(epoll_timeout);
//处理读取到事件
//1 处理系统属性变化事件
//2 处理一种被称为"chorded keyboard"的键盘输入时间
//3 回收僵尸进程
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)();
}
}
👀关注公众号:Android老皮!!!欢迎大家来找我探讨交流👀