El proceso de inicio en Android en ARM

Introducción

Android es un SO para dispositivos empotrados con pantallas táctiles. De momento nos vamos a centrar en dispositivos basados en la arquitectura ARM que como veremos, fija ya una serie de características para el arranque del sistema. Todo lo que hablaremos a partir de ahora será para Linux ARM.

Gestión del arranque

En el momento que alimentamos el sistema entra en marcha el proceso de arranque del sistema, "boot loader", que será quien finalmente tras inicializar los diferentes dispositivos que forman el sistema, terminar cargando el núcleo Linux. No vamos a entrar en todos los detalles previos al comienzo del arranque del núcleo.

Inicio del núcleo

El núcleo se comienza a ejecutar y recibe parámetros como la cantidad de memoria y su localización, el tipo de sistema, y otros parámetros adicionales que se le puedan pasar. El momento de inicio del núcleo lo podemos estudiar con adb logcat:

adb logcat
--------- beginning of /dev/log/main
I/cm      (   64): Welcome to Android 2.2 / CyanogenMod-6.0.0-N1-RC2
I/cm      (   65):                                                 _
I/cm      (   66):  __ __ _ ___  _ _  __  ___  __ _ _  _  _  __  __))
I/cm      (   67): ((_ \(/'((_( ((\( ((_)((_( (('((\( ((`1( ((_)((_(
I/cm      (   68):      ))                 _))
I/cm      (   69):
I/DEBUG   (   83): debuggerd: Jul 24 2010 18:38:25
D/AK8973  (   90): AK8973 daemon 1.2.3 Start
D/AK8973  (   90): (Library version : 1.2.1.620)
--------- beginning of /dev/log/system
I/Vold    (   81): Vold 2.1 (the revenge) firing up
I/Netd    (   82): Netd 1.0 starting
D/Vold    (   81): Volume sdcard state changing -1 (Initializing) -> 0 (No-Media)
D/Vold    (   81): Volume sdcard state changing 0 (No-Media) -> 2 (Pending)
D/Vold    (   81): Volume sdcard state changing 2 (Pending) -> 1 (Idle-Unmounted)
D/AndroidRuntime(   85):
D/AndroidRuntime(   85): >>>>>>>>>>>>>> AndroidRuntime START <<<<<<<<<<<<<<
I/AndroidRuntime(   85): Heap size: -Xmx16m
D/AndroidRuntime(   85): CheckJNI is OFF
D/dalvikvm(   85): creating instr width table
I/        (   86): ServiceManager: 0xacd0
I/AudioHardwareQSD(   86): open /system/etc/vpimg success
I/AudioHardwareQSD(   86): Firmware /system/etc/vpimg size 27612
I/AudioHardwareQSD(   86): Total 27612 bytes put to user space buffer.

A continuación sigue todo el inicio del núcleo Linux con montones de mensajes según va inicializando dispositivos hardware. Vemos que se accede ya en este punto a ficheros como "/system/etc/vpimg" necesarios para inicializar el audio del sistema. Este sistema de ficheros inicial está dentro del "ramdisk".

El ramdisk

El primer sistema raíz con comandos a ejecutar por parte del núcleo es el que viene en el "ramdisk". Es un disco que se copia a memoria, no hace falta tener acceso a dispositivos físicos, y por ejemplo, puede tener drivers necesarios para acceder a dispositivos de almacenamiento.

El núcleo y el ramdisk están ambos juntos dentro del fichero "boot.img" que se carga dentro del sistema ARM para llevar el arranque y que es un fichero con un formato especial y firmado. Veamos como ejemplo el que viene en CM6:

unzip -l update-cm-6.0.0-N1-RC1-signed.zip | grep boot.img
  2390016  2008-02-28 21:33   boot.img
unzip  ../update-cm-6.0.0-N1-RC1-signed.zip boot.img
Archive:  ../update-cm-6.0.0-N1-RC1-signed.zip
signed by SignApk
  inflating: boot.img
split_bootimg.pl boot.img
...
Writing boot.img-kernel ... complete.
Writing boot.img-ramdisk.gz ... complete.

Ya tenemos tanto el kernel como el sistema de ficheros inicial ramdisk. Vamos a estudiar este:

mkdir ramdisk
cd ramdisk
gzip -dc ../boot.img-ramdisk.gz | cpio -i
tree
.
|-- data
|-- default.prop
|-- dev
|-- init
|-- init.goldfish.rc
|-- init.mahimahi.rc
|-- init.rc
|-- proc
|-- sbin
|   `-- adbd
|-- sys
`-- system

Vemos que el "ramdisk" es muy sencillo y básicamente lo único que tiene es el programa "init" y "adbd" para poder entrar por adb en el sistema.

El programa init

Dentro del ramdisk está el programa "init" que es al que llama el núcleo para poner en marcha el sistema. Manual de Android init.

Como vemos de su código fuente, lo que hace a parte de crear ya algunos directorios, es interpretar los ficheros:

...
parse_config_file("/init.rc");
...
get_hardware_name();
    snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);
    parse_config_file(tmp);
...

El nombre del hardware lo saca de "/proc/cpuinfo". En una N1

# cat /proc/cpuinfo | grep Hardware
Hardware        : mahimahi

Los primeros pasos que da los encontramos en "init.rc":

head -30 init.rc | grep " " 
on init
sysclktz 0
loglevel 3
# setup the global environment
    export PATH /sbin:/system/sbin:/system/bin:/system/xbin
    export LD_LIBRARY_PATH /system/lib
    export ANDROID_BOOTLOGO 1
    export ANDROID_CACHE /cache
    export ANDROID_ROOT /system
    export ANDROID_ASSETS /system/app
    export ANDROID_DATA /data
    export EXTERNAL_STORAGE /mnt/sdcard
    export ASEC_MOUNTPOINT /mnt/asec
    export BOOTCLASSPATH /system/framework/core.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar
...
exec /system/bin/sysinit

    class_start default

Vemos la importancia de Java en la plataforma ya que desde el inicio se define el CLASSPATH para el arranque. En este fichero se configuran parámetros del núcleo, se crean sistemas de ficheros, se configuran los permisos, se hace una configuración inicial de la red, se definen varios servicios y se inician algunos como "adbd",

En "init.rc" también es montan ya las particiones con el sistema completo, como "/system". Y se da paso al inicio habitual con los scripts dentro de "/system/etc/init.d" al ejecutar "/system/bin/sysinit".

Dentro de "init.mahimahi.rc" se inicializa todo el hardware y servicios asociados específicos del dispositivo, y se intenta configurar la wifi.

Con "class_start default" se inician todos los servicios relacionados con la clase "default", salvo aquello que tienen la opción "disabled". Todos los servicios están en la clase "default" salvo que se diga lo contrario.

De entre todos los servicios que se inician el clave es Zygote y System Manager.

Para ver todos los servicios disponibles:

# /system/bin/service list
Found 52 services:
0       phone: [com.android.internal.telephony.ITelephony]
1       iphonesubinfo: [com.android.internal.telephony.IPhoneSubInfo]
...
50      media.player: [android.media.IMediaPlayerService]
51      media.audio_flinger: [android.media.IAudioFlinger]

El servicio Zygote

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    socket zygote stream 666
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/po

Zygote es el que se encarga de lanzar la máquina virtual Dalvik por lo que ya tenemos la plataforma Java en marcha.

El servicio Service Manager

service servicemanager /system/bin/servicemanager
    user system
    critical
    onrestart restart zygote
    onrestart restart media

El servicio servicemanager ya actua dentro del mundo Java y se encarga ya de ir iniciando los diferentes servicios Java de Android.

En su parte de implementación Java podemos ya ver como va iniciando diferentes servicios.

Referencias

InitAndroid (last edited 2010-10-05 10:26:26 by AlvaroDelCastillo)