Tài liệu này cung cấp cho đối tác hướng dẫn để cải thiện thời gian khởi động cho một số ứng dụng Thiết bị Android. Thời gian khởi động là một thành phần quan trọng ảnh hưởng đến hiệu suất của hệ thống người dùng phải đợi quá trình khởi động hoàn tất thì mới sử dụng được thiết bị. Đối với thiết bị chẳng hạn như ô tô khởi động nguội thường xuyên hơn, khởi động nhanh thời gian là vô cùng quan trọng (không ai thích phải chờ hàng chục giây chỉ để nhập một đích điều hướng).
Android 8.0 hỗ trợ một số cải tiến giúp giảm thời gian khởi động trên nhiều thành phần. Bảng sau đây tóm tắt các hiệu suất này mức tăng (được đo 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 |
|
Nhân hệ điều hành của thiết bị |
|
Dò I/O |
|
init.*.rc |
|
Ảnh động khởi động |
|
Chính sách SELinux | Lưu 0.2s vào bởi genfscon |
Trình tải khởi động Optimize
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 rất nhiều ghi nhật ký. (Trên các thiết bị Google Pixel, chúng tôi nhận thấy trình tải khởi động 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 việc lưu trữ thông tin khác vào bộ nhớ có cơ chế riêng để truy xuất.
- Để giải nén hạt nhân, 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 hạt nhân khác nhau có thể có cách tải và thời gian giải nén và một số lựa chọn có thể hiệu quả hơn các lựa chọn khác cho phần cứng cụ thể.
- Kiểm tra thời gian chờ không cần thiết để khởi động lại/vào chế độ đặc biệt và giảm thiểu chúng.
- Truyền thời gian khởi động dành cho trình tải khởi động vào nhân hệ điều hành dưới dạng lệnh cmdline.
- Kiểm tra xung nhịp CPU và cân nhắc việc song song hoá (yêu cầu hỗ trợ đa lõi) để tải kernel và khởi chạy I/O.
Tối ưu hoá hiệu quả I/O
Việc cải thiện hiệu quả I/O là yếu tố rất quan trọng để làm cho thời gian khởi động nhanh hơn và khả năng đọc mọi thứ không cần thiết đều nên bị trì hoãn 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
Nhân hệ điều hành Linux sẽ đọc trước khi tệp được đọc từ đầu hoặc khi các khối được đọc tuần tự, do đó cần phải tinh chỉnh trình lập lịch biểu I/O dành riêng cho việc khởi động (có tải công việc khác nhau) hơn các ứng dụng thông thường).
Những thiết bị hỗ trợ cập nhật (A/B) liền mạch được hưởng lợi rất nhiều từ hệ thống tệp dò 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 thông 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 hàm băm dm-verity bằng cách sử dụng cấu hình nhân kernel DM_VERITY_HASH_PREPLACE_MIN_SIZE (kích thước mặc định là 128).
- Để tăng độ ổn định của hệ thống tệp và hoạt động kiểm tra bắt buộc bị bỏ qua diễn ra trên 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 nhân (cũng được sử dụng bởi systrace):
trace_event=block,ext4 in BOARD_KERNEL_CMDLINE
Để phân tích quyền truy cập vào tệp cho từng tệp, hãy thực hiện các thay đổi sau đối với nhân (chỉ hạt nhân phát triển; không sử dụng trong hạt nhân 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 để 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 bảng phân tích 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à thường dành vài giây trong các giai đoạn khởi tạo khác nhau.
Chạy các tác vụ song song
Mặc dù khởi tạo Android hiện tại ít nhiều là một quy trình theo luồng, nhưng bạn vẫn có thể thực hiện song một số tác vụ.
- Thực thi các lệnh chậm trong dịch vụ tập lệnh shell và kết hợp dịch vụ đó sau bằng cách
đang 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
. - Xác định các thao tác chậm trong khởi tạo. Hệ thống ghi nhật ký 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 xét nhật ký này có thể cho thấy cơ hội cải thiện.
- Bắt đầu sớm các dịch vụ và bật các thiết bị ngoại vi ở đường dẫn quan trọng. Cho ví dụ: một số SOC yêu cầu phải 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ề trạng thái "wait for" dịch vụ" — đây thường là dấu hiệu cho thấy cần phải bắt đầu một dịch vụ phụ thuộc đầu tiên.
- Xoá mọi dịch vụ và lệnh không dùng đến trong init.*.rc. Bất kỳ giá trị nào không được sử dụng trong init giai đoạn đầu phải được trì hoãn để quá trình khởi động hoàn tất.
Lưu ý: Dịch vụ tài sản là một phần của quy trình khởi tạo, vì vậy, việc gọi
setproperty
trong quá trình khởi động có thể gây ra độ trễ dài nếu khởi động đang bận trong
các lệnh được tích hợp sẵn.
Sử dụng tính năng điều chỉnh bộ lập lịch
Sử dụng tính năng điều chỉnh bộ lập lịch biểu để 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ã hoá dựa trên tệp có thể khởi động zygote sớm hơn khi zygote bắt đầu trình kích hoạt (theo mặc định, zygote được khởi chạy tại lớp chính, muộn hơn nhiều so với hợp tử-bắt đầu). Khi thực hiện việc này, hãy nhớ cho phép zygote chạy trong tất cả CPU (như chế độ cài đặt cpuset không chính xác có thể buộc zygote chạy trong một số CPU cụ thể).
Tắt chế độ tiết kiệm điện
Trong khi khởi động thiết bị, chế độ cài đặt tiết kiệm điện cho các thành phần như UFS và/hoặc CPU Thống đốc có thể bị vô hiệu hoá.
Thận trọng: Bạn phải bật tính năng tiết kiệm điện trong chế độ sạc để đạt được hiệu quả cao.
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 quá trình khởi chạy không quan trọng
Quá trình khởi tạo không quan trọng như ZRAM có thể được trì hoãn đến boot_complete
.
on property:sys.boot_completed=1 # Enable ZRAM on boot_complete swapon_all /vendor/etc/fstab.${ro.hardware}
Ảnh động khi khởi động trong Optimize
Hãy sử dụng các mẹo sau để tối ưu hoá ảnh động khởi động.
Thiết lập 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 kết nối dữ liệu người dùng phân vù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 chậm trễ cho khởi động dịch vụ khởi động ảnh động.
Để ảnh động khởi động bắt đầu sớm, hãy chia liên kết 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 chạy kiểm tra, sau đó bắt đầu dịch vụ ảnh động khởi động và các phần phụ thuộc của dịch vụ này (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 chạy kiểm tra.
Hoạt ảnh khởi động sẽ khởi động nhanh hơn nhiều (và theo thời gian không đổi) bất kể sck.
Hoàn tất bước làm sạch
Sau khi nhận được tín hiệu thoát, hình động khởi động sẽ phát phần cuối cùng, thời lượng có thể làm chậm thời gian khởi động. Một hệ thống giúp khởi động nhanh chóng không cần thời gian dài để ẩn hiệu quả mọi cải tiến đã thực hiện. Bạn nên tạo nên vòng lặp lặp lại và phần kết thúc ngắn gọn.
Tối ưu hoá SELinux
Sử 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) sạch. Biểu thức chính quy có định dạng kém
có thể dẫn đến nhiều hao tổn khi phù hợp với 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ả/sys/devices
thư mục con chứa "abc", cho phép so khớp cho 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ỉ bật đối sánh cho/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ố phù hợp với tệp vào nhân hệ điều hành trong tệp nhị phân SELinux, nơi hạt nhân áp dụng chúng cho nhân hệ điều hành được tạo hệ thống tệp. Việc này cũng giúp sửa các tệp do nhân hệ điều hành bị gắn nhãn sai, tránh có thể xảy ra giữa các quy trình trong không gian người dùng cố gắng truy cập các tệp này trước khi việc gắn nhãn lại diễn ra.
Công cụ và phương thức
Sử dụng các công cụ sau để giúp bạn thu thập dữ liệu cho các mục tiêu tối ưu hoá.
Biểu đồ khởi động
Sơ đồ khởi động cung cấp bảng chi tiết về tải CPU và I/O của tất cả các quy trình cho toàn bộ hệ thống. Công cụ này không yêu cầu xây dựng lại hình ảnh hệ thống và có thể được dùng làm kiểm tra sự an toàn trước khi sử dụng systrace.
Cách bật biểu đồ khởi động:
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
để ngừng thu thập
dữ liệu mọi lúc.
bootchart.png
không tồn tại, hãy
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
(đặt tại~/Documents/bootchart/pybootchartgui.py
)
Systrace
Systrace cho phép thu thập cả dấu vết hạt nhân và dấu vết Android trong quá trình khởi động. Việc trực quan hoá systrace có thể giúp phân tích vấn đề cụ thể trong khởi động. (Tuy nhiên, để kiểm tra số liệu trung bình hoặc số liệu tích luỹ trong toàn bộ quá trình khởi động, sẽ dễ dàng hơn nếu trực tiếp xem dấu vết nhân hệ điều hành).
Cách bật systrace trong khi 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 đoạn mã 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 đoạn mã 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 (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.