Tài liệu này cung cấp hướng dẫn cho đối tác về cách cải thiện thời gian khởi động cho một số thiết bị Android cụ thể. Thời gian khởi động là một thành phần quan trọng của hiệu suất hệ thống vì người dùng phải đợi quá trình khởi động hoàn tất thì mới có thể sử dụng thiết bị. Đối với các thiết bị như ô tô, nơi quá trình khởi động nguội xảy ra thường xuyên hơn, thời gian khởi động nhanh là rất quan trọng (không ai thích chờ hàng chục giây chỉ để nhập một đích đến).
Android 8.0 giúp giảm thời gian khởi động bằng cách hỗ trợ một số điểm cải tiến trên nhiều thành phần. Bảng sau đây tóm tắt những điểm cải tiến về hiệu suất này (được đo lường trên thiết bị Google Pixel và Pixel XL).
Thành phần | Cải thiện |
---|---|
Trình tải khởi động |
|
Hạt nhân thiết bị |
|
Điều chỉnh I/O |
|
init.*.rc |
|
Ảnh động khởi động |
|
Chính sách SELinux | Tiết kiệm 0,2 giây nhờ genfscon |
Tối ưu hoá trình tải khởi động
Cách tối ưu hoá trình tải khởi động để cải thiện thời gian khởi động:
- Để ghi nhật ký:
- Tắt tính năng ghi nhật ký vào UART vì có thể mất nhiều thời gian với nhiều hoạt động ghi nhật ký. (Trên các thiết bị Google Pixel, chúng tôi nhận thấy việc này làm chậm trình tải khởi động 1,5 giây).
- Chỉ ghi nhật ký các trường hợp lỗi và cân nhắc lưu trữ thông tin khác vào bộ nhớ bằng một cơ chế riêng để truy xuất.
- Để giải nén nhân hệ điều hành, hãy cân nhắc sử dụng LZ4 cho phần cứng hiện đại thay vì GZIP (ví dụ: bản vá). Xin lưu ý rằng các tuỳ chọn nén nhân có thể có thời gian tải và giải nén khác nhau, đồng thời một số tuỳ chọn có thể hoạt động hiệu quả hơn so với các tuỳ chọn khác đối với phần cứng cụ thể của bạn.
- Kiểm tra thời gian chờ không cần thiết để loại bỏ hiện tượng nảy/chuyển sang chế độ đặc biệt và giảm thiểu thời gian chờ.
- Truyền thời gian khởi động trong trình tải khởi động đến hạt nhân dưới dạng cmdline.
- Kiểm tra xung nhịp CPU và cân nhắc việc song song hoá (yêu cầu hỗ trợ nhiều lõi) để tải hạt nhân và khởi chạy I/O.
Tối ưu hoá hiệu suất I/O
Việc cải thiện hiệu suất I/O là rất quan trọng để rút ngắn thời gian khởi động và bạn nên trì hoãn việc đọc mọi nội dung không cần thiết cho đến sau khi khởi động (trên Google Pixel, khoảng 1,2 GB dữ liệu được đọc khi khởi động).
Điều chỉnh hệ thống tệp
Tính năng đọc trước của nhân Linux sẽ kích hoạt khi một tệp được đọc từ đầu hoặc khi các khối được đọc tuần tự, khiến bạn cần phải điều chỉnh các tham số của trình lập lịch biểu I/O dành riêng cho việc khởi động (có đặc điểm tải công việc khác với các ứng dụng thông thường).
Các thiết bị hỗ trợ bản cập nhật liền mạch (A/B) sẽ hưởng lợi rất nhiều từ việc điều chỉnh hệ thống tệp trong lần khởi động đầu tiên (ví dụ: 20 giây trên Google Pixel). Ví dụ: chúng tôi đã điều chỉnh các tham số sau cho Google Pixel:
on late-fs # boot time fs tune # boot time fs tune write /sys/block/sda/queue/iostats 0 write /sys/block/sda/queue/scheduler cfq write /sys/block/sda/queue/iosched/slice_idle 0 write /sys/block/sda/queue/read_ahead_kb 2048 write /sys/block/sda/queue/nr_requests 256 write /sys/block/dm-0/queue/read_ahead_kb 2048 write /sys/block/dm-1/queue/read_ahead_kb 2048 on property:sys.boot_completed=1 # end boot time fs tune write /sys/block/sda/queue/read_ahead_kb 512 ...
Khác
- Bật kích thước tìm nạp trước băm dm-verity bằng cách sử dụng cấu hình hạt nhân DM_VERITY_HASH_PREFETCH_MIN_SIZE (kích thước mặc định là 128).
- Để hệ thống tệp ổn định hơn và không còn tình trạng kiểm tra bắt buộc bị bỏ qua xảy ra trong mỗi lần khởi động, hãy sử dụng công cụ tạo ext4 mới bằng cách đặt TARGET_USES_MKE2FS trong BoardConfig.mk.
Phân tích I/O
Để hiểu các hoạt động I/O trong quá trình khởi động, hãy sử dụng dữ liệu ftrace của hạt nhân (cũng được systrace sử dụng):
trace_event=block,ext4 in BOARD_KERNEL_CMDLINE
Để phân tích quyền truy cập tệp cho từng tệp, hãy thực hiện các thay đổi sau đối với hạt nhân (chỉ hạt nhân phát triển; không sử dụng trong hạt nhân phát hành công khai):
diff --git a/fs/open.c b/fs/open.c index 1651f35..a808093 100644 --- a/fs/open.c +++ b/fs/open.c @@ -981,6 +981,25 @@ } EXPORT_SYMBOL(file_open_root); +static void _trace_do_sys_open(struct file *filp, int flags, int mode, long fd) +{ + char *buf; + char *fname; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return; + fname = d_path(&filp-<f_path, buf, PAGE_SIZE); + + if (IS_ERR(fname)) + goto out; + + trace_printk("%s: open(\"%s\", %d, %d) fd = %ld, inode = %ld\n", + current-<comm, fname, flags, mode, fd, filp-<f_inode-<i_ino); +out: + kfree(buf); +} + long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode) { struct open_flags op; @@ -1003,6 +1022,7 @@ } else { fsnotify_open(f); fd_install(fd, f); + _trace_do_sys_open(f, flags, mode, fd);
Sử dụng các tập lệnh sau để phân tích hiệu suất khởi động.
system/extras/boottime_tools/bootanalyze/bootanalyze.py
Đo lường thời gian khởi động với bảng chi tiết các bước quan trọng trong quá trình khởi động.system/extras/boottime_tools/io_analysis/check_file_read.py boot_trace
Cung cấp thông tin truy cập cho mỗi tệp.system/extras/boottime_tools/io_analysis/check_io_trace_all.py boot_trace
Cung cấp thông tin chi tiết ở cấp hệ thống.
Tối ưu hoá init.*.rc
Init là cầu nối từ hạt nhân cho đến khi khung được thiết lập và các thiết bị thường mất vài giây ở các giai đoạn khởi chạy khác nhau.
Chạy các tác vụ song song
Mặc dù quá trình khởi động Android hiện tại ít nhiều là một quy trình đơn luồng, nhưng bạn vẫn có thể thực hiện một số tác vụ song song.
- Thực thi các lệnh chậm trong dịch vụ tập lệnh shell và tham gia lệnh đó sau bằng cách chờ thuộc tính cụ thể. Android 8.0 hỗ trợ trường hợp sử dụng này bằng một lệnh
wait_for_property
mới. - Xác định các thao tác chậm trong quá trình khởi động. Hệ thống ghi lại lệnh khởi động exec/wait_for_prop hoặc bất kỳ hành động nào mất nhiều thời gian (trong Android 8.0, bất kỳ lệnh nào mất hơn 50 mili giây). Ví dụ:
init: Command 'wait_for_coldboot_done' action=wait_for_coldboot_done returned 0 took 585.012ms
Việc xem xét nhật ký này có thể cho biết những cơ hội cải thiện.
- Bắt đầu các dịch vụ và bật các thiết bị ngoại vi trong đường dẫn quan trọng sớm. Ví dụ: một số SOC yêu cầu bắt đầu các dịch vụ liên quan đến bảo mật trước khi bắt đầu SurfaceFlinger. Xem lại nhật ký hệ thống khi ServiceManager trả về "wait for service" (chờ dịch vụ) — đây thường là dấu hiệu cho thấy bạn phải khởi động một dịch vụ phụ thuộc trước tiên.
- Xoá mọi dịch vụ và lệnh không dùng đến trong init.*.rc. Mọi thứ không được sử dụng trong quá trình khởi tạo giai đoạn đầu phải được trì hoãn cho đến khi khởi động xong.
Lưu ý: Dịch vụ thuộc tính là một phần của quá trình khởi động, vì vậy, việc gọi setproperty
trong quá trình khởi động có thể dẫn đến độ trễ lâu nếu quá trình khởi động đang bận trong các lệnh tích hợp.
Sử dụng tính năng điều chỉnh trình lập lịch biểu
Sử dụng tính năng điều chỉnh trình lập lịch biểu để khởi động sớm. Ví dụ trên điện thoại Google Pixel:
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 # or just disable EAS during boot on init write /sys/kernel/debug/sched_features NO_ENERGY_AWARE on property:sys.boot_completed=1 write /sys/kernel/debug/sched_features ENERGY_AWARE
Một số dịch vụ có thể cần tăng mức độ ưu tiên trong quá trình khởi động. Ví dụ:
init.zygote64.rc: service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server class main priority -20 user root ...
Bắt đầu zygote sớm
Các thiết bị có phương thức mã hoá dựa trên tệp có thể khởi động zygote sớm hơn tại trình kích hoạt zygote-start (theo mặc định, zygote được khởi chạy tại lớp main, muộn hơn nhiều so với zygote-start). Khi thực hiện việc này, hãy nhớ cho phép zygote chạy trong tất cả CPU (vì việc đặt cpuset không chính xác có thể buộc zygote chạy trong các CPU cụ thể).
Tắt chế độ tiết kiệm pin
Trong quá trình khởi động thiết bị, bạn có thể tắt chế độ cài đặt tiết kiệm pin cho các thành phần như UFS và/hoặc trình quản lý CPU.
Thận trọng: Bạn nên bật chế độ tiết kiệm pin ở chế độ sạc để tăng hiệu quả.
on init # Disable UFS powersaving write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 0 write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 0 write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 0 write /sys/module/lpm_levels/parameters/sleep_disabled Y on property:sys.boot_completed=1 # Enable UFS powersaving write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 1 write /sys/module/lpm_levels/parameters/sleep_disabled N on charger # Enable UFS powersaving write /sys/devices/soc/${ro.boot.bootdevice}/clkscale_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/clkgate_enable 1 write /sys/devices/soc/${ro.boot.bootdevice}/hibern8_on_idle_enable 1 write /sys/class/typec/port0/port_type sink write /sys/module/lpm_levels/parameters/sleep_disabled N
Hoãn quá trình khởi động không quan trọng
Bạn có thể trì hoãn quá trình khởi chạy không quan trọng như ZRAM đến boot_complete
.
on property:sys.boot_completed=1 # Enable ZRAM on boot_complete swapon_all /vendor/etc/fstab.${ro.hardware}
Tối ưu hoá ảnh động khởi động
Hãy áp dụng các mẹo sau để tối ưu hoá ảnh động khởi động.
Định cấu hình tính năng bắt đầu sớm
Android 8.0 cho phép bắt đầu ảnh động khởi động sớm, trước khi gắn phân vùng userdata. Tuy nhiên, ngay cả khi sử dụng chuỗi công cụ ext4 mới trong Android 8.0, fsck vẫn được kích hoạt định kỳ vì lý do an toàn, gây ra độ trễ trong việc khởi động dịch vụ bootanimation.
Để bắt đầu bootanimation sớm, hãy chia quá trình gắn fstab thành hai giai đoạn:
- Trong giai đoạn đầu, chỉ gắn các phân vùng (chẳng hạn như
system/
vàvendor/
) không yêu cầu kiểm tra khi chạy, sau đó khởi động các dịch vụ ảnh động khởi động và các phần phụ thuộc của dịch vụ đó (chẳng hạn như servicemanager và surfaceflinger). - Trong giai đoạn thứ hai, hãy gắn các phân vùng (chẳng hạn như
data/
) yêu cầu kiểm tra khi chạy.
Ảnh động khởi động sẽ bắt đầu nhanh hơn nhiều (và trong thời gian không đổi) bất kể fsck.
Kết thúc sạch sẽ
Sau khi nhận được tín hiệu thoát, bootanimation sẽ phát phần cuối cùng, thời lượng của phần này có thể làm chậm thời gian khởi động. Một hệ thống khởi động nhanh không cần đến ảnh động dài có thể che giấu hiệu quả mọi điểm cải tiến đã thực hiện. Bạn nên làm cho cả vòng lặp lặp lại và phần kết thúc ngắn gọn.
Tối ưu hoá SELinux
Hãy áp dụng các mẹo sau để tối ưu hoá SELinux nhằm cải thiện thời gian khởi động.
- Sử dụng biểu thức chính quy (regex) rõ ràng. Biểu thức chính quy được định dạng không đúng cách có thể dẫn đến nhiều hao tổn khi so khớp chính sách SELinux cho
sys/devices
trongfile_contexts
. Ví dụ: biểu thức chính quy/sys/devices/.*abc.*(/.*)?
buộc quét tất cả thư mục con/sys/devices
chứa "abc", cho phép khớp với cả/sys/devices/abc
và/sys/devices/xyz/abc
. Việc cải thiện biểu thức chính quy này thành/sys/devices/[^/]*abc[^/]*(/.*)?
sẽ chỉ cho phép khớp với/sys/devices/abc
. - Di chuyển nhãn sang genfscon. Tính năng SELinux hiện có này truyền các tiền tố so khớp tệp vào nhân trong tệp nhị phân SELinux, trong đó nhân áp dụng các tiền tố này cho các hệ thống tệp do nhân tạo. Điều này cũng giúp khắc phục các tệp do hạt nhân tạo ra bị gắn nhãn sai, ngăn chặn các điều kiện tương tranh có thể xảy ra giữa các quy trình không gian người dùng đang cố gắng truy cập vào các tệp này trước khi quá trình gắn nhãn lại diễn ra.
Công cụ và phương pháp
Hãy sử dụng các công cụ sau đây để thu thập dữ liệu cho các mục tiêu tối ưu hoá.
Bootchart
Bootchart cung cấp thông tin chi tiết về mức tải CPU và I/O của tất cả các quy trình cho toàn bộ hệ thống. Phương thức này không yêu cầu tạo lại hình ảnh hệ thống và có thể được dùng làm bước kiểm tra nhanh trước khi đi sâu vào systrace.
Cách bật bootchart:
adb shell 'touch /data/bootchart/enabled'
adb reboot
Sau khi khởi động, hãy tìm nạp biểu đồ khởi động:
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
Khi hoàn tất, hãy xoá /data/bootchart/enabled
để không thu thập dữ liệu mỗi lần.
bootchart.png
không tồn tại, hãy làm như sau:
- Chạy các lệnh sau:
sudo apt install python-is-python3
cd ~/Documents
git clone https://github.com/xrmx/bootchart.git
cd bootchart/pybootchartgui
mv main.py.in main.py
- Cập nhật
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
để trỏ đến bản sao cục bộ củapybootchartgui
(nằm tại~/Documents/bootchart/pybootchartgui.py
)
Systrace
Systrace cho phép thu thập cả dấu vết hạt nhân và Android trong quá trình khởi động. Hình ảnh của systrace có thể giúp phân tích vấn đề cụ thể trong quá trình khởi động. (Tuy nhiên, để kiểm tra số trung bình hoặc số tích luỹ trong toàn bộ quá trình khởi động, bạn nên trực tiếp xem dấu vết nhân).
Cách bật systrace trong quá trình khởi động:
- Trong
frameworks/native/cmds/atrace/atrace.rc
, hãy thay đổi:write /sys/kernel/debug/tracing/tracing_on 0 write /sys/kernel/tracing/tracing_on 0
Đến:
# write /sys/kernel/debug/tracing/tracing_on 0 # write /sys/kernel/tracing/tracing_on 0
- Trong tệp
device.mk
, hãy thêm dòng sau:PRODUCT_PROPERTY_OVERRIDES += debug.atrace.tags.enableflags=802922 PRODUCT_PROPERTY_OVERRIDES += persist.traced.enable=0
- Trong tệp
BoardConfig.mk
của thiết bị, hãy thêm nội dung sau:BOARD_KERNEL_CMDLINE := ... trace_buf_size=64M trace_event=sched_wakeup,sched_switch,sched_blocked_reason,sched_cpu_hotplug
- Trong tệp
init.rc
dành riêng cho thiết bị, hãy thêm nội dung sau:on property:sys.boot_completed=1 // This stops tracing on boot complete 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
-
Sau khi khởi động, hãy tìm nạp dấu vết:
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
Thao tác này sẽ bật tính năng theo dõi (tính năng này bị tắt theo mặc định).
Để phân tích chi tiết về I/O, hãy thêm cả khối và ext4 và f2fs.