El núcleo de Linux tiene más de 30 millones de líneas de código. El de MINIX 3 tiene unas 12.000 en su parte privilegiada. Ambos hacen esencialmente lo mismo, pero han tomado decisiones opuestas sobre qué código merece ejecutarse con todos los privilegios de la máquina. Esa decisión —cuánto meter dentro del núcleo y cuánto dejar fuera— es la gran cuestión arquitectónica de los sistemas operativos, y lleva cuarenta años sin resolverse del todo. En esta lección verás las respuestas que se han dado, entenderás por qué cada una acierta y falla en cosas distintas, y comprobarás en meteo-01 cómo Linux gestiona un compromiso pragmático con los módulos cargables. Al final sabrás explicar por qué un fallo en un controlador de disco puede tumbar el servidor entero en un diseño y no en otro.

Contenido

  1. La pregunta de fondo: qué va dentro del núcleo
  2. Sistemas monolíticos
  3. Diseño por capas
  4. Microkernel y el paso de mensajes
  5. El coste de rendimiento del microkernel
  6. Núcleos híbridos
  7. Exokernel y unikernel
  8. Módulos cargables del núcleo en Linux
  9. Tabla comparativa
  10. Ejemplo aplicado: un controlador defectuoso en meteo-01

La pregunta de fondo: qué va dentro del núcleo

Recuerda de la primera lección: el código que se ejecuta en modo núcleo puede hacer cualquier cosa con la máquina —acceder a toda la memoria, programar cualquier dispositivo, deshabilitar interrupciones. El código en modo usuario solo puede pedir favores al núcleo.

De ahí sale la tensión central del diseño:

  • Cuanto más código hay dentro del núcleo, más rápido va. Dos partes del núcleo se comunican con una simple llamada a función: unos pocos nanosegundos, sin cambio de contexto ni copia de datos.
  • Cuanto más código hay dentro del núcleo, más frágil e inseguro es el sistema. Un puntero erróneo en cualquier línea de esos millones puede corromper cualquier estructura del sistema. No hay red de seguridad: en modo núcleo no existe la protección de memoria que sí protege a los procesos.

Todas las arquitecturas que vamos a ver son posiciones distintas en esa balanza entre rendimiento y aislamiento.

Sistemas monolíticos

En un núcleo monolítico, todos los servicios del sistema operativo —planificador, gestor de memoria, sistemas de archivos, pila de red, controladores de dispositivos— se compilan en un único programa que se ejecuta entero en modo núcleo, en un mismo espacio de direcciones.

Cómo se comunican sus partes: mediante llamadas a función normales. Cuando el sistema de archivos ext4 necesita leer un bloque, llama directamente a la función del controlador del disco. Coste: nanosegundos.

Ventajas.

  • Rendimiento máximo. No hay fronteras que cruzar dentro del núcleo.
  • Acceso directo a las estructuras compartidas. El planificador puede consultar directamente el estado de memoria de un proceso sin pedirlo a nadie.
  • Simplicidad conceptual para quien programa dentro: todo está accesible.

Inconvenientes.

  • Sin aislamiento interno. Un fallo en cualquier parte puede corromperlo todo.
  • Difícil de mantener. Con millones de líneas y todo accesible desde todo, las dependencias implícitas proliferan.
  • Una parte no se puede reiniciar sin reiniciar el sistema entero.

Ejemplos: Linux, los UNIX clásicos, FreeBSD, MS-DOS (monolítico y además sin protección alguna).

Una precisión frecuente: que Linux sea monolítico no significa que esté mal estructurado. Tiene interfaces internas muy claras (el VFS para sistemas de archivos, el modelo de dispositivos, la API de módulos). Lo que define lo monolítico no es el desorden sino que todo comparte el mismo espacio de direcciones privilegiado.

Diseño por capas

El diseño por capas organiza el núcleo en niveles, donde cada capa solo puede usar servicios de la capa inmediatamente inferior y ofrece servicios a la superior. El sistema THE (Dijkstra, 1968) fue el ejemplo canónico, con seis niveles del 0 (planificación) al 5 (el operador).

La ventaja es la comprobabilidad: puedes verificar cada capa asumiendo que las inferiores son correctas, lo que reduce enormemente el esfuerzo de razonamiento.

El problema es que la realidad no se deja ordenar en capas estrictas. ¿Va el gestor de memoria por debajo del sistema de archivos? Parece que sí, porque el sistema de archivos necesita búferes en memoria. Pero el gestor de memoria necesita el área de intercambio (swap), que está en el disco, que gestiona el sistema de archivos. Hay una dependencia circular real, y ninguna ordenación de capas la resuelve limpiamente.

Por eso el diseño por capas puro casi no se usa, aunque su influencia es enorme: casi todos los núcleos modernos están estructurados en capas aproximadas, con niveles que se saltan cuando hace falta.

Microkernel y el paso de mensajes

La idea del microkernel es llevar el aislamiento al extremo: dejar en modo núcleo únicamente lo que es imposible hacer de otro modo, y sacar todo lo demás a procesos de usuario ordinarios llamados servidores.

Lo que queda dentro del microkernel es muy poco:

  • Gestión de espacios de direcciones (lo mínimo, porque requiere tocar la MMU).
  • Planificación básica de hilos.
  • Comunicación entre procesos (IPC), que es su función estrella.

Fuera, como procesos normales sin privilegios: el sistema de archivos, la pila de red, los controladores de dispositivos, el gestor de memoria de alto nivel, la gestión de procesos.

Cómo se comunican ahora las partes: mediante paso de mensajes. Cuando una aplicación quiere leer un fichero, envía un mensaje al servidor de ficheros; ese servidor envía otro mensaje al servidor del disco; la respuesta recorre el camino inverso. El microkernel se limita a transportar mensajes de un proceso a otro.

graph TB
    subgraph MONO["Núcleo monolítico (Linux)"]
        direction TB
        MU1["Modo usuario: meteo-api · ingestor · bash"]
        MK1["Modo núcleo: planificador + memoria + VFS + ext4<br/>+ pila TCP/IP + driver de disco + driver de red"]
        MU1 -->|"llamada al sistema"| MK1
        MK1 -->|"llamadas a función internas (ns)"| MK1
    end

    subgraph MICRO["Microkernel (MINIX 3)"]
        direction TB
        MU2["Modo usuario: meteo-api"]
        S1["Servidor de ficheros"]
        S2["Driver de disco"]
        S3["Servidor de procesos"]
        MK2["Modo núcleo: IPC + planificación mínima + MMU"]
        MU2 -->|"mensaje"| MK2
        MK2 -->|"mensaje"| S1
        S1 -->|"mensaje"| MK2
        MK2 -->|"mensaje"| S2
        MK2 -->|"mensaje"| S3
    end

Lo que el diagrama muestra es la diferencia esencial: en el monolítico, la caja de modo núcleo es enorme y sus partes se llaman entre sí gratis; en el microkernel, la caja privilegiada es diminuta y cada interacción entre componentes cruza la frontera de modo dos veces.

Las ventajas del microkernel son reales y notables:

  • Aislamiento de fallos. Si el controlador de disco falla, es un proceso de usuario el que muere. El sistema puede detectarlo y reiniciarlo sin que la máquina se caiga.
  • Núcleo verificable. seL4, un microkernel de unas 10.000 líneas, tiene una demostración matemática formal de que su implementación cumple su especificación. Esto es sencillamente imposible con 30 millones de líneas.
  • Superficie de ataque mínima. Un fallo en el servidor de ficheros no da control de la máquina, solo de ese servidor.
  • Mantenibilidad. Los componentes tienen interfaces explícitas y obligatorias: no hay atajos posibles.

Ejemplos: MINIX 3 (usado también en el Intel Management Engine, lo que lo convierte en uno de los sistemas más desplegados del mundo sin que casi nadie lo sepa), QNX (muy usado en automoción y sistemas críticos), L4 y seL4, GNU Hurd.

El coste de rendimiento del microkernel

Aquí está el problema histórico del microkernel, y merece la pena cuantificarlo porque es la razón de que no haya ganado.

Comparemos lo que cuesta que meteo-api lea un bloque del fichero del día:

En un monolítico:

1. Llamada al sistema read()        → 1 cambio de modo (~100-500 ns)
2. VFS llama a ext4                 → llamada a función (~ns)
3. ext4 llama al driver de disco    → llamada a función (~ns)
4. Retorno al usuario               → 1 cambio de modo
Total en sobrecoste: ~2 cambios de modo

En un microkernel:

1. read() → mensaje al microkernel        → cambio de modo
2. microkernel → servidor de ficheros     → cambio de contexto + copia
3. servidor ficheros → microkernel        → cambio de modo
4. microkernel → driver de disco          → cambio de contexto + copia
5. driver → microkernel (con los datos)   → cambio de modo
6. microkernel → servidor de ficheros     → cambio de contexto + copia
7. servidor de ficheros → microkernel     → cambio de modo
8. microkernel → meteo-api                → cambio de contexto + copia
Total en sobrecoste: ~8 cambios de modo y 4 cambios de contexto

Un cambio de contexto cuesta del orden de 1 a 5 µs, contando el vaciado de cachés y de la TLB. Si estimamos 2 µs por cambio de contexto, el microkernel añade unos 8 µs de sobrecoste a una operación que en un SSD tarda 50 µs: un 16 % más de latencia. Y para operaciones que se resuelven en caché de páginas (0,08 µs), el sobrecoste sería de 100 veces el trabajo útil.

Este es exactamente el argumento de Linus Torvalds en su célebre debate de 1992 con Andrew Tanenbaum, autor de MINIX. Tanenbaum sostenía que el diseño monolítico era "un salto atrás a los años 70"; Torvalds respondía que la portabilidad y el rendimiento importaban más que la elegancia. Ambos tenían razón en su terreno, y la historia les ha dado la razón a los dos en ámbitos distintos: Linux domina los servidores, QNX domina la automoción crítica.

Matiz importante y actual: el coste del paso de mensajes se ha reducido muchísimo. Jochen Liedtke demostró con L4 en los años 90 que una implementación de IPC muy cuidada podía ser 10-20 veces más rápida que las de Mach, hasta el punto de hacer el microkernel viable. Descartar el microkernel citando cifras de 1990 es un error frecuente.

Núcleos híbridos

Un núcleo híbrido adopta la estructura conceptual del microkernel (componentes con interfaces claras, algunos servicios como servidores) pero ejecuta la mayoría de ellos dentro del espacio de direcciones del núcleo para evitar el coste del paso de mensajes.

  • Windows NT y todos sus descendientes: tiene una capa de abstracción de hardware (HAL), un ejecutivo con subsistemas bien delimitados y un micronúcleo interno, pero controladores y gestor gráfico corren en modo núcleo por rendimiento. La decisión de mover el subsistema gráfico al núcleo en NT 4.0 fue exactamente este compromiso: más velocidad a cambio de que un fallo del driver gráfico pudiera tumbar el sistema.
  • macOS / XNU: el nombre significa X is Not Unix. Combina el microkernel Mach (que aporta IPC, gestión de memoria y hilos) con un servidor BSD monolítico que aporta la interfaz UNIX, sistemas de archivos y red, todo en el mismo espacio de direcciones. Tiene la estructura de un microkernel sin pagar el coste del paso de mensajes entre sus dos mitades.

El término "híbrido" tiene detractores: Torvalds ha señalado que un microkernel cuyos servidores corren todos en modo núcleo es, funcionalmente, un monolítico bien estructurado. Es una crítica justa desde el punto de vista del aislamiento, que es lo que de verdad distingue las arquitecturas.

Exokernel y unikernel

Dos ideas más radicales que conviene conocer aunque su uso sea minoritario.

Exokernel (MIT, años 90). Parte de una crítica: las abstracciones del sistema operativo, aunque cómodas, imponen decisiones que a veces perjudican a la aplicación. Una base de datos sabe mejor que el sistema operativo cómo debe cachear sus propios bloques, pero está obligada a pasar por la caché genérica del núcleo.

La propuesta del exokernel es que el núcleo solo proteja y multiplexe el hardware, sin abstraerlo, y que cada aplicación construya (con una biblioteca) las abstracciones que le convengan. Nunca se adoptó comercialmente, pero su crítica está viva: mecanismos actuales como O_DIRECT (saltarse la caché del núcleo), io_uring o el acceso a la red desde espacio de usuario son concesiones exokernel dentro de sistemas convencionales.

Unikernel. Lleva la idea a su extremo práctico: se compila la aplicación junto con las partes del sistema operativo que necesita en una única imagen que arranca directamente sobre un hipervisor. No hay separación entre aplicación y núcleo porque solo hay una aplicación.

Aplicado a Meteora: un unikernel de meteo-api sería una imagen de unos pocos megabytes que contendría el servidor HTTP, la pila TCP/IP y un sistema de ficheros mínimo de solo lectura. Arrancaría en milisegundos y no incluiría ni bash, ni ssh, ni gestor de usuarios, ni nada que un atacante pudiera aprovechar. Sus inconvenientes son igual de claros: no se puede depurar entrando por SSH, cada cambio exige recompilar la imagen entera, y el ecosistema de herramientas es escaso. Ejemplos: MirageOS, IncludeOS, Unikraft.

Módulos cargables del núcleo en Linux

Linux es monolítico, pero no rígido. Los módulos cargables (Loadable Kernel Modules, LKM) permiten añadir y quitar código del núcleo en caliente, sin reiniciar. Es la respuesta pragmática al principal inconveniente práctico del monolitismo: no tener que compilar un núcleo distinto para cada combinación de hardware.

Muy importante: un módulo se ejecuta en modo núcleo, con todos los privilegios. No hay ningún aislamiento adicional. Los módulos aportan flexibilidad de despliegue, no robustez.

lsmod | head -6
Module                  Size  Used by
ext4                  978944  1
nvme                   57344  3
e1000e                307200  0
crc32c_intel           16384  1
xfs                  2170880  0
loop                   32768  0

Columna a columna:

  • Module: nombre del módulo. ext4 es el sistema de archivos, nvme el controlador del SSD, e1000e el de la tarjeta de red Intel.
  • Size: bytes de código que ocupa en memoria del núcleo. xfs ocupa 2,1 MB.
  • Used by: cuántos otros componentes dependen de él. ext4 tiene 1 porque hay un sistema de archivos montado usándolo. xfs tiene 0: está cargado pero no se usa, y podría descargarse para ahorrar memoria y reducir superficie de ataque.

Para investigar un módulo concreto:

modinfo e1000e | head -8
filename:       /lib/modules/6.1.0-18-amd64/kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko
version:        3.2.6-k
license:        GPL v2
description:    Intel(R) PRO/1000 Network Driver
author:         Intel Corporation
srcversion:     A1B2C3D4E5F6A7B8C9D0
depends:
retpoline:      Y
intree:         Y

Qué mirar aquí y por qué:

  • filename: la ruta del fichero .ko (kernel object). Es código compilado para esta versión exacta del núcleo; un módulo compilado para otra versión no cargará.
  • license: GPL v2: si un módulo no declara licencia compatible con GPL, el núcleo se marca como "contaminado" (tainted) y muchos desarrolladores no aceptarán informes de fallo. Es un mecanismo de presión legal y técnica a la vez.
  • depends: otros módulos necesarios. Vacío aquí; insmod fallaría si faltasen dependencias, mientras que modprobe las resuelve automáticamente.
  • intree: Y: el módulo forma parte del árbol oficial del núcleo, no es de un tercero. Los módulos fuera del árbol (típicamente controladores propietarios de tarjetas gráficas) son una fuente clásica de inestabilidad.

Gestión de módulos:

sudo modprobe -r xfs        # descargar el módulo xfs y lo que dependa de él
sudo modprobe xfs           # cargarlo, resolviendo dependencias
cat /proc/sys/kernel/tainted
  • modprobe -r descarga. Solo funciona si el Used by es 0; si algo lo usa, falla con Module xfs is in use. Es una protección elemental, porque descargar un módulo en uso corrompería el sistema.
  • modprobe (sin -r) carga y resuelve dependencias, a diferencia de insmod, que carga un fichero .ko concreto sin resolverlas.
  • /proc/sys/kernel/tainted devuelve 0 si el núcleo está limpio. Un valor distinto indica que se ha cargado algo no oficial o que ha ocurrido un error grave previo. Es lo primero que conviene mirar al diagnosticar inestabilidad inexplicable.

Tabla comparativa

Criterio Monolítico Por capas Microkernel Híbrido Unikernel
Código en modo núcleo Todo Todo Mínimo (~10-50 K líneas) Casi todo Todo (una sola app)
Rendimiento Muy alto Alto Menor (paso de mensajes) Muy alto Muy alto
Aislamiento de fallos Ninguno dentro del núcleo Ninguno Excelente Escaso No aplica
Un driver defectuoso Tumba el sistema Tumba el sistema Muere y se reinicia Tumba el sistema Tumba la imagen
Mantenibilidad Difícil a gran escala Buena en teoría Muy buena Buena Simple pero rígida
Verificación formal Inviable Difícil Alcanzada (seL4) Inviable Posible en el ámbito
Extensible en caliente Sí, con módulos Depende Sí (reiniciar servidores) Sí, con drivers No
Ejemplos Linux, FreeBSD THE, Multics (parcial) MINIX 3, QNX, seL4 Windows NT, macOS/XNU MirageOS, Unikraft
Dónde domina Servidores, escritorio, móvil Histórico y académico Automoción, aviónica, crítico Escritorio de consumo Nube especializada

Una lectura útil de esta tabla: cada arquitectura gana en el ámbito donde su prioridad es la que importa. Linux domina donde manda el rendimiento y la variedad de hardware. QNX domina donde un fallo cuesta vidas y hay que certificar el sistema ante un regulador. No hay una arquitectura mejor en abstracto.

Ejemplo aplicado: un controlador defectuoso en meteo-01

Imaginemos que el controlador e1000e de la tarjeta de red tiene un error: en una condición rara —un paquete malformado con una longitud declarada mayor que la real— escribe 64 bytes más allá del final de un búfer.

En Linux (monolítico)

El controlador se ejecuta en modo núcleo, en el mismo espacio de direcciones que todo lo demás. Esos 64 bytes se escriben sobre lo que hubiera a continuación en la memoria del núcleo. Dos escenarios:

  • Escenario visible. La zona corrompida contiene punteros de una estructura crítica. Al usarlos, el núcleo provoca un kernel panic: se detiene por completo. meteo-01 se cae entero, con ingestor, agregador, meteo-api y todo lo demás. Se pierden los datos aún no volcados a disco (los 8 MB de Dirty que veíamos en la lección anterior) y hay que reiniciar la máquina.
  • Escenario peor: corrupción silenciosa. La zona corrompida contiene datos de la caché de páginas, concretamente parte del fichero 2026-08-31.dat. No hay ningún fallo visible. El sistema sigue funcionando. meteo-api sirve durante horas lecturas con valores erróneos a los clientes de Meteora, y más tarde el núcleo vuelca esa caché corrupta al disco, haciendo el daño permanente. Nadie se entera hasta que un cliente reclama.

El segundo escenario es peor que el primero, y es la consecuencia más grave de la falta de aislamiento: no es solo que un fallo pueda tumbar el sistema, es que puede no tumbarlo y corromper datos en silencio.

Si el sistema llega a caerse, quedaría rastro:

sudo dmesg -T | grep -iE 'panic|oops|BUG' | tail -3
sudo journalctl -k -b -1 -p err --no-pager | tail -5
  • dmesg -T muestra el búfer de mensajes del núcleo con marcas de tiempo legibles (-T).
  • journalctl -k filtra solo mensajes del núcleo; -b -1 los del arranque anterior, que es donde estará la causa de la caída; -p err limita a nivel de error.

En MINIX 3 o QNX (microkernel)

El controlador de red es un proceso de usuario con su propio espacio de direcciones. Al escribir 64 bytes fuera de su búfer, ocurre una de dos cosas:

  • Si la dirección está fuera de las páginas asignadas al proceso, el hardware genera un fallo de página y el sistema mata al proceso del controlador. El resto del sistema sigue intacto.
  • Si la dirección cae dentro de la memoria del propio controlador, corrompe sus propios datos, pero la corrupción está confinada: no puede alcanzar la caché de páginas ni las estructuras del núcleo.

En MINIX 3 hay además un componente llamado servidor de reencarnación (reincarnation server) que vigila a los controladores y los reinicia automáticamente al detectar que han muerto. El resultado práctico sería:

  • Interrupción del servicio de red durante unos milisegundos.
  • Algunas lecturas de estaciones perdidas, que se reenviarán.
  • ingestor, agregador y meteo-api siguen vivos, con sus conexiones TCP posiblemente rotas pero su estado intacto.
  • Los datos ya recibidos no se corrompen.
  • Una entrada en el registro y ninguna llamada de madrugada.

La conclusión honesta

Con este ejemplo parece evidente que el microkernel es mejor, y en robustez lo es. Pero hay que ser justos con el conjunto:

  • Ese fallo del controlador es muy poco frecuente. Los controladores del árbol oficial de Linux están probados en millones de máquinas.
  • El coste de rendimiento del microkernel se paga en todas y cada una de las operaciones, no solo cuando hay fallos.
  • Linux tiene sus propias mitigaciones: KASLR, memoria del núcleo de solo lectura, verificación de firmas de módulos, y proyectos como Rust-for-Linux, que introduce en el núcleo un lenguaje que impide por construcción precisamente este tipo de desbordamiento.

Cuál elegir depende del coste del fallo. Para meteo-01, una caída significa unas horas sin datos meteorológicos: molesto y caro, pero asumible, así que Linux es la elección correcta. Para el sistema de frenado de un coche, el coste del fallo es inaceptable, y por eso ahí se usa QNX y se paga el sobrecoste sin discutir.

Errores Comunes y Consejos

  • Creer que monolítico significa mal estructurado. Linux está muy bien estructurado internamente. Lo monolítico se refiere al espacio de direcciones compartido, no a la calidad del diseño.
  • Pensar que los módulos cargables convierten a Linux en un microkernel. No: un módulo se ejecuta con todos los privilegios y puede corromper el sistema exactamente igual que el código compilado dentro. Aportan flexibilidad, no aislamiento.
  • Descartar el microkernel citando cifras de rendimiento de 1990. El trabajo de Liedtke con L4 mejoró el coste del IPC en un orden de magnitud, y seL4 ha demostrado que un microkernel verificado formalmente es viable en producción.
  • Suponer que un fallo en el núcleo siempre se manifiesta como una caída. La corrupción silenciosa es más peligrosa precisamente porque no se ve. Ante datos inexplicablemente erróneos, comprobar /proc/sys/kernel/tainted y el registro del núcleo debería ser un reflejo.
  • Instalar módulos fuera del árbol sin pensarlo. Los controladores de terceros son una causa estadísticamente destacada de inestabilidad, y contaminan el núcleo, lo que complica cualquier diagnóstico posterior.
  • Consejo: cuando evalúes una arquitectura, no preguntes "cuál es mejor" sino "cuánto cuesta un fallo aquí". Esa pregunta ordena la decisión inmediatamente.

Ejercicios

Ejercicio 1

Para cada situación, indica qué arquitectura de núcleo sería más adecuada y justifica la respuesta con el criterio del coste del fallo y de los requisitos de rendimiento:

  1. Un sistema de control de un quirófano robotizado.
  2. Un servidor de bases de datos que atiende 50.000 consultas por segundo.
  3. La centralita de un coche que gestiona frenos y dirección asistida.
  4. Un microservicio en la nube que se despliega miles de veces al día y solo sirve una API.

Ejercicio 2

Examina el estado de los módulos de tu propia máquina Linux (o de una máquina virtual) y responde:

  1. ¿Cuántos módulos hay cargados y cuánta memoria del núcleo ocupan en total?
  2. ¿Hay algún módulo con Used by a 0 que podrías descargar?
  3. ¿Está el núcleo contaminado (tainted)? Si lo está, averigua por qué.

Escribe los comandos que usarías y explica qué buscas con cada uno.

Ejercicio 3

meteo-01 sufre reinicios inexplicables cada dos o tres días, siempre de madrugada. No hay ningún patrón claro en la carga. Diseña un plan de diagnóstico de al menos cinco pasos orientado a determinar si la causa está en el núcleo (y en qué parte), justificando qué información busca cada paso.

Soluciones

Solución 1

1. Quirófano robotizado → microkernel (QNX, seL4 o similar). El coste del fallo es una vida humana, así que el aislamiento y la certificabilidad dominan cualquier otro criterio. Además, este tipo de sistema debe pasar certificaciones (IEC 62304, IEC 61508) que exigen demostrar el comportamiento del software; con un núcleo de 30 millones de líneas eso es inabordable, mientras que seL4 aporta una verificación formal completa. El rendimiento requerido es modesto: mover un brazo robótico no exige millones de operaciones por segundo, sino que cada una llegue a tiempo.

2. Servidor de bases de datos a 50.000 consultas/s → monolítico (Linux). Aquí manda el rendimiento. A ese ritmo, cada microsegundo de sobrecoste por operación se traduce en costes reales de hardware. El sobrecoste del paso de mensajes sería inaceptable. Además, el coste de un fallo es alto pero acotado: una caída significa indisponibilidad y recuperación desde el journal, no un daño irreversible. La respuesta correcta al riesgo aquí no es cambiar de arquitectura sino replicar: varias máquinas con conmutación por error. Nota adicional: las bases de datos son precisamente el caso de uso que motivó la crítica exokernel, y por eso usan O_DIRECT para gestionar su propia caché.

3. Centralita de frenos → microkernel de tiempo real (QNX es el estándar de facto en automoción). Combina las dos exigencias más duras: coste de fallo inaceptable y plazos estrictos. Necesita aislamiento (que un fallo en el módulo de infoentretenimiento no toque el de frenado) y determinismo (respuesta garantizada en un tiempo acotado). Es el escenario donde el sobrecoste del microkernel se paga sin discusión, y donde además la separación de componentes permite certificar cada uno a un nivel de criticidad distinto.

4. Microservicio en la nube → unikernel, o alternativamente contenedor sobre Linux. El unikernel encaja bien: la imagen es mínima (megabytes), arranca en milisegundos (importante si se escala bajo demanda), y su superficie de ataque es diminuta al no incluir shell ni utilidades. Sus inconvenientes principales —dificultad de depuración e imposibilidad de entrar a la máquina— importan poco en un despliegue inmutable donde la respuesta ante un problema es reemplazar la instancia. En la práctica, sin embargo, la opción dominante hoy es un contenedor sobre Linux, porque el ecosistema de herramientas es incomparablemente mejor: es un ejemplo claro de que la madurez del ecosistema pesa tanto como los méritos técnicos.

Solución 2

1. Número de módulos y memoria ocupada:

lsmod | tail -n +2 | wc -l
lsmod | tail -n +2 | awk '{suma += $2} END {printf "%.1f MB\n", suma/1048576}'
  • lsmod lista los módulos; tail -n +2 descarta la línea de cabecera para no contarla ni sumarla.
  • wc -l cuenta las líneas restantes, es decir, los módulos.
  • En el segundo comando, awk acumula la segunda columna (Size, en bytes) en la variable suma y al final (END) la imprime convertida a MB. Un sistema de escritorio típico tiene entre 80 y 150 módulos y 20-40 MB; un servidor minimalista, bastantes menos.

2. Módulos descargables:

lsmod | awk '$3 == 0 {print $1, $2}'
  • $3 == 0 selecciona las líneas cuya tercera columna (Used by) es cero, es decir, módulos que nadie usa ahora mismo.
  • Advertencia importante: Used by a 0 no garantiza que sea seguro descargarlo. Un módulo de un sistema de archivos puede estar a 0 y hacer falta en cuanto se monte un dispositivo de ese tipo; un módulo de sonido a 0 dejará de funcionar en cuanto se reproduzca algo. En un servidor de producción, no se descargan módulos a la ligera: lo correcto es incluirlos en la lista negra (/etc/modprobe.d/blacklist.conf) y verificar tras un reinicio controlado.

3. Núcleo contaminado:

cat /proc/sys/kernel/tainted
  • Si devuelve 0, el núcleo está limpio y no hay más que investigar.
  • Si devuelve otro número, es una máscara de bits donde cada bit indica un motivo. Para interpretarlo:
for i in $(seq 0 18); do
  if (( $(cat /proc/sys/kernel/tainted) & (1 << i) )); then echo "bit $i activo"; fi
done
dmesg | grep -i taint

El bucle comprueba bit a bit cuáles están activos mediante un desplazamiento (1 << i) y un AND lógico. Los motivos más habituales son el bit 0 (módulo propietario cargado, típicamente el controlador de NVIDIA), el bit 12 (módulo fuera del árbol oficial) y el bit 7 (la máquina sufrió previamente un fallo grave del que se recuperó). Este último es el más relevante para diagnosticar inestabilidad: indica que ya ha ocurrido algo malo aunque el sistema siga en pie. La segunda línea busca el mensaje explicativo que el núcleo suele dejar en el momento de contaminarse.

Solución 3

Paso 1: ¿fue una caída o un reinicio ordenado?

last -x reboot shutdown | head -10
journalctl --list-boots | head -10

last -x muestra el historial de arranques y apagados. Si aparece shutdown, alguien o algo lo ordenó (una actualización automática, un temporizador). Si solo aparece reboot sin shutdown previo, fue una caída. Esta distinción es la primera bifurcación del diagnóstico y ahorra investigar en la dirección equivocada.

Paso 2: buscar el rastro en el arranque anterior.

journalctl -k -b -1 -p warning --no-pager | tail -50

-b -1 accede al arranque anterior, que es donde estará la causa. Si los últimos mensajes muestran Kernel panic, Oops, BUG: o un Call Trace, tenemos la pila de llamadas del fallo y podemos identificar el módulo implicado. Si el registro se corta de golpe sin ningún error, es muy sospechoso de un problema de hardware o de corte de alimentación, porque un fallo de software casi siempre deja algo escrito.

Paso 3: comprobar contaminación y módulos de terceros.

cat /proc/sys/kernel/tainted
lsmod | while read m _ _; do modinfo "$m" 2>/dev/null | grep -q 'intree:.*Y' || echo "fuera del árbol: $m"; done

Un núcleo contaminado por un módulo fuera del árbol es un candidato inmediato. El bucle recorre los módulos cargados y señala los que no forman parte del árbol oficial.

Paso 4: correlacionar con la actividad de madrugada.

ls -l /etc/cron.d/ /etc/cron.daily/
systemctl list-timers --all

Que los reinicios sean siempre de madrugada es la pista más fuerte del enunciado. Hay que averiguar qué ocurre a esa hora: el agregador de las 02:30, una copia de seguridad, una actualización automática de paquetes, un análisis de integridad. Si el reinicio coincide sistemáticamente con una de estas tareas, esa tarea somete al sistema a una carga concreta (E/S intensiva, presión de memoria) que dispara el fallo latente.

Paso 5: descartar causas que no son del núcleo.

journalctl -b -1 | grep -i 'out of memory\|oom-killer'
sudo smartctl -a /dev/sda | grep -iE 'reallocated|pending|temperature'
sudo dmidecode -t memory | grep -i 'error'

Antes de culpar al núcleo hay que descartar tres sospechosos habituales: que el OOM killer haya actuado (falta de memoria, no fallo del núcleo), que el disco esté degradado (los atributos SMART de sectores reasignados o pendientes lo delatan) y que haya errores de memoria RAM. Un módulo de RAM defectuoso produce exactamente este cuadro: caídas aleatorias, sin patrón de software, a menudo bajo carga. Para confirmarlo, memtest86+ durante varias horas es la prueba definitiva.

Paso 6 (si nada anterior concluye): habilitar diagnóstico persistente.

sudo apt install kdump-tools     # o el equivalente de la distribución

kdump reserva memoria para arrancar un núcleo secundario tras un panic y volcar la imagen de memoria del núcleo caído a disco. Es la única forma de analizar a posteriori un panic que no llegó a escribirse en el registro. Complementariamente, si la máquina no responde en absoluto, habilitar el watchdog del núcleo permite reiniciar automáticamente y al menos acotar la indisponibilidad mientras se investiga.

Conclusión

La arquitectura del núcleo se reduce a una decisión: cuánto código ejecutar con todos los privilegios de la máquina. El diseño monolítico lo mete todo dentro y gana rendimiento a costa de no tener ningún aislamiento interno; el microkernel deja fuera casi todo y gana robustez y verificabilidad a costa del paso de mensajes; los híbridos adoptan la estructura del segundo con el rendimiento del primero, sacrificando el aislamiento que le daba sentido. El diseño por capas aportó una forma de razonar que sobrevive aunque su forma pura sea impracticable, y exokernel y unikernel cuestionan desde el otro extremo si las abstracciones del sistema deben ser obligatorias.

Linux resuelve el problema práctico del monolitismo con los módulos cargables, que aportan flexibilidad de despliegue pero, conviene insistir, ningún aislamiento adicional. Y el ejemplo del controlador defectuoso en meteo-01 deja la lección clave: la falta de aislamiento no solo puede tumbar el servidor, sino algo peor —corromper datos en silencio.

La conclusión general es que no hay una arquitectura mejor, sino una pregunta que ordena la elección: cuánto cuesta un fallo. Para meteo-01, unas horas sin datos; para un sistema de frenado, una vida.

Todo esto ha girado alrededor de una frontera que hemos usado constantemente sin explicarla: la que separa el modo usuario del modo núcleo. En la siguiente lección, Modo Usuario, Modo Núcleo y Llamadas al Sistema, veremos exactamente cómo funciona esa frontera, cómo se cruza paso a paso y cuánto cuesta cruzarla.

Fundamentos de Sistemas Operativos

Módulo 1: Introducción a los Sistemas Operativos

Módulo 2: Gestión de Recursos

Módulo 3: Concurrencia

Módulo 4: Estructuras de Archivos

Módulo 5: Protección y Seguridad del Sistema

Módulo 6: Virtualización y Contenedores

Módulo 7: Administración y Diagnóstico en la Práctica

© Copyright 2026. Todos los derechos reservados