Android系统启动的核心流程

前言

Android 源码基于 8.0。

init进程是 Linux 内核启动完成后在用户空间启动的第一个进程,主要负责初始化工作、启动属性服务、解析 init.rc 文件并启动 Zygote 进程。

Zygote 进程是一个进程孵化器,负责创建虚拟机实例、应用程序进程、系统服务进程 SystemServer。他通过 fork(复制进程)的方式创建子进程,子进程能继承父进程的系统资源如常用类、注册的 JNI 函数、主题资源、共享库等。

由于 Zygote 进程启动时会创建虚拟机实例,由 Zygote fork 出的应用程序进程和 SystemServer 进程则可以在内部获取到一个虚拟机实例副本。

Zygote的启动

init 进程会解析配置文件 init.rc,来启动一些需要在开机时就启动的系统进程,如 Zygote 进程、ServiceManager 进程等。

init.rc 是由 Android 初始化语言编写的脚本配置。由于 Android 5.0 开始支持了 64bit 程序,在 init.rc 里改成了通过 ${ro.zygote} 的值来引入 Zygote 相关的配置,

//system/core/rootdir/init.rc
import /init.${ro.zygote}.rc

${ro.zygote} 的取值有 4 种,在 init.rc 的同级目录 /system/core/rootdir 下,可以看到 4 个 Zygote 相关的配置文件,表示系统所支持程序的 bit 位数:

  1. init.zygote32.rc,Zygote 进程的执行程序路径为 /system/bin/app_process
  2. init.zygote64.rc,Zygote 进程的执行程序路径为 /system/bin/app_process64
  3. init.zygote32_64.rc,会启动两个 Zygote 进程,有两个执行程序,32 为主模式
  4. init.zygote64_32.rc,会启动两个 Zygote 进程,有两个执行程序,64 为主模式

我们看到 init.zygote32.rc 文件:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    ...

第一行中,service 表示 Zygote 进程以服务的形式来启动,zygote 则是进程的名字,/system/bin/app_process 是执行程序的路径,后面几项则是传给执行程序的参数,其中 --start-system-server 表示在 Zygote 进程启动后需要启动 SystemServer 进程。

然后是最后一行,Zygote 进程是使用 socket 来进行跨进程通信的,所以会创建一个名为 zygote 的 socket,660 表示访问权限 rw-rw----,表示文件拥有者和同一群组用户具有读写权限。

init 进程启动后,通过 fork 和 execve 来启动 Zygote 进程。

//system/core/init/service.cpp
bool Service::Start() {
    //fork出子进程
    pid = fork();
    if (pid == 0) {//子进程会返回0,父进程会返回子进程的pid
        //strs[0]是执行程序的路径,即execve会运行app_process
        if (execve(strs[0], (char**) &strs[0], (char**) ENV) < 0) {
        }
    }
}

运行执行程序 app_process 的入口函数 main。

//frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[]){
    if (zygote) {
        //启动 Zygote,进入 ZygoteInit.main 函数
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    }
}

至此 Zygote 就正式启动了。

Zygote 启动

综上,init 进程读取配置文件 init.rc 后,fork 出 Zygote 进程,通过 execve 函数执行 Zygote 的执行程序 app_process,进入 ZygoteInit 类的 main 函数。

下面详细分析 app_main 和 ZygoteInit。

native 层 app_main

前边可知 app_main.cpp 的 main 函数会调用 runtime.start()。

//frameworks/base/core/jni/AndroidRuntime.cpp
void AndroidRuntime::start(...){
    //1. 启动 java 虚拟机
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    //2. 为 java 虚拟机注册 JNI 方法
    if (startReg(env) < 0) {
        return;
    }
    //根据传入的参数找到 ZygoteInit 类和他的 main 函数
    //3. 通过 JNI 调用 ZygoteInit 的 main 函数
    env->CallStaticVoidMethod(startClass, startMeth, strArray);
}

Java 层 ZygoteInit

来到 ZygoteInit 的 main 函数。

//ZygoteInit.java
public static void main(String argv[]) {
    //是否要创建 SystemServer
    boolean startSystemServer = false;
    //默认的 socket 名字
    String socketName = "zygote";
    //是否要延迟资源的预加载
    boolean enableLazyPreload = false;

    for (int i = 1; i < argv.length; i++) {
        if ("start-system-server".equals(argv[i])) {
            //在 init.rc 文件中,有 --start-system-server 参数,表示要创建 SystemServer
            startSystemServer = true;
        } else if ("--enable-lazy-preload".equals(argv[i])) {
            //init.rc 没有这个参数,资源的预加载不会被延迟
            enableLazyPreload = true;
        } else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
            //init.rc 可以通过 --socket-name= 指定 socket 名字来覆盖默认值
            socketName = argv[i].substring(SOCKET_NAME_ARG.length());
        }
    }

    //1. 创建服务端 socket,名字为 socketName 即 zygote
    zygoteServer.registerServerSocket(socketName);

    if (!enableLazyPreload) {
        //2. 没有被延迟,就预加载资源
        preload(bootTimingsTraceLog);
    }

    if (startSystemServer) {
        //3. fork 并启动 SystemServer 进程
        startSystemServer(abiList, socketName, zygoteServer);
    }

    //4. 等待 AMS 请求(AMS 会通过 socket 请求 Zygote 来创建应用程序进程)
    zygoteServer.runSelectLoop(abiList);
}

总结一下 native 层的 3 个环节和 Java 层的 4 个环节:


image.png

SystemServer 启动

SystemServer 进程主要负责创建启动系统服务如 AMS、WMS 和 PMS 等。
从前边可知 SystemServer 进程由 Zygote 进程 fork 出来并启动,在 ZygoteInit 类中。

//ZygoteInit.java
private static boolean startSystemServer(...){
    String args[] = {
        //...
        //启动的类名:
        "com.android.server.SystemServer",
    };
    //fork 进程,由 native 层实现
    pid = Zygote.forkSystemServer();
    //处理 SystemServer 进程
    handleSystemServerProcess(parsedArgs);
}

private static void handleSystemServerProcess(...){
    ZygoteInit.zygoteInit(...);
}

public static final void zygoteInit(...){
    //启动 binder 线程池
    ZygoteInit.nativeZygoteInit();
    //内部经过层层调用,找到 "com.android.server.SystemServer" 类和他的 main 函数,然后执行
    RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}

这里启动了 binder 线程池,SystemServer 进程就可以用 binder 机制来跨进程通信了(Zygote 进程是用 socket 来通信的),接着进入了 SystemServer 的 main 函数。

//SystemServer.java
public static void main(String[] args) {
    new SystemServer().run();
}

private void run() {
    //创建 looper
    Looper.prepareMainLooper();
    //加载动态库 libandroid_servers.so
    System.loadLibrary("android_servers");
    //创建系统上下文
    createSystemContext();

    //创建 SSM,用于服务的创建、启动和生命周期管理
    mSystemServiceManager = new SystemServiceManager(mSystemContext);

    //服务根据优先级被分成 3 批来启动:
    //启动引导服务,如 AMS、PMS 等
    startBootstrapServices();
    //启动核心服务
    startCoreServices();
    //启动其他服务
    startOtherServices();

    //开启 looper 循环
    Looper.loop();
}

看下 AMS 的启动。

//SystemServer.java
private void startBootstrapServices() {
    //由SSM创建启动
    mActivityManagerService = mSystemServiceManager.startService(
        ActivityManagerService.Lifecycle.class).getService();
    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
}

private void startOtherServices() {
    //AMS 准备就绪
    mActivityManagerService.systemReady(...);
}

总结一下,SystemServer 进程被创建后,主要做了 3 件事情:启动 binder 线程池、创建 SystemServiceManager(SSM)、用 SSM 启动各种服务。

image.png

Launcher 的启动

Launcher作为Android的桌面,用于管理应用图标和桌面组件。

前边可知SystemServer进程会启动各种服务,其中PackageManagerService启动后会将系统中的应用程序安装完成,然后由AMS来启动Launcher。

//SystemServer.java
private void startOtherServices() {
    //AMS 准备就绪
    mActivityManagerService.systemReady(...);
}

跟进 ActivityManagerService。

//ActivityManagerService.java
public void systemReady(...) {
    //经过层层调用来到 startHomeActivityLocked
}

boolean startHomeActivityLocked(...) {
    //最终会启动 Launcher 应用的 Activity
    mActivityStarter.startHomeActivityLocked(...);
}

Activity 类是 Launcher.java,剩下的流程就是加载已安装的应用程序信息,然后展示,就不具体分析了。

Launcher 的启动

总结

Android 系统启动的核心流程如下:

  1. Linux 内核启动
  2. init 进程启动
  3. init 进程 fork 出 Zygote 进程
  4. Zygote 进程 fork 出 SystemServer 进程
  5. SystemServer 进程启动各项服务(PMS、AMS 等)
  6. AMS 服务启动 Launcher 桌面
Android 系统启动

Zygote 进程启动好服务端 socket 后,便会等待 AMS 的 socket 请求,来创建应用程序进程。

细节补充

  • Zygote 的跨进程通信没有使用 binder,而是 socket,所以应用程序进程的 binder 机制不是继承而来,而是进程创建后自己启动的。
  • Zygote 跨进程通信之所以用 socket 而不是 binder,是因为 binder 通信是多线程的,而 Zygote 需要在单线程状态下 fork 子进程来避免死锁问题。
  • PMS、AMS 等系统服务启动后会调用 ServiceManager.addService() 注册,然后运行在自己的工作线程。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容