Contenido


Visión general del disco RAM inicial de Linux (initrd)

Conozca más acerca de su anatomía, creación y utilización en el proceso de arranque de Linux

¿Qué es un disco RAM inicial?

El disco RAM inicial (initrd) es un sistema de archivos raíz inicial que se monta antes de que el sistema de archivos raíz esté disponible. El initrd está ligado al kernel y se carga como parte del procedimiento de arranque del kernel. Después, el kernel monta su initrd como parte del proceso de arranque de dos etapas, para cargar los módulos que hacen que los sistemas de archivos reales estén disponibles y se obtengan en el sistema de archivos raíz real.

Para lograrlo, el initrd contiene un conjunto mínimo de directorios y ejecutables, como la herramienta insmod para instalar módulos de kernel en el kernel.

En el caso de sistemas Linux de escritorio o de servidor, el initrd es un sistema de archivos transitorio. Su vida es corta, ya que solo sirve como un puente hacia el sistema de archivos raíz. En los sistemas incorporados que no tengan almacenamiento mutable, el initrd es el sistema de archivos raíz permanente. Este artículo explora ambos contextos.

Anatomía del initrd

La imagen de initrd contiene los ejecutables necesarios y los archivos de sistema para soportar el arranque de la segunda etapa de los sistemas Linux.

El método para crear el disco RAM inicial puede variar dependiendo de la versión de Linux que se esté ejecutando. Antes de Fedora Core 3, el initrd se construía con el loop device. El loop device es un controlador de dispositivos que permite montar un archivo como un dispositivo de bloque y, después, interpreta el sistema de archivos que representa. Es posible que el loop device no esté presente en su kernel, pero se puede activar a través de la herramienta de configuración del kernel (make menuconfig) seleccionando Controladores de Dispositivos > Bloquear Dispositivos > Soporte para Dispositivo Loopback. Es posible inspeccionar el loop device de la siguiente manera (el nombre del archivo initrd puede variar):

Listado 1. Inspeccionar el initrd (antes del FC3)
# mkdir temp ; cd temp
# cp /boot/initrd.img.gz .
# gunzip initrd.img.gz
# mount -t ext -o loop initrd.img /mnt/initrd
# ls -la /mnt/initrd
#

Ahora se puede inspeccionar el subdirectorio /mnt/initrd para ver el contenido de initrd. Observe que incluso si el archivo de la imagen de initrd no acabase con el sufijo .gz, es un archivo comprimido y se podría añadir el sufijo .gz para comprimirlo con gunzip.

Empezando con Fedora Core 3, la imagen initrd predeterminada es un archivo cpio comprimido. En vez de montar el archivo como una imagen comprimida con el loop device, se puede utilizar un archivo cpio. Para inspeccionar el contenido de un archivo cpio, utilice los siguientes comandos:

Listado 2. Inspeccionar el initrd (FC3 y posterior)
# mkdir temp ; cd temp
# cp /boot/initrd-2.6.14.2.img initrd-2.6.14.2.img.gz
# gunzip initrd-2.6.14.2.img.gz
# cpio -i --make-directories < initrd-2.6.14.2.img
#

El resultado es un sistema de archivos raíz pequeño, como se muestra en el Listado 3. El pequeño, pero necesario, conjunto de aplicaciones está presente en el directorio ./bin, entre ellas nash (no es un shell, sino un intérprete de scripts), insmod para cargar módulos del kernel y lvm (herramientas del gestor de volúmenes lógicos).

Listado 3. Estructura predeterminada del directorio initrd de Linux
# ls -la
#
drwxr-xr-x  10 root root    4096 May 7 02:48 .
drwxr-x---  15 root root    4096 May 7 00:54 ..
drwxr-xr-x  2  root root    4096 May 7 02:48 bin
drwxr-xr-x  2  root root    4096 May 7 02:48 dev
drwxr-xr-x  4  root root    4096 May 7 02:48 etc
-rwxr-xr-x  1  root root     812 May 7 02:48 init
-rw-r--r--  1  root root 1723392 May 7 02:45 initrd-2.6.14.2.img
drwxr-xr-x  2  root root    4096 May 7 02:48 lib
drwxr-xr-x  2  root root    4096 May 7 02:48 loopfs
drwxr-xr-x  2  root root    4096 May 7 02:48 proc
lrwxrwxrwx  1  root root       3 May 7 02:48 sbin -> bin
drwxr-xr-x  2  root root    4096 May 7 02:48 sys
drwxr-xr-x  2  root root    4096 May 7 02:48 sysroot
#

En el Listado 3, el archivo init está en el directorio raíz. Este archivo, al igual que el proceso de arranque tradicional de Linux, se invoca cuando la imagen initrd se descomprime en el disco RAM. Expondremos esto posteriormente en el artículo.

Herramientas para crear un initrd

Volvamos al principio para entender formalmente cómo se construye en primer lugar la imagen de initrd. Para los sistemas Linux tradicionales, la imagen initrd se crea durante el proceso de compilación de Linux. Hay numerosas herramientas, como mkinitrd, que se pueden utilizar para construir automáticamente un initrd con las bibliotecas y módulos que se necesitan para conectarlo con el sistema de archivos raíz real. La utilidad mkinitrd es, de hecho, un script de shell, para que se pueda ver exactamente como logra su resultado. También está la utilidad YAIRD (Otro Mkinitrd), que permite personalizar todos los aspectos de la construcción de initrd.

Construir manualmente un disco RAM inicial personalizado

Debido a que en muchos de los sistemas incorporados que se basan en Linux no hay un disco duro, el initrd también sirve como el sistema de archivos raíz permanente. El Listado 4 muestra cómo crear una imagen de initrd. Estoy utilizando un escritorio estándar de Linux para que usted pueda seguir sin un objetivo incorporado. Aparte de la compilación cruzada, los conceptos (según se aplican a la construcción de initrd) son los mismos para un objetivo incorporado.

Listado 4. Utilidad (mkird) para crear un initrd personalizado
#!/bin/bash

# Realizando mantenimiento...
rm -f /tmp/ramdisk.img
rm -f /tmp/ramdisk.img.gz

# Constantes de Ramdisk
RDSIZE=4000
BLKSIZE=1024

# Crear una imagen de ramdisk vacía
dd if=/dev/zero of=/tmp/ramdisk.img bs=$BLKSIZE count=$RDSIZE

# Hacerla un sistema de archivos ext2 montable
/sbin/mke2fs -F -m 0 -b $BLKSIZE /tmp/ramdisk.img $RDSIZE

# Montarlo para que pueda rellenar
mount /tmp/ramdisk.img /mnt/initrd -t ext2 -o loop=/dev/loop0

# Rellenar el sistema de archivos (subdirectorios)
mkdir /mnt/initrd/bin
mkdir /mnt/initrd/sys
mkdir /mnt/initrd/dev
mkdir /mnt/initrd/proc

# Agarrar busybox y crear los enlaces simbólicos
pushd /mnt/initrd/bin
cp /usr/local/src/busybox-1.1.1/busybox .
ln -s busybox ash
ln -s busybox mount
ln -s busybox echo
ln -s busybox ls
ln -s busybox cat
ln -s busybox ps
ln -s busybox dmesg
ln -s busybox sysctl
popd

# Agarrar los archivos de desarrollo necesarios
cp -a /dev/console /mnt/initrd/dev
cp -a /dev/ramdisk /mnt/initrd/dev
cp -a /dev/ram0 /mnt/initrd/dev
cp -a /dev/null /mnt/initrd/dev
cp -a /dev/tty1 /mnt/initrd/dev
cp -a /dev/tty2 /mnt/initrd/dev

# Equiparar sbin con bin
pushd /mnt/initrd
ln -s bin sbin
popd

# Crear el archivo init
cat >> /mnt/initrd/linuxrc << EOF
#!/bin/ash
echo
echo "El initrd sencillo está activo"
echo
mount -t proc /proc /proc
mount -t sysfs none /sys
/bin/ash --login
EOF

chmod +x /mnt/initrd/linuxrc

# Acabando...
umount /mnt/initrd
gzip -9 /tmp/ramdisk.img
cp /tmp/ramdisk.img.gz /boot/ramdisk.img.gz

Para crear un initrd, empiece creando un archivo vacío, utilizando /dev/zero (una secuencia de ceros) como entrada escrita para el archivo ramdisk.img. El archivo resultante ocupa 4 MB (4000 bloques de 1 K). Después, utilice el comando mke2fs para crear un sistema de archivos ext2 (segunda ampliación) con el archivo vacío. Ahora que este archivo es un sistema de archivos ext2, utilice el loop device para montar el archivo en /mnt/initrd. En el punto de montaje, tiene un directorio que representa un sistema de archivos ext2 que se puede rellenar desde el initrd. La mayor parte del resto del script brinda esta funcionalidad.

El siguiente paso es crear los subdirectorios necesarios que compondrán su sistema de archivos raíz: /bin, /sys, /dev y /proc. Sólo se necesita un puñado de ellos (por ejemplo, no hay presentes bibliotecas), pero contienen bastante funcionalidad.

Para que su sistema de archivos raíz sea útil, utilice BusyBox. Esta utilidad es una única imagen que contiene muchas utilidades individuales que habitualmente se encuentran en los sistemas Linux (como ash, awk, sed, insmod, etc.). La ventaja de BusyBox es que empaqueta muchas utilidades en una, mientras comparte sus elementos comunes, lo que da como resultado una imagen mucho menor. Esto es ideal para los sistemas incorporados. Copie la imagen de BusyBox desde su directorio original a su raíz en el directorio /bin. Después, se crea una cantidad de enlaces simbólicos que apuntan a la utilidad de BusyBox. BusyBox averigua que utilidad fue invocada y ejecuta esa funcionalidad. En este directorio se crea un pequeño conjunto de enlaces para soportar su script initt (con todos los enlaces del comando apuntando a BusyBox).

El siguiente paso es crear un pequeño número de archivos especiales de dispositivos. Yo copio esos directamente desde mi subdirectorio /dev actual utilizando la opción (archivo) -a actual para preservar sus atributos.

El penúltimo paso es generar el archivo linuxrc. Después de que el kernel monte el disco RAM, busca un archivo init para ejecutarlo. Si el kernel no encuentra el archivo init , invoca el archivo linuxrc como su script de inicialización. En este archivo se realiza la configuración básica del entorno, como montar el sistema de archivos /proc. Además de /proc, también monto el sistema de archivos /sys y emito un mensaje hacia la consola. Finalmente, invoco ash (un clon de Bourne Shell) para poder interactuar con el sistema de archivos raíz. El archivo linuxrc después se hace ejecutable a través de chmod.

Finalmente, su sistema de archivos raíz está completo. Está desmontado y comprimido con gzip. El archivo resultante (ramdisk.img.gz) se copia al subdirectorio /boot para poder ser cargado a través de GNU GRUB.

Para construir el disco RAM inicial, sólo hay que invocar mkird para que la imagen sea automáticamente creada y copiada en /boot.

Cómo probar el disco RAM inicial personalizado

La nueva imagen de initrd está en /boot, así que, el siguiente paso es probarla con su kernel predeterminado. Ahora puede reiniciar su sistema Linux. Cuando GRUB aparezca, pulse la tecla C para activar la utilidad de la línea de comando en GRUB. Ahora puede interactuar con GRUB para definir un kernel específico y una imagen de initrd a cargar. El comando kernel permite definir el archivo del kernel, y el comando initrd permite especificar el archivo particular de la imagen de initrd. Cuando éstos estén definidos, utilice el comando boot para arrancar el kernel, tal como se muestra en el Listado 5.

Listado 5. Utilizar GRUB para arrancar manualmente el kernel e initrd
    GNU GRUB  versión 0.95  (memoria 638.000 inferior / 97.216.000 superior)

[ Se soporta una edición mínima de la línea como BASH. Para la primera palabra, TAB
  hace una lista con las posibles finalizaciones del comando. En cualquier otro lugar, TAB hace una lista con las posibles
  finalizaciones de un dispositivo/nombre de archivo. ESC hace salir en cualquier momento.]

grub> kernel /bzImage-2.6.1
   [Linux-bzImage, setup=0x1400, size=0x29672e]

grub> initrd /ramdisk.img.gz
   [Linux-initrd @ 0x5f2a000, 0xb5108 bytes]

grub> boot

Descomprimiendo Linux... Vale, arrancando el kernel.

Después de que el kernel se inicie, verifica para ver si hay disponible una imagen de initrd (hablare más sobre esto después) y, después, la carga y la monta como si fuese el sistema de archivos raíz. Cn el Listado 6 se puede ver el final de esta inicialización particular de Linux. Cuando el ash shell se inicia, está disponible para ingresar comandos. En este ejemplo, exploro el sistema de archivos raíz e interrogo una entrada de un sistema de archivos virtual proc. También demuestro que se puede escribir en el sistema de archivos tocando uno de sus archivos (por lo tanto, creándolo). Observe aquí que el primer proceso que se ha creado es linuxrc (habitualmente init).

Listado 6. Arrancar un kernel Linux con un sencillo initrd
...
md: Detectando automáticamente las matrices de RAID
md: autorun
md: ... autorun HECHO.
RAMDISK: Imagen comprimida encontrada en el bloque 0
VFS: Raíz montada (sistema de archivos ext2).
Liberando la memoria no utilizada del kernel: 208k liberados
/ $ ls
bin         etc       linuxrc       proc        sys
dev         lib       lost+found    sbin
/ $ cat /proc/1/cmdline
/bin/ash/linuxrc
/ $ cd bin
/bin $ ls
ash      cat      echo     mount    sysctl
busybox  dmesg    ls       ps
/bin $ touch zfile
/bin $ ls
ash      cat      echo     mount    sysctl
busybox  dmesg    ls       ps       zfile

Arrancar con un disco RAM inicial disk

Ahora que ha visto cómo construir y utilizar un disco RAM inicial personalizado, esta sección explora cómo el kernel identifica y monta el initrd como su sistema de archivos raíz. Voy a hacer un recorrido por algunas de las principales funciones de la cadena de arranque y a explicar lo que está ocurriendo.

El cargador de arranque, como GRUB, identifica el kernel que se debe cargar y copia la imagen de este kernel y los initrd asociados en la memoria. En el subdirectorio ./init, que está bajo el directorio de origen del kernel Linux, se puede encontrar la mayor parte de esta funcionalidad.

El kernel se invoca después de que su imagen y la de initrd hayan sido descomprimidas y copiadas en la memoria. Se realizan varias inicializaciones y, eventualmente, se encontrará en init/main.c:init() (subdir/file:function). Esta función realiza una gran cantidad de inicializaciones del subsistema. Aquí se realiza una llamada a init/do_mounts.c:prepare_namespace(), que se utiliza para preparar el espacio de nombres (montar el sistema de archivos dev, RAID o md, dispositivos, y, finalmente, el initrd). La carga de initrd se realiza a través de una llamada a init/do_mounts_initrd.c:initrd_load().

La función initrd_load() llama a init/do_mounts_rd.c:rd_load_image(), que determina la imagen del disco RAM que se debe cargar a través de una llamada a init/do_mounts_rd.c:identify_ramdisk_image(). Esta función verifica el número mágico de la imagen, para determinar si es un formato minux, etc2, romfs, cramfs o gzip. Tras volver a initrd_load_image, se realiza una llamada a init/do_mounts_rd:crd_load(). Esta función asigna espacio para el disco RAM, calcula la verificación por redundancia cíclica (CRC) y, después, descomprime y carga la imagen del disco RAM en la memoria. En este punto, usted tiene la imagen de initrd en un dispositivo de bloque apto para el montaje.

Ahora, para montar el dispositivo de bloque como raíz hay que empezar con una llamada a init/do_mounts.c:mount_root(). Se crea el dispositivo raíz y se realiza una llamada a init/do_mounts.c:mount_block_root(). Desde ahí, se llama a init/do_mounts.c:do_mount_root() , que llama a fs/namespace.c:sys_mount() para montar el sistema de archivos raíz y, después, hacer un chdir al mismo. Aquí es donde se ve el conocido mensaje que se muestra en el Listado 6: VFS: Raíz montada (sistema de archivos ext2).

Finalmente, hay que volver a la función init y llamar a init/main.c:run_init_process. Esto provoca una llamada a execve para que inicie el proceso init (en este caso /linuxrc). El linuxrc puede ser un ejecutable o un script (mientras que el intérprete del script esté disponible para ello).

La jerarquía de las funciones a las que se llama se muestra en el Listado 7. Aquí no se muestran todas las funciones que se invocan para copiar y montar el disco RAM inicial, pero esto proporciona una visión general aproximada del flujo general.

Listado 7. Jerarquía de las principales funciones para cargar y montar initrd
init/main.c:init
  init/do_mounts.c:prepare_namespace
    init/do_mounts_initrd.c:initrd_load
      init/do_mounts_rd.c:rd_load_image
        init/do_mounts_rd.c:identify_ramdisk_image
        init/do_mounts_rd.c:crd_load
          lib/inflate.c:gunzip
    init/do_mounts.c:mount_root
      init/do_mounts.c:mount_block_root
         init/do_mounts.c:do_mount_root
           fs/namespace.c:sys_mount
  init/main.c:run_init_process
    execve

Arranque sin disco

Para arrancar un kernel y un sistema de archivos raíz de ramdisk no es necesario un disco local (disquete o CD-ROM), lo que es similar a los escenarios de arranque incorporado. Se puede utilizar el Dynamic Host Configuration Protocol (o DHCP) para identificar parámetros de la red, como la dirección IP y la máscara de subred. Después se puede utilizar el Protocolo Trivial de Transferencia de Archivos (o TFTP, por sus siglas en inglés) para transferir la imagen del kernel y la imagen del ramdisk inicial al dispositivo local. Una vez transferidas, el kernel Linux se puede arrancar y el initrd se puede montar, como se hace en el arranque de imágenes locales.

Cómo reducir el initrd

Cuando está construyendo un sistema incorporado y quiere la menor imagen de initrd posible, hay que considerar algunos consejos. El primero es utilizar BusyBox (como se muestra en este artículo). BusyBox toma varios megabytes de utilidades y los reduce a varios cientos de kilobytes.

En este ejemplo, la imagen de BusyBox está enlazada estáticamente para que no se requieran bibliotecas. Sin embargo, si necesita la biblioteca estándar de C (para sus binarios personalizados), hay otras opciones aparte del masivo glibc. La primera biblioteca pequeña es uClibc, que es una versión minimizada de la biblioteca estándar de C para sistemas con espacio limitado. dietlib es otra biblioteca que es ideal para los entornos con espacio limitado. Recuerde que tendrá que utilizar esas bibliotecas para recompilar los binarios que quiera poner en su sistema incorporado, así que necesitará realizar algo de trabajo extra (aunque merece la pena).

Resumen

El disco RAM inicial se creó originalmente para soportar la conexión del kernel al sistema de archivos raíz definitivo, a través de un sistema de archivos raíz transitorio. El initrd también es útil como sistema de archivos raíz no persistente, que se monta en un disco RAM para los sistemas Linux incorporados.


Recursos para Descargar


Temas relacionados

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=Linux
ArticleID=1062771
ArticleTitle=Visión general del disco RAM inicial de Linux (initrd)
publish-date=07312006