Android 12 init 启动流程分析

Posted pecuyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 12 init 启动流程分析相关的知识,希望对你有一定的参考价值。

文章托管在gitee上 Android Notes , 同步csdn
本文基于android12 分析

概述

init是 Android 启动的第一个用户空间进程,它的地位非常重要,它fork产生系统的一些关键进程(如zygote,surfaceflinger进程),而zygote进一步fork产生system_server和其他应用进程,通过这套逻辑构建了Android的进程层次结构体系。init进程的功能包含但不限于以下:

  • 挂载系统分区和加载一些内核模块
  • 加载sepolicy 及使能 selinux
  • 支持属性服务
  • 启动脚本rc文件解析
  • 执行事件触发器和属性改变的事件
  • 子进程死亡监听,回收僵尸进程
  • 非oneshot服务保活

通过ps命令看看init进程信息

# ps -A|grep init                                                                                                           
root             1     0 10847128  4020 do_epoll_wait       0 S init  # 这个是 init 进程
root           166     1 10817360  1916 do_sys_poll         0 S init  # 这个是 subcontext 进程

在启动内核的start_kernel函数流程中,会调用run_init_process函数执行init程序,来启动init进程

run_init_process

在Android中执行的init是/init

/// @kernel_common/init/main.c
static int run_init_process(const char *init_filename)

	argv_init[0] = init_filename;
	pr_info("Run %s as init process\\n", init_filename);
	return do_execve(getname_kernel(init_filename),
		(const char __user *const __user *)argv_init,
		(const char __user *const __user *)envp_init);

/init 实际上是一个软链接,指向的是/system/bin/init

# ls /init -lZ                                                                                           
lrwxr-x--- 1 root shell u:object_r:init_exec:s0  16 2021-12-20 15:52 /init -> /system/bin/init

接下来,进入init的main函数。

main

main执行分为几个阶段:

  • FirstStage 挂载一些基础文件系统和加载内核模块等
  • selinux_setup 执行selinux的初始化
  • SecondStage 挂载其他文件系统,启动属性服务,执行boot流程等,主要逻辑都在这里实现
/// @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
    setpriority(PRIO_PROCESS, 0, -20);
    if (!strcmp(basename(argv[0]), "ueventd"))  // 处理uventd启动,共用一个main
        return ueventd_main(argc, argv);
    

    if (argc > 1) 
        if (!strcmp(argv[1], "subcontext"))  // subcontext 子进程入口,用于执行来自init的某些任务
            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); // 启动第一阶段

FirstStageMain

第一阶段初始化

/// @system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) 
    if (REBOOT_BOOTLOADER_ON_PANIC) // 设置panic处理器
        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.
    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"));
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    CHECKCALL(mkdir("/dev/dm-user", 0755));
    CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
    // /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.
    CHECKCALL(chmod("/proc/cmdline", 0440)); // 只读
    std::string cmdline;
    android::base::ReadFileToString("/proc/cmdline", &cmdline);
    // Don't expose the raw bootconfig to unprivileged processes.
    chmod("/proc/bootconfig", 0440);
    std::string bootconfig;
    android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
    gid_t groups[] = AID_READPROC;
    CHECKCALL(setgroups(arraysize(groups), groups));
    CHECKCALL(mount("sysfs", "/sys", "sysfs", 0, NULL));
    CHECKCALL(mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL));

    CHECKCALL(mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11)));

    if constexpr (WORLD_WRITABLE_KMSG) 
        CHECKCALL(mknod("/dev/kmsg_debug", S_IFCHR | 0622, makedev(1, 11)));
    

    CHECKCALL(mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8)));
    CHECKCALL(mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9)));

    // This is needed for log wrapper, which gets called before ueventd runs.
    CHECKCALL(mknod("/dev/ptmx", S_IFCHR | 0666, makedev(5, 2)));
    CHECKCALL(mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3)));

    // 重要的在第一阶段挂载,其他可以在rc执行流程中挂载
    // 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/
    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.
    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.
    CHECKCALL(mkdir("/mnt/product", 0755));

    // /debug_ramdisk is used to preserve additional files from the 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
    CHECKCALL(mount("tmpfs", kSecondStageRes, "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
                    "mode=0755,uid=0,gid=0"))
#undef CHECKCALL

    SetStdioToDevNull(argv);
    // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
    // talk to the outside world...
    InitKernelLogging(argv); // 初始化 kernel logger

    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;
    if (stat("/", &old_root_info) != 0) 
        PLOG(ERROR) << "Could not stat(\\"/\\"), not freeing ramdisk";
        old_root_dir.reset();
    

    auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;

    boot_clock::time_point module_start_time = boot_clock::now();
    int module_count = 0;
    // 加载内核模块
    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);
        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);
    

    // 拷贝prop,Copied ramdisk prop to /second_stage_resources/system/etc/ramdisk/build.prop
    if (access(kBootImageRamdiskProp, F_OK) == 0) 
        std::string dest = GetRamdiskPropForSecondStage();
        std::string dir = android::base::Dirname(dest);
        std::error_code ec;
        if (!fs::create_directories(dir, ec) && !!ec) 
            LOG(FATAL) << "Can't mkdir " << dir << ": " << ec.message();
        
        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.
    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.
        if (access(adb_debug_prop_src, F_OK) == 0 &&
            !fs::copy_file(adb_debug_prop_src, kDebugRamdiskProp, ec)) 
            LOG(WARNING) << "Can't copy " << adb_debug_prop_src << " to " << kDebugRamdiskProp
                         << ": " << ec.message();
        
        if (access(userdebug_plat_sepolicy_cil_src, F_OK) == 0 &&
            !fs::copy_file(userdebug_plat_sepolicy_cil_src, kDebugRamdiskSEPolicy, ec)) 
            LOG(WARNING) << "Can't copy " << userdebug_plat_sepolicy_cil_src << " to "
                         << kDebugRamdiskSEPolicy << ": " << ec.message();
        
        // setenv for second-stage init to read above kDebugRamdisk* files.
        setenv("INIT_FORCE_DEBUGGABLE", "true", 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.
        if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) 
            LOG(FATAL) << "Could not bind mount /first_stage_ramdisk to itself";
        
        SwitchRoot("/first_stage_ramdisk");
    

    // Mounts partitions specified by fstab in device tree.
		// 通过fstab的配置挂载,在Android中通常在 /system/etc/fstab.xxx ,/vendor/etc/fstab.xxx 等地方
    if (!DoFirstStageMount(!created_devices)) // 挂载一些必要分区,如/system
        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);
    

    SetInitAvbVersionInRecovery();

    setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),
           1);

    /// 再次执行init,此处传入了 selinux_setup 参数,会进入 selinux初始化阶段
    /// 从init main 方法可知,此过程调用 SetupSelinux
    const char* path = "/system/bin/init";
    const char* args[] = path, "selinux_setup", nullptr;
    auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
    d

以上是关于Android 12 init 启动流程分析的主要内容,如果未能解决你的问题,请参考以下文章

Android O: init进程启动流程分析(阶段一)

Android系统启动流程分析

Android 12 init rc脚本解析和事件执行流程

Android 12 init rc脚本解析和事件执行流程

Android O: init进程启动流程分析(阶段三)

Android 7.0系统启动流程分析