Android13系统启动流程-2init进程

263 阅读30分钟

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);
}

  1. 首先创建sigaction结构体 分配内存
  2. sigfillset掩码填充 保证同一时刻处理信号时堵塞其他信号
  3. 设置信号处理函数
  4. 设置信号标志位
  5. 将一些异常信号与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/下的内核模块

第四部分 文件拷贝

  1. 将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老皮!!!欢迎大家来找我探讨交流👀