likes
comments
collection
share

如何应对Android面试官 -> Android 系统启动流程浅析

作者站长头像
站长
· 阅读数 2

前言


如何应对Android面试官 -> Android 系统启动流程浅析

本章节主要分析 Android 系统启动流程;

Android系统启动的大概流程


当我们按下电源键的时候,就会从 Boot Rom 执行代码,就会加载一个 Boot loader 引导程序「此引导程序就会引导用户选择哪个系统启动,如果只有一个系统则直接命中」,Boot loader 就会拉起 Linux Kernel,Linux Kernel 就会从固定位置读取设置的一些参数,根据这些参数来加载驱动。Linux Kernel 通过 syscall 系统调用拉起用户态的第一个进程「init 进程,这个 init 进程会:初始化 Android 的一些属性服务,启动一系列的守护进程,启动 zygote 进程,启动 media server(多媒体相关的服务)」,当 zygote 进程启动之后会孵化出 system_server 进程,这个进程就会开启一系列的服务例如 ActivityManagerService,WindowManagerService,PackageManagerService,PowerManagerService 等等大概80多个服务;当我们通过 launcher 进程启动一个 app 的时候,就会通过zygote 进程 fork 出一个子进程,这个子进程就是我们 app 所在进程;

如何应对Android面试官 -> Android 系统启动流程浅析

看到上面的流程,可能大家会有疑问了,为什么不 fork init 进程,不 fork SystemServer 进程呢?带着这些疑问我们继续往下分析;

init 进程分析


我们先来看下 init 进程的启动流程,进入 init.cpp 文件的 main 函数看起;

/*
*
* 1.C++中主函数有两个参数,第一个参数argc表示参数个数,第二个参数是参数列表,也就是具体的参数
*
* 2.init的main函数有两个其它入口,一是参数中有ueventd,进入ueventd_main,二是参数中有watchdogd,进入watchdogd_main
*
*/
int main(int argc, char** argv) {
    /*
    * 1.strcmp是String的一个函数,比较字符串,相等返回0
    * 2.C++中0也可以表示false
    * 3.basename是C库中的一个函数,得到特定的路径中的最后一个'/'后面的内容,
    * 比如/sdcard/miui_recovery/backup,得到的结果是backup
    */
    if (!strcmp(basename(argv[0]), "ueventd")) { //当argv[0]的内容为ueventd时,strcmp的值为0,!strcmp为1, 1表示true,也就执行ueventd_main,ueventd主要是负责设备节点的创建、权限设定等一
些列工作
        return ueventd_main(argc, argv);
    }
    // watchdogd俗称看门狗,用于系统出问题时重启系统
    if (!strcmp(basename(argv[0]), "watchdogd")) {
        return watchdogd_main(argc, argv);
    }

    if (argc > 1 && !strcmp(argv[1], "subcontext")) {
        InitKernelLogging(argv);
        const BuiltinFunctionMap function_map;
        return SubcontextMain(argc, argv, &function_map);
    }
    // 初始化重启系统的处理信号,内部通过 sigaction 注册信号,当监听到该信号时重启系统
    if (REBOOT_BOOTLOADER_ON_PANIC) {
        InstallRebootSignalHandlers();
    }
    // 查看是否有环境变量INIT_SECOND_STAGE
    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
    /*
     * 1.init的main方法会执行两次,由is_first_stage控制,first_stage就是第一阶段要做的事
     */
    if (is_first_stage) {
        boot_clock::time_point start_time = boot_clock::now();

        // Clear the umask. 清空文件权限
        umask(0);

        clearenv();
        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.
        // mount 用来挂载文件系统,mount 属于 linux 系统调用
        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));
        // Don't expose the raw commandline to unprivileged processes.
        // 用于修改文件/目录的读写权限
        chmod("/proc/cmdline", 0440);
        gid_t groups[] = { AID_READPROC };
        setgroups(arraysize(groups), groups);
        mount("sysfs", "/sys", "sysfs", 0, NULL);
        mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
        // mknod用于创建Linux中的设备文件
        mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));

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

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

        // Mount staging areas for devices managed by vold
        // See storage config details at http://source.android.com/devices/storage/
        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.
        mkdir("/mnt/vendor", 0755);

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

        LOG(INFO) << "init first stage started!";

        if (!DoFirstStageMount()) {
            LOG(FATAL) << "Failed to mount required partitions early ...";
        }
        // Avb即Android Verfied boot,功能包括Secure Boot, verfying boot 和 dmverity,
        // 原理都是对二进制文件进行签名,在系统启动时进行认证,确保系统运行的是合法的二进制镜像文件。
        // 其中认证的范围涵盖:bootloader,boot.img,system.img
        SetInitAvbVersionInRecovery(); // 在刷机模式下初始化avb的版本,不是刷机模式直接跳过

        // Enable seccomp if global boot option was passed (otherwise it is enabled in zygote).
        global_seccomp();

        // Set up SELinux, loading the SELinux policy.
        SelinuxSetupKernelLogging();
        SelinuxInitialize(); // 加载SELinux policy,也就是安全策略

        // We're in the kernel domain, so re-exec init to transition to the init domain now
        // that the SELinux policy has been loaded.
        /*
         * 1.这句英文大概意思是,我们执行第一遍时是在kernel domain,所以要重新执行init文件,切换到init domain,
         * 这样SELinux policy才已经加载进来了
         * 2.后面的security_failure函数会调用panic重启系统
         */
        if (selinux_android_restorecon("/init", 0) == -1) {
            PLOG(FATAL) << "restorecon failed of /init failed";
        }

        setenv("INIT_SECOND_STAGE", "true", 1);

        static constexpr uint32_t kNanosecondsPerMillisecond = 1e6;
        uint64_t start_ms = start_time.time_since_epoch().count() / kNanosecondsPerMillisecond;
        setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1);

        char* path = argv[0];
        char* args[] = { path, nullptr };
        execv(path, args);

        // execv() only returns if an error happened, in which case we
        // panic and never fall through this conditional.
        PLOG(FATAL) << "execv(\"" << path << "\") failed";
    }

    // At this point we're in the second stage of init.
    InitKernelLogging(argv);
    LOG(INFO) << "init second stage started!";

    // 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));
    // 初始化属性系统,并从指定文件读取属性
    property_init();

    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.
    //接下来的一系列操作都是从各个文件读取一些属性,然后通过property_set设置系统属性
    // If arguments are passed both on the command line and in DT,
    // properties set in DT always have priority over the command-line ones.

    /*
     * 1.这句英文的大概意思是,如果参数同时从命令行和DT传过来,DT的优先级总是大于命令行的
     * 2.DT即device-tree,中文意思是设备树,这里面记录自己的硬件配置和系统运行参数,参考http://www.wowotech.net/linux_kenrel/why-dt.html
     */
    process_kernel_dt(); // 处理DT属性
    process_kernel_cmdline(); // 处理命令行属性

    // Propagate the kernel variables to internal variables
    // used by init as well as the current required properties.
    export_kernel_boot_props(); // 处理其他的一些属性

    // Make the time that init started available for bootstat to log.
    property_set("ro.boottime.init", getenv("INIT_STARTED_AT"));
    property_set("ro.boottime.init.selinux", getenv("INIT_SELINUX_TOOK"));

    // Set libavb version for Framework-only OTA match in Treble build.
    const char* avb_version = getenv("INIT_AVB_VERSION");
    if (avb_version) property_set("ro.boot.avb_version", avb_version);

    // Clean up our environment.
    // 清空这些环境变量,因为之前都已经存入到系统属性中去了
    unsetenv("INIT_SECOND_STAGE"); 
    unsetenv("INIT_STARTED_AT");
    unsetenv("INIT_SELINUX_TOOK");
    unsetenv("INIT_AVB_VERSION");

    // Now set up SELinux for second stage.
    SelinuxSetupKernelLogging();
    SelabelInitialize();
    SelinuxRestoreContext();
    // 创建epoll实例,并返回epoll的文件描述符
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd == -1) {
        PLOG(FATAL) << "epoll_create1 failed";
    }
    // 主要是创建handler处理子进程终止信号,创建一个匿名socket并注册到epoll进行监听
    sigchld_handler_init();

    if (!IsRebootCapable()) {
        // If init does not have the CAP_SYS_BOOT capability, it is running in a container.
        // In that case, receiving SIGTERM will cause the system to shut down.
        InstallSigtermHandler();
    }
    // 从文件中加载一些属性,读取usb配置
    property_load_boot_defaults();
    // 设置ro.boot.flash.locked 属性
    export_oem_lock_status();
    // 开启一个socket监听系统属性的设置
    start_property_service();
    // 设置sys.usb.controller 属性
    set_usb_controller();
    //方法映射 “class_start” -> "do_class_start"
    const BuiltinFunctionMap function_map;
    /*
     * 1.C++中::表示静态方法调用,相当于java中static的方法
     */
    Action::set_function_map(&function_map); // 将function_map存放到Action中作为成员属性

    subcontexts = InitializeSubcontexts();

    ActionManager& am = ActionManager::GetInstance();
    ServiceList& sm = ServiceList::GetInstance();
    
    // 解析 init.rc 配置文件,然后会把 zygote 进程拉起来;我们来看下 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();

    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(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");
    am.QueueBuiltinAction(SetMmapRndBitsAction, "SetMmapRndBits");
    am.QueueBuiltinAction(SetKptrRestrictAction, "SetKptrRestrict");
    am.QueueBuiltinAction(keychord_init_action, "keychord_init");
    am.QueueBuiltinAction(console_init_action, "console_init");

    // Trigger all the boot actions to get us started.
    am.QueueEventTrigger("init");

    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
    // wasn't ready immediately after wait_for_coldboot_done
    am.QueueBuiltinAction(MixHwrngIntoLinuxRngAction, "MixHwrngIntoLinuxRng");

    // 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");

    while (true) {
        // By default, sleep until something happens.
        int epoll_timeout_ms = -1;

        if (do_shutdown && !shutting_down) {
            do_shutdown = false;
            if (HandlePowerctlMessage(shutdown_command)) {
                shutting_down = true;
            }
        }

        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            am.ExecuteOneCommand();
        }
        if (!(waiting_for_prop || Service::is_exec_service_running())) {
            if (!shutting_down) {
                auto next_process_restart_time = RestartProcesses();

                // If there's a process that needs restarting, wake up in time for that.
                if (next_process_restart_time) {
                    epoll_timeout_ms = std::chrono::ceil<std::chrono::milliseconds>(
                                           *next_process_restart_time - boot_clock::now())
                                           .count();
                    if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
                }
            }

            // If there's more work to do, wake up again immediately.
            if (am.HasMoreCommands()) epoll_timeout_ms = 0;
        }

        epoll_event ev;
        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
        if (nr == -1) {
            PLOG(ERROR) << "epoll_wait failed";
        } else if (nr == 1) {
            ((void (*)()) ev.data.ptr)();
        }
    }

    return 0;
}

最核心的是这里 LoadBootScripts(am, sm); 这个函数会解析 init.rc 配置文件,然后会把 zygote 进程拉起来;我们来看下 init.rc 是如何解析的;

init.rc 解析流程

init.rc是一个非常重要的配置文件,它是由Android初始化语言(Android Init Language)编写的脚本,它主要包含五种类型语句:Action(Action中包含了一系列的 Command )、Commands( init 语言中的命令)、Services(由 init 进程启动的服务)、Options(对服务进行配置的选项)和 Import(引入其他配置文件)

init.rc 这个配置文件在 4.3 以后,zygote 进程的来就不在这里执行了,而是改成了 init.zygote32.rc 和 init.zygote64.rc 这两个文件;以及 init.zygote32_64.rc 和 init.zygote64_32.rc

这几个文件的区别就是启动 32 还是 64,而 32_64 表示一个主从模式,优先看 32 位能不能启动,不能启动则启动 64 位,反之则是优先启动 64 位;

我们来看下这个 init.rc 中的一些命令

on init # L41

    sysclktz 0
    
    # Mix device-specific information into the entropy pool
    copy /proc/cmdline /dev/urandom
    copy /default.prop /dev/urandom
    
on <trigger> [&& <trigger>]* //设置触发器
    <command>
    <command> //动作触发之后要执行的命令
service <name> <pathname> [ <argument> ]* //<service的名字><执行程序路径><传递参数> 
    <option> //Options是Services的参数配置. 它们影响Service如何运行及运行时机
    
    group <groupname> [ <groupname>\* ] //在启动Service前将group改为第一个groupname,第一个groupname是必须有的,默认值为root(或许默认值是无),第二个groupname可以不设置,用于追加组(通过setgroups)
    
   priority <priority> //设置进程优先级. 在-20~19之间,默认值是0,能过setpriority实现 
   
   socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]//创建一个unix域的socket,名字叫/dev/socket/name , 并将fd返回给Service. type 只能是"dgram", "stream" or "seqpacket"
   

我们接着进入 init.cpp 中看下 init.rc 具体是如何解析的

static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}

首先是调用 CreateParser 创建一个解析器

Parser CreateParser(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser;

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

    return parser;
}

解析的时候是分三种类型的,分别是 service 类型、on 类型、import 类型,然后对应使用不同类型的解析器;解析器创建出来之后,调用 ParseConfig 进行解析;

bool Parser::ParseConfig(const std::string& path, size_t* parse_errors) {
    *parse_errors = 0;
    if (is_dir(path.c_str())) {
        return ParseConfigDir(path, parse_errors);
    }
    return ParseConfigFile(path, parse_errors);
}

ParseConfig 最终调用到 ParseConfigFile 进行解析;

bool Parser::ParseConfigFile(const std::string& path, size_t* parse_errors) {
    LOG(INFO) << "Parsing file " << path << "...";
    android::base::Timer t;
    auto config_contents = ReadFile(path);
    if (!config_contents) {
        LOG(ERROR) << "Unable to read config file '" << path << "': " << config_contents.error();
        return false;
    }

    config_contents->push_back('\n');  // TODO: fix parse_config.
    ParseData(path, *config_contents, parse_errors);
    for (const auto& [section_name, section_parser] : section_parsers_) {
        section_parser->EndFile();
    }

    LOG(VERBOSE) << "(Parsing " << path << " took " << t << ".)";
    return true;
}

ParseConfigFile 最终调用到 ParseData 函数进行解析,这个函数就相当于把 init.rc 文件读取到内存中了;

void Parser::ParseData(const std::string& filename, const std::string& data, size_t* parse_errors) {
    // TODO: Use a parser with const input and remove this copy
    std::vector<char> data_copy(data.begin(), data.end());
    data_copy.push_back('\0');

    parse_state state;
    state.line = 0;
    state.ptr = &data_copy[0];
    state.nexttoken = 0;

    SectionParser* section_parser = nullptr;
    int section_start_line = -1;
    std::vector<std::string> args;

    auto end_section = [&] {
        if (section_parser == nullptr) return;

        if (auto result = section_parser->EndSection(); !result) {
            (*parse_errors)++;
            LOG(ERROR) << filename << ": " << section_start_line << ": " << result.error();
        }

        section_parser = nullptr;
        section_start_line = -1;
    };

    for (;;) {
        switch (next_token(&state)) {
            case T_EOF:
                end_section();
                return;
            case T_NEWLINE:
                state.line++;
                if (args.empty()) break;
                // If we have a line matching a prefix we recognize, call its callback and unset any
                // current section parsers.  This is meant for /sys/ and /dev/ line entries for
                // uevent.
                for (const auto& [prefix, callback] : line_callbacks_) {
                    if (android::base::StartsWith(args[0], prefix)) {
                        end_section();

                        if (auto result = callback(std::move(args)); !result) {
                            (*parse_errors)++;
                            LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                        }
                        break;
                    }
                }
                if (section_parsers_.count(args[0])) {
                    end_section();
                    section_parser = section_parsers_[args[0]].get();
                    section_start_line = state.line;
                    if (auto result =
                            // 这是一个抽象方向,具体实现在不同类型的解析器中(service、on、import)
                            section_parser->ParseSection(std::move(args), filename, state.line);
                        !result) {
                        (*parse_errors)++;
                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                        section_parser = nullptr;
                    }
                } else if (section_parser) {
                    // 一行一行的进行解析
                    if (auto result = section_parser->ParseLineSection(std::move(args), state.line);
                        !result) {
                        (*parse_errors)++;
                        LOG(ERROR) << filename << ": " << state.line << ": " << result.error();
                    }
                }
                args.clear();
                break;
            case T_TEXT:
                args.emplace_back(state.text);
                break;
        }
    }
}

读取 init.zygote64.rc 配置文件中的内容,进行语法分析;

// 这是一个抽象方向,具体实现在不同类型的解析器中(service、on、import)

section_parser->ParseSection(std::move(args), filename, state.line);

service 解析流程


我们来看下 Service 中的 ParseSection 的具体实现;

Result<Success> ServiceParser::ParseSection(std::vector<std::string>&& args,
                                            const std::string& filename, int line) {
    if (args.size() < 3) {
        return Error() << "services must have a name and a program";
    }
    // 读取文件名称
    const std::string& name = args[1];
    if (!IsValidName(name)) {
        return Error() << "invalid service name '" << name << "'";
    }

    Subcontext* restart_action_subcontext = nullptr;
    if (subcontexts_) {
        for (auto& subcontext : *subcontexts_) {
            if (StartsWith(filename, subcontext.path_prefix())) {
                restart_action_subcontext = &subcontext;
                break;
            }
        }
    }

    std::vector<std::string> str_args(args.begin() + 2, args.end());
    // 读取之后,创建了一个 service 对象;
    service_ = std::make_unique<Service>(name, restart_action_subcontext, str_args);
    return Success();
}

创建 service 对象之后,通过 ParseLineSection 一行一行的进行内容的解析

Result<Success> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {
    return service_ ? service_->ParseLine(std::move(args)) : Success();
}

解析完成之后,会调用 end_section() 函数,把解析的 service 添加到 vector 列表中

Result<Success> ServiceParser::EndSection() {
    if (service_) {
        Service* old_service = service_list_->FindService(service_->name());
        if (old_service) {
            if (!service_->is_override()) {
                return Error() << "ignored duplicate definition of service '" << service_->name()
                               << "'";
            }

            service_list_->RemoveService(*old_service);
            old_service = nullptr;
        }
        // 把解析的 service 添加到 vector 列表中,这个 service_list 就是一个列表,里面使用的是 vector
        service_list_->AddService(std::move(service_));
    }

    return Success();
}

所以 init 的整体流程总结如下:

  1. 创建和挂载启动所需要的文件目录;
  2. 初始化和启动属性服务;
  3. 解析init.rc配置文件并启动zygote进程;

Zygote 进程


我们接下来分析下 zygote 进程

Zygote 概述

Zygote 中文翻译为『受精卵』,正如其名,它主要用于孵化子进程。在 Android 系统中有以下两种程序:Java 应用程序,主要基于 ART 虚拟机,所有的应用程序 Apk 都属于这类 Native 程序,也就是利用 C 或 C++ 语言开发的程序,如 bootanimation。所有的 Java 应用程序进程及系统服务 SystemServer 进程都由 Zygote 进程通过 Linux 的 fork() 函数孵化出来的,这也就是为什么把它称为 Zygote 的原因,因为他就像一个受精卵,孵化出无数子进程,而 native 程序则由 Init 程序创建启动。Zygote 进程最初的名字不是『Zygote』而是『app_process』,这个名字是在 Android.mk 文件中定义的 Zygote 是 Android 中的第一个 ART 虚拟机,它通过 Socket 的方式与其他进程进行通信。这里的『其他进程』其实主要是系统进程——SystemServer

Zygote 触发过程

init.rc 会解析 zygote 下的 service,解析出来之后,最终是要执行里面的命令,在它准备执行里面的命令的时候,每个命令执行哪个函数,它都有一个映射表,这个映射表就存放在 builtins.cpp 的 map 函数中;

const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
    // clang-format off
    static const Map builtin_functions = {
        {"bootchart",               {1,     1,    {false,  do_bootchart}}},
        {"chmod",                   {2,     2,    {true,   do_chmod}}},
        {"chown",                   {2,     3,    {true,   do_chown}}},
        {"class_reset",             {1,     1,    {false,  do_class_reset}}},
        {"class_restart",           {1,     1,    {false,  do_class_restart}}},
        // 这个命令,执行的就是 do_class_start 函数
        {"class_start",             {1,     1,    {false,  do_class_start}}},
        {"class_stop",              {1,     1,    {false,  do_class_stop}}},
        {"copy",                    {2,     2,    {true,   do_copy}}},
        {"domainname",              {1,     1,    {true,   do_domainname}}},
        {"enable",                  {1,     1,    {false,  do_enable}}},
        {"exec",                    {1,     kMax, {false,  do_exec}}},
        {"exec_background",         {1,     kMax, {false,  do_exec_background}}},
        {"exec_start",              {1,     1,    {false,  do_exec_start}}},
        {"export",                  {2,     2,    {false,  do_export}}},
        {"hostname",                {1,     1,    {true,   do_hostname}}},
        {"ifup",                    {1,     1,    {true,   do_ifup}}},
        {"init_user0",              {0,     0,    {false,  do_init_user0}}},
        {"insmod",                  {1,     kMax, {true,   do_insmod}}},
        {"installkey",              {1,     1,    {false,  do_installkey}}},
        {"load_persist_props",      {0,     0,    {false,  do_load_persist_props}}},
        {"load_system_props",       {0,     0,    {false,  do_load_system_props}}},
        {"loglevel",                {1,     1,    {false,  do_loglevel}}},
        {"mkdir",                   {1,     4,    {true,   do_mkdir}}},
        // TODO: Do mount operations in vendor_init.
        // mount_all is currently too complex to run in vendor_init as it queues action triggers,
        // imports rc scripts, etc.  It should be simplified and run in vendor_init context.
        // mount and umount are run in the same context as mount_all for symmetry.
        {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
        {"mount",                   {3,     kMax, {false,  do_mount}}},
        {"umount",                  {1,     1,    {false,  do_umount}}},
        {"readahead",               {1,     2,    {true,   do_readahead}}},
        {"restart",                 {1,     1,    {false,  do_restart}}},
        {"restorecon",              {1,     kMax, {true,   do_restorecon}}},
        {"restorecon_recursive",    {1,     kMax, {true,   do_restorecon_recursive}}},
        {"rm",                      {1,     1,    {true,   do_rm}}},
        {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
        {"setprop",                 {2,     2,    {true,   do_setprop}}},
        {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
        {"start",                   {1,     1,    {false,  do_start}}},
        {"stop",                    {1,     1,    {false,  do_stop}}},
        {"swapon_all",              {1,     1,    {false,  do_swapon_all}}},
        {"symlink",                 {2,     2,    {true,   do_symlink}}},
        {"sysclktz",                {1,     1,    {false,  do_sysclktz}}},
        {"trigger",                 {1,     1,    {false,  do_trigger}}},
        {"verity_load_state",       {0,     0,    {false,  do_verity_load_state}}},
        {"verity_update_state",     {0,     0,    {false,  do_verity_update_state}}},
        {"wait",                    {1,     2,    {true,   do_wait}}},
        {"wait_for_prop",           {2,     2,    {false,  do_wait_for_prop}}},
        {"write",                   {2,     2,    {true,   do_write}}},
    };
    // clang-format on
    return builtin_functions;
}

// 这个命令,执行的就是 do_class_start 函数

{"class_start", {1, 1, {false, do_class_start}}}

这个函数,我们来看下干了什么

static Result<Success> do_class_start(const BuiltinArguments& args) {
    // Starting a class does not start services which are explicitly disabled.
    // They must  be started individually.
    for (const auto& service : ServiceList::GetInstance()) {
        if (service->classnames().count(args[1])) {
            if (auto result = service->StartIfNotDisabled(); !result) {
                LOG(ERROR) << "Could not start service '" << service->name()
                           << "' as part of class '" << args[1] << "': " << result.error();
            }
        }
    }
    return Success();
}

通过一个 for 循环,遍历前面说到的 ServiceList,这个 vector 类型的 list,就是存放的前面创建的各种服务;

取出所有的服务,调用 StartIfNotDisabled 函数进行启动,如果这个服务没有被禁用;

Result<Success> Service::StartIfNotDisabled() {
    if (!(flags_ & SVC_DISABLED)) {
        return Start();
    } else {
        flags_ |= SVC_DISABLED_START;
    }
    return Success();
}

这个函数会调用 Start 函数进行启动,这个 Start 函数中就调用了 fork 函数,创建了一个子进程;

Result<Success> Service::Start() {
    // 省略部分代码
    
    pid_t pid = -1;
    if (namespace_flags_) {
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {
        pid = fork(); // 创建子进程
    }
    // if 中的逻辑都是在子进程中执行的;
    if (pid == 0) {
        // 省略部分代码
        
        // 启动 app_main.cpp 中 main 函数
        if (!ExpandArgsAndExecv(args_)) {
            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
        }
    }
}

ExpandArgsAndExecv(args_) 这个函数会通过 execv 函数启动 app_main.cpp 中 main 函数;

static bool ExpandArgsAndExecv(const std::vector<std::string>& args) {
    std::vector<std::string> expanded_args;
    std::vector<char*> c_strings;

    expanded_args.resize(args.size());
    c_strings.push_back(const_cast<char*>(args[0].data()));
    for (std::size_t i = 1; i < args.size(); ++i) {
        if (!expand_props(args[i], &expanded_args[i])) {
            LOG(FATAL) << args[0] << ": cannot expand '" << args[i] << "'";
        }
        c_strings.push_back(expanded_args[i].data());
    }
    c_strings.push_back(nullptr);

    return execv(c_strings[0], c_strings.data()) == 0;
}

我们来看下 main 中做了什么;

int main(int argc, char* const argv[])
{
    if (!LOG_NDEBUG) {
      String8 argv_String;
      for (int i = 0; i < argc; ++i) {
        argv_String.append(""");
        argv_String.append(argv[i]);
        argv_String.append("" ");
      }
      ALOGV("app_process main with argv: %s", argv_String.string());
    }
    // 启动 Zygote
    AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
    
    // 省略部分代码
    ...
    //
}

整体流程大致如下:

如何应对Android面试官 -> Android 系统启动流程浅析

Zygote 启动过程

在app_main.cpp的main函数中,主要做的事情就是参数解析. 这个函数有两种启动模式:

  1. 一种是 zygote 模式,也就是初始化 zygote 进程,传递的参数有--start-system-server --socketname=zygote,前者表示启动 SystemServer,后者指定 socket 的名称;
  2. 一种是 application 模式,也就是启动普通应用程序,传递的参数有 class 名字以及 class 带的参数;

两者最终都是调用 AppRuntime 对象的 start 函数,加载 ZygoteInit 或 RuntimeInit 两个 Java 类,并将之前整理的参数传入进去

int main(int argc, char* const argv[]) {
    
    ++i;  // Skip unused "parent dir" argument.
    // 启动不同的进程;
    while (i < argc) {
        const char* arg = argv[i++];
        
        if (strcmp(arg, "--zygote") == 0) {
            // zygote 类型的
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            // application 类型的
            application = true;
        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
            niceName.setTo(arg + 12);
        } else if (strncmp(arg, "--", 2) != 0) {
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }
}

经过 while 循环的判断之后,如果是 zygote 进程,就会直接调用 start 函数,传递 com.android.internal.os.ZygoteInit 进行 zygote 的启动;

if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }
}

我们接着来看下这个 start 函数中都做了什么

void AndroidRuntime::start(const char* className, const Vector<String8>&options, bool zygote)
{
  ...
    JNIEnv* env;

    // JNI_CreateJavaVM L1015
    // 启动虚拟机
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    
    onVmCreated(env);
    // 进行注册
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
}

start 函数中主要是 startVm 函数启动了我们的虚拟机;startReg 函数映射表的注册;

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
    // 省略部分代码
    ...
    // 
    
    /*
     * Initialize the VM.
     *
     * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
     * If this call succeeds, the VM is ready, and we can start issuing
     * JNI calls.
     */
     // 创建虚拟机,然后启动它
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        return -1;
    }  
}

我们来看下是如何创建并启动的;

bool Runtime::Create(RuntimeArgumentMap&& runtime_options) {
  if (Runtime::instance_ != nullptr) {
    return false;
  }
  // 创建 Runtime 并调用 Init 进行初始化
  instance_ = new Runtime;
  Locks::SetClientCallback(IsSafeToCallAbort);
  // 初始化
  if (!instance_->Init(std::move(runtime_options))) {
    instance_ = nullptr;
    return false;
  }
  return true;
}

// 创建 Runtime 并调用 Init 进行初始化

Init中主要做了如下几件事情;

  1. new gc::Heap // 创建堆管理对象
  2. JavaVMExt::Create //创建java虚拟机对象
  3. Thread::Attach //连接主线程
  4. new ClassLinker //创建类连接器
  5. class_linker_->InitFromBootImage //初始化类连接器

初始化成功之后,就会调用到 Java 层的 ZygoteInit.java 的 main 函数;

public static void main(String argv[]) {
        ZygoteServer zygoteServer = new ZygoteServer();

        // Mark zygote start. This ensures that thread creation will throw
        // an error.
        ZygoteHooks.startZygoteNoThreadCreation();

        // Zygote goes into its own process group.
        try {
            Os.setpgid(0, 0);
        } catch (ErrnoException ex) {
            throw new RuntimeException("Failed to setpgid(0,0)", ex);
        }

        final Runnable caller;
        try {
            // Report Zygote start time to tron unless it is a runtime restart
            if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
                MetricsLogger.histogram(null, "boot_zygote_init",
                        (int) SystemClock.elapsedRealtime());
            }

            String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
            TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
                    Trace.TRACE_TAG_DALVIK);
            bootTimingsTraceLog.traceBegin("ZygoteInit");
            RuntimeInit.enableDdms();

            boolean startSystemServer = false;
            String socketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = true;
                } else if ("--enable-lazy-preload".equals(argv[i])) {
                    enableLazyPreload = true;
                } else if (argv[i].startsWith(ABI_LIST_ARG)) {
                    abiList = argv[i].substring(ABI_LIST_ARG.length());
                } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
                    socketName = argv[i].substring(SOCKET_NAME_ARG.length());
                } else {
                    throw new RuntimeException("Unknown command line argument: " + argv[i]);
                }
            }

            if (abiList == null) {
                throw new RuntimeException("No ABI list supplied.");
            }

            zygoteServer.registerServerSocketFromEnv(socketName);
            // In some configurations, we avoid preloading resources and classes eagerly.
            // In such cases, we will preload things prior to our first fork.
            if (!enableLazyPreload) {
                bootTimingsTraceLog.traceBegin("ZygotePreload");
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
                    SystemClock.uptimeMillis());
                preload(bootTimingsTraceLog);
                EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
                    SystemClock.uptimeMillis());
                bootTimingsTraceLog.traceEnd(); // ZygotePreload
            } else {
                Zygote.resetNicePriority();
            }

            // Do an initial gc to clean up after startup
            bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
            gcAndFinalize();
            bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC

            bootTimingsTraceLog.traceEnd(); // ZygoteInit
            // Disable tracing so that forked processes do not inherit stale tracing tags from
            // Zygote.
            Trace.setTracingEnabled(false, 0);

            Zygote.nativeSecurityInit();

            // Zygote process unmounts root storage spaces.
            Zygote.nativeUnmountStorageOnInit();

            ZygoteHooks.stopZygoteNoThreadCreation();

            if (startSystemServer) {
                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);

                // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
                // child (system_server) process.
                if (r != null) {
                    r.run();
                    return;
                }
            }

            Log.i(TAG, "Accepting command socket connections");

            // The select loop returns early in the child process after a fork and
            // loops forever in the zygote.
            caller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            zygoteServer.closeServerSocket();
        }

        // We're in the child process and have exited the select loop. Proceed to execute the
        // command.
        if (caller != null) {
            caller.run();
        }
    }

Java 层的 main 函数主要做了以下事情;

  1. new ZygoteServer(); //新建Zygote服务器端
  2. registerServerSocketFromEnv(socketName);//注册Socket
  3. preload 这里做了大量的耗时操作,这也是系统开机耗时的主要原因;
  4. gcAndFinalize //主动进行一次资源GC
  5. forkSystemServer
  6. zygoteServer.runSelectLoop
  7. caller.run()

fork 函数


我们接下来讲解下 fork 函数;

在 Linux 中是没有线程和进程的概念的,它们都是使用的 thread_task 这样的一个结构体,区别在于线程是资源共享的;

当我们由一个进程创建另一个进程的时候,就是使用的 fork 函数;这里要注意的是 fork 函数会返回两次,一次是 pid = 0,表示子进程创建成功,接下来进入子进程执行流程,一次是 pid > 0 成功创建子进程,并且继续执行父进程流程代码;返回非正数(<0),创建子进程失败,失败原因主要有:进程数超过系统所能创建的上限,errno会被设置为EAGAIN系统内存不足,errno会被设置为ENOMEM;

如何应对Android面试官 -> Android 系统启动流程浅析

使用 fork() 函数得到的子进程是父进程的一个复制品,如果什么都不做,它们两个是一模一样的,它从父进程处继承了整个进程的地址空间:包括进程上下文(进程执行活动全过程的静态描述)、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。子进程所独有的只有它的进程号,计时器等(只有小量信息)。因此,使用 fork() 函数的代价是很大的;

子进程与父进程的区别

  1. 除了文件锁以外,其他的锁都会被继承;
  2. 各自的进程ID和父进程ID不同;
  3. 子进程的未决告警被清除;
  4. 子进程的未决信号集设置为空集;

写时拷贝 (copy-on-write)

Linux 的 fork() 使用是通过写时拷贝 (copy- on-write) 实现。写时拷贝是一种可以推迟甚至避免拷贝数据的技术。内核此时并不复制整个进程的地址空间,而是让父子进程共享同一个地址空间。只有在需要写入的时候才会复制地址空间,从而使各个进行拥有各自的地址空间。也就是说,资源的复制是在需要写入的时候才会进行,在此之前,只有以只读方式共享;

多线程进程的 fork 调用

在 POSIX 标准中,fork 的行为是这样的:复制整个用户空间的数据(通常使用 copy-on-write 的策略,所以可以实现的速度很快)以及所有系统对象,然后仅复制当前线程到子进程。这里:所有父进程中别的线程,到了子进程中都是突然蒸发掉的;

假设这么一个环境,在 fork 之前,有一个子线程 lock 了某个锁,获得了对锁的所有权。fork 以后,在子进程中,所有的额外线程都人间蒸发了。而锁却被正常复制了,在子进程看来,这个锁没有主人,所以没有任何人可以对它解锁。当子进程想 lock 这个锁时,不再有任何手段可以解开了。程序发生死锁

常见问题


Q:为什么要专门使用 Zygote 进程去孵化应用进程,而不是让 system_server 去孵化呢?

A:为什么要专门使用 Zygote 进程去孵化应用进程,而不是让 system_server 去孵化呢?首先 system_server 相比 Zygote 多运行了 AMS、WMS 等服务,这些对一个应用程序来说是不需要的。另外进程的 fork() 对多线程不友好,仅会将发起调用的线程拷贝到子进程,这可能会导致死锁,而 system_server 中肯定是有很多线程的。

Q:System_server 为什么要在 Zygote 中启动,而不是由 init 直接启动?

A:Zygote 作为一个孵化器,可以提前加载一些资源,这样 fork() 时基于 Copy-On-Write 机制创建的其 他进程就能直接使用这些资源,而不用重新加载。比如 system_server 就可以直接使用 Zygote 中的 JNI 函数、共享库、常用的类、以及主题资源;

Q:Zygote 为什么不采用 Binder 机制进行 IPC 通信?

A:Binder 机制中存在 Binder 线程池,是多线程的,如果 Zygote 采用 Binder 的话就存在上面说的 fork() 与 多线程的问题了。其实严格来说,Binder 机制不一定要多线程,所谓的 Binder 线程只不过是 在循环读取 Binder 驱动的消息而已,只注册一个 Binder 线程也是可以工作的,比如 service manager 就是这样的。实际上 Zygote 尽管没有采取 Binder 机制,它也不是单线程的,但它在 fork() 前主动停止 了其他线程,fork() 后重新启动了;

下一章预告


Dex格式分析

欢迎三连


来都来了,点个关注,点个赞吧,你的支持是我最大的动力~~~