Cette page fournit des conseils pour améliorer le temps de démarrage.
Supprimer les symboles de débogage des modules
De la même manière que les symboles de débogage sont supprimés du noyau sur un environnement de production assurez-vous de supprimer également les symboles de débogage des modules. Suppression du débogage les symboles des modules améliorent le temps de démarrage en réduisant les éléments suivants:
- Temps nécessaire pour lire les binaires à partir de Flash.
- Temps nécessaire pour décompresser le ramdisk.
- Temps nécessaire pour charger les modules.
Supprimer le symbole de débogage des modules peut économiser plusieurs secondes au démarrage.
Le masquage de symboles est activé par défaut dans le build de la plate-forme Android, mais pour l'activer explicitement, définissez BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES
dans votre configuration spécifique à l'appareil sous device/vendor/device.
Utiliser la compression LZ4 pour le noyau et le ramdisk
Gzip génère une sortie compressée plus petite que LZ4, mais LZ4 décompresse plus rapidement que Gzip. Pour le noyau et les modules, la réduction absolue de la taille de stockage obtenue avec Gzip n'est pas aussi importante que l'avantage de temps de décompression de LZ4.
La plate-forme Android prend désormais en charge la compression ramdisk LZ4.
via BOARD_RAMDISK_USE_LZ4
. Vous pouvez définir cette option dans votre
spécifique à l'appareil. La compression du noyau peut être définie via defconfig du noyau.
En passant à LZ4, vous devriez pouvoir démarrer de 500 ms à 1 000 ms plus rapidement.
Éviter l'enregistrement excessif dans vos pilotes
Sous ARM64 et ARM32, les appels de fonction situés à plus d'une distance spécifique du site d'appel nécessitent une table de saut (appelée table de liaison de procédure ou PLT) pour pouvoir encoder l'adresse de saut complète. Les modules étant chargés dynamiquement, ces tables de renvoi doivent être corrigées lors du chargement du module. Les appels qui ont besoin "relocation" sont des entrées qui comportent des ajouts explicites (ou RELA, en abrégé) au format ELF.
Le noyau Linux effectue une certaine optimisation de la taille de la mémoire (telle que l'optimisation des taux de réussite de la mise en cache) lors de l'allocation de la PLT. Grâce à cette stratégie en amont,
commit,
le schéma d'optimisation présente une complexité de type O(N^2)
, où N
est le nombre de
RELA de type R_AARCH64_JUMP26
ou R_AARCH64_CALL26
. Par conséquent, avoir moins de RELA de ce type permet de réduire le temps de chargement du module.
Un schéma de codage courant qui augmente le nombre
Les RELA R_AARCH64_CALL26
ou R_AARCH64_JUMP26
correspondent à une journalisation excessive dans un
pilote. Chaque appel à printk()
ou à tout autre schéma de journalisation ajoute généralement une entrée RELA CALL26
/JUMP26
. Dans le texte de commit de l'étape en amont
commit,
Notez que même après l'optimisation, les six modules prennent environ 250 ms
car ces six modules étaient les six principaux modules
le plus de journalisation.
La réduction de la journalisation peut vous permettre d'économiser environ 100 à 300 ms sur les temps de démarrage, en fonction sur l'excès de la journalisation existante.
Activer la vérification asynchrone de manière sélective
Lors du chargement d'un module, si le périphérique pris en charge a déjà été
renseignés à partir du DT (devicetree) et ajouté au cœur du pilote, puis l’appareil
la vérification est effectuée dans le contexte de l'appel module_init()
. Lorsqu'une vérification d'appareil
effectué dans le contexte de module_init()
, le chargement du module ne peut pas se terminer tant que
la vérification est terminée. Étant donné que le chargement de module est principalement sérialisé, un appareil qui prend relativement longtemps à interroger ralentit le temps de démarrage.
Pour éviter des temps de démarrage plus lents, activez la vérification asynchrone pour les modules qui utilisent un tout en sondant leurs appareils. L'activation de la vérification asynchrone pour tous les modules peut ne pas être bénéfique, car le temps nécessaire pour créer un thread et lancer la vérification peut être aussi long que le temps nécessaire pour vérifier l'appareil.
Les appareils connectés via un bus lent, comme l'I2C, le chargement du micrologiciel dans leur fonction de sonde et les appareils qui utilisent beaucoup de matériel l'initialisation peut entraîner un problème de temporalité. Le meilleur moyen d'identifier quand cela se produit est de collecter le temps d'exploration pour chaque pilote et de le trier.
Pour activer la vérification asynchrone pour un module, il ne suffit pas d'utiliser uniquement
définir la PROBE_PREFER_ASYNCHRONOUS
dans le code du pilote. Pour les modules, vous devez également ajouter module_name.async_probe=1
dans la ligne de commande du kernel ou transmettre async_probe=1
en tant que paramètre de module lors du chargement du module à l'aide de modprobe
ou insmod
.
L'activation de la vérification asynchrone peut vous faire économiser environ 100 à 500 ms sur le temps de démarrage. en fonction de votre matériel/pilote.
Analysez votre pilote CPUfreq le plus tôt possible
Plus tôt les vérifications du pilote CPUfreq sont effectuées, plus vite vous pourrez faire évoluer le CPU
jusqu'à atteindre la fréquence maximale (ou une valeur maximale limitée thermiquement) au démarrage. La
plus vite le CPU, plus
le démarrage est rapide. Cette consigne s'applique également aux pilotes devfreq
qui contrôlent la fréquence de la DRAM, de la mémoire et de l'interconnexion.
Avec les modules, l'ordre de chargement peut dépendre du niveau initcall
et de l'ordre de compilation ou d'association des pilotes. Utilisez un alias MODULE_SOFTDEP()
pour vous assurer que le pilote cpufreq
fait partie des premiers modules à charger.
En plus de charger le module tôt, vous devez vous assurer les dépendances pour vérifier le pilote CPUfreq ont également été vérifiées. Par exemple, si vous avez besoin d'une poignée d'horloge ou de régulateur pour contrôler la fréquence de votre processeur, assurez-vous qu'elles sont d'abord sondées. Ou vous pourriez avoir besoin de pilotes thermiques pour avant le pilote CPUfreq s'il est possible que vos CPU soient au démarrage. Faites donc tout votre possible pour vous assurer que les pilotes CPUfreq et devfreq pertinents effectuent une analyse dès que possible.
Les économies réalisées en analysant votre pilote CPUfreq à un stade précoce peuvent être très faibles ou très importantes, selon la rapidité avec laquelle vous pouvez les analyser et la fréquence à laquelle le bootloader laisse les processeurs en place.
Déplacer les modules vers la partition d'initialisation de deuxième étape, vendor ou vendor_dlkm
Comme le processus d'initialisation de la première étape est sérialisé,
la possibilité de charger en parallèle
le processus de démarrage. Si un module n'est pas nécessaire pour que l'initialisation de premier niveau soit terminée, déplacez-le vers l'initialisation de deuxième niveau en le plaçant dans la partition du fournisseur ou vendor_dlkm
.
L'initialisation de la première étape ne nécessite pas d'interroger plusieurs appareils pour passer à l'initialisation de la deuxième étape. Seules les fonctionnalités de stockage de la console et du flash sont nécessaires pour un flux de démarrage normal.
Chargez les pilotes essentiels suivants:
watchdog
reset
cpufreq
Pour le mode fastbootd
de récupération et d'espace utilisateur, l'initialisation de la première étape nécessite d'examiner davantage d'appareils (tels que USB) et d'afficher. Conservez une copie de ces modules dans le ramdisk de première étape et dans la partition du fournisseur ou vendor_dlkm
. Ils peuvent ainsi être chargés lors de l'initialisation de la première étape pour la récupération ou le flux de démarrage fastbootd
. Toutefois,
Ne chargez pas les modules du mode récupération lors de la première étape de l'initialisation pendant le démarrage normal
le flux de travail. Les modules en mode Recovery peuvent être reportés à l'initialisation de la deuxième étape pour réduire
au démarrage. Tous les autres modules qui ne sont pas
nécessaires à la première étape de l'initialisation doivent être
déplacé vers le fournisseur ou la partition vendor_dlkm
.
À partir d'une liste d'appareils de feuilles (par exemple, le UFS ou la série), le script dev needs.sh
recherche tous les pilotes, appareils et modules nécessaires pour que les dépendances ou les fournisseurs (par exemple, les horloges, les régulateurs ou gpio
) puissent effectuer des analyses.
Le transfert de modules vers l'initialisation de deuxième étape réduit les temps de démarrage comme suit :
- Réduction de la taille du disque RAM.
- Cela permet des lectures Flash plus rapides lorsque le bootloader charge le ramdisk (étape de démarrage sérialisée).
- Cela accélère les vitesses de décompression lorsque le noyau décompresse le ramdisk (étape de démarrage sérialisée).
- La deuxième étape init fonctionne en parallèle, ce qui masque le temps de chargement du module avec le travail effectué à la deuxième étape : "init".
Le fait de déplacer les modules vers la deuxième étape peut permettre d'économiser 500 à 1 000 ms sur les temps de démarrage, en fonction en fonction du nombre de modules que vous pouvez déplacer à la deuxième étape d'initialisation.
Logistique de chargement du module
La dernière version d'Android comprend des configurations de carte qui contrôlent les modules à copier à chaque étape et ceux à charger. Cette section porte sur dans le sous-ensemble suivant:
BOARD_VENDOR_RAMDISK_KERNEL_MODULES
Cette liste de modules à copier dans le ramdisk.BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD
. Cette liste de modules à charger lors de l'initialisation de la première étape.BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD
Cette liste de modules être chargé lorsque la récupération oufastbootd
est sélectionné à partir du ramdisk.BOARD_VENDOR_KERNEL_MODULES
Cette liste de modules à copier dans le fournisseur ou partitionvendor_dlkm
dans le répertoire/vendor/lib/modules/
.BOARD_VENDOR_KERNEL_MODULES_LOAD
Cette liste de modules à charger lors de la deuxième étape de l'initialisation.
Les modules de démarrage et de récupération de ramdisk doivent également être copiés
Partition vendor_dlkm
au niveau de /vendor/lib/modules
. Copiez ces modules
la partition du fournisseur garantit que les modules
ne sont pas invisibles lors de la deuxième étape d'initialisation,
ce qui est utile pour déboguer et collecter des modinfo
pour les rapports de bugs.
La duplication ne devrait pas prendre beaucoup d'espace sur le fournisseur ou la partition vendor_dlkm
, à condition que l'ensemble de modules de démarrage soit réduit au minimum. Assurez-vous que le fichier modules.list
du fournisseur contient une liste filtrée de modules dans /vendor/lib/modules
.
La liste filtrée garantit que les temps de démarrage ne sont pas affectés par le chargement des modules
(ce qui est un processus coûteux).
Assurez-vous que les modules du mode de récupération se chargent en groupe. Chargement des modules en mode Recovery... en mode récupération ou au début de la deuxième étape init dans chaque flux de démarrage.
Vous pouvez utiliser les fichiers Board.Config.mk
de l'appareil pour effectuer ces actions comme indiqué
dans l'exemple suivant:
# 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)))
Cet exemple présente un sous-ensemble plus facile à gérer de BOOT_KERNEL_MODULES
et RECOVERY_KERNEL_MODULES
à spécifier localement dans les fichiers de configuration de la carte. Le script précédent recherche et remplit chacun des sous-ensembles de modules à partir des modules de noyau disponibles sélectionnés, laissant les modules restants pour l'initialisation de la deuxième étape.
Pour l'initialisation de deuxième étape, nous vous recommandons d'exécuter le chargement du module en tant que service afin qu'il ne bloque pas le flux de démarrage. Utilisez un script shell pour gérer le chargement du module afin que d'autres aspects logistiques, tels que la gestion et l'atténuation des erreurs, ou l'achèvement du chargement du module, puissent être signalés (ou ignorés) si nécessaire.
Vous pouvez ignorer une erreur de chargement du module de débogage qui n'est pas présente dans les builds utilisateur.
Pour ignorer cet échec, définissez la propriété vendor.device.modules.ready
sur
déclencher les étapes ultérieures du démarrage du script init rc
pour poursuivre le lancement
l'écran. Reportez-vous à l'exemple de script suivant si le code suivant est présent dans /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
Dans le fichier de configuration rc du matériel, le service one shot
peut être spécifié avec :
service insmod-sh /vendor/etc/init.insmod.sh /vendor/etc/init.insmod.<hw>.cfg
class main
user root
group root system
Disabled
oneshot
Des optimisations supplémentaires peuvent être effectuées après le passage des modules de la première à la deuxième étape. Vous pouvez utiliser la fonctionnalité de liste de blocage modprobe pour diviser par étapes pour inclure le chargement différé des modules non essentiels. Le chargement des modules utilisés exclusivement par un HAL spécifique peut être différé pour ne les charger que lorsque le HAL est démarré.
Pour améliorer les temps de démarrage apparents, vous pouvez choisir spécifiquement des modules dans le
de modules qui sont plus faciles à charger après le lancement
l'écran. Par exemple, vous pouvez charger explicitement les modules
décodeur vidéo ou Wi-Fi après l'effacement du flux de démarrage init
(sys.boot_complete
)
signal de propriété Android, par exemple). Assurez-vous que les HAL pour les modules de chargement tardif bloquent suffisamment longtemps lorsque les pilotes du noyau ne sont pas présents.
Vous pouvez également utiliser la commande wait<file>[<timeout>]
d'init dans le script de démarrage rc pour attendre que certaines entrées sysfs
indiquent que les modules de pilote ont terminé les opérations de sonde. Par exemple, vous pouvez attendre que le pilote d'affichage termine son chargement en arrière-plan de la récupération ou de fastbootd
, avant de présenter les graphiques du menu.
Initialiser la fréquence du processeur sur une valeur raisonnable dans le bootloader
Tous les SoC/produits ne peuvent pas démarrer le CPU à la fréquence la plus élevée en raison de problèmes thermiques ou d'alimentation lors des tests de boucle de démarrage. Toutefois, assurez-vous que le le bootloader définit la fréquence de tous les CPU en ligne aussi élevée possible pour un SoC ou un produit. C'est très important, car, avec une modulaire, la décompression init ramdisk a lieu avant que CPUfreq le pilote peut être chargé. Par conséquent, si le processeur est laissé à la limite inférieure de sa fréquence par le bootloader, le temps de décompression du ramdisk peut être plus long qu'un kernel compilé de manière statique (après avoir ajusté la différence de taille du ramdisk) car la fréquence du processeur serait très faible lors d'une tâche intensive (décompression). Il en va de même pour la mémoire et la fréquence d'interconnexion.
Initialiser la fréquence du processeur de gros processeurs dans le bootloader
Avant le chargement du pilote CPUfreq
, le noyau n'est pas au courant des fréquences de processeur et n'adapte pas la capacité de planification du processeur à leur fréquence actuelle. Le noyau peut migrer des threads
vers le gros processeur si la charge est
suffisamment élevé sur le petit CPU.
Assurez-vous que les processeurs principaux sont au moins aussi performants que les processeurs de petite taille pour la fréquence à laquelle le bootloader les laisse. Par exemple, si le grand processeur est deux fois plus performant que le petit processeur pour la même fréquence, mais que le bootloader définit la fréquence du petit processeur sur 1,5 GHz et celle du grand processeur sur 300 MHz, les performances de démarrage vont baisser si le noyau déplace un thread vers le grand processeur. Dans cet exemple, si vous pouvez démarrer le grand processeur à 750 MHz, vous devez le faire, même si vous ne prévoyez pas de l'utiliser explicitement.
Les pilotes ne doivent pas charger le micrologiciel lors de la première initialisation.
Il peut arriver que le micrologiciel doive être chargé lors de la première étape d'initialisation. Mais en général, les pilotes ne doivent pas charger de micrologiciel au début init, en particulier dans le contexte de la vérification de l'appareil. Le chargement du micrologiciel lors de l'initialisation de la première étape entraîne l'arrêt de l'ensemble du processus de démarrage si le micrologiciel n'est pas disponible dans le ramdisk de la première étape. Et même si le micrologiciel est présent dans le premier stage de la ramdisk, il entraîne toujours un retard inutile.