上一篇文章我们详细解析了 init.rc 文件的语法规范。同学们可以对照着去看看 Android/system/core/rootdir/init.rc 这个文件。我们会发现里面有很多 event trigger, 比如 on boot
, on fs
等,然后系统的各种核心服务,基本上都是在这些 event trigger 里面启动的。以前一直搞不明白这些 event trigger 的先后执行顺序。这次把源码看了一遍过,终于搞明白了。本文不会从源码的角度去分析 init 的启动流程,只是把自己看过源码之后的理解, 整理成一张思维导图,相信同学们看过这张图之后应该能大概明白 init 都干了哪些活。这些活的先后顺序,这就足够了。
init 启动流程概要:
从上图来看, init 的初始化可以分为三个阶段:
- FirstStageMain
这个阶段的工作主要是挂载一些虚拟文件系统,还有挂载两个系统最核心的分区 system, vendor。
这两步都是为了后续的工作做准备。比如设置 selinux 需要从 system, vendor 分区中读取 sepolicy。 SecondStageMain 需要从 system, vendor 分区中读取 property 信息,需要启动系统核心服务。 - SetupSelinux
设置 Selinux 安全机制。 - SecondStageMain
启动 property 服务(从这里可以知道 property 服务是运行在 init 进程里面的),然后加载各分区下的 .rc 文件。然后把各种事件加入到事件队列。最后进入事件循环,按队列方式(先进先出)处理事件队列中的事件。
从上面的流程来看,其实 init 把大部分的初始化工作都放到 init.rc 中, 这样可以更灵活的适配各种平台的定制需求。
需要提一下的是各阶段的启动方式,第一阶段的启动是由 kernel 启动的,这里不讨论,第二,三阶段的启动比较有意思,都是通过 exec 来启动的:
1 | const char* path = "/system/bin/init"; |
1 | const char* path = "/system/bin/init"; |
我也不知道为什么要通过 execv 启动一个新进程替换旧进程的方式来切换各阶段呢?这样相比直接一个函数调用来说有什么好处呢?知道的同学麻烦指导一下哈。
单独把 secondStage 部分的流程拎出来扩展:
这张图主要是想表现一下各事件触发器的触发时序,各服务的启动时序。整理如下:
0. 初始化 biner 驱动相关
early-init 事件
启动了 ueventd, apexd-bootstrap 服务init 事件
1
2
3
4
5
6
7
8
9
10
11on init
# 创建各种系统运行所需的目录,更改各种目录的权限
mkdir /mnt/media_rw 0750 root media_rw
mkdir /mnt/user 0755 root root
# Start logd before any other services run to ensure we capture all of their logs.
start logd
# Start essential services.
start servicemanager
start hwservicemanager
start vndservicemanagerlate-init 事件
把一堆的事件按顺序加入事件队列1
2
3
4
5
6
7
8
9
10
11on late-init
trigger early-fs
trigger fs
trigger post-fs
trigger late-fs
trigger post-fs-data
trigger load_persist_props_action
trigger zygote-start
trigger firmware_mounts_complete
trigger early-boot
trigger bootearly-fs 事件
启动 vold 服务,负责外接磁盘的挂载。fs 事件
Action 定义在 vendor/etc/init/init.ranchu.rc 中, 只有一个动作,根据 fstab 挂载所有分区。1
mount_all /vendor/etc/fstab.ranchu
在挂完文件系统后,代码中会把 nonencrypted 事件加入到事件队列。
post-fs 事件
重新挂载 / 根分区为只读分区,创建各种系统目录。late-fs 事件
启动 early_hal 类型的所有服务,当然被 disable 的除外。post-fs-data 事件
启动 bootchart, apexd, 等待 apexd 服务完成load_persist_props_action 事件
加载 /data 分区下的 persist propertyzygote-start 事件
Android 世界的鼻祖在这里启动了firmware_mounts_complete 事件
这个不知道干啥用的了early-boot 事件
目前没有使用boot 事件
启动 hal, core 类型的所有服务,当然被 disable 的除外。nonencrypted 事件
启动 main 类型的所有服务,当然被 disable 的除外。
以上,我们可以知道各种服务的启动顺序了:
最先启动的是 logd, 和三个 servicemanager 服务。
接下来按这个顺序来启动各种类型的服务:
early_hal –> hal –> core –> main