Trang này cung cấp các mẹo giúp cải thiện thời gian khởi động.
Xoá biểu tượng gỡ lỗi khỏi mô-đun
Tương tự như cách xoá biểu tượng gỡ lỗi khỏi nhân trên thiết bị sản xuất, hãy nhớ xoá biểu tượng gỡ lỗi khỏi mô-đun. Việc xoá biểu tượng gỡ lỗi khỏi mô-đun giúp giảm thời gian khởi động bằng cách giảm các yếu tố sau:
- Thời gian cần thiết để đọc các tệp nhị phân từ bộ nhớ flash.
- Thời gian cần thiết để giải nén ramdisk.
- Thời gian cần thiết để tải các mô-đun.
Việc xoá biểu tượng gỡ lỗi khỏi mô-đun có thể giúp bạn tiết kiệm vài giây trong quá trình khởi động.
Tính năng xoá biểu tượng được bật theo mặc định trong bản dựng nền tảng Android, nhưng
để bật tính năng này một cách rõ ràng, hãy đặt
BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES trong cấu hình dành riêng cho thiết bị của bạn
trong device/vendor/device.
Sử dụng tính năng nén LZ4 cho nhân và ramdisk
Gzip tạo ra kết quả nén nhỏ hơn so với LZ4, nhưng LZ4 giải nén nhanh hơn Gzip. Đối với nhân và mô-đun, việc giảm kích thước bộ nhớ tuyệt đối khi sử dụng Gzip không đáng kể so với lợi ích về thời gian giải nén của LZ4.
Tính năng hỗ trợ nén ramdisk LZ4 đã được thêm vào bản dựng nền tảng Android thông qua BOARD_RAMDISK_USE_LZ4. Bạn có thể đặt tuỳ chọn này trong cấu hình dành riêng cho thiết bị. Bạn có thể đặt tính năng nén nhân thông qua kernel defconfig.
Việc chuyển sang LZ4 sẽ giúp thời gian khởi động nhanh hơn từ 500 mili giây đến 1000 mili giây.
Tránh ghi nhật ký quá mức trong trình điều khiển
Trong ARM64 và ARM32, các lệnh gọi hàm cách xa vị trí gọi hơn một khoảng cách cụ thể cần có bảng nhảy (gọi là bảng liên kết quy trình hoặc PLT) để có thể mã hoá địa chỉ nhảy đầy đủ. Vì các mô-đun được tải một cách linh hoạt, nên các bảng nhảy này cần được sửa trong quá trình tải mô-đun. Các lệnh gọi cần di chuyển được gọi là các mục di chuyển có phần bổ sung rõ ràng (hoặc RELA, viết tắt) trong định dạng ELF.
Nhân Linux thực hiện một số hoạt động tối ưu hoá kích thước bộ nhớ (chẳng hạn như tối ưu hoá kết quả tìm kiếm trong bộ nhớ cache) khi phân bổ PLT. Với cam kết ngược dòng
này,
lược đồ tối ưu hoá có độ phức tạp O(N^2), trong đó N là số lượng
RELA thuộc loại R_AARCH64_JUMP26 hoặc R_AARCH64_CALL26. Vì vậy, việc có ít RELA thuộc các loại này sẽ giúp giảm thời gian tải mô-đun.
Một mẫu mã hoá phổ biến làm tăng số lượng RELA R_AARCH64_CALL26 hoặc R_AARCH64_JUMP26 là ghi nhật ký quá mức trong trình điều khiển. Mỗi lệnh gọi đến printk() hoặc bất kỳ lược đồ ghi nhật ký nào khác thường thêm một mục RELA CALL26/JUMP26. Trong văn bản cam kết trong cam kết ngược dòng,
,hãy lưu ý rằng ngay cả khi tối ưu hoá, 6 mô-đun cũng mất khoảng 250 mili giây
để tải. Đó là vì 6 mô-đun đó là 6 mô-đun hàng đầu có
lượng nhật ký nhiều nhất.
Việc giảm ghi nhật ký có thể giúp bạn tiết kiệm khoảng 100 – 300 mili giây thời gian khởi động tuỳ thuộc vào mức độ ghi nhật ký hiện có.
Bật tính năng thăm dò không đồng bộ một cách có chọn lọc
Khi một mô-đun được tải, nếu thiết bị mà mô-đun đó hỗ trợ đã được điền sẵn từ DT (cây thiết bị) và được thêm vào lõi trình điều khiển, thì quá trình thăm dò thiết bị sẽ được thực hiện trong bối cảnh lệnh gọi module_init(). Khi quá trình thăm dò thiết bị được thực hiện trong bối cảnh module_init(), mô-đun sẽ không thể hoàn tất quá trình tải cho đến khi quá trình thăm dò hoàn tất. Vì quá trình tải mô-đun chủ yếu được tuần tự hoá, nên một thiết bị mất thời gian tương đối dài để thăm dò sẽ làm chậm thời gian khởi động.
Để tránh thời gian khởi động chậm hơn, hãy bật tính năng thăm dò không đồng bộ cho các mô-đun mất một khoảng thời gian để thăm dò thiết bị. Việc bật tính năng thăm dò không đồng bộ cho tất cả các mô-đun có thể không mang lại lợi ích vì thời gian cần thiết để phân nhánh một luồng và bắt đầu quá trình thăm dò có thể cao bằng thời gian cần thiết để thăm dò thiết bị.
Các thiết bị được kết nối thông qua một bus chậm như I2C, các thiết bị tải chương trình cơ sở trong hàm thăm dò và các thiết bị thực hiện nhiều hoạt động khởi tạo phần cứng có thể dẫn đến vấn đề về thời gian. Cách tốt nhất để xác định thời điểm này xảy ra là thu thập thời gian thăm dò cho mọi trình điều khiển và sắp xếp thời gian đó.
Để bật tính năng thăm dò không đồng bộ cho một mô-đun, bạn không chỉ cần
đặt cờ PROBE_PREFER_ASYNCHRONOUS
trong mã trình điều khiển. Đối với các mô-đun, bạn cũng cần thêm
module_name.async_probe=1 vào dòng lệnh nhân
hoặc truyền async_probe=1 làm tham số mô-đun khi tải mô-đun bằng
modprobe hoặc insmod.
Việc bật tính năng thăm dò không đồng bộ có thể giúp bạn tiết kiệm khoảng 100 – 500 mili giây thời gian khởi động tuỳ thuộc vào phần cứng/trình điều khiển của bạn.
Thăm dò trình điều khiển CPUfreq càng sớm càng tốt
Trình điều khiển CPUfreq của bạn thăm dò càng sớm thì bạn càng có thể điều chỉnh tần số CPU lên mức tối đa (hoặc một số mức tối đa bị giới hạn về nhiệt) trong quá trình khởi động. CPU càng nhanh thì quá trình khởi động càng nhanh. Nguyên tắc này cũng áp dụng cho các trình điều khiển devfreq kiểm soát tần số DRAM, bộ nhớ và kết nối.
Với các mô-đun, thứ tự tải có thể phụ thuộc vào cấp initcall và thứ tự biên dịch hoặc liên kết của các trình điều khiển. Sử dụng bí danh MODULE_SOFTDEP() để đảm bảo trình điều khiển cpufreq nằm trong số ít mô-đun đầu tiên được tải.
Ngoài việc tải mô-đun sớm, bạn cũng cần đảm bảo tất cả các phần phụ thuộc để thăm dò trình điều khiển CPUfreq cũng đã thăm dò. Ví dụ: nếu bạn cần một đồng hồ hoặc bộ điều chỉnh để kiểm soát tần số của CPU, hãy đảm bảo các đồng hồ hoặc bộ điều chỉnh đó được thăm dò trước. Hoặc bạn có thể cần tải các trình điều khiển nhiệt trước trình điều khiển CPUfreq nếu CPU của bạn có thể quá nóng trong quá trình khởi động. Vì vậy, hãy làm những gì bạn có thể để đảm bảo trình điều khiển CPUfreq và devfreq có liên quan thăm dò càng sớm càng tốt.
Mức tiết kiệm khi thăm dò trình điều khiển CPUfreq sớm có thể rất nhỏ đến rất lớn tuỳ thuộc vào thời điểm bạn có thể thăm dò các trình điều khiển này và tần số mà trình tải khởi động để CPU ở đó.
Di chuyển mô-đun sang giai đoạn khởi tạo thứ hai, phân vùng nhà cung cấp hoặc vendor_dlkm
Vì quá trình khởi tạo giai đoạn đầu tiên được tuần tự hoá, nên không có nhiều cơ hội để song song hoá quá trình khởi động. Nếu không cần mô-đun để hoàn tất quá trình khởi tạo giai đoạn đầu tiên, hãy di chuyển mô-đun sang giai đoạn khởi tạo thứ hai bằng cách đặt mô-đun đó vào phân vùng nhà cung cấp hoặc vendor_dlkm.
Quá trình khởi tạo giai đoạn đầu tiên không yêu cầu thăm dò một số thiết bị để chuyển sang giai đoạn khởi tạo thứ hai. Bạn chỉ cần các tính năng lưu trữ flash và bảng điều khiển cho quy trình khởi động thông thường.
Tải các trình điều khiển thiết yếu sau:
watchdogresetcpufreq
Đối với chế độ khôi phục và không gian người dùng fastbootd, quá trình khởi tạo giai đoạn đầu tiên yêu cầu thăm dò thêm nhiều thiết bị (chẳng hạn như USB) và màn hình. Giữ một bản sao của các mô-đun này trong ramdisk giai đoạn đầu tiên và trong phân vùng nhà cung cấp hoặc vendor_dlkm. Điều này cho phép các mô-đun được tải trong quá trình khởi tạo giai đoạn đầu tiên cho quy trình khởi động khôi phục hoặc fastbootd. Tuy nhiên, đừng tải các mô-đun chế độ khôi phục trong quá trình khởi tạo giai đoạn đầu tiên trong quy trình khởi động thông thường. Bạn có thể hoãn các mô-đun chế độ khôi phục sang giai đoạn khởi tạo thứ hai để giảm thời gian khởi động. Tất cả các mô-đun khác không cần thiết trong quá trình khởi tạo giai đoạn đầu tiên sẽ được chuyển sang phân vùng nhà cung cấp hoặc vendor_dlkm.
Với danh sách các thiết bị lá (ví dụ: UFS hoặc nối tiếp),
dev needs.sh
tập lệnh sẽ tìm tất cả các trình điều khiển, thiết bị và mô-đun cần thiết cho các phần phụ thuộc hoặc
nhà cung cấp (ví dụ: đồng hồ, bộ điều chỉnh hoặc gpio) để thăm dò.
Việc di chuyển các mô-đun sang giai đoạn khởi tạo thứ hai sẽ giảm thời gian khởi động theo những cách sau:
- Giảm kích thước ramdisk.
- Điều này giúp đọc bộ nhớ flash nhanh hơn khi trình tải khởi động tải ramdisk (bước khởi động được tuần tự hoá).
- Điều này giúp tốc độ giải nén nhanh hơn khi nhân giải nén ramdisk (bước khởi động được tuần tự hoá).
- Quá trình khởi tạo giai đoạn thứ hai hoạt động song song, ẩn thời gian tải mô-đun với công việc được thực hiện trong quá trình khởi tạo giai đoạn thứ hai.
Việc di chuyển các mô-đun sang giai đoạn thứ hai có thể giúp bạn tiết kiệm 500 – 1000 mili giây thời gian khởi động tuỳ thuộc vào số lượng mô-đun bạn có thể di chuyển sang giai đoạn khởi tạo thứ hai.
Hậu cần tải mô-đun
Bản dựng Android mới nhất có các cấu hình bo mạch kiểm soát mô-đun nào sao chép sang từng giai đoạn và mô-đun nào tải. Phần này tập trung vào tập hợp con sau:
BOARD_VENDOR_RAMDISK_KERNEL_MODULES. Danh sách các mô-đun cần sao chép vào ramdisk.BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD. Danh sách các mô-đun cần tải trong quá trình khởi tạo giai đoạn đầu tiên.BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD. Danh sách các mô-đun cần tải khi bạn chọn khôi phục hoặcfastbootdtừ ramdisk.BOARD_VENDOR_KERNEL_MODULES. Danh sách các mô-đun cần sao chép vào phân vùng nhà cung cấp hoặcvendor_dlkmtại thư mục/vendor/lib/modules/.BOARD_VENDOR_KERNEL_MODULES_LOAD. Danh sách các mô-đun cần tải trong quá trình khởi tạo giai đoạn thứ hai.
Các mô-đun khởi động và khôi phục trong ramdisk cũng phải được sao chép vào phân vùng nhà cung cấp hoặc vendor_dlkm tại /vendor/lib/modules. Việc sao chép các mô-đun này vào phân vùng nhà cung cấp đảm bảo các mô-đun không bị ẩn trong quá trình khởi tạo giai đoạn thứ hai, điều này hữu ích cho việc gỡ lỗi và thu thập modinfo cho báo cáo lỗi.
Việc sao chép sẽ tốn ít dung lượng trên phân vùng nhà cung cấp hoặc vendor_dlkm miễn là tập hợp mô-đun khởi động được giảm thiểu. Đảm bảo rằng tệp modules.list của nhà cung cấp có danh sách các mô-đun đã lọc trong /vendor/lib/modules.
Danh sách đã lọc đảm bảo thời gian khởi động không bị ảnh hưởng bởi việc tải lại các mô-đun (đây là một quy trình tốn kém).
Đảm bảo các mô-đun chế độ khôi phục tải dưới dạng một nhóm. Bạn có thể tải các mô-đun chế độ khôi phục ở chế độ khôi phục hoặc ở đầu quá trình khởi tạo giai đoạn thứ hai trong mỗi quy trình khởi động.
Bạn có thể sử dụng tệp Board.Config.mk của thiết bị để thực hiện các thao tác này như trong ví dụ sau:
# All kernel modules
KERNEL_MODULES := $(wildcard $(KERNEL_MODULE_DIR)/*.ko)
KERNEL_MODULES_LOAD := $(strip $(shell cat $(KERNEL_MODULE_DIR)/modules.load)
# First stage ramdisk modules
BOOT_KERNEL_MODULES_FILTER := $(foreach m,$(BOOT_KERNEL_MODULES),%/$(m))
# Recovery ramdisk modules
RECOVERY_KERNEL_MODULES_FILTER := $(foreach m,$(RECOVERY_KERNEL_MODULES),%/$(m))
BOARD_VENDOR_RAMDISK_KERNEL_MODULES += \
$(filter $(BOOT_KERNEL_MODULES_FILTER) \
$(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))
# ALL modules land in /vendor/lib/modules so they could be rmmod/insmod'd,
# and modules.list actually limits us to the ones we intend to load.
BOARD_VENDOR_KERNEL_MODULES := $(KERNEL_MODULES)
# To limit /vendor/lib/modules to just the ones loaded, use:
# BOARD_VENDOR_KERNEL_MODULES := $(filter-out \
# $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES))
# Group set of /vendor/lib/modules loading order to recovery modules first,
# then remainder, subtracting both recovery and boot modules which are loaded
# already.
BOARD_VENDOR_KERNEL_MODULES_LOAD := \
$(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
$(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))
BOARD_VENDOR_KERNEL_MODULES_LOAD += \
$(filter-out $(BOOT_KERNEL_MODULES_FILTER) \
$(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))
# NB: Load order governed by modules.load and not by $(BOOT_KERNEL_MODULES)
BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD := \
$(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))
# Group set of /vendor/lib/modules loading order to boot modules first,
# then the remainder of recovery modules.
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD := \
$(filter $(BOOT_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD))
BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD += \
$(filter-out $(BOOT_KERNEL_MODULES_FILTER), \
$(filter $(RECOVERY_KERNEL_MODULES_FILTER),$(KERNEL_MODULES_LOAD)))
Ví dụ này trình bày một tập hợp con BOOT_KERNEL_MODULES và RECOVERY_KERNEL_MODULES dễ quản lý hơn để được chỉ định cục bộ trong các tệp cấu hình bo mạch. Tập lệnh trước đó sẽ tìm và điền từng mô-đun trong tập hợp con từ các mô-đun nhân có sẵn đã chọn, để lại các mô-đun còn lại cho quá trình khởi tạo giai đoạn thứ hai.
Đối với quá trình khởi tạo giai đoạn thứ hai, bạn nên chạy quá trình tải mô-đun dưới dạng một dịch vụ để quá trình này không chặn quy trình khởi động. Sử dụng tập lệnh shell để quản lý quá trình tải mô-đun để các hoạt động hậu cần khác, chẳng hạn như xử lý và giảm thiểu lỗi hoặc hoàn tất quá trình tải mô-đun, có thể được báo cáo lại (hoặc bỏ qua) nếu cần.
Bạn có thể bỏ qua lỗi tải mô-đun gỡ lỗi không có trên bản dựng người dùng.
Để bỏ qua lỗi này, hãy đặt thuộc tính vendor.device.modules.ready để kích hoạt các giai đoạn sau của quy trình khởi động tập lệnh init rc để tiếp tục đến màn hình khởi chạy. Tham khảo tập lệnh ví dụ sau, nếu bạn có mã sau
trong /vendor/etc/init.insmod.sh:
#!/vendor/bin/sh
. . .
if [ $# -eq 1 ]; then
cfg_file=$1
else
# Set property even if there is no insmod config
# to unblock early-boot trigger
setprop vendor.common.modules.ready
setprop vendor.device.modules.ready
exit 1
fi
if [ -f $cfg_file ]; then
while IFS="|" read -r action arg
do
case $action in
"insmod") insmod $arg ;;
"setprop") setprop $arg 1 ;;
"enable") echo 1 > $arg ;;
"modprobe") modprobe -a -d /vendor/lib/modules $arg ;;
. . .
esac
done < $cfg_file
fi
Trong tệp rc phần cứng, bạn có thể chỉ định dịch vụ one shot bằng:
service insmod-sh /vendor/etc/init.insmod.sh /vendor/etc/init.insmod.<hw>.cfg
class main
user root
group root system
Disabled
oneshot
Bạn có thể thực hiện các hoạt động tối ưu hoá bổ sung sau khi các mô-đun chuyển từ giai đoạn đầu tiên sang giai đoạn thứ hai. Bạn có thể sử dụng tính năng danh sách chặn modprobe để chia quy trình khởi động giai đoạn thứ hai để bao gồm quá trình tải mô-đun bị hoãn của các mô-đun không thiết yếu. Bạn có thể hoãn quá trình tải các mô-đun chỉ được sử dụng bởi một HAL cụ thể để chỉ tải các mô-đun khi HAL được khởi động.
Để cải thiện thời gian khởi động rõ ràng, bạn có thể chọn cụ thể các mô-đun trong dịch vụ tải mô-đun có lợi hơn cho việc tải sau màn hình khởi chạy. Ví dụ: bạn có thể tải muộn một cách rõ ràng các mô-đun cho bộ giải mã video hoặc Wi-Fi sau khi quy trình khởi động ban đầu đã được xoá (sys.boot_complete tín hiệu thuộc tính Android, ví dụ). Đảm bảo HAL cho các mô-đun tải muộn chặn đủ lâu khi không có trình điều khiển nhân.
Ngoài ra, bạn có thể sử dụng lệnh wait<file>[<timeout>] của init trong tập lệnh rc quy trình khởi động để đợi các mục sysfs đã chọn cho biết các mô-đun trình điều khiển đã hoàn tất các thao tác thăm dò. Ví dụ về điều này là đợi trình điều khiển màn hình hoàn tất quá trình tải ở chế độ nền của quá trình khôi phục hoặc fastbootd, trước khi trình bày đồ hoạ trình đơn.
Khởi tạo tần số CPU thành một giá trị hợp lý trong trình tải khởi động
Không phải tất cả SoC/sản phẩm đều có thể khởi động CPU ở tần số cao nhất do các vấn đề về nhiệt hoặc nguồn trong quá trình kiểm thử vòng lặp khởi động. Tuy nhiên, hãy đảm bảo trình tải khởi động đặt tần số của tất cả các CPU trực tuyến ở mức cao nhất có thể một cách an toàn cho SoC hoặc sản phẩm. Điều này rất quan trọng vì với nhân mô-đun đầy đủ, quá trình giải nén ramdisk ban đầu sẽ diễn ra trước khi trình điều khiển CPUfreq có thể được tải. Vì vậy, nếu CPU được trình tải khởi động để ở đầu dưới của tần số, thì thời gian giải nén ramdisk có thể lâu hơn nhân được biên dịch tĩnh (sau khi điều chỉnh cho sự khác biệt về kích thước ramdisk) vì tần số CPU sẽ rất thấp khi thực hiện công việc chuyên sâu về CPU (giải nén). Điều tương tự cũng áp dụng cho tần số bộ nhớ và kết nối.
Khởi tạo tần số CPU của các CPU lớn trong trình tải khởi động
Trước khi trình điều khiển CPUfreq được tải, nhân không biết tần số CPU và không điều chỉnh dung lượng lập lịch CPU cho tần số hiện tại. Nhân có thể di chuyển các luồng sang CPU lớn nếu mức tải đủ cao trên CPU nhỏ.
Đảm bảo các CPU lớn có hiệu suất ít nhất bằng các CPU nhỏ cho tần số mà trình tải khởi động để các CPU này ở đó. Ví dụ: nếu CPU lớn có hiệu suất gấp đôi CPU nhỏ cho cùng một tần số, nhưng trình tải khởi động đặt tần số của CPU nhỏ thành 1, 5 GHz và tần số của CPU lớn thành 300 MHz, thì hiệu suất khởi động sẽ giảm nếu nhân di chuyển một luồng sang CPU lớn. Trong ví dụ này, nếu an toàn khi khởi động CPU lớn ở 750 MHz, bạn nên làm như vậy ngay cả khi bạn không có ý định sử dụng rõ ràng.
Trình điều khiển không được tải chương trình cơ sở trong quá trình khởi tạo giai đoạn đầu tiên
Có thể có một số trường hợp không thể tránh khỏi khi cần tải chương trình cơ sở trong quá trình khởi tạo giai đoạn đầu tiên. Nhưng nói chung, trình điều khiển không được tải bất kỳ chương trình cơ sở nào trong quá trình khởi tạo giai đoạn đầu tiên, đặc biệt là trong bối cảnh thăm dò thiết bị. Việc tải chương trình cơ sở trong quá trình khởi tạo giai đoạn đầu tiên khiến toàn bộ quá trình khởi động bị dừng nếu chương trình cơ sở không có trong ramdisk giai đoạn đầu tiên. Và ngay cả khi chương trình cơ sở có trong ramdisk giai đoạn đầu tiên, chương trình cơ sở này vẫn gây ra sự chậm trễ không cần thiết.