1. Android启动流程中的关键日志解析
作为一名长期从事Android系统开发的工程师,我经常需要分析系统启动过程中的各种日志。其中,boot_progress_start这个标记点尤为重要,它标志着Android系统从内核空间正式进入用户空间初始化阶段。今天我就结合源码和实际调试经验,详细解析这个关键日志背后的技术细节。
在Android启动过程中,系统会通过打印一系列boot_progress_*日志来标记各个关键阶段的起始和结束时间。这些日志对于性能优化和问题排查至关重要。boot_progress_start作为第一个用户空间标记点,它的准确理解能帮助我们定位系统启动早期的性能瓶颈。
2. boot_progress_start的实质含义
2.1 日志产生的源码位置
通过追踪Android源码,我们可以发现boot_progress_start日志是在init进程的main()函数中打印的。具体位置在system/core/init/main.cpp:
cpp复制int main(int argc, char** argv) {
// 初始化日志系统
InitLogging(argv, &android::base::KernelLogger);
// 打印启动进度标记
LOG(INFO) << "boot_progress_start";
// 执行第一阶段的初始化
if (!DoFirstStageMount()) {
LOG(ERROR) << "Failed to mount required partitions early";
return 1;
}
// ...后续初始化代码
}
这个日志的打印时机非常关键,它发生在:
- 内核完成初始化后
- 用户空间第一个进程init启动时
- 任何重要的挂载操作之前
2.2 与bootchart的对应关系
在实际性能分析中,我们常用bootchart工具来可视化系统启动过程。在bootchart生成的图表中,boot_progress_start对应的是zygote64进程的起点时刻。这是因为:
- init进程是zygote的父进程
- zygote的启动依赖于init进程完成基础环境初始化
- boot_progress_start标记了zygote启动的前提条件已就绪
下图展示了这个对应关系:
[此处应有bootchart图表示意图]
3. 从内核到用户空间的过渡
3.1 内核启动流程
在讨论boot_progress_start之前,有必要简要回顾内核的启动过程:
- 内核完成硬件初始化和驱动加载
- 创建第一个用户进程init(PID=1)
- 创建kthreadd内核线程(PID=2)
- 移交控制权给用户空间
3.2 init进程的双阶段初始化
init进程的初始化分为两个主要阶段:
第一阶段初始化(init_first_stage):
- 建立基本目录结构
- 挂载必要的文件系统(如/dev、/proc)
- 设置基本的环境变量
第二阶段初始化(init_second_stage):
- 解析init.rc脚本
- 启动关键系统服务
- 准备zygote启动环境
boot_progress_start正是在这两个阶段之间打印的,标志着基础环境已准备就绪。
4. zygote启动机制详解
4.1 init.rc的解析过程
init.rc是Android初始化脚本的核心文件,其中定义了各种服务的启动条件和顺序。与zygote相关的关键部分包括:
rc复制# system/core/rootdir/init.rc
on late-init
# 启动zygote服务
start zygote
start zygote_secondary
4.2 zygote服务定义
在Android 10及以后版本中,zygote服务定义在独立的rc文件中:
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
writepid /dev/cpuset/foreground/tasks
4.3 zygote启动时序
zygote的实际启动时序如下:
- init进程解析init.rc
- 触发late-init事件
- 启动zygote服务
- 执行app_process64可执行文件
- 进入ZygoteInit.main()
5. system_server的启动过程
5.1 从zygote到system_server
system_server是Android系统服务的核心载体,它的启动流程如下:
- zygote进程fork出system_server进程
- 执行SystemServer.main()方法
- 启动各种系统服务(AMS、PMS等)
- 进入Looper消息循环
关键代码在frameworks/base/services/java/com/android/server/SystemServer.java:
java复制public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
// 1. 初始化系统上下文
createSystemContext();
// 2. 启动引导服务
startBootstrapServices();
// 3. 启动核心服务
startCoreServices();
// 4. 启动其他服务
startOtherServices();
// 5. 进入消息循环
Looper.loop();
}
5.2 启动时间点标记
在system_server启动过程中,会打印多个关键日志:
boot_progress_system_run:SystemServer.main()开始执行boot_progress_pms_start:PackageManagerService启动boot_progress_ams_ready:ActivityManagerService准备就绪
这些日志与boot_progress_start一起,构成了完整的启动性能分析指标体系。
6. 性能分析与优化实践
6.1 启动时间测量方法
在实际项目中,我们通过以下方式测量启动时间:
- 收集内核日志:
bash复制adb logcat -b kernel -d | grep boot_progress
- 使用bootchart工具:
bash复制adb shell 'touch /data/bootchart/enabled'
adb reboot
adb pull /data/bootchart/
- 分析关键时间间隔:
- 内核启动 → boot_progress_start
- boot_progress_start → boot_progress_system_run
- boot_progress_system_run → boot_progress_ams_ready
6.2 常见性能瓶颈
根据经验,启动过程中常见的性能问题包括:
- init阶段延迟:
- 文件系统挂载耗时
- 设备节点创建缓慢
- SELinux策略加载时间长
- zygote启动延迟:
- 类预加载耗时
- 资源预加载缓慢
- JIT编译器初始化
- system_server启动延迟:
- 服务初始化顺序不合理
- 同步操作阻塞主线程
- 磁盘I/O竞争
6.3 优化建议
针对上述瓶颈,以下优化措施通常有效:
- 并行化初始化:
- 将不相互依赖的服务初始化改为并行
- 使用异步加载机制
- 延迟初始化:
- 将非关键路径的服务延迟加载
- 使用按需初始化的策略
- 资源预加载优化:
- 精简预加载的类和资源
- 优化资源索引
- I/O优化:
- 调整文件系统挂载参数
- 优化磁盘调度策略
7. 调试技巧与实战经验
7.1 日志过滤技巧
在实际调试中,我常用的日志过滤命令组合:
bash复制# 跟踪启动关键日志
adb logcat -v threadtime | grep -E 'boot_progress|ActivityManager|SystemServer'
# 查看zygote启动详细日志
adb logcat -s Zygote --pid=`pgrep -f zygote`
7.2 常见问题排查
问题1:boot_progress_start日志未打印
可能原因:
- init进程崩溃
- 内核未正确移交控制权
- 日志缓冲区溢出
排查步骤:
- 检查内核日志最后打印的内容
- 确认init进程是否存活
- 检查/sys/fs/pstore中的崩溃日志
问题2:boot_progress_start到zygote启动间隔过长
可能原因:
- init.rc脚本执行缓慢
- 设备节点初始化耗时
- SELinux策略加载慢
排查步骤:
- 在init.rc中添加时间戳日志
- 使用strace跟踪init进程
- 检查SELinux策略加载时间
7.3 性能优化案例
在某次项目优化中,我们发现boot_progress_start到zygote启动耗时异常(约2秒)。通过分析发现:
- 根本原因:init.rc中串行执行了大量shell命令
- 优化方案:
- 将不相互依赖的命令改为并行执行
- 用C++实现替代部分shell命令
- 优化效果:该阶段耗时降低至800ms
关键修改示例:
rc复制# 优化前
service preload1 /system/bin/sh /vendor/bin/preload1.sh
class main
service preload2 /system/bin/sh /vendor/bin/preload2.sh
class main
depends_on preload1
# 优化后
service preload1 /system/bin/sh /vendor/bin/preload1.sh
class parallel
service preload2 /system/bin/sh /vendor/bin/preload2.sh
class parallel
8. 进阶:启动时序的自定义扩展
8.1 添加自定义启动标记
在实际开发中,我们可能需要添加自定义的启动进度标记。这可以通过以下方式实现:
- 在C++代码中:
cpp复制#include <log/log.h>
ALOGI("boot_progress_custom_start");
- 在Java代码中:
java复制import android.util.Log;
Log.i(TAG, "boot_progress_custom_start");
8.2 启动动画同步
了解boot_progress_start的准确时机后,我们可以优化启动动画的显示逻辑:
- 在内核启动阶段显示静态LOGO
- 检测到boot_progress_start后切换到动画
- system_server启动完成后结束动画
实现示例:
cpp复制// 在surfaceflinger中
if (checkBootProgress("boot_progress_start")) {
startBootAnimation();
}
8.3 启动时间统计工具
基于这些知识,我们可以开发自定义的启动时间分析工具:
python复制import re
def parse_boot_log(logfile):
markers = {}
with open(logfile) as f:
for line in f:
if "boot_progress" in line:
time = extract_timestamp(line)
marker = extract_marker(line)
markers[marker] = time
# 计算各阶段耗时
phases = {
"kernel_boot": markers["boot_progress_start"] - kernel_start_time,
"init_to_zygote": markers["boot_progress_zygote_start"] - markers["boot_progress_start"],
# 其他阶段...
}
return phases
9. 兼容性考量
9.1 不同Android版本的差异
需要注意,boot_progress_start的精确含义在不同Android版本中可能有细微差别:
- Android 8及之前:zygote由init直接启动
- Android 9及之后:引入zygote衍生进程机制
- Android 12:新增启动阶段划分
9.2 64位与32位系统
在64位和32位混合系统中,zygote的启动更为复杂:
- 主zygote(64位):/system/bin/app_process64
- 辅助zygote(32位):/system/bin/app_process32
- 两者启动时序需要仔细协调
9.3 厂商定制化影响
各厂商可能会修改默认的启动流程:
- 添加自定义的init阶段
- 修改zygote的预加载内容
- 增加额外的系统服务
这要求我们在分析时需要:
- 检查厂商的init.rc修改
- 确认zygote的启动参数变化
- 注意vendor分区中的自定义服务
10. 总结与个人实践建议
通过多年的Android系统开发经验,我认为要真正掌握启动过程的分析,需要:
- 建立完整的时序概念:从内核到框架的每个阶段都要清晰
- 掌握核心日志标记:boot_progress_*系列是关键路标
- 熟练使用分析工具:logcat、bootchart、systrace等
- 理解zygote机制:这是Android系统的独特设计
在实际项目中,我通常会:
- 先通过bootchart获取整体启动概况
- 然后针对异常阶段深入分析日志
- 最后使用systrace定位具体瓶颈
对于想要深入Android系统启动机制的同学,我的建议是:
- 从AOSP源码入手,理解init和zygote的实现
- 在实际设备上验证理论分析
- 尝试修改启动参数观察影响
- 积累常见问题的解决模式