Tài liệu này cung cấp hướng dẫn dành cho đối tác để cải thiện thời gian khởi động cho các 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 trước khi có thể sử dụng thiết bị. Đối với các thiết bị như ô tô, nơi xảy ra hiện tượng khởi động nguội thường xuyên hơn, việc có thời gian khởi động nhanh là rất quan trọng (không ai thích chờ đợi hàng chục giây chỉ để nhập điểm đến điều hướng).
Android 8.0 cho phép giảm thời gian khởi động bằng cách hỗ trợ một số cải tiến trên nhiều thành phần. Bảng sau đây tóm tắt những cải tiến về hiệu suất này (được đo trên thiết bị Google Pixel và Pixel XL).
Thành phần | Sự cải tiến |
---|---|
Bộ nạp khởi động |
|
Nhân thiết bị |
|
Điều chỉnh I/O |
|
init.*.rc |
|
Khởi động hoạt hình |
|
Chính sách SELinux | Đã lưu 0,2 giây bởi genfscon |
Tối ưu hóa bộ nạp khởi động
Để tối ưu hóa bộ nạp khởi động để cải thiện thời gian khởi động:
- Để ghi nhật ký:
- Vô hiệu hóa tính năng ghi nhật ký vào UART vì việc này có thể mất nhiều thời gian khi ghi nhật ký nhiều. (Trên các thiết bị Google Pixel, chúng tôi nhận thấy nó làm chậm bộ tải khởi động 1,5 giây).
- Chỉ ghi lại các tình huống lỗi và cân nhắc việc lưu trữ các thông tin khác vào bộ nhớ bằng cơ chế truy xuất riêng.
- Để giải nén kernel, 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á ). Hãy nhớ rằng các tùy chọn nén hạt nhân khác nhau có thể có thời gian tải và giải nén khác nhau và một số tùy chọn có thể hoạt động tốt hơn các tùy chọn khác đối với phần cứng cụ thể của bạn.
- Kiểm tra thời gian chờ đợi không cần thiết để gỡ lỗi/vào chế độ đặc biệt và giảm thiểu chúng.
- Chuyển thời gian khởi động trong bộ nạp khởi động sang kernel dưới dạng cmdline.
- Kiểm tra xung nhịp CPU và xem xét việc song song hóa (yêu cầu hỗ trợ đa lõi) để tải hạt nhân và khởi tạo I/O.
Tối ưu hóa hiệu quả I/O
Việc cải thiện hiệu suất I/O là rất quan trọng để giúp thời gian khởi động nhanh hơn và việc đọc mọi thứ không cần thiết nên được hoãn lại 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 tin
Nhân Linux bắt đầu đọc trước khi một tệp được đọc từ đầu hoặc khi các khối được đọc tuần tự, do đó cần phải điều chỉnh các tham số của bộ lập lịch I/O cụ thể để khởi động (có đặc tính khối lượng công việc khác với các ứng dụng thông thường).
Các thiết bị hỗ trợ cập nhật liền mạch (A/B) được hưởng lợi rất nhiều từ việc điều chỉnh hệ thống tệp khi khởi động lần đầu (ví dụ: 20 giây trên Google Pixel). Một 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 ...
Điều khoản khác
- Bật kích thước tìm nạp trước băm dm-verity bằng cấu hình kernel DM_VERITY_HASH_PREFETCH_MIN_SIZE (kích thước mặc định là 128).
- Để có độ ổn định hệ thống tệp tốt hơn và loại bỏ kiểm tra bắt buộc 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 vào/ra
Để hiểu các hoạt động I/O trong khi khởi động, hãy sử dụng dữ liệu kernel ftrace (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 kernel (chỉ kernel phát triển; không sử dụng trong kernel sản xuất):
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 để trợ giúp phân tích hiệu suất khởi động.
-
system/extras/boottime_tools/bootanalyze/bootanalyze.py
Đo thời gian khởi động bằng cách phân tích các bước quan trọng trong quy 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 hóa init.*.rc
Init là cầu nối từ kernel cho đến khi framework được thiết lập và các thiết bị thường dành vài giây ở các giai đoạn init khác nhau.
Chạy các tác vụ song song
Mặc dù init Android hiện tại ít nhiều là một tiến 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 lệnh
wait_for_property
mới. - Xác định các hoạt động chậm trong init. Hệ thống ghi lại lệnh init 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 lại nhật ký này có thể chỉ ra các cơ hội cải tiến.
- Bắt đầu dịch vụ và kích hoạt sớm các thiết bị ngoại vi trong đường dẫn quan trọng. Ví dụ: một số SOC yêu cầu khởi động các dịch vụ liên quan đến bảo mật trước khi khởi động SurfaceFlinger. Xem lại nhật ký hệ thống khi ServiceManager trả về "chờ dịch vụ" — đây thường là dấu hiệu cho thấy dịch vụ phụ thuộc phải được khởi động trước.
- Xóa mọi dịch vụ và lệnh không sử dụng trong init.*.rc. Bất cứ thứ gì không được sử dụng trong giai đoạn đầu init sẽ được hoãn lại để hoàn thành quá trình khởi động.
Lưu ý: Dịch vụ thuộc tính là một phần của quy trình init, vì vậy việc gọi setproperty
trong khi khởi động có thể dẫn đến độ trễ lâu nếu init bận trong các lệnh dựng sẵn.
Sử dụng điều chỉnh lịch trình
Sử dụng điều chỉnh lịch trình để khởi động sớm. Ví dụ từ 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 hợp tử sớm
Các thiết bị có mã hóa dựa trên tệp có thể khởi động hợp tử sớm hơn khi kích hoạt khởi động hợp tử (theo mặc định, hợp tử được khởi chạy ở lớp chính, muộn hơn nhiều so với khởi động hợp tử). Khi thực hiện việc này, hãy đảm bảo cho phép hợp tử chạy trong tất cả các CPU (vì cài đặt bộ xử lý sai có thể buộc hợp tử chạy trong các CPU cụ thể).
Vô hiệu hóa tiết kiệm năng lượng
Trong quá trình khởi động thiết bị, cài đặt tiết kiệm năng lượng cho các thành phần như UFS và/hoặc bộ điều chỉnh CPU có thể bị tắt.
Thận trọng: Nên bật tính năng tiết kiệm năng lượng ở chế độ sạc để đạt 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
Trì hoãn việc khởi tạo không quan trọng
Việc khởi tạo không quan trọng như ZRAM có thể được hoãn lại thành boot_complete
.
on property:sys.boot_completed=1 # Enable ZRAM on boot_complete swapon_all /vendor/etc/fstab.${ro.hardware}
Tối ưu hóa hoạt ảnh khởi động
Sử dụng các mẹo sau để tối ưu hóa hoạt ảnh khởi động.
Định cấu hình bắt đầu sớm
Android 8.0 cho phép bắt đầu hoạt ảnh khởi động sớm trước khi gắn phân vùng dữ liệu người dùng. 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 tình trạng chậm trễ khi khởi động dịch vụ bootanimation.
Để bắt đầu quá trình bootanimation sớm, hãy chia fstab mount thành hai giai đoạn:
- Trong giai đoạn đầu, chỉ gắn kết các phân vùng (chẳng hạn như
system/
vàvendor/
) không yêu cầu kiểm tra lần chạy, sau đó khởi động các dịch vụ hoạt ảnh khởi động và các phần phụ thuộc của nó (chẳng hạn như servicemanager và surfaceflinger). - Trong giai đoạn thứ hai, gắn kết các phân vùng (chẳng hạn như
data/
) yêu cầu kiểm tra lần chạy.
Hoạt ảnh khởi động sẽ được 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, độ dài 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 chóng không cần những hình ảnh động dài dòng có thể che giấu bất kỳ cải tiến nào được thực hiện một cách hiệu quả. Chúng tôi khuyên bạn nên làm ngắn cả vòng lặp và phần cuối.
Tối ưu hóa SELinux
Sử dụng các mẹo sau để tối ưu hóa SELinux nhằm cải thiện thời gian khởi động.
- Sử dụng các biểu thức chính quy rõ ràng (regex) . Regex được định dạng kém có thể dẫn đến nhiều chi phí khi 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 nhầm tất cả các thư mục con/sys/devices
có chứa "abc", cho phép khớp cả/sys/devices/abc
và/sys/devices/xyz/abc
. Cải thiện biểu thức chính quy này thành/sys/devices/[^/]*abc[^/]*(/.*)?
sẽ chỉ kích hoạt kết quả khớp cho/sys/devices/abc
. - Di chuyển nhãn sang genfscon . Tính năng SELinux hiện có này chuyển các tiền tố khớp tệp vào hạt nhân trong hệ nhị phân SELinux, nơi hạt nhân áp dụng chúng cho các hệ thống tệp do hạt nhân tạo ra. Điều này cũng giúp sửa các tệp do kernel tạo bị gắn nhãn sai, ngăn ngừa tình trạng chạy đua có thể xảy ra giữa các quá trình trong không gian người dùng đang cố gắng truy cập các tệp này trước khi việc gắn nhãn lại xảy ra.
Công cụ và phương pháp
Sử dụng các công cụ sau để giúp bạn thu thập dữ liệu cho mục tiêu tối ưu hóa.
Sơ đồ khởi động
Bootchart cung cấp thông tin phân tích tải CPU và I/O của tất cả các tiến trình cho toàn bộ hệ thống. Nó không yêu cầu xây dựng lại hình ảnh hệ thống và có thể được sử dụng để kiểm tra tình trạng nhanh chóng trước khi đi sâu vào systrace.
Để kích hoạt biểu đồ khởi động:
adb shell 'touch /data/bootchart/enabled'
adb reboot
Sau khi khởi động, lấy biểu đồ khởi động:
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh
Khi hoàn tất, hãy xóa /data/bootchart/enabled
để tránh việc 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 kernel và Android trong quá trình khởi động. Hình dung 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 lũy trong toàn bộ quá trình khởi động, việc xem trực tiếp dấu vết kernel sẽ dễ dàng hơn).
Để bật systrace trong khi khởi động:
- Trong
frameworks/native/cmds/atrace/atrace.rc
, 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
, 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ị, thêm phần 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 thông tin 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, 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
Điều này cho phép theo dõi (bị tắt theo mặc định).
Để phân tích I/O chi tiết, hãy thêm khối, ext4 và f2fs.