Android启动流程分析(1)-init进程

2022-08-14  本文已影响0人  鼻涕粑粑
启动过程

老是有在群里看到大佬们讨论Android系统的第一个始祖进程是init进程,对于有过多年开发经验的我,应用程序开发跟init进程是扯不上关系的,为了跟上大佬的脚步,不得不花时间看了一些列Android启动流程的文章,认认真真的跟着源码走了一遍,虽然对于实际开发应用没有什么提升,感觉也没啥鸟用,但走一遍确实对于整个Android的分层结构有了更深刻的认识,可以跟上群里大佬吹逼的脚步了,接下来就把我梳理的流程记录一下

从按开机键上电开始,Android在上电后会通过汇编指令去加载uboot引导程序,然后由uboot从分区中加载内核镜像,并启动内核,Android的内核使用的是Linux内核,内核这部分对于应用开发涉及不多,但是需要知道的是,Linux内核启动主要涉及到3个特殊的进程

这三个进程是内核的基础,init进程是Android用户空间的始祖进程,让代码从kernel层运行到native层的代码,然后再通过fork去孵化Java进程的始祖进程zygote,后续的app进程的启动都是由zygote进行fork孵化出来 启动流程.png 结合Android的系统分层架构,我们知道架构层级关系是kernel->native->framwork->apps,所以启动流程需要去分析其中关键的init进程和zygote进程,通过adb shell ps从当前手机的进程的关系可以很明显看出上面的父子关系 pspid.png

Init(pid=1)用户空间的始祖进程,孵化出了zygote进程(zygote进程有兼容的64位和32位,所以当前手机是2个zygote进程),然后所有的用户的apps(类似微信,系统相册,照相机等)都是由zygote进程孵化产生的。

init进程

接下来分析init进程的启动流程

内核代码初始化会执行到kernel/common/init/main.c ->kernel_init()

static int __ref kernel_init(void *unused){
    ...
    if (!try_to_run_init_process("/sbin/init") ||
        !try_to_run_init_process("/etc/init") ||
        !try_to_run_init_process("/bin/init") ||   //后续流程传入参数是"/bin/init"
        !try_to_run_init_process("/bin/sh"))
        return 0;
}

static int try_to_run_init_process(const char *init_filename)
{
    int ret;
    ret = run_init_process(init_filename);  //执行run_init_process方法
    return ret;
}

static int run_init_process(const char *init_filename)
{
  //执行kernel_execve指令,这个指令就是内核空间去执行用户空间的程序
    return kernel_execve(init_filename, argv_init, envp_init); 
}
Android.bp

/system/core/init

执行用户空间的init,会执行init文件夹下面的Android.bp脚本文件,里面可以可以看到init的执行入口函数在main.cpp代码中

cc_binary {
    name: "init_second_stage",
    recovery_available: true,
    stem: "init",
    defaults: ["init_defaults"],
    static_libs: ["libinit"],
    required: [
        "e2fsdroid",
        "init.rc",
        "mke2fs",
        "sload_f2fs",
        "make_f2fs",
        "ueventd.rc",
    ],
    srcs: ["main.cpp"],
    symlinks: ["ueventd"],
    target: {
        recovery: {
            cflags: ["-DRECOVERY"],
            exclude_shared_libs: [
                "libbinder",
                "libutils",
            ],
        },
    },
}
main.cpp

init的代码位于/system/core/init 下面,找到main函数的执行逻辑

//C++主函数有两个参数,argc表示参数的个数,第二个是参数的列表,具体的参数
int main(int argc, char** argv) { 
   //strcmp是是String的函数,比较字符串
   //basename是C的一个函数,得到特定路径中最后一个/后的内容
    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")) {
            return SetupSelinux(argv);
        }

        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    return FirstStageMain(argc, argv);
}

从这段代码看,主要执行的流程就是FirstStageMain/SetupSelinux/SecondStageMain/SubcontextMain

- FirstStageMain

第一次执行main函数的时候,没有携带任何参数,所以会最先执行FirstStageMain方法

int FirstStageMain(int argc, char** argv) {
   
    //挂载 创建 文件
    CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
    CHECKCALL(mkdir("/dev/pts", 0755));
    CHECKCALL(mkdir("/dev/socket", 0755));
    ...
   
    //重定向 输入输出
    SetStdioToDevNull(argv);
  
    //初始化内核的日志打印
    InitKernelLogging(argv);

    //启动 selinux_setup
    const char* path = "/system/bin/init";
    const char* args[] = {path, "selinux_setup", nullptr};
    auto fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);
    close(fd);
    execv(path, const_cast<char**>(args));

    return 1;
}
- SetupSelinux

接下来再次执行main.cpp中的main函数,传入了selinux_setup参数

int SetupSelinux(char** argv) {
    //重复第一步里面的重定向输入输出和日志打印
    SetStdioToDevNull(argv);
    InitKernelLogging(argv);

    ......
    const char* path = "/system/bin/init";
    const char* args[] = {path, "second_stage", nullptr};
    execv(path, const_cast<char**>(args));

    return 1;
}

这部分的作用主要是Linux的安全策略,管理Android系统的权限,保证系统内核代码运行的稳定

- SecondStageMain

接下来再次执行main.cpp中的main函数,传入了second_stage参数

//$init.cpp
int SecondStageMain(int argc, char** argv) {
  //再一次出现了这两个
    SetStdioToDevNull(argv);
    InitKernelLogging(argv);
  
  ......
    //创建一块共享的内存空间,初始化属性域
    PropertyInit();

  ......
    Epoll epoll;
    //处理子进程终止信号,杀死僵尸进程
    //初始化子进程退出信号处理函数,并调用epoll_ctl设置 property fd可读的回调函数
    InstallSignalFdHandler(&epoll);
    InstallInitNotifier(&epoll);
    //启动属性服务,调用epoll_ctl设置 property fd 可读的回调函数
    StartPropertyService(&property_fd);

  ......
    //匹配命令和函数之间的关系
    const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();
    Action::set_function_map(&function_map);

  ......
    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();

    //解析init.rc
    LoadBootScripts(am, sm);
  ......
    //类似Looper循环,死循环进入等待
    while (true) {
        auto pending_functions = epoll.Wait(epoll_timeout);
    }

    return 0;
}

//解析init.rc文件
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    //创建解析器
    Parser parser = CreateParser(action_manager, service_list);
 
    ......
    parser.ParseConfig("/system/etc/init/hw/init.rc");
}

//.rc文件的语法里面就有service on import等关键字,创造一个解析器
Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;

    parser.AddSectionParser("service", std::make_unique<ServiceParser>(
                                               &service_list, GetSubcontext(), std::nullopt));
    parser.AddSectionParser("on", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));
    parser.AddSectionParser("import", std::make_unique<ImportParser>(&parser));

    return parser;
}

//parse.cpp
bool Parser::ParseConfig(const std::string& path) {
    if (is_dir(path.c_str())) {
        return ParseConfigDir(path);
    }
    return ParseConfigFile(path);
}

bool Parser::ParseConfigDir(const std::string& path) {
    for (const auto& file : files) {
        if (!ParseConfigFile(file)) {
        }
    }
    return true;
}

bool Parser::ParseConfigFile(const std::string& path) {
    ParseData(path, &config_contents.value());
    return true;
}

//解析init文件
void Parser::ParseData(const std::string& filename, std::string* data) {
    for (;;) {
        switch (next_token(&state)) {
            case T_EOF:
                return;
            case T_NEWLINE: {
                break;
            }
            case T_TEXT:
                break;
        }
    }
}

//init.rc
import /system/etc/init/hw/init.${ro.zygote}.rc //${ro.zygote}由厂商定义,与平台相关

on late-init
    # Now we can start zygote for devices with file based encryption
    trigger zygote-start

on zygote-start && property:ro.crypto.state=unencrypted
    # A/B update verifier that marks a successful boot.
    exec_start update_verifier_nonencrypted
    start statsd  
    start netd      //start 对应的映射关系定义于 /system/core/init/builtins.cpp中
    start zygote    //调用 start 对应的处理函数,启动名为 zygote 的服务 (传入init.zygote.rc中定义的参数)
    start zygote_secondary
小结

init进程会去解析android.bp文件,从会找到执行入口是main.cpp的main()函数,而这一块会根据不同的入参分步进行初始化过程

解析init.rc文件里面会有start zygote命令,然后就会去执行zygote进程的启动流程,这部分在下一篇进行整理

上一篇 下一篇

猜你喜欢

热点阅读