En esta página, se proporcionan sugerencias para mejorar el tiempo de inicio.
Quita los símbolos de depuración de los módulos
Al igual que los símbolos de depuración se quitan del kernel en un dispositivo de producción, asegúrate de quitarlos también de los módulos. Quita la depuración de los módulos ayuda al inicio mediante la reducción de lo siguiente:
- Es el tiempo que se tarda en leer los objetos binarios de Flash.
- Es el tiempo que lleva descomprimir el ramdisk.
- El tiempo que lleva cargar los módulos.
Quitar el símbolo de depuración de los módulos puede ahorrar varios segundos durante el inicio.
La eliminación de símbolos está habilitada de forma predeterminada en la compilación de la plataforma de Android, pero
para habilitarlos explícitamente, establece
BOARD_DO_NOT_STRIP_VENDOR_RAMDISK_MODULES
en la configuración específica de tu dispositivo
en device/vendor/device.
Usa la compresión LZ4 para el kernel y el ramdisk
Gzip genera un resultado comprimido más pequeño en comparación con LZ4, pero se descomprime más rápido que Gzip. En el caso del kernel y los módulos, la reducción absoluta del tamaño de almacenamiento por usar Gzip no es tan significativa en comparación con el beneficio de tiempo de descompresión de LZ4.
Se agregó compatibilidad con la compresión de ramdisk LZ4 a la compilación de la plataforma de Android a través de BOARD_RAMDISK_USE_LZ4
. Puedes establecer esta opción en la configuración específica del dispositivo. La compresión del kernel se puede configurar con la defconfig.
Si cambias a LZ4, el tiempo de inicio debería ser de 500 ms a 1,000 ms más rápido.
Cómo evitar registros excesivos en tus controladores
En ARM64 y ARM32, las llamadas a funciones que están a más de una distancia específica de el sitio de la llamada necesita que una tabla de salto (llamada tabla de vinculación de procedimientos o PLT) se y se puede codificar la dirección de salto completa. Dado que los módulos se cargan de forma dinámica, estas tablas de salto deben corregirse durante la carga del módulo. Las llamadas que necesitan reasignación se denominan entradas de reasignación con complementos explícitos (o RELA, en resumen) en el formato ELF.
El kernel de Linux optimiza el tamaño de la memoria (como el acierto de caché).
(optimización) cuando se asigna el PLT. Con esta confirmación upstream, el esquema de optimización tiene una complejidad de O(N^2)
, donde N
es la cantidad de RELA de tipo R_AARCH64_JUMP26
o R_AARCH64_CALL26
. Es por eso que tener menos RELA
de estos tipos es útil para reducir el tiempo de carga del módulo.
Un patrón de programación común que aumenta la cantidad de RELA de R_AARCH64_CALL26
o R_AARCH64_JUMP26
es el registro excesivo en un controlador. Por lo general, cada llamada a printk()
o a cualquier otro esquema de registro agrega una entrada RELA CALL26
/JUMP26
. En el texto de confirmación del flujo de
confirmación,
Ten en cuenta que incluso con la optimización, los seis módulos tardan alrededor de 250 ms
ya que esos seis módulos fueron los seis módulos principales con
la mayor cantidad de registros.
Reducir el registro puede ahorrar entre 100 y 300 ms en los tiempos de inicio, según cuán excesivo sea el registro existente.
Habilitar el sondeo asíncrono de forma selectiva
Cuando se carga un módulo, si el dispositivo que admite ya se propagó desde el DT (árbol de dispositivos) y se agregó al núcleo del controlador, la prueba del dispositivo se realiza en el contexto de la llamada a module_init()
. Cuando se realiza una prueba de dispositivo en el contexto de module_init()
, el módulo no puede terminar de cargarse hasta que se completa la prueba. Dado que la carga de módulos está mayormente serializada, un dispositivo que tarda un tiempo relativamente largo en sondear ralentiza el tiempo de inicio.
Para evitar tiempos de inicio más lentos, habilita el sondeo asíncrono para los módulos que tarden mientras sondean sus dispositivos. Habilita el sondeo asíncrono para todos los módulos podría no ser beneficioso, ya que el tiempo que lleva bifurcar un hilo y comenzar puede ser tan alto como el tiempo que demore en sondear el dispositivo.
Dispositivos que se conectan a través de un bus lento, como I2C, dispositivos que no la carga del firmware en la función de sondeo y los dispositivos que hacen mucho hardware la inicialización puede generar un problema de tiempo. La mejor manera de identificar cuándo ocurre esto es recopilar el tiempo de sondeo de cada controlador y ordenarlo.
Para habilitar la sondeo asíncrono de un módulo, no es suficiente con solo configurar la marca PROBE_PREFER_ASYNCHRONOUS
en el código del controlador. En el caso de los módulos, también debes agregar module_name.async_probe=1
a la línea de comandos del kernel o pasar async_probe=1
como parámetro del módulo cuando lo cargues con modprobe
o insmod
.
Habilitar la sondeo asíncrono puede ahorrar entre 100 y 500 ms en los tiempos de inicio, según el hardware o los controladores.
Prueba el controlador de CPUfreq lo antes posible
Cuanto antes sondee el controlador de CPUfreq, antes podrás escalar la frecuencia de la CPU al máximo (o a un máximo limitado térmicamente) durante el inicio. cuanto más rápida sea la CPU, más rápido será el inicio. Este lineamiento también se aplica a devfreq
.
controladores que controlan la DRAM, la memoria y la frecuencia de interconexión.
Con los módulos, el orden de la carga puede depender del nivel de initcall
y
el orden de compilación o vinculación de los controladores. Usa un alias MODULE_SOFTDEP()
para hacer
Asegúrate de que el controlador cpufreq
esté entre los primeros módulos en cargar.
Además de cargar el módulo antes, también debes asegurarte de que todos los para sondear el controlador de CPUfreq. Por ejemplo, si necesitas un controlador de reloj o regulador para controlar la frecuencia de la CPU, asegúrate de que se sondeen primero. También es posible que debas cargar los controladores térmicos antes del controlador de CPUfreq si es posible que las CPUs se calienten demasiado durante el inicio. Así que, haga lo posible para asegurarse de que la CPUfreq y los recursos relevantes los controladores de devfreq sondeos lo antes posible.
El ahorro por sondear el controlador de CPUfreq de manera anticipada puede ser desde muy pequeño hasta muy grandes, dependiendo de qué tan temprano puedas hacer el sondeo y con qué frecuencia el bootloader deja las CPU en su interior.
Se movieron los módulos a la inicialización de la segunda etapa, la partición del proveedor o vendor_dlkm.
Debido a que el proceso de inicialización de la primera etapa está serializado, no hay muchas oportunidades para paralelizar el proceso de inicio. Si no se necesita un módulo para que finalice la inicialización de la primera etapa, muévelo a la inicialización de la segunda etapa colocándolo en la partición del proveedor o vendor_dlkm
.
La inicialización de la primera etapa no requiere sondear varios dispositivos para llegar a la inicialización de la segunda etapa. Solo se requieren las capacidades de almacenamiento flash y de la consola para un flujo de inicio normal.
Carga los siguientes controladores esenciales:
watchdog
reset
cpufreq
Para el modo de recuperación y el espacio de usuario fastbootd
, la inicialización de la primera etapa requiere más dispositivos para sondear (como USB) y mostrar. Mantén una copia de estos módulos en el disco RAM de la primera etapa y en la partición del proveedor o vendor_dlkm
. Esto les permite cargarse en la inicialización de la primera etapa para la recuperación o el flujo de inicio de fastbootd
. Sin embargo,
No cargues los módulos del modo de recuperación en el inicio de la primera etapa durante el inicio normal.
de tu flujo de trabajo. Los módulos de modo de recuperación pueden aplazarse al init de la segunda etapa para disminuir la
tiempo de inicio. Todos los demás módulos que no se necesitan en la inicialización de la primera etapa deben trasladarse a la partición del proveedor o vendor_dlkm
.
Dada una lista de dispositivos finales (por ejemplo, el UFS o el dispositivo en serie), la secuencia de comandos dev needs.sh
encuentra todos los controladores, dispositivos y módulos necesarios para que las dependencias o proveedores (por ejemplo, relojes, reguladores o gpio
) realicen la prueba.
Mover módulos a la inicialización de la segunda etapa disminuye los tiempos de inicio de las siguientes maneras:
- Reducción del tamaño del ramdisk
- Esto genera lecturas de flash más rápidas cuando el bootloader carga el ramdisk (paso de inicio serializado).
- Esto genera velocidades de descompresión más rápidas cuando el kernel descomprime el ramdisk (paso de inicio serializado).
- La inicialización de la segunda etapa funciona en paralelo, lo que oculta el tiempo de carga del módulo con el trabajo que se realiza en la inicialización de la segunda etapa.
Mover módulos a la segunda etapa puede ahorrar entre 500 y 1,000 ms en los tiempos de inicio, según de cuántos módulos puedes pasar a la init de la segunda etapa.
Logística de la carga del módulo
Las últimas configuraciones de la placa de funciones de compilación de Android que controlan qué los módulos se copian en cada etapa y qué módulos se cargan. Esta sección se centra en el siguiente subconjunto:
BOARD_VENDOR_RAMDISK_KERNEL_MODULES
Esta lista de módulos se copiará en el ramdisk.BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD
: Esta es una lista de módulos que se cargarán en la inicialización de la primera etapa.BOARD_VENDOR_RAMDISK_RECOVERY_KERNEL_MODULES_LOAD
Esta lista de módulos para se cargará cuando se seleccione la recuperación o se seleccionefastbootd
del disco RAM.BOARD_VENDOR_KERNEL_MODULES
Esta lista de módulos se copiará en proveedor o a la particiónvendor_dlkm
en el directorio/vendor/lib/modules/
.BOARD_VENDOR_KERNEL_MODULES_LOAD
Esta lista de módulos que se cargarán en init de segunda etapa.
Los módulos de inicio y recuperación en el ramdisk también se deben copiar en el proveedor o en la partición vendor_dlkm
en /vendor/lib/modules
. Copiar estos módulos en la partición del proveedor garantiza que no sean invisibles durante la inicialización de la segunda etapa, lo que es útil para depurar y recopilar modinfo
para informes de errores.
La duplicación debería costar un espacio mínimo en el proveedor o vendor_dlkm
.
partición
siempre y cuando se minimice el conjunto de módulos de inicio. Asegúrate de que la configuración del proveedor
El archivo modules.list
tiene una lista filtrada de módulos en /vendor/lib/modules
.
La lista filtrada garantiza que los tiempos de inicio no se vean afectados por la carga de los módulos
(lo que es un proceso costoso).
Asegúrate de que los módulos del modo de recuperación se carguen como un grupo. La carga de módulos del modo de recuperación se puede realizar en el modo de recuperación o al comienzo de la inicialización de la segunda etapa en cada flujo de inicio.
Puedes usar los archivos Board.Config.mk
del dispositivo para realizar estas acciones, como se muestra en el siguiente ejemplo:
# 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)))
En este ejemplo, se muestra un subconjunto más fácil de administrar de BOOT_KERNEL_MODULES
y RECOVERY_KERNEL_MODULES
que se especifica de forma local en los archivos de configuración de la placa. La secuencia de comandos anterior encuentra y completa cada uno de los módulos de subconjunto del
módulos de kernel disponibles seleccionados, dejando los demás módulos por segundo
init de la etapa.
Para init de segunda etapa, recomendamos ejecutar la carga del módulo como servicio para no bloquea el flujo de inicio. Usa una secuencia de comandos de shell para administrar la carga del módulo para que otros aspectos logísticos, como el manejo y la mitigación de errores, o la carga de módulos del anuncio completo, se pueden informar (o ignorar) si es necesario.
Puedes ignorar una falla de carga del módulo de depuración que no está presente en las compilaciones del usuario.
Para ignorar esta falla, establece la propiedad vendor.device.modules.ready
en
Activa etapas posteriores del flujo de inicio de secuencias de comandos de init rc
para continuar con el lanzamiento
en la pantalla. Consulta la siguiente secuencia de comandos de ejemplo, si tienes el siguiente código en /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
En el archivo rc de hardware, el servicio one shot
se podría especificar con lo siguiente:
service insmod-sh /vendor/etc/init.insmod.sh /vendor/etc/init.insmod.<hw>.cfg
class main
user root
group root system
Disabled
oneshot
Se pueden realizar optimizaciones adicionales después de que los módulos pasen del primer módulo a la segunda etapa. Puedes usar la función de lista de bloqueo de modprobe para dividir el flujo de inicio de la segunda etapa y, así, incluir la carga diferida de módulos no esenciales. Se puede aplazar la carga de módulos que usa exclusivamente una HAL específica para cargar los módulos solo cuando se inicia la HAL.
Para mejorar los tiempos de inicio aparente, puedes elegir módulos en la sección
de carga de módulos propicio para la carga después del lanzamiento
en la pantalla. Por ejemplo, puedes cargar de forma explícita los módulos con retraso para
decodificador de video o Wi-Fi después de que se haya borrado el flujo de inicio de inicio
(sys.boot_complete
)
como el indicador de propiedad de Android). Asegúrate de que las HALs de carga tardía
Los módulos se bloquean lo suficiente cuando los controladores del kernel no están presentes.
Como alternativa, puedes usar el comando wait<file>[<timeout>]
de init en el archivo de inicio
La secuencia de comandos de Flow rc espera a que las entradas seleccionadas de sysfs
muestren que los módulos del controlador
completaron las operaciones de sondeo. Un ejemplo de esto es esperar a que
mostrar el controlador para completar la carga en segundo plano de la recuperación o fastbootd
antes de presentar los gráficos del menú.
Inicializa la frecuencia de la CPU en un valor razonable en el bootloader
Es posible que no todos los SoC o productos puedan iniciar la CPU a la frecuencia más alta debido a problemas térmicos o de energía durante las pruebas de bucle de inicio. Sin embargo, asegúrate de que el bootloader configure la frecuencia de todas las CPUs en línea lo más alta posible de forma segura para un SoC o producto. Esto es muy importante porque, con un kernel completamente modular, la descompresión de init ramdisk se produce antes de que se pueda cargar el controlador de CPUfreq. Por lo tanto, si el bootloader deja la CPU en el extremo inferior de su frecuencia, el tiempo de descompresión del ramdisk puede tardar más que un kernel compilado de forma estática (después de ajustar la diferencia de tamaño del ramdisk) porque la frecuencia de la CPU sería muy baja cuando se realiza un trabajo intensivo en la CPU (descompresión). Lo mismo se aplica a la memoria y la frecuencia de interconexión.
Inicializa la frecuencia de la CPU grande en el bootloader
Antes de que se cargue el controlador CPUfreq
, el kernel no conoce las frecuencias de la CPU y no escala la capacidad de programación de la CPU para su frecuencia actual. El kernel puede migrar subprocesos a la CPU grande si la carga se
es lo suficientemente alta
en la CPU pequeña.
Asegúrese de que las CPU grandes tengan, al menos, el mismo rendimiento que las CPU pequeñas para el la frecuencia con la que el bootloader los deja. Por ejemplo, si la CPU grande es 2 veces más eficaz que una CPU pequeña para la misma frecuencia, pero bootloader establece la frecuencia de la CPU pequeña en 1.5 GHz y la frecuencia de la a 300 MHz, el rendimiento del inicio disminuirá si el kernel empieza mueve un subproceso a la CPU grande. En este ejemplo, si es seguro iniciar a 750 MHz, deberías hacerlo incluso si no planeas usarlo explícitamente.
Los controladores no deben cargar el firmware en la inicialización de la primera etapa.
Puede haber algunos casos inevitables en los que se deba cargar el firmware en la inicialización de la primera etapa. Sin embargo, en general, los controladores no deben cargar ningún firmware en la inicialización de la primera etapa, en especial en el contexto de la prueba del dispositivo. La carga del firmware en la inicialización de la primera etapa hace que se detenga todo el proceso de inicio si el firmware no está disponible en el ramdisk de la primera etapa. Incluso si el firmware está presente en la primera etapa ramdisk, aún causa un retraso innecesario.