背景介绍
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>
使用perfboot.py分析启动各个阶段耗费的时间
perfboot.py同样是aosp提供的工具,在system/core/init/perfboot.py
工具的使用比较简单,直接使用python2运行即可,如果提示需要安装adb库,可以直接使用development/python-packages/adb
运行结果如下:
抓取开机过程中的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的服务都会有日志打印
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/…