Android 进阶解密笔记:系统启动流程(一)解析init进程启动过程

2,603 阅读3分钟
一、init 进程

init 进程是 Android 系统中用户进程的第一个进程,进程号为 1,被赋予很多极其重要的职责,如创建 Zygote 进程和属性服务等。init 进程由多个源文件组成,文件位于 system/core/init 目录。

二、init 进程启动流程

1658820327653.jpg

Android系统启动流程的前几步:

  1. 启动电源以及系统启动

    按下电源时,引导芯片代码从预定义的地方(固化在 ROM)开始执行,加载引导程序 BootLoader 到 RAM 中,然后执行。

  2. 引导程序 BootLoader

    是 Android 操作系统运行之前的一个小程序,把系统 OS 拉起来并运行。

  3. Linux 内核启动

    当内核启动时,设置缓存、被保护存储器、计划列表、加载驱动。在内核完成系统设置后,首先在系统文件中寻找 init.rc 文件,并启动 init 进程。

  4. init 进程启动

    主要用来初始化和启动属性服务,也用来启动 Zygote 进程。

三、init入口函数

Linux 内核加载完成之后,第一件事就是启动 init 进程,首先会在系统文件中寻找 init.rc 文件

文件位置:system/core/init/init.cpp。

init.cpp 的入口函数 main() 方法代码流程:

int main(int argc, char** argv) {
    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }
    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }
    umask(0);
    add_environment("PATH", _PATH_DEFPATH);
    bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
    //① 创建和挂载启动所需的文件目录
    if (is_first_stage) {
        mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
        mkdir("/dev/pts", 0755);
        mkdir("/dev/socket", 0755);
        mount("devpts", "/dev/pts", "devpts", 0, NULL);
        #define MAKE_STR(x) __STRING(x)
        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
        mount("sysfs", "/sys", "sysfs", 0, NULL);
    }
    open_devnull_stdio();
    klog_init();
    klog_set_level(KLOG_NOTICE_LEVEL);
    NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
    if (!is_first_stage) {
        // Indicate that booting is in progress to background fw loaders, etc.
        close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
        //② property_init():对属性进行初始化
        property_init();
        process_kernel_dt();
        process_kernel_cmdline();
        export_kernel_boot_props();
    }
 ...
    //③ start_property_service():启动属性服务
    start_property_service();
    const BuiltinFunctionMap function_map;
    Action::set_function_map(&function_map);
    Parser& parser = Parser::GetInstance();
    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
    parser.AddSectionParser("on", std::make_unique<ActionParser>());
    parser.AddSectionParser("import", std::make_unique<ImportParser>());
    //④ parse.ParseConfig("/init.rc"):解析 init.rc 配置文件
    parser.ParseConfig("/init.rc");
   ...   
       while (true) {
        if (!waiting_for_exec) {
            am.ExecuteOneCommand();
            //⑤ restart_processes():重启死去的进程
            restart_processes();
        }
        int timeout = -1;
        if (process_needs_restart) {
            timeout = (process_needs_restart - gettime()) * 1000;
            if (timeout < 0)
                timeout = 0;
        }
        if (am.HasMoreCommands()) {
            timeout = 0;
        }
        bootchart_sample(&timeout);
        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
        if (nr == -1) {
            ERROR("epoll_wait failed: %s\n", strerror(errno));
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }
    return 0;
}

在 main() 函数中,②③ 分别完成了属性服务的初始化和启动,④ 用于解析 init.rc 文件,① 用于挂载系统运行时目录(系统停止时消失)。

四、解析 init.rc 文件

文件位置:system/core/init/init_parse.cpp

init.rc 是由 Android 初始化语言编写的一个非常重要的配置脚本文件。Android 初始化语言主要包含 5 种类型的语句:Action、Service、Command、Option、Import

Android 8.0 对 init.rc 文件进行了拆分,每个服务对应一个 rc 文件,其中 Zygote 启动脚本在 init.ZygoteXX.rc 中定义,64 位处理器对应 init.Zygote64.rc,对应文件目录 system/core/rootdir/init.zygote64.rc

在启动 Zygote 之前 首先会解析 Service 类型语句,把 Service 对象加入到 Service 链表中

init 启动 Zygote 会遍历 Service 链表,找到 class_name 为 main 的 Service,其中:

① Zygote 的 classname 为 main
② Zygote 执行程序的路径为 /system/bin/app_process64,对应文件为 app_main.cpp

init.rc 文件最后会调用 Service#Start() 函数(函数所在文件为 system/core/init/service.cpp),如果 Service 已经运行,则不再启动,否则最终通过会 execve(…) 函数去启动子进程,这样 Service 子进程就会被启动,如果当前启动的 Service 是 Zygote,则会进入 app_main.cpp 的 mian() 函数,也就是在 Zygote 的 main() 函数,接着就会调用 AppRuntime#start() 方法启动 Zygote,至此 Zygote 就启动了。

五、属性服务

Windows平台上有一个注册表管理器,注册表的内容采用键值对的形式来记录用户、软件的一些使用信息。即使系统或者软件重启,它还是能够根据之前在注册表中的记录,进行相应的初始化工作。Android也提供了一个类似的机制,叫做属性服务。

六、init进程总结

讲到这,总结起来init进程主要做了三件事: 1.创建一些文件夹并挂载设备 2.初始化和启动属性服务 3.解析init.rc配置文件并启动zygote进程

参考资料:

《Android系统启动流程(一)》