1. El segundo eslabón
Al final del tema 20, el firmware leyó un archivo .efi de la partición EFI y saltó a él. Podría parecer que lo natural sería cargar el kernel directamente. No lo hace, por una limitación concreta: el firmware solo entiende el sistema de archivos FAT, y el kernel está en /boot, dentro de una partición con el sistema de archivos de Linux, que el firmware no sabe leer. Hace falta un intermediario.
Un cargador de arranque (bootloader) es el programa que el firmware ejecuta para iniciar un sistema operativo. Localiza el kernel en el disco, lo copia a la memoria junto con los datos que necesita, y le transfiere el control.
Sus tareas son cinco:
| Tarea | En qué consiste |
|---|---|
| Leer su configuración | Un archivo que enumera los sistemas disponibles y cómo iniciar cada uno. |
| Ofrecer un menú | Para elegir entre varios sistemas operativos o varias versiones del kernel. |
| Cargar el kernel | Leer el archivo de la imagen y copiarlo a la memoria. |
| Cargar el initramfs | Un segundo archivo, que el kernel necesitará enseguida. Lo vemos en la sección 8. |
| Pasar los parámetros y saltar | Entregar al kernel una línea de texto con opciones, y cederle el procesador. |
El cargador es un programa de vida muy corta: se ejecuta durante uno o dos segundos y no deja nada suyo en memoria. Cuando el kernel toma el control, el cargador ha desaparecido.
En una instalación nativa o en una máquina virtual, todo lo de este tema se puede comprobar. En WSL no hay cargador ni directorio /boot: Windows inicia el kernel por su cuenta. Allí funcionan solo los comandos que consultan /proc/cmdline y dmesg.
2. GRUB
El cargador de casi todas las distribuciones de Linux es GRUB, de GRand Unified Bootloader, un programa del proyecto GNU. La versión en uso es la segunda, y por eso en algunos sistemas sus comandos y directorios se llaman grub2.
GRUB resuelve la limitación del firmware trayendo consigo lo que hace falta: sus propios drivers de sistemas de archivos. Sabe leer ext4, Btrfs, XFS y una veintena más, además de FAT. Es, como el firmware, un sistema operativo en miniatura, con un intérprete de comandos y módulos que se cargan según la necesidad.
Sus archivos están repartidos entre dos particiones, y cada una la lee un programa distinto:
El archivo grub.cfg de la partición EFI tiene tres líneas: indica en qué partición está el directorio /boot/grub y remite a la configuración completa que hay allí. Con eso, la secuencia queda así:
- El firmware ejecuta
shimx64.efi, que verifica la firma de GRUB, como vimos al tratar el arranque seguro. shimejecutagrubx64.efi.- GRUB lee el
grub.cfgmínimo, carga su driver de ext4 y abre la partición raíz. - Lee
/boot/grub/grub.cfgy actúa según lo que dice.
Con el firmware antiguo, GRUB debía empezar en los 446 bytes del MBR, donde no cabe casi nada. Ese fragmento cargaba una segunda parte, de unos 30 KB, guardada en los sectores libres entre el MBR y la primera partición. Es la razón del espacio de 1 MB que encontramos en el tema 20, con la primera partición en el sector 2048. La segunda parte ya contenía los drivers necesarios para llegar a /boot/grub. El arranque por etapas del que hablamos entonces se repetía dentro del propio cargador.
3. Leer una entrada del menú
Cada opción del menú de GRUB corresponde a un bloque menuentry del archivo de configuración. Este es el de la entrada principal, con algunas líneas omitidas:
menuentry 'Ubuntu' --class ubuntu --class gnu-linux --class os {
insmod gzio
insmod part_gpt
insmod ext2
search --no-floppy --fs-uuid --set=root 3f2a8c1e-7b4d-4e9a-b6c0-5d1f9e2a7c38
linux /boot/vmlinuz-6.8.0-45-generic root=UUID=3f2a8c1e-7b4d-4e9a-b6c0-5d1f9e2a7c38 ro quiet
initrd /boot/initrd.img-6.8.0-45-generic
}
| Línea | Qué hace |
|---|---|
insmod part_gpt, insmod ext2 | Carga módulos de GRUB: el que entiende tablas GPT y el que lee los sistemas de archivos de la familia ext. |
search --fs-uuid --set=root … | Recorre las particiones hasta encontrar la que tiene ese identificador, y la toma como base para las rutas siguientes. |
linux /boot/vmlinuz-… parámetros | Carga en memoria la imagen del kernel. Todo lo que sigue al nombre del archivo es la línea de parámetros que se entregará al kernel. |
initrd /boot/initrd.img-… | Carga en memoria el initramfs. |
Al llegar a la llave de cierre, GRUB ejecuta de manera implícita el comando boot: salta al kernel. Las dos líneas resaltadas contienen todo lo esencial, y son los tres elementos del resto del tema: la imagen, los parámetros y el initramfs.
El identificador de 36 caracteres aparece dos veces, con destinatarios distintos. En search lo usa GRUB, para encontrar la partición donde está el archivo del kernel. En root=UUID= lo usará el kernel, mucho después, para saber qué partición es la raíz del sistema. Suelen ser la misma, y no tienen por qué serlo.
Se usa un identificador, y no un nombre como /dev/nvme0n1p2, porque los nombres dependen del orden en que se detectan los discos y pueden cambiar al conectar otro. El UUID está grabado dentro del sistema de archivos:
$ findmnt -no SOURCE,UUID /
/dev/nvme0n1p2 3f2a8c1e-7b4d-4e9a-b6c0-5d1f9e2a7c38
En muchas instalaciones el menú no se muestra: GRUB inicia la primera entrada sin esperar. Para verlo hay que mantener pulsada la tecla Mayúsculas o pulsar Esc justo después del POST. Dentro del menú, tres teclas importan:
- Enter inicia la entrada seleccionada.
eabre la entrada en un editor. Se pueden cambiar sus líneas y arrancar con Ctrl+X. El cambio vale solo para esa vez.cabre el intérprete de comandos de GRUB, donde se puede explorar el disco conlsy escribir a mano las líneaslinux,initrdyboot.
4. Configurar GRUB
El archivo grub.cfg lleva en su primera línea la advertencia de que no debe editarse. Lo genera un programa, a partir de dos fuentes: un archivo de ajustes, /etc/default/grub, y un conjunto de scripts, en /etc/grub.d, que detectan los kernels instalados y los demás sistemas operativos del equipo.
GRUB_DEFAULT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_TIMEOUT=0
GRUB_CMDLINE_LINUX_DEFAULT="quiet"
GRUB_CMDLINE_LINUX=""
| Ajuste | Significado |
|---|---|
GRUB_DEFAULT | Qué entrada se inicia si no se elige otra. El 0 es la primera. |
GRUB_TIMEOUT_STYLE | Si el menú se muestra (menu) o queda oculto (hidden). |
GRUB_TIMEOUT | Cuántos segundos espera antes de iniciar la entrada por defecto. |
GRUB_CMDLINE_LINUX_DEFAULT | Parámetros del kernel para el arranque normal. |
GRUB_CMDLINE_LINUX | Parámetros para todos los arranques, incluido el de recuperación. |
Después de modificar ese archivo hay que regenerar la configuración:
$ sudo update-grub # Ubuntu y Debian
Sourcing file `/etc/default/grub'
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-6.8.0-45-generic
Found initrd image: /boot/initrd.img-6.8.0-45-generic
Found linux image: /boot/vmlinuz-6.8.0-41-generic
Found initrd image: /boot/initrd.img-6.8.0-41-generic
Found Windows Boot Manager on /dev/nvme0n1p1@/EFI/Microsoft/Boot/bootmgfw.efi
done
$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg # Fedora
La salida muestra al generador recorriendo /boot. Encuentra dos versiones del kernel: la actual y la anterior, que las distribuciones conservan a propósito. Si una actualización del kernel deja el sistema sin arrancar, la versión previa sigue disponible en el submenú de opciones avanzadas. La última línea detectada es el otro sistema operativo del equipo, cuyo cargador GRUB puede invocar.
El gestor de paquetes ejecuta este mismo comando cada vez que instala o elimina un kernel, y por eso el menú se mantiene al día sin intervención.
5. vmlinuz: la imagen del kernel
En el tema 11 encontramos el kernel como un archivo de 15 MB en /boot. El nombre tiene historia. Los kernels de Unix se llamaban unix; cuando incorporaron memoria virtual, vmunix; Linux adoptó vmlinux para su ejecutable, y vmlinuz, con zeta, para la versión comprimida.
$ sudo file /boot/vmlinuz-$(uname -r)
/boot/vmlinuz-6.8.0-45-generic: Linux kernel x86 boot executable bzImage, version 6.8.0-45-generic (buildd@lcy02-amd64-075) #45-Ubuntu SMP PREEMPT_DYNAMIC …, RO-rootFS, swap_dev 0XE, Normal VGA
No es un ejecutable ELF como los del tema 9, ni podría serlo: no hay nadie todavía que sepa cargar un ELF. Es un formato propio, llamado bzImage, formado por dos partes pegadas una a continuación de la otra:
| Parte | Tamaño | Contenido |
|---|---|---|
| Código de preparación | Pequeño, sin comprimir | Una cabecera con datos para el cargador, y un programa que sabe descomprimir. |
| El kernel | Casi todo el archivo, comprimido | El ejecutable vmlinux completo. |
Es un archivo autoextraíble. El cargador lo copia a la memoria tal como está y salta a la primera parte; esta descomprime la segunda en otra zona de la memoria, donde ocupa varias veces más, y le cede el control. Se comprime porque leer del disco era, y en el arranque sigue siendo, más lento que descomprimir en memoria: un archivo menor arranca antes.
La cabecera es el canal de comunicación entre el cargador y el kernel. GRUB lee de ella qué versión de protocolo entiende esa imagen, y escribe en ella lo que el kernel necesita saber: en qué dirección de memoria dejó el initramfs y dónde está la línea de parámetros.
6. Los parámetros del kernel
Un programa común recibe opciones por su línea de comandos. El kernel también: la línea que sigue al nombre de la imagen en grub.cfg. El kernel la conserva y la publica en /proc/cmdline, y es la segunda línea que escribe en su registro al arrancar, como vimos en el tema 11.
El siguiente programa la lee y la descompone:
#include <stdio.h>
#include <string.h>
int main(void)
{
char linea[4096];
FILE *f = fopen("/proc/cmdline", "r");
if (f == NULL) {
perror("/proc/cmdline");
return 1;
}
if (fgets(linea, sizeof(linea), f) == NULL) {
fclose(f);
return 1;
}
fclose(f);
/* separar por espacios; en cada parte, buscar el primer '=' */
for (char *p = strtok(linea, " \n"); p != NULL; p = strtok(NULL, " \n")) {
char *igual = strchr(p, '=');
if (igual == NULL) {
printf("%-12s (opcion sin valor)\n", p);
} else {
*igual = '\0';
printf("%-12s = %s\n", p, igual + 1);
}
}
return 0;
}
$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-6.8.0-45-generic root=UUID=3f2a8c1e-7b4d-4e9a-b6c0-5d1f9e2a7c38 ro quiet
$ gcc -Wall -Wextra -g cmdline.c -o cmdline
$ ./cmdline
BOOT_IMAGE = /boot/vmlinuz-6.8.0-45-generic
root = UUID=3f2a8c1e-7b4d-4e9a-b6c0-5d1f9e2a7c38
ro (opcion sin valor)
quiet (opcion sin valor)
Es la línea de grub.cfg, con un añadido: BOOT_IMAGE, que GRUB inserta para dejar constancia de qué archivo cargó. Los otros tres parámetros son los habituales:
root=indica qué partición contiene el sistema de archivos raíz.ropide montarla al principio en modo de solo lectura, para poder comprobar su integridad antes de escribir en ella. Más adelante se la vuelve a montar con escritura.quiethace que el kernel no muestre sus mensajes en pantalla durante el arranque. Siguen quedando en el registro.
Existen cientos de parámetros. Estos son los que más se usan al diagnosticar un problema:
| Parámetro | Efecto |
|---|---|
Quitar quiet | El kernel muestra todos sus mensajes. Es lo primero que se hace ante un arranque que se detiene. |
nomodeset | No activa el driver de video durante el arranque. Sirve cuando la pantalla queda en negro. |
init=/bin/bash | Ejecuta una shell como primer programa, en lugar del sistema de inicio normal. |
systemd.unit=rescue.target | Inicia un modo de recuperación, con los servicios mínimos. |
mem=4G | Limita la memoria que el kernel usará. Permite probar cómo se comporta un sistema con menos. |
console=ttyS0 | Envía los mensajes a un puerto serie, para equipos sin pantalla. |
El kernel reconoce los parámetros que le conciernen. Los que no reconoce no producen error: los entrega como argumentos al primer programa que ejecuta. Es el caso de systemd.unit, que el kernel ignora y el sistema de inicio interpreta.
7. El problema del disco raíz
El kernel está en memoria y conoce, por el parámetro root=, la partición que debe usar como raíz. Solo le falta montarla. Y aquí reaparece, en otra escala, el círculo del tema 20.
Para montar esa partición, el kernel necesita el driver del controlador del disco. En el tema 11 vimos que la mayor parte del kernel se distribuye en módulos, y que el del disco, nvme, era uno de ellos. Los módulos están en /lib/modules. Ese directorio está en la partición raíz.
El kernel necesita leer el disco para obtener el driver que le permitiría leer el disco. Y el caso del driver es el más simple. La partición raíz puede estar además:
- cifrada, y entonces hay que pedir una contraseña y descifrarla;
- repartida entre varios discos, que hay que detectar y ensamblar;
- en otro equipo de la red, y hay que configurar la tarjeta y obtener una dirección;
- identificada por un UUID, que es un dato del sistema de archivos: para encontrarlo hay que examinar todas las particiones.
Varias de esas tareas las hacen programas de usuario, no el kernel. Hacen falta, entonces, módulos y programas antes de tener un sistema de archivos donde guardarlos.
Una salida sería compilar dentro del kernel todos los drivers imaginables. Así se hace en los equipos de hardware fijo, pero para una distribución que debe arrancar en cualquier PC daría un kernel enorme. La solución general es otra.
8. initramfs: un sistema provisional en memoria
El initramfs es un archivo que contiene un sistema de archivos mínimo, con los módulos y programas necesarios para llegar a la partición raíz. El cargador de arranque lo copia a la memoria junto con el kernel, y el kernel lo usa como raíz provisional hasta poder montar la definitiva.
La idea aprovecha que el cargador ya sabe leer el disco. Si puede cargar un archivo, puede cargar dos: es la línea initrd de grub.cfg. El kernel recibe en memoria un paquete con todo lo que le falta, y no tiene que leer ningún dispositivo para usarlo.
El archivo es el initrd.img de 68 MB que vimos en /boot. Se puede inspeccionar sin extraerlo:
$ lsinitramfs /boot/initrd.img-$(uname -r) | wc -l
2184
$ lsinitramfs /boot/initrd.img-$(uname -r) | grep -E '^init$|bin/busybox|nvme\.ko'
init
usr/bin/busybox
usr/lib/modules/6.8.0-45-generic/kernel/drivers/nvme/host/nvme.ko.zst
En Fedora el comando equivalente es lsinitrd. Entre los dos mil archivos hay tres clases de contenido:
| Contenido | Ejemplo | Para qué |
|---|---|---|
Un programa llamado init, en la raíz | init | Es lo primero que el kernel ejecuta. Dirige todo el proceso. |
| Módulos del kernel | nvme.ko.zst | Los drivers de almacenamiento, de sistemas de archivos y de teclado. |
| Programas y bibliotecas | busybox | Una shell y utilidades reducidas, y las herramientas de cifrado y de volúmenes. |
busybox es un único ejecutable que reúne versiones simplificadas de más de cien comandos, de ls a mount. Es la manera habitual de tener un espacio de usuario completo en muy poco lugar.
El archivo lo construye la distribución, a medida para cada equipo y cada versión del kernel, y lo regenera cuando se instala un kernel nuevo. En Ubuntu lo hace update-initramfs, y en Fedora, dracut.
Su trabajo, una vez que el kernel lo ejecuta, es breve:
- Cargar los módulos del hardware detectado, entre ellos el del disco.
- Esperar a que aparezcan los dispositivos, y buscar la partición indicada por
root=. - Prepararla si hace falta: pedir la contraseña, ensamblar los volúmenes.
- Montarla.
- Cambiar de raíz: hacer que esa partición pase a ser
/, liberar la memoria que ocupaba el sistema provisional y ejecutar el programa de inicio de la partición real.
Tras el último paso, del initramfs no queda nada. Como el cargador de arranque, es un andamio: imprescindible durante la obra, y retirado al terminarla.
Los dos nombres se usan para lo mismo, por una razón histórica. El mecanismo original, initrd, cargaba en memoria la imagen de un disco, que el kernel trataba como un dispositivo más. El actual, initramfs, es un simple archivo empaquetado que el kernel extrae en un sistema de archivos en memoria. El nombre antiguo sobrevive en el comando de GRUB y en el archivo de /boot.
9. Del salto al primer programa
Con las tres piezas presentadas, esta es la secuencia desde que GRUB cede el control hasta que el sistema definitivo empieza a ejecutarse:
- GRUB copia a la memoria la imagen y el initramfs, anota en la cabecera de la imagen dónde dejó cada cosa y la línea de parámetros, despide al firmware y salta. cargador
- El código de preparación descomprime el kernel y salta a su punto de entrada. kernel
- El kernel se instala en la máquina. Lee el mapa de memoria que dejó el firmware, construye sus tablas de memoria y la tabla de vectores de interrupción del tema 17, y registra la entrada de las llamadas al sistema. kernel
- Inicializa sus subsistemas: el gestor de memoria, el planificador, el temporizador del tema 19. Habilita las interrupciones. kernel
- Pone en marcha los demás núcleos del procesador, que hasta este momento estaban detenidos. kernel
- Inicializa los drivers que lleva incorporados y recorre los buses, como describimos en el tema 8. kernel
- Extrae el initramfs en un sistema de archivos en memoria, que pasa a ser la raíz. kernel
- Crea el primer proceso y ejecuta en él
/init. Es la primera vez desde el encendido que la CPU pasa a modo usuario. modo usuario - El
initdel initramfs carga módulos, localiza la raíz y la monta. Cada una de esas acciones es una llamada al sistema. modo usuario - Cambia de raíz y ejecuta
/sbin/initde la partición real. modo usuario
El paso 8 es un momento singular. Todo lo anterior —firmware, cargador, inicialización del kernel— se ejecutó con privilegio total. A partir de ahí rige el orden que estudiamos en el tema 12: los programas en modo usuario, y el kernel atendiendo sus llamadas y las interrupciones. Lo que anticipamos allí con la frase «por orden de llegada» queda ahora completo.
El proceso creado en ese paso recibe el número 1, y no termina nunca. En el paso 10 no se crea otro: el mismo proceso reemplaza su programa por el del sistema real, con la llamada execve que vimos en el tema 16. Lo que ese programa hace es el contenido del tema 22.
10. El arranque, contado por el kernel
La secuencia anterior no hay que creerla: el kernel la deja escrita. Su registro empieza en el instante cero, y cada paso tiene su línea. Esta es una selección, con las marcas de tiempo en segundos:
$ sudo dmesg | less
[ 0.000000] Linux version 6.8.0-45-generic (buildd@lcy02-amd64-075) …
[ 0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-6.8.0-45-generic root=UUID=3f2a8c1e-… ro quiet
[ 0.000000] BIOS-e820: [mem 0x0000000000100000-0x00000000bd2a9fff] usable
[ 0.000000] efi: EFI v2.8 by American Megatrends
[ 0.058102] smp: Bringing up secondary CPUs ...
[ 0.071340] smp: Brought up 1 node, 4 CPUs
[ 0.214661] Trying to unpack rootfs image as initramfs...
[ 0.892315] Freeing initrd memory: 69632K
[ 1.012274] Run /init as init process
[ 1.480391] nvme nvme0: pci function 0000:01:00.0
[ 1.497120] nvme0n1: p1 p2
[ 2.105882] EXT4-fs (nvme0n1p2): mounted filesystem 3f2a8c1e-… ro with ordered data mode
[ 2.391005] systemd[1]: systemd 255.4-1ubuntu8 running in system mode
| Línea | Paso de la sección 9 | Qué muestra |
|---|---|---|
Command line | 1 | Los parámetros que entregó GRUB. |
BIOS-e820, efi | 3 | El kernel leyendo el mapa de memoria y las tablas del firmware. |
Bringing up secondary CPUs | 5 | El arranque de los otros núcleos. Hasta aquí trabajaba uno solo. |
Trying to unpack rootfs image | 7 | La extracción del initramfs, y luego la liberación de la memoria que ocupaba el archivo. |
Run /init as init process | 8 | El primer programa de usuario. Ocurre un segundo después de empezar. |
nvme nvme0, nvme0n1: p1 p2 | 9 | El módulo del disco, recién cargado, detecta el dispositivo y sus dos particiones. |
EXT4-fs … mounted filesystem … ro | 9 | La partición raíz montada, en solo lectura, como pedía el parámetro. |
systemd[1] | 10 | El programa de inicio del sistema real, ya en ejecución como proceso 1. |
El orden de las líneas resuelve la paradoja de la sección 7. El disco aparece en el registro después de Run /init: cuando el primer programa empezó, el kernel todavía no podía ver ningún disco. Fue ese programa, tomado de la memoria, el que cargó el módulo que lo hizo visible.
Las líneas concretas varían con la versión del kernel y con el equipo. Para encontrar estos hitos en el tuyo sirve sudo dmesg | grep -E 'Command line|smp:|initramfs|Run /init|EXT4-fs|systemd\[1\]'.
11. Cuánto tarda cada etapa
El sistema de inicio registra la duración de cada eslabón de la cadena, y la informa con un comando:
$ systemd-analyze
Startup finished in 4.512s (firmware) + 2.210s (loader) + 2.391s (kernel) + 5.933s (userspace) = 15.046s
graphical.target reached after 5.901s in userspace.
| Etapa | Abarca | Tema |
|---|---|---|
firmware | Desde el encendido hasta que el firmware ejecuta el cargador: inicialización y POST. | 20 |
loader | El tiempo de GRUB: leer la configuración, esperar en el menú, cargar los dos archivos. | 21 |
kernel | Desde que el kernel recibe el control hasta que ejecuta el programa de inicio del sistema real. Incluye el initramfs. | 21 |
userspace | El sistema de inicio poniendo en marcha los servicios hasta mostrar la pantalla de acceso. | 22 |
Es el diagrama de cuatro eslabones del tema 20, con un cronómetro en cada uno. Los dos primeros tiempos los mide el firmware y los comunica al sistema; en un equipo con BIOS, o en una máquina virtual, no aparecen. En algunas distribuciones, el tiempo del initramfs se informa aparte.
Los valores sorprenden la primera vez. El kernel, que es la pieza más compleja, resulta la etapa más breve. El firmware suele tardar más que él. Y la parte mayor corresponde al espacio de usuario, que es donde hay más margen para ajustar.
12. Cuando el arranque falla
Un equipo que no arranca parece un problema opaco, pero la cadena tiene eslabones bien definidos, y lo que aparece en pantalla indica cuál se rompió:
| En pantalla | Eslabón | Causa habitual |
|---|---|---|
| Un mensaje del firmware: «No bootable device» | Firmware | No hay entrada de arranque válida, o el disco no se detecta. |
El indicador grub rescue> | GRUB, al empezar | GRUB se inició pero no encuentra su directorio /boot/grub: la partición cambió de lugar o fue borrada. |
El indicador grub> | GRUB | Encontró sus módulos, pero no el archivo de configuración. |
Kernel panic - not syncing: VFS: Unable to mount root fs | Kernel | El kernel arrancó sin initramfs, o sin el driver del disco. |
El indicador (initramfs) | initramfs | No encontró la partición indicada en root=: el UUID no coincide con ninguna. |
| Pantalla en negro después del menú | Kernel | El driver de video. Se prueba con nomodeset. |
| «You are in emergency mode» | Espacio de usuario | La raíz se montó, pero un paso posterior falló. Es asunto del tema 22. |
La cuarta fila es el pánico del kernel que presentamos en el tema 18, en su variante más frecuente. El kernel funciona, pero no tiene raíz ni primer programa que ejecutar, y sin eso no puede hacer nada. La quinta es más amable: el initramfs no logra su cometido y, en lugar de detenerse, ofrece su shell de busybox para investigar.
El recurso principal ante cualquiera de estos problemas es el editor de GRUB. Pulsando e sobre una entrada se puede modificar la línea linux para esa única vez:
- Quitar
quiet, para ver en qué mensaje se detiene el arranque. - Si eso no basta, añadir
systemd.unit=rescue.target, o en último casoinit=/bin/bash, para obtener una shell de administrador con el sistema apenas iniciado. - O elegir, en las opciones avanzadas del menú, la versión anterior del kernel.
El segundo punto tiene una lectura inquietante. Cualquiera que se siente ante el equipo puede editar la entrada, añadir init=/bin/bash y obtener una shell de root sin que nadie le pida una contraseña. Las contraseñas las comprueba el sistema en marcha, y aquí el sistema todavía no arrancó. No es un defecto de GRUB: quien controla el arranque controla la máquina. Las defensas son poner una contraseña al propio GRUB y, sobre todo, cifrar el disco, de modo que sin la clave no haya nada que leer.
13. Otros cargadores
GRUB es el más extendido, y no el único. Las alternativas muestran que las tareas de la sección 1 pueden repartirse de otras maneras:
| Cargador | Dónde se usa | Particularidad |
|---|---|---|
| GRUB | La mayoría de las distribuciones | Funciona con BIOS y con UEFI, y lee casi cualquier sistema de archivos. |
| systemd-boot | Algunas distribuciones, solo con UEFI | Muy simple. No tiene drivers propios: exige que el kernel esté en la partición EFI, donde el firmware puede leerlo. |
| El kernel, directamente | Sistemas mínimos y contenedores de arranque | La imagen de Linux incluye una cabecera que la hace pasar por un programa .efi. El firmware puede ejecutarla sin cargador. |
| U-Boot | Placas ARM y equipos embebidos | Hace a la vez de firmware y de cargador. |
| Windows Boot Manager | Windows | Es el archivo bootmgfw.efi que vimos en la partición EFI. |
La tercera fila responde la pregunta con que empezó el tema. El firmware sí puede cargar el kernel directamente, con una condición: que el archivo esté donde él sabe leer. Un desarrollo reciente lleva la idea al extremo y reúne el kernel, el initramfs y la línea de parámetros en un único archivo .efi, firmado como un todo. Simplifica la cadena de confianza del arranque seguro, porque ya no hay partes sueltas que verificar por separado.
Hay, por último, un caso en que el cargador es el propio kernel. La llamada al sistema kexec permite a un kernel en ejecución cargar otro en memoria y saltar a él, sin pasar por el firmware. Se usa para reiniciar servidores en segundos, saltándose un POST que en esos equipos puede durar minutos.
14. Resumen
- El cargador de arranque existe porque el firmware no sabe leer el sistema de archivos donde está el kernel. Lo localiza, lo copia a la memoria y le cede el control.
- GRUB trae sus propios drivers de sistemas de archivos. Su parte inicial está en la partición EFI; su configuración y sus módulos, en
/boot/grub. - Cada entrada del menú se reduce a dos líneas:
linux, con la imagen y los parámetros, einitrd, con el initramfs. grub.cfgno se edita: se genera conupdate-gruba partir de/etc/default/grub.vmlinuzes una imagen autoextraíble: un pequeño programa descompresor seguido del kernel comprimido.- Los parámetros del kernel son su línea de comandos. Se consultan en
/proc/cmdline, y los más comunes sonroot=,royquiet. - El kernel no puede montar la raíz sin drivers que están guardados en la raíz. El initramfs rompe ese círculo.
- El initramfs es un sistema de archivos mínimo que el cargador deja en memoria. Su programa
initcarga los módulos, monta la raíz real y le cede el lugar. - El kernel se descomprime, construye sus tablas, inicia sus subsistemas y los demás núcleos, y ejecuta
/initcomo proceso 1: el primer paso a modo usuario. dmesgregistra cada paso con su marca de tiempo, ysystemd-analyzeinforma la duración de cada etapa.- Lo que aparece en pantalla cuando el arranque falla indica qué eslabón se rompió. El editor de GRUB permite cambiar los parámetros para un solo arranque.
15. Preguntas de repaso
Intenta responder cada una antes de abrir la respuesta.
1. Si el firmware ya sabe cargar un archivo, ¿por qué hace falta un cargador de arranque?
Porque el firmware solo entiende el sistema de archivos FAT de la partición EFI, y el kernel está normalmente en una partición con el sistema de archivos de Linux. El cargador trae los drivers necesarios para leerla, y además ofrece un menú y pasa los parámetros.
2. ¿Qué hacen las líneas linux e initrd de una entrada de GRUB?
linux carga en memoria la imagen del kernel y define la línea de parámetros que se le entregará. initrd carga en memoria el archivo del initramfs.
3. En una entrada de GRUB aparece dos veces el mismo UUID. ¿Quién usa cada una?
El de search lo usa GRUB, para localizar la partición donde están los archivos del kernel. El de root=UUID= lo usa el sistema, ya iniciado el kernel, para saber qué partición montar como raíz.
4. ¿Por qué se identifica la partición raíz por UUID y no por un nombre como /dev/sda2?
Porque los nombres de dispositivo dependen del orden en que el kernel detecta los discos, y pueden cambiar al añadir o quitar uno. El UUID está grabado en el sistema de archivos y no cambia.
5. ¿Por qué no hay que editar /boot/grub/grub.cfg?
Porque es un archivo generado. Cada vez que se instala un kernel o se ejecuta update-grub, se vuelve a crear a partir de /etc/default/grub y de los scripts de /etc/grub.d, y los cambios hechos a mano se pierden.
6. ¿Qué significa que vmlinuz sea autoextraíble?
Que el archivo contiene, delante del kernel comprimido, un pequeño programa sin comprimir que sabe descomprimirlo. El cargador solo copia el archivo a la memoria y salta a ese programa.
7. ¿Qué hace el kernel con un parámetro que no reconoce?
No lo trata como error. Lo entrega como argumento al primer programa que ejecuta, que puede interpretarlo. Así llegan al sistema de inicio opciones como systemd.unit=.
8. Explica el círculo que resuelve el initramfs.
Para montar la partición raíz, el kernel necesita el driver del disco, y a veces programas para descifrar o ensamblar volúmenes. Esos módulos y programas están guardados en la partición raíz. El initramfs los pone en memoria, cargados por el cargador de arranque, antes de que haga falta leer ningún disco.
9. ¿Qué queda del initramfs una vez que el sistema arrancó?
Nada. Después de montar la raíz real, su programa init cambia de raíz, libera la memoria que ocupaba el sistema provisional y ejecuta el programa de inicio de la partición definitiva.
10. En dmesg, la línea del disco aparece después de Run /init as init process. ¿Por qué?
Porque el driver del disco es un módulo, y quien lo carga es el programa init del initramfs. Hasta que ese programa se ejecuta, el kernel no tiene cómo ver el disco.
11. ¿Cuál es el primer momento, desde el encendido, en que la CPU se ejecuta en modo usuario?
Cuando el kernel crea el primer proceso y ejecuta en él el programa /init del initramfs. El firmware, el cargador y la inicialización del kernel se ejecutan con privilegio total.
12. El equipo muestra Kernel panic - not syncing: VFS: Unable to mount root fs. ¿Qué eslabones funcionaron y cuál falló?
El firmware y el cargador funcionaron, y el kernel se inició. Falló el montaje de la raíz: el kernel no recibió un initramfs, o no dispone del driver para ese disco o ese sistema de archivos.
13. ¿Por qué añadir init=/bin/bash da una shell de administrador sin pedir contraseña?
Porque el kernel ejecuta como primer proceso el programa que se le indique, con todos los privilegios. Las contraseñas las verifica el sistema de inicio de sesión, que en ese arranque no llega a ejecutarse.
Para practicar
- Ejecuta
cmdlineen tu sistema y explica cada parámetro. Compara el valor derootcon la salida defindmnt -no SOURCE,UUID /. - Busca la entrada principal con
sudo grep -A12 "^menuentry" /boot/grub/grub.cfg | head -20e identifica las líneassearch,linuxeinitrd. - Cuenta cuántos kernels hay instalados con
ls /boot/vmlinuz-*. ¿Cuál está en uso, segúnuname -r? - Compara el tamaño de la imagen del kernel con el de su initramfs. ¿Cuál es mayor, y por qué?
- Busca en tu initramfs el módulo del controlador de tu disco y el programa
init. Averigua conlsmodqué otros módulos cargados deberían estar allí. - Localiza en
dmesglos hitos de la sección 10 y calcula cuánto tiempo pasó entreRun /inity el montaje de la raíz. - Ejecuta
systemd-analyzey ordena las etapas de mayor a menor. Prueba tambiénsystemd-analyze blame | head. - En una máquina virtual, reinicia, abre el menú de GRUB, pulsa
ey quitaquietde la línealinux. Arranca con Ctrl+X y observa los mensajes. Comprueba al reiniciar que el cambio no se conservó. - También en una máquina virtual, arranca con
init=/bin/bash. ¿Qué número de proceso tiene la shell, segúnecho $$? Ejecutamount | head -3y observa cómo está montada la raíz.