Android 系统源码-1:Android 系统启动流程源码分析

本文详细分析了Android系统的启动流程,从Linux内核加载后执行init进程,到启动Zygote服务,再到SystemServer和Launcher的启动。通过解析init.rc文件,探讨了如何通过service指令创建进程,特别关注了Zygote的启动,包括预加载资源、启动SystemServer以及最终启动Launcher的过程。
摘要由CSDN通过智能技术生成

现在我们来梳理下 Android 系统的启动过程。Android 启动过程还是比较重要的,因为在这个过程中除了要完成 Linux 系统的初始化工作还要完成 Android 的基础服务和启动界面的初始化工作。

在这篇文章中,我们不打算过多深入源码。因为 Android 中任何一个功能模块在 Framework 层都涉及大量的代码调用。过多深入源码只会让我们迷失在一层层的调用栈中。相比之下,我更倾向于只出一些核心代码,另外梳理下调用栈的流程。当我们需要深入研究这方面的内容的时候,知道去哪里找答案就够了。

1、系统启动

按下电源之后,首先加载引导程序 BootLoader 到 RAM;然后,执行引导程序 BootLoader 以把系统 OS 拉起来;接着,启动 Linux 内核;内核中启动的第一个用户进程是 init 进程,init 进程会通过解析 init.rc 来启动 zygote 服务;Zygote 又会进一步的启动 SystemServer;在 SystemServer 中,Android 会启动一系列的系统服务供用户调用。

Android 系统中 init 程序对应的 Android.mk 位于 system/core/init/Android.mk,是一种 Makefile 文件,用来向编译系统描述我们的源代码。我们可以使用 make 工具来执行该文件。所以,mk 文件就像是 Shell 脚本一样。

1.1 执行 init 程序

Linux 内核加载完成后,首先启动 init 进程。在 8.0 的源码中系统启动的第一个阶段是创建启动所需的各种目录。而在最新的源码中,这部分代码被包含在了 init_first_stage 中:

    // platform/system/core/init/init_first_stage.cpp
    int main(int argc, char** argv) {
        if (REBOOT_BOOTLOADER_ON_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);
        umask(0);
        CHECKCALL(clearenv());
        CHECKCALL(setenv("PATH", _PATH_DEFPATH, 1));
        // 创建目录
        CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
        CHECKCALL(mkdir("/dev/pts", 0755));
        CHECKCALL(mkdir("/dev/socket", 0755));
        // ...
    #undef CHECKCALL
        auto reboot_bootloader = [](const char*) { RebootSystem(ANDROID_RB_RESTART2, "bootloader"); };
        InitKernelLogging(argv, reboot_bootloader);
        // ...
        const char* path = "/system/bin/init";
        const char* args[] = {path, nullptr};
        execv(path, const_cast<char**>(args));
        return 1;
    }

在系统启动过程中会多次调用 execv(),每次调用该方法时会重新执行 main() 方法。该方法如下:

int execv(const char *progname, char *const argv[]);   //#include <unistd.h>

execv() 会停止执行当前的进程,并且以 progname 应用进程替换被停止执行的进程,进程 ID 不会改变。

然后是 init.cpp 进程的入口函数 main:

    // platform/system/core/init/init.cpp
    int main(int argc, char** argv) {
        if (!strcmp(basename(argv[0]), "ueventd")) {
            return ueventd_main(argc, argv);
        }
        if (argc > 1 && !strcmp(argv[1], "subcontext")) {
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap function_map;
            return SubcontextMain(argc, argv, &function_map);
        }
        if (REBOOT_BOOTLOADER_ON_PANIC) {
            // 初始化重启系统的处理信号
            InstallRebootSignalHandlers();
        }
        // ...
        property_init(); // 初始化属性服务
        // ...
        Epoll epoll; // 创建 epoll 句柄
        if (auto result = epoll.Open(); !result) {
            PLOG(FATAL) << result.error();
        }

        InstallSignalFdHandler(&epoll);
        // ...
        StartPropertyService(&epoll); // 启动属性服务
        // ...

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

        LoadBootScripts(am, sm); // 加载启动脚本
        // ...
        // 充电模式不启动系统,否则启动系统
        std::string bootmode = GetProperty("ro.bootmode", "");
        if (bootmode == "charger") {
            am.QueueEventTrigger("charger");
        } else {
            am.QueueEventTrigger("late-init");
        }
        // ...
        return 0;
    }

这里会在 LoadBootScripts() 方法中解析 init.rc 文件。关于该文件指令的含义可以参考 AOSP 中的文档:《Android Init Language》. 完成解析相关的类是 ActionManagerParserXXParser,均位于 system/core/init 目录下面。除此之外,还有 ActionService 等类。它们的作用是,各种 Parser 用来解析 rc 文件中的指令。解析出的指令会被封装成 ActionService 等对象。

打开该文件我们可以看到其中包含了下面两行代码,这里使用了占位符,也就是说,它会根据当前的环境变量加载当前目录下对应的文件。并且,我们可以看到在 system/core/rootdir 目录下面确实存在着 init.zygote64.rcinit.zygote32.rc 等文件。

import /init.${
   ro.hardware}.rc
import /init.${
   ro.zygote}.rc

rinit.zygote64.rc 为例,它表示通知 init 进程创建名为 zygote 的进程。执行路径是 /system/bin/app_process64

// platform/system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    // ...

我们可以看出它使用了 service 指令,所以它将被解析成 Service.

注意到在 init.cpp 的 main() 方法的最后,如果非充电模式将触发 late-init. 在 rc 中配置了对 late-init 事件的监听,通过 on 来实现的。同时,它又使用 trigger 触发了其他的命令。这些命令也都是通过 on 来监听的。(当然,rc 只是一种配置文件,而实际的逻辑是被解析之后在程序中完成的。)

late-init 事件触发的事件当中包含了 zygote-start 事件. 而 zygote-start 监听实现又根据监听条件又多种。不过,它们都会调用 start zygote 方法。这里的 start 会被映射到 builtins 类的 do_start() 方法。该方法会调用 Service 的 start() 方法。该方法主要是调用 clone 或 fork 创建子进程,然后调用 execve 执行配置的二进制文件,另外根据之前在 rc 文件中的配置,去执行这些配置。因此程序将开始执行 app_process64.

// platform/system/core/init/service.cpp
Result<Success> Service::Start() {
    // ...
    pid_t pid = -1;
    if (namespace_flags_) {
        pid = clone(nullptr, nullptr, namespace_flags_ | SIGCHLD, nullptr);
    } else {
        pid = fork();
    }

    if (pid == 0) {
        umask(077);
        // ...
        // 内部调用 execv() 来执行
        if (!ExpandArgsAndExecv(args_, sigstop_)) {
            PLOG(ERROR) << "cannot execve('" << args_[0] << "')";
        }
        _exit(127);
    }
    // ...
    return Success();
}

映射关系参考源码:system/core/init/builtins.cpp
关于 rc 文件的命令的解析,可以参考《Android 8.0 系统启动流程之init.rc解析与service流程(七)》

上述 rc 文件的 /system/bin/app_process64 对应的 mk 文件位于 /base/cmds/app_process/Android.mk 目录下面。从该文件中我们可以看出,不论 app_process、app_process32 还是 app_process64,对应的源文件都是 app_main.cpp. 于是程序将进入 app_main.cpp 的 main() 方法。

进入 main() 方法之后先要进行指令的参数的解析,

// platform/frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[])
{
    // ...
    bool zygote = false;
    bool startSystemServer = false;
    bool application = false;
    String8 niceName;
    String8 className;

    ++i;  // Skip unused "parent dir" argument.
    while (i < argc) {
        const char* arg = argv[i++];
        if (strcmp(arg, "--zygote") == 0) {
            zygote = true;
            niceName = ZYGOTE_NICE_NAME;
        } else if (strcmp(arg, "--start-system-server") == 0) {
            startSystemServer = true;
        } else if (strcmp(arg, "--application") == 0) {
            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;
        }
    }
    // ...
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        app_usage();
    }
}

我们从之前的 rc 文件中可以看出,参数为 --zygote,因此将调用 ZygoteInit 的 main() 方法继续执行。这里的 runtime 是 AndroidRuntime,这里的 start() 方法是一种 JNI 调用。这里将会调用 Java 中的静态 main() 方法继续执行。 这种调用方式还是比较重要的,我们经常在 Java 中调用 C++ 的方法,而这里是在 C++ 中调用 Java 的方法。它的源码位于 base\core\jni\AndroidRuntime.cpp.

// platform/frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    // ...

    // 获取ANDROID_ROOT环境变量
    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);
    }

    // 启动虚拟机
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    // ... 解析 main 函数以在下面进行触发

    // 启动线程,当前线程将会变成虚拟机的主线程,并且直到虚拟机退出的时候才结束。
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
    } else {
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        }
    }
    // ...
}

在上面的方法中,我们可以看出启动虚拟机的时候需要调用 startVM() 方法来启动。当虚拟机启动完成之后使用句柄函数 env 来执行 ZygoteInit 的静态 main() 方法。

1.2 启动 Zygote

根据上面的分析,系统已经启动了虚拟机。并且在虚拟机启动完成之后,程序进入了 ZygoteInitmain() 方法中,

    // platform/framework/base/core/java/com/android/internal/os/ZygoteInit.java
    public static void main(String argv[]) {
   
        // ...
        try {
   
            // ...
            boolean startSystemServer = false;
            String socketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
            for (int i = 1; i < argv.length; i++) {
   
                if (
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值