Na tej stronie znajdziesz wskazówki, które pomogą Ci skrócić czas uruchamiania.
Usuń symbole debugowania z modułów
Podobnie jak w przypadku symboli debugowania usuwanych z jądra na urządzeniu produkcyjnym, musisz też usunąć symbole debugowania z modułów. Usuwanie symboli debugowania z modułów skraca czas uruchamiania, ponieważ zmniejsza:
- Czas potrzebny na odczyt plików binarnych we Flashu.
- Czas potrzebny na rozpakowanie pliku ramdisk.
- Czas potrzebny na załadowanie modułów.
Usunięcie symboli debugowania z modułów może oszczędzić kilka sekund podczas uruchamiania.
Usuwanie symboli jest domyślnie włączone w kompilacji platformy Android, ale aby włączyć je wyraźnie, ustaw parametr BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES
w konfiguracji dotyczącej konkretnego urządzenia w folderze device/vendor/device.
Kompresja LZ4 dla jądra i ramdisk
Gzip generuje mniejsze skompresowane dane wyjściowe niż LZ4, ale LZ4 kompresuje się szybciej niż Gzip. W przypadku jądra i modułów bezwzględna pamięć masowa rozmiar pliku z programu Gzip nie jest tak istotny w porównaniu korzyści czasu dekompresji LZ4.
Do tworzenia na platformie Android dodano obsługę kompresji LZ4 dla dysku RAM (BOARD_RAMDISK_USE_LZ4
). Możesz ją skonfigurować w konfiguracji dotyczącej konkretnego urządzenia. Kompresję jądra można ustawić za pomocą defconfig jądra.
Przejście na LZ4 powinno przyspieszyć rozruch od 500 do 1000 ms.
Unikaj nadmiernego zapisywania sterowników
W procesorach ARM64 i ARM32 wywołania funkcji wykraczające poza konkretną odległość od witryna do rozmów wymaga tabeli łączenia procedur (PLT). w celu zakodowania pełnego adresu skoku. Ponieważ moduły są ładowane dynamicznie, te tabele przekierowań muszą być naprawiane podczas wczytywania modułów. Połączenia, które wymagają relokacja są nazywane wpisami relokacji z jawnymi dodatkami (w skrócie RELA) w formacie ELF.
Podczas przydzielania PLT jądro Linuxa wykonuje optymalizację rozmiaru pamięci (np. optymalizację trafień do pamięci podręcznej). W przypadku tego elementu nadrzędnego
zatwierdzenia,
schemat optymalizacji ma złożoność O(N^2)
, gdzie N
to liczba
RELA typu R_AARCH64_JUMP26
lub R_AARCH64_CALL26
. Mniej takich treści
pomagają skrócić czas wczytywania modułów.
Jednym z częstych wzorców kodowania, który zwiększa liczbę R_AARCH64_CALL26
lub R_AARCH64_JUMP26
RELA, jest nadmierne rejestrowanie w sterowniku. Każde wywołanie funkcji printk()
lub dowolnego innego schematu rejestrowania zwykle powoduje dodanie wpisu CALL26
/JUMP26
RELA. W tekście zatwierdzenia w elemencie nadrzędnym
zatwierdzenia,
Nawet po optymalizacji 6 modułów zajmuje ok. 250 ms.
ponieważ wszystkie te 6 modułów było 6 pierwszymi modułami z
z największym zapisywaniem danych.
Zmniejszenie ilości logowania może pozwolić zaoszczędzić 100–300 ms czasu uruchamiania w zależności od tego, jak nadmierne jest obecne logowanie.
Włącz sondowanie asynchroniczne, selektywnie
Gdy wczytany moduł obsługuje urządzenie, które zostało już wypełnione z poziomu DT (drzewo urządzenia) i dodane do rdzenia sterownika, wówczas badanie urządzenia jest wykonywane w kontekście wywołania module_init()
. Gdy module_init()
przeprowadza skanowanie urządzenia, moduł nie może zakończyć wczytywania, dopóki skanowanie nie zostanie ukończone. Ponieważ wczytywanie modułów jest w większości serializowane, urządzenie, które potrzebuje stosunkowo dużo czasu na przeprowadzenie sondowania, wydłuża czas uruchamiania.
Aby uniknąć wydłużonego czasu uruchamiania, włącz asynchroniczne sprawdzanie w przypadku modułów, które potrzebują więcej czasu na sprawdzenie urządzeń. Włączanie asynchronicznego sondowania dla wszystkich modułów może nie być korzystne, ponieważ czas potrzebny na rozdzielenie wątku i uruchomienie sondowania może być taki sam jak czas potrzebny na sondowanie urządzenia.
urządzeń połączonych za pośrednictwem wolnej magistrali, np. I2C, oraz urządzeń, które korzystają z sieci wczytywane oprogramowanie układowe w funkcji sondy oraz urządzenia wykonujące wiele zadań sprzętowych. może to spowodować problem z czasem. Najlepszym sposobem na określenie, kiedy to nastąpi, jest zebranie czasu próbkowania dla każdego sterownika i posortowanie go.
Aby włączyć sondowanie asynchroniczne w module, nie wystarczy jedynie
ustaw PROBE_PREFER_ASYNCHRONOUS
w kodzie kierowcy. W przypadku modułów musisz też dodać module_name.async_probe=1
do wiersza poleceń jądra lub przekazać async_probe=1
jako parametr modułu podczas wczytywania modułu za pomocą modprobe
lub insmod
.
Włączenie sondowania asynchronicznego pozwala zaoszczędzić około 100–500 ms przy uruchamianiu w zależności od używanego sprzętu i sterowników.
Jak najszybciej sprawdź sterownik CPUfreq.
Im wcześniej sonduje sterownik CPUfreq, tym szybciej można skalować procesor
do maksymalnej (lub częściowej lub ograniczonej termicznej) podczas uruchamiania.
Im szybszy procesor, tym szybsze uruchamianie. Ta zasada dotyczy również: devfreq
sterowniki, które kontrolują częstotliwość DRAM, pamięci i połączeń międzysieciowych.
W przypadku modułów kolejność obciążeń może zależeć od poziomu initcall
skompilować lub połączyć sterowniki. Użyj aliasu MODULE_SOFTDEP()
, aby
sprawdź, czy sterownik cpufreq
jest jednym z pierwszych modułów do wczytania.
Oprócz wczesnego załadowania modułu należy również upewnić się, że wszystkie zależności umożliwiające sondowanie sterownika CPUfreq. Jeśli na przykład potrzebujesz uchwytu zegara lub regulatora do sterowania częstotliwością procesora, upewnij się, najpierw są sprawdzani. Może też być konieczne załadowanie sterowników termicznych przed sterownikiem CPUfreq, jeśli istnieje możliwość, że procesory staną się zbyt gorące podczas uruchamiania. Zrób, co w Twojej mocy, aby interfejsy CPUfreq i devfreq sprawdzały się jak najwcześniej.
Oszczędności wynikające z wczesnego sondowania sterownika CPUfreq mogą być od bardzo niewielkich do bardzo dużych. w zależności od tego, jak szybko i z jaką częstotliwością chcesz je zbadać program rozruchowy pozostawia procesory w tym trybie.
Przenoszenie modułów do drugiej fazy inicjalizacji, partycji dostawcy lub partycji vendor_dlkm
Ponieważ proces inicjalizacji pierwszego etapu jest serializowany, nie ma wielu możliwości równoległego uruchamiania procesu uruchamiania. Jeśli moduł nie jest potrzebny do
do zakończenia pierwszego etapu, przenieś moduł do inicjowania drugiego etapu, umieszczając go
w partycji dostawcy lub vendor_dlkm
.
Inicjowanie pierwszego etapu nie wymaga sondowania kilku urządzeń, aby przejść do drugiego etapu init. Potrzebne są tylko możliwości konsoli i pamięci flash podczas uruchamiania.
Załaduj te niezbędne sterowniki:
watchdog
reset
cpufreq
W przypadku trybu odzyskiwania i przestrzeni użytkownika fastbootd
pierwsza faza inicjalizacji wymaga więcej urządzeń do sondowania (np. USB) i wyświetlacza. Zachowaj kopie tych modułów w
na dysku RAM pierwszego etapu oraz w partycji dostawcy lub vendor_dlkm
. Dzięki temu można je załadować na pierwszym etapie inicjalizacji w celu odzyskiwania lub fastbootd
uruchamiania. Nie wczytuj jednak modułów trybu przywracania w ramach pierwszego etapu inicjalizacji podczas normalnego procesu uruchamiania. Aby skrócić czas uruchamiania, moduły trybu odzyskiwania można odroczyć do drugiej fazy inicjalizacji. Wszystkie inne moduły, które nie są potrzebne na etapie inicjalizacji 1, należy przenieść do partycji dostawcy lub vendor_dlkm
.
Na podstawie listy urządzeń końcowych (np. UFS lub seryjnych) skrypt dev needs.sh
odnajduje wszystkie sterowniki, urządzenia i moduły potrzebne do sprawdzenia zależności lub dostawców (np. zegarów, regulatorów lub gpio
).
Przeniesienie modułów do drugiej fazy inicjalizacji skraca czas uruchamiania w następujący sposób:
- Zmniejszenie rozmiaru dysku RAM.
- Dzięki temu wczytywanie z pamięci flash jest szybsze, gdy bootloader wczytuje ramdysk (seryalizacja kroku rozruchu).
- Przyspiesza to dekompresję, gdy jądro dekompresuje ramdisk (serializowany krok uruchamiania).
- Drugi etap inicjalizacji działa równolegle, co ukrywa czas wczytywania modułu dzięki pracy wykonywanej na drugim etapie inicjalizacji.
Przeniesienie modułów do drugiego etapu może zaoszczędzić 500–1000 ms czasu uruchamiania w zależności od urządzenia. ile modułów możesz przenieść do drugiego etapu.
Moduł wczytujący informacje związane z logistyką
Najnowsza kompilacja Androida zawiera konfiguracje płyt, które określają, które są kopiowane do poszczególnych etapów oraz które się ładują. Ta sekcja skupia się w następującym podzbiorze:
BOARD_VENDOR_RAMDISK_KERNEL_MODULES
. Lista modułów do skopiowania na dysk RAM.BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD
Ta lista modułów do załadowania w pierwszym etapie.BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD
. Lista modułów, które mają być wczytane, gdy z dysku RAM wybrano opcję odzyskiwania lubfastbootd
.BOARD_VENDOR_KERNEL_MODULES
Ta lista modułów do skopiowania do dostawcy lub partycjivendor_dlkm
w katalogu/vendor/lib/modules/
.BOARD_VENDOR_KERNEL_MODULES_LOAD
Ta lista modułów do załadowania do rozpoczęcia drugiego etapu.
Moduł uruchamiania i moduł odzyskiwania na dysku RAM muszą też zostać skopiowane na partycję dostawcy lub vendor_dlkm
w miejscu /vendor/lib/modules
. Skopiowanie tych modułów do
partycja dostawcy zapewnia, że moduły nie są niewidoczne podczas drugiego etapu,
co przydaje się przy debugowaniu i zbieraniu danych modinfo
na potrzeby raportów o błędach.
Duplikaty powinny zajmować minimalną ilość miejsca na partycji dostawcy lub vendor_dlkm
, o ile zestaw modułów rozruchowych jest zminimalizowany. Upewnij się, że
Plik modules.list
zawiera przefiltrowaną listę modułów w folderze /vendor/lib/modules
.
Przefiltrowana lista sprawia, że wczytywanie modułów nie wpływa na czas uruchamiania
(co jest kosztowne).
Upewnij się, że moduły trybu odzyskiwania wczytują się jako grupa. Wczytuję moduły trybu przywracania można wykonać w trybie przywracania lub na początku drugiego etapu i inicj przy każdym rozruchu.
Za pomocą plików Board.Config.mk
na urządzeniu możesz wykonywać te działania w takiej postaci, w jakiej zostały wykryte
w tym przykładzie:
# 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)))
Ten przykład pokazuje łatwiejszy w zarządzaniu podzbiór elementów BOOT_KERNEL_MODULES
i RECOVERY_KERNEL_MODULES
, który można określić lokalnie w plikach konfiguracji tablicy. Poprzedni skrypt znajduje i wypełnia każdy z modułów podzbioru
wybranych dostępnych modułów jądra, a pozostałe
zainicjowanie etapu.
Na potrzeby inicjowania drugiego etapu zalecamy uruchomienie modułu jako usługi, dzięki czemu nie blokuje rozruchu. Zarządzanie wczytywaniem modułu za pomocą skryptu powłoki tak aby inne usługi logistyczne, np. obsługa błędów i ich łagodzenie, lub obciążenie modułów realizacji i można je zgłosić ponownie (lub zignorować), w razie potrzeby.
Możesz zignorować błąd wczytywania modułu debugowania, który nie występuje w kompilacji użytkownika.
Aby zignorować ten błąd, ustaw we właściwości vendor.device.modules.ready
wartość
aktywuj późniejsze etapy procesu rozruchowego skryptu init rc
, aby kontynuować uruchomienie
ekranu. Jeśli w pliku /vendor/etc/init.insmod.sh
masz ten kod:
#!/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
W pliku rc sprzętu usługa one shot
może być określona za pomocą:
service insmod-sh /vendor/etc/init.insmod.sh /vendor/etc/init.insmod.<hw>.cfg
class main
user root
group root system
Disabled
oneshot
Dodatkowe optymalizacje można wprowadzić po przeniesieniu modułów z pierwszego etapu na drugi. Za pomocą funkcji modprobe blocklist możesz podzielić drugi etap procesu uruchamiania, aby uwzględnić opóźnione wczytywanie nieistotnych modułów. Wczytywanie modułów używanych wyłącznie przez konkretny interfejs HAL można opóźnić, aby wczytać je dopiero po uruchomieniu interfejsu.
Aby skrócić czas uruchamiania, możesz w usłudze ładowania modułów wybrać konkretne moduły, które są bardziej odpowiednie do ładowania po ekranie uruchamiania. Możesz na przykład wyraźnie opóźnić wczytywanie modułów o
z dekodera wideo lub sieci Wi-Fi po wyczyszczeniu procesu inicjującego.
(sys.boot_complete
sygnał usługi na Androidzie). Upewnij się, że bloki HAL dla ładowanych późno modułów są blokowane na tyle długo, na ile to możliwe, gdy nie ma sterowników jądra.
Możesz też użyć w skrypcie uruchamiania komend wait<file>[<timeout>]
, aby poczekać na wybrane wpisy sysfs
, które pokazują, że moduły sterownika zakończyły operacje sondowania. Przykładem może być oczekiwanie na zakończenie wczytywania sterownika wyświetlacza w tle podczas odzyskiwania lub fastbootd
, zanim zostanie wyświetlona grafika menu.
Inicjowanie częstotliwości procesora do rozsądnej wartości w programie rozruchowym
Nie wszystkie układy SOC/produkty mogą uruchamiać procesor z najwyższą częstotliwością z powodu problemów z termostatem lub zasilaniem podczas testów pętli rozruchowej. Upewnij się jednak, że bootloader ustawia częstotliwość wszystkich onlineowych procesorów tak wysoko, jak to jest bezpieczne dla SoC lub produktu. To bardzo ważne, ponieważ dzięki pełnemu jeśli jest to modułowe jądro, inicjowana dekompresja pamięci RAM jest przeprowadzana przed można załadować sterownik. Jeśli więc bootloader pozostawi procesor przy niższej częstotliwości, czas dekompresji dysku RAM może być dłuższy niż w przypadku skompilowanego statycznie jądra (po uwzględnieniu różnicy w rozmiarze dysku RAM), ponieważ częstotliwość procesora byłaby bardzo niska podczas wykonywania prac intensywnie obciążających procesor (dekompresji). To samo dotyczy pamięci i częstotliwości połączeń.
Inicjowanie częstotliwości procesora dużych procesorów w programie rozruchowym
Zanim wczytany zostanie sterownik CPUfreq
, jądro nie zna częstotliwości procesora i nie dostosowuje pojemności procesora do bieżącej częstotliwości. Jeśli obciążenie procesora o małej mocy jest wystarczająco duże, jądro może przenieść wątki na procesor o dużej mocy.
Upewnij się, że duże procesory mają co najmniej taką samą wydajność jak małe procesory w przypadku częstotliwości, z jaką działają. Jeśli na przykład duży procesor ma dwukrotnie większą wydajność niż mały procesor przy tej samej częstotliwości, ale bootloader ustawia częstotliwość małego procesora na 1,5 GHz, a dużego na 300 MHz, to wydajność rozruchu spadnie, jeśli jądro przeniesie wątek na duży procesor. W tym przykładzie, jeśli można bezpiecznie uruchomić duże urządzenie procesora o częstotliwości 750 MHz, należy go używać, nawet jeśli nie zamierzasz go bezpośrednio używać.
Sterowniki nie powinny ładować oprogramowania układowego w trakcie inicjowania pierwszego etapu
Mogą wystąpić nieuniknione sytuacje, w których oprogramowanie układowe musi zostać najpierw załadowane. zainicjowanie etapu. Z reguły sterowniki nie powinny ładować oprogramowania w pierwszym etapie zwłaszcza w kontekście sondy urządzenia. Ładowanie oprogramowania układowego w ramach pierwszej fazy inicjalizacji powoduje zatrzymanie całego procesu uruchamiania, jeśli oprogramowanie nie jest dostępne na dysku RAM pierwszej fazy. Nawet jeśli oprogramowanie układowe jest dostępne na pierwszym etapie, powoduje niepotrzebne opóźnienie.