第二章 Android 系统启动.md

何言 2021年10月22日 53次浏览

2.1 init 进程启动过程

init 进程是 Android 系统中用户空间的第一个进程,进程号是 1 。该进程由多个源文件构成,位于 system/core/init 中:

2.1.1 引入 init 进程

Android 启动流程:

  1. 启动电源,系统启动

    加载 BootLoader 到 RAM 中,并开始执行

  2. 引导程序 BootLoader

    把系统 OS 拉起并运行

  3. Linux 内核启动

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

  4. init 进程启动

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

2.1.2 init 进程入口函数

init 进程入口文件位于 system/core/init/init.cpp

其中做了许多事情,但我们只需要关注几点:

  1. 创建和挂在启动所需的文件目录,其中挂载了 tmpfs, devpts, proc, sysfs 和 selinuxfs 五种文件系统,在系统停止时这些目录的会消失
  2. 调用 property_init 函数对属性进行初始化,调用 start_property_service 函数启动属性服务
  3. 调用 signal_handler_init 函数用于设置子进程信号处理函数,函数位于 system/core/init/singnal_handler.cpp 中,主要防止 init 进程的子进程成为僵尸进程。系统会在子进程暂停和终时发出 SIGCHLD 信号,而 signal_handler_init 函数接收该信号。
  4. 解析 init.rc 文件,解析的代码位于 system/core/init/init_parse.cpp 中。

僵尸进程的危害

在 UNIX/Linux 中,父进程使用 fork 创建子进程,在子进程终止后,如果父进程不知道子进程已经终止,则此时子进程的信息依然保留在系统进程表中。如果系统进程表被耗尽,则系统无法创建新的进程。(类似内存泄漏)

2.1.3 解析 init.rc

init.rc 由 Android 初始化语言 (Android Init Language) 编写的脚本,主要包含五种语句:Action, Command, Service, Option 和 Import 。

# system/core/rootdir/init.rc
on init
    sysclktz 0

    # Mix device-specific information into the entropy pool
    copy /proc/cmdline /dev/urandom
    copy /system/etc/prop.default /dev/urandom
    
    ……
    
on boot
    # basic network init
    ifup lo
    hostname localhost
    domainname localdomain
    
    ……

以上只截取一部分,on init 和 on boot 是 Action 类型,格式如下:

on <trigger> [&& <trigger>]*  # 设置触发器
	<command>
	<command>	# 动作触发后执行的命令

为了分析如何创建 Zygote,我们主要查看 Service 类型语句,格式如下:

service <name> <pathname> [<argument>]* # <service 名字> <执行程序路径> <传递参数>
	<option>
	<option> # service 修饰,影响何时,如何启动 service

Android 8.0 中对 init.rc 文件进行了拆分,每个服务对应要给 rc 文件,我们要分析的 Zygote 启动脚本在 init.zygoteXX.rc 中定义,这里以 init.zygote64.rc 文件为例:

# system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

这里通知 init 进程创建名为 zygote 的服务(实际上是子进程),执行程序的路径为 /system/bin/app_process64,之后为参数, class main 指的是 Zygtote 的 classname 为 main 。后面会深入分析。

2.1.4 解析 Service 类型语句

init.rc 中 Action 类型语句由 ActionParser 进行解析,Service 类型语句采用 ServiceParser 进行解析

2.1.5 init 启动 Zygote

这里主要讲解 Zygote 这个 Service 的启动流程,在 Zygote 的启动脚本中,我们看见 Zygote 的 classname 为 main 。在 init.rc 中有如下代码:

# system/core/rootdir/init.rc
on nonencrypted
    class_start main # 1
    class_start late_start

class_start 是一个 command,对应函数为 do_class_start,最终会解析该文件创建进程并执行对应的函数 。

2.1.6 属性服务

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

init 进程启动时会启动属性服务,并为其分配内存,用来存储这些信息,如果需要直接读取即可。之前我们提到 init.cpp 的 main 函数中与属性服务相关的代码有如下两行:

// system/core/init/init.cpp#main()
property_init(); // 初始化
start_property_service(); // 启动

这里属性服务实际上会启动一个 SocketServer,其他进程通过 Socket 与属性服务进行通讯 。同时这里使用了 epolll 。

Linux 新内核中,epoll 用来替换 select,epoll 是 Linux 内核为处理大批量文件描述符而做了改进的 poll,是 Linux 下多路复用 I/O 接口 select/poll 的增强版本。 epoll 内部使用红黑树来保存事件,select 使用数组。

2.1.7 init 进程启动总结

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

2.2 Zygote 进程启动过程

2.2.1 Zygote 概述

在 init.rc 文件中采用了 Import 类型语句来引入 Zygote 启动脚本,这些脚本都是 Android 初始化语音

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

可以看到这里文件名含有变量,其中 ro.zygote 的取值有以下四种:

  • init.zygote32.rc
  • init.zygote32_64.rc
  • init.zygote64.rc
  • init.zygote64_32.rc

这些启动脚本都位于 system/core/rootdir 中 。

先来回顾一下 Android 初始化语言中 service 类型的语句:

service <name> <pathname> [ <argument> ]* # <service 名字> <执行程序路径> <参数>
<option>
<option> # 是 service 的修饰词,决定什么时候,如何启动 Service

下面分别介绍以下 Zygote 启动脚本:

  1. init.zygote32.rc

    表示支持纯 32 位程序,文件内容如下:

    service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
        class main
        priority -20
        user root
        group root readproc
        socket zygote stream 660 root system
        onrestart write /sys/android_power/request_state wake
        onrestart write /sys/power/state on
        onrestart restart audioserver
        onrestart restart cameraserver
        onrestart restart media
        onrestart restart netd
        onrestart restart wificond
        writepid /dev/cpuset/foreground/tasks
    

    进程名字为 zygote,执行程序为 app_process,classname 为 main。如果 audioserver 等进程终止,就需要 restart

  2. init.zygote32_64.rc

    表示既支持 32 位程序也支持 64 位程序,内容如下:

    service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
        class main
        priority -20
        user root
        group root readproc
        socket zygote stream 660 root system
        onrestart write /sys/android_power/request_state wake
        onrestart write /sys/power/state on
        onrestart restart audioserver
        onrestart restart cameraserver
        onrestart restart media
        onrestart restart netd
        onrestart restart wificond
        writepid /dev/cpuset/foreground/tasks
    
    service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
        class main
        priority -20
        user root
        group root readproc
        socket zygote_secondary stream 660 root system
        onrestart restart zygote
        writepid /dev/cpuset/foreground/tasks
    

    会启动两个 service,其中一个为 zygote,执行程序为 app_process32 ,为主模式,另一个为 zygote_secondary,执行程序为 app_process64,为辅模式。

2.2.3 Zygote 进程启动过程介绍

init 启动 Zygote 时主要是调用 app_main.cpp 的 main 函数中的 AppRuntime 的 start 方法来启动,其时序图如下:

image20210918170233324.png

  1. 创建 AppRuntime 并调用其 start 方法,启动 Zygote 进程
  2. 创建 Java 虚拟机并为 Java 虚拟机注册 JNI 方法
  3. 通过 JNI 调用 ZygoteInit 的 main 方法进入 Zygote 的 Java 层
  4. 通过 registerZygoteSocket 方法创建 SocketServer,并通过 runSelectLoop 方法等待 AMS 的请求来创建新的应用进程
  5. 启动 SystemServer 进程

2.3 SystemServer 处理过程

SystemServer 进程主要用于创建系统服务,例如 AMS,WMS 和 PMS 。

2.3.1 Zygote 处理 SystemServer 进程

image20210918194013146.png

SystemServer 进程创建后,主要进行以下工作:

  1. 启动 Binder 线程池
  2. 创建 SystemServiceManager ,类似各种系统服务的微服务注册中心
  3. 启动各种系统服务

其中各种系统服务分为三类

  1. 引导服务,主要为 AMS,PMS,PowerManagerServices 等服务
  2. 核心服务,主要为 DropBoxManagerService,BatteryService,UsageStatsService 和 WebViewUpdateService 服务
  3. 其他服务,CameraService,AlarmManagerService,VrManagerService 等服务,这些服务都是 SystemService 的派生类

其中其他服务总共有 100 多个,这里只列出部分

因表格较大,这里直接拍照,有需要时在进行记录

image20210924202110733.png

2.4 Launcher 启动过程

2.4.1 Launcher 概述

launcher 就是启动器。

2.4.2 Launcher 启动过程介绍

SystemServer 启动过程中会启动 PMS,PMS 启动时会安装系统中已经安装的所有应用(将安装应用的信息或数据从非运行内存中拉入内存中)。在此之前会将 Launcher 应用启动 。

因为启动器实际上也是一个 APP,桌面实际上也是一个 Activity ,因此这里过程可能会和 Activity 启动流程有点像,不过作为启动器还是有些许不同。以下是启动过程时序图:

image20210924210811350.png

这里给出进程通讯图:

image20210928152025063.png

2.5 Android 系统启动流程

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

  2. 引导程序 BootLoader

  3. Linux 内核启动

    设置缓存、被保护存储器、计划列表、加载启动。当完成设置时,会在系统中寻找 init.rc 文件并解析,启动 init 进程 进入下一过程。

  4. init 进程启动

    初始化和启动属性服务,启动 Zygote 进程

  5. Zygote 进程启动

    创建 Java 虚拟机并未 Java 虚拟机注册 JNI 方法,创建 SocketServer,启动 SystemServer 进程

  6. SystemServer 进程启动

    启动 Binder 线程池和 SystemServiceManager,启动各种系统服务 。

  7. Launcher 启动

    AMS 会启动 Launcher,Launcher 启动后,系统启动完毕,用户可使用 Launcher 启动其他 App 。

image20210930155248570.png