Android系统开机时间优化

2,618 阅读5分钟

背景介绍

Android作为一个操作系统可以运行在各种设备中。很多设备对开机启动时间都有一个标准。开机时间过长,会影响到用户体验。本文主要介绍开机优化相关的知识。

分析开机性能的工具和使用方法

bootchart

Bootchart是一个可以用来分析启动时间的工具,目前的android P版本已经集成了这个工具。关于bootchart的详细介绍可以参考:elinux.org/Bootchart

1.如何在Android上启用bootchart功能?

adb shell "touch /data/bootchart/enabled"  
adb reboot  

系统重启开机后,就会生成bootchart抓取到的系统启动数据。该数据在/data/bootchart下面。

2.如何查看和分析启动数据?

aosp源码里面提供了抓取bootchart数据并解析生成Bootchart.png的脚本,脚本路径为system/core/init/grab-bootchart.sh。但是因为目前ubuntu系统不自带也无法通过apt安装上bootchart工具,这个脚本直接运行会报错。不过我们可以通过这个脚本知道bootchart工具的使用方法。

3.如何给ubuntu安装上bootchart工具?

bootchart2
这个工具是bootchart的升级版,源码地址在github.com/xrmx/bootch… bootlog.tgz"(注意,这里使用python3来运行)同样可以得到bootchart.png,而且展示的数据更加完整。推荐使用这个工具。

4.如何比较两个bootchart数据的差异?

AOSP源码工程提供了比较工具system/core/init/compare-bootcharts.py。使用方法比较简单,将两个bootchart.tgz作为比较对象即可。

python compare-bootcharts.py <srcbootchart> <dstbootchart>  

输出如下:
HYxeOO.png

使用perfboot.py分析启动各个阶段耗费的时间

perfboot.py同样是aosp提供的工具,在system/core/init/perfboot.py
工具的使用比较简单,直接使用python2运行即可,如果提示需要安装adb库,可以直接使用development/python-packages/adb
运行结果如下:
HYxgnU.png

抓取开机过程中的systrace日志来分析

修改文件frameworks/native/cmds/atrace/atrace.rc

on late-init
...
# Tracing disabled by default
     write /sys/kernel/debug/tracing/tracing_on 0  
     write /sys/kernel/tracing/tracing_on 0  

#write /sys/kernel/debug/tracing/tracing_on 0  
#write /sys/kernel/tracing/tracing_on 0  

保证在开机过程中,trace是打开状态。
在device.mk中增加

PRODUCT_PROPERTY_OVERRIDES += debug.atrace.tags.enableflags=802922  
PRODUCT_PROPERTY_OVERRIDES += persist.traced.enable=0  

设置trace中需要enable的flag,并关闭掉perfetto。
在QNX中增加

BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug,block,ext4,f2fs  

在cmdline中设置trace buffer size和需要enable的flag。
在init.rc中修改

on property:sys.boot_completed=1  
    write /d/tracing/tracing_on 0  
    write /d/tracing/events/ext4/enable 0  
    write /d/tracing/events/f2fs/enable 0  
    write /d/tracing/events/block/enable 0  

在系统启动完成后,再关闭trace的输出。最后再抓取开机过程中生成的trace信息。

adb root && adb shell atrace --async_stop -z -c -o /data/local/tmp/boot_trace  
adb pull /data/local/tmp/boot_trace  
$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py --from-file=boot_trace  

最后就可以得到开机过程中的systrace日志,用chrome打开分析整个开机过程。

打开init的dump方法,通过init日志分析rc脚本

rc脚本定义的位置较多,不太好通过脚本源码,来按照时序分析。我们可以通过打开init源码的DumpState方法来让init打印出解析完成后的rc脚本的情况。修改如下:

diff --git a/init/builtins.cpp b/init/builtins.cpp
index 8bd92ccdd..edc233629 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -413,7 +413,7 @@ static void import_late(const std::vector<std::string>& args, size_t start_index

     // Turning this on and letting the INFO logging be discarded adds 0.2s to
     // Nexus 9 boot time, so it's disabled by default.
-    if (false) DumpState();
+    if (true) DumpState();
 }

 /* mount_fstab
diff --git a/init/init.cpp b/init/init.cpp
index 4fe115e92..9bba9910a 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -715,7 +715,7 @@ int main(int argc, char** argv) {

     // Turning this on and letting the INFO logging be discarded adds 0.2s to
     // Nexus 9 boot time, so it's disabled by default.
-    if (false) DumpState();
+    if (true) DumpState();

     am.QueueEventTrigger("early-init");

另外,因为早期init日志是通过kernel打印的,日志流量过大会有被丢弃和被冲走的问题,所以还需要修改printk.c的代码。修改如下:

--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -55,7 +55,7 @@ void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...)
 {
 }

-#define __LOG_BUF_LEN  (1 << CONFIG_LOG_BUF_SHIFT)
+#define __LOG_BUF_LEN  (1 << 17)

开机性能的常规优化思路和优化点

调整开机阶段文件系统的配置

在init.rc中修改

+on late-fs
+    write /sys/block/vda/queue/iostats 0
+    write /sys/block/vda/queue/scheduler cfq
+    write /sys/block/vda/queue/read_ahead_kb 2048
+    write /sys/block/vdb/queue/iostats 0
+    write /sys/block/vdb/queue/scheduler cfq
+    write /sys/block/vdb/queue/read_ahead_kb 2048
+    write /sys/block/dm-0/queue/read_ahead_kb 2048
+
+on property:sys.boot_completed=1
+    write /sys/block/vda/queue/iostats 1
+    write /sys/block/vda/queue/read_ahead_kb 512
+    write /sys/block/vdb/queue/iostats 1
+    write /sys/block/vdb/queue/read_ahead_kb 512
+    write /sys/block/dm-0/queue/read_ahead_kb 512

主要目的是在开机阶段放大文件系统的读取buffer,可以提升IO的顺序读性能,开机阶段正好顺序读发生的概率更高。等到开机结束后,再改为512。实测性能收益在1s左右。

使用mke2fs工具作为ext4文件系统生成工具

在BoardConfig.mk中增加

TARGET_USES_MKE2FS := true  

性能收益在1s左右。

cpuset tune

on init
    # boottime stune
    write /dev/stune/schedtune.prefer_idle 1
    write /dev/stune/schedtune.boost 100

on property:sys.boot_completed=1
    # reset stune
    write /dev/stune/schedtune.prefer_idle 0
    write /dev/stune/schedtune.boost 0

实测性能收益500ms。

排查Init关键日志,对于启动超过50ms的服务都会有日志打印

H5OvUf.png

pkms多线程扫描

对开机包扫描安装做多线程优化,性能收益根据预装的应用情况不一样。大概500ms收益。

其他调试注意事项

init.rc的修改和验证

init.rc位于Android系统根目录下,无法使用adb push替换直接生效,如果需要验证init.rc的改动:
1.需要修改init.rc
2.然后编译system.img
3.adb shell busybox telnet 193.18.1.200 进入到QNX模式
4.然后reset -f 进入到fastboot模式
5.接着使用fastboot flash la_system_a system.img刷入system.img

参考文档

source.android.com/devices/tec…
www.cnblogs.com/arnoldlu/p/…