← Fundamentos de Sistemas Operativos Bloque 6 · Tipos y diseños de sistemas operativos · Tema 24 de 34

Tema 24

Arquitecturas de kernel

Todo sistema operativo tiene un kernel, pero no todos ponen lo mismo dentro. Decidir qué se ejecuta con privilegio total y qué queda fuera es la elección de diseño más discutida de la materia. En este tema comparamos las tres respuestas clásicas, medimos lo que cuesta cada una y cargamos en el kernel de Linux un módulo escrito por nosotros.

1. Qué va dentro del kernel

En el tema 11 enunciamos un principio: todo lo que pueda hacerse fuera del kernel conviene hacerlo fuera, porque cada línea que se ejecuta en modo privilegiado es una línea cuyo error puede detener la máquina. El tema 18 le puso nombre a esa consecuencia: un puntero nulo en un programa termina con una señal; el mismo puntero en un driver, con un pánico.

El principio es claro. Lo difícil es decidir hasta dónde llevarlo. Estos son los componentes que un sistema operativo necesita, ordenados aproximadamente de más a menos imprescindibles en modo kernel:

Componente¿Necesita modo kernel?
Cambio de contexto, manejo de interrupciones, tablas de memoriaSí, sin alternativa: usan instrucciones privilegiadas.
PlanificadorEl mecanismo, sí. La política podría decidirse fuera.
Comunicación entre procesosSí: alguien debe poder cruzar la frontera entre dos espacios de direcciones.
Drivers de dispositivosDiscutible. Necesitan acceso al hardware, que el kernel podría concederles uno por uno.
Sistemas de archivosDiscutible. Son lógica sobre bloques de disco.
Protocolos de redDiscutible. Son lógica sobre paquetes.
Interfaz gráficaNo, aunque algunos sistemas la pusieron dentro por rendimiento.

La arquitectura de un kernel es la decisión sobre qué componentes del sistema operativo se ejecutan en modo kernel, dentro de un mismo espacio de direcciones, y cuáles se ejecutan como procesos en modo usuario.

Las filas «discutibles» son las que separan a las tres arquitecturas de este tema. Y son la mayor parte del código: en Linux, los drivers solos superan la mitad de los 35 millones de líneas.

2. El kernel monolítico

En un kernel monolítico, todos los servicios del sistema operativo —planificación, memoria, sistemas de archivos, red y drivers— forman un único programa, que se ejecuta entero en modo kernel y en un solo espacio de direcciones.

Es el diseño original de Unix, y el de Linux y los sistemas BSD. «Monolítico» significa «de una sola piedra»: no quiere decir desordenado. Por dentro, el código está dividido en subsistemas con interfaces definidas. Lo que lo caracteriza es que no hay barreras de protección entre ellos. El sistema de archivos llama al driver del disco con una llamada a función corriente, y los dos pueden leer y escribir las estructuras de datos del otro.

VentajasInconvenientes
Rendimiento. La comunicación entre componentes cuesta lo que una llamada a función: unos nanosegundos.Un error en cualquier parte afecta a todo. Un driver defectuoso puede corromper la memoria del planificador.
Simplicidad para compartir. Todos los componentes ven los mismos datos, sin copiarlos.Mucho código con privilegio total. Más líneas en modo kernel son más oportunidades para un fallo de seguridad.
Un único camino de entrada. Una llamada al sistema se atiende completa sin salir del kernel.Los fallos no se aíslan. No se puede reiniciar un componente dañado: hay que reiniciar la máquina.

3. El microkernel

Un microkernel mantiene en modo kernel solo los mecanismos que no pueden estar en otro lugar: los espacios de direcciones, la planificación básica y la comunicación entre procesos. Los demás servicios son procesos comunes, llamados servidores, que se ejecutan en modo usuario y se comunican mediante mensajes.

El sistema de archivos es un proceso. El driver del disco es otro proceso. La red, otro. Cada uno tiene su espacio de direcciones, protegido por la unidad de gestión de memoria como el de cualquier programa. Cuando una aplicación quiere leer un archivo, no «entra» al sistema de archivos: le envía un mensaje y espera la respuesta. El kernel se limita a llevar los mensajes de un proceso a otro.

Comparación de dos arquitecturas: en el kernel monolítico, archivos, red, drivers, memoria y planificador están en modo kernel; en el microkernel, los servidores de archivos y de red y los drivers son procesos en modo usuario, y en modo kernel queda solo el paso de mensajes y la planificación Kernel monolítico Microkernel MODO USUARIO MODO KERNEL MODO USUARIO MODO KERNEL Aplicaciones Archivos · Red · Drivers Memoria · Planificador Aplicación Archivos Driver de disco Red Mensajes · Planificación Memoria básica
A la derecha, el sistema de archivos, la red y los drivers son procesos como la aplicación. Lo que queda en modo kernel es una fracción mínima.

Las propiedades de este diseño son el reverso exacto del anterior:

VentajasInconvenientes
Aislamiento. Un driver con un error termina con una señal, como cualquier programa. El resto del sistema sigue.Rendimiento. Cada pedido entre componentes cruza la frontera del kernel y cambia de proceso.
Recuperación. Un servidor caído se puede reiniciar sin reiniciar la máquina.Complejidad de diseño. Los componentes ya no comparten datos; hay que definir un protocolo de mensajes para cada interacción.
Poco código con privilegio total. Un microkernel tiene del orden de diez mil líneas, que se pueden revisar una por una.Coordinación. Operaciones que involucran a varios servidores son difíciles de hacer correctas y eficientes.

La tercera ventaja se llevó a su extremo con seL4, un microkernel del que existe una demostración matemática, verificada por computadora, de que su código cumple su especificación. Es algo impensable para un kernel de millones de líneas.

Los representantes principales son Mach, de los años ochenta, que influyó en casi todos los posteriores; MINIX, creado para la enseñanza; QNX, de uso comercial en automóviles y equipos médicos; y la familia L4.

4. El precio de los mensajes

El inconveniente del microkernel se puede contar. Sigamos una operación simple, leer un bloque de un archivo que no está en la caché, en las dos arquitecturas.

En un kernel monolítico es el recorrido del tema 13: la aplicación ejecuta syscall, el kernel atiende el pedido completo —el sistema de archivos llama al driver con una llamada a función— y vuelve. En un microkernel, cada flecha es un mensaje:

  1. La aplicación envía «leer» al servidor de archivos. Entra al kernel, que copia el mensaje y cambia al proceso del servidor. cambio de contexto
  2. El servidor de archivos determina qué bloque necesita y envía un mensaje al driver del disco. El kernel cambia al proceso del driver. cambio de contexto
  3. El driver da la orden al dispositivo y espera. Cuando llega la interrupción, el kernel la convierte en un mensaje para el driver. interrupción
  4. El driver responde al servidor de archivos con el bloque. cambio de contexto
  5. El servidor de archivos responde a la aplicación con los datos. cambio de contexto
MonolíticoMicrokernel
Entradas al kernel1, más la interrupción4 como mínimo, más la interrupción
Cambios de procesoNinguno obligado4 como mínimo
Copias de los datosUna: del kernel a la aplicaciónVarias, salvo que se comparta memoria
Entre el sistema de archivos y el driverUna llamada a funciónDos mensajes

En el tema 19 vimos que un cambio de contexto cuesta del orden de un microsegundo y que, además, invalida las cachés. Los primeros microkernels, con Mach a la cabeza, resultaron mucho más lentos que los sistemas monolíticos, y esa mala fama los acompañó durante años.

La generación siguiente, la familia L4, demostró que buena parte del problema era de implementación: con un paso de mensajes diseñado al detalle, el costo se redujo a una fracción. La diferencia con un kernel monolítico no desapareció, pero dejó de ser prohibitiva. En un equipo donde el aislamiento importa más que el último punto de rendimiento, es un precio razonable.

5. Un debate de 1992

La discusión entre las dos arquitecturas tiene un episodio célebre. En enero de 1992, pocos meses después de que Linus Torvalds publicara las primeras versiones de Linux, el profesor Andrew Tanenbaum, autor de MINIX y de uno de los libros de texto más usados de la materia, escribió en un foro un mensaje titulado «Linux es obsoleto».

Su argumento era que el diseño monolítico pertenecía a los años setenta, y que escribir un kernel así en 1991 era un paso atrás; el futuro eran los microkernels. Torvalds, que tenía veintidós años, respondió que los microkernels eran superiores en teoría y que en la práctica los existentes funcionaban peor; que el suyo estaba disponible, hacía lo que la gente necesitaba y era más rápido.

Treinta años después, el balance da algo de razón a cada uno:

El episodio ilustra una regla frecuente en ingeniería: un diseño que existe y funciona suele ganarle a uno mejor que todavía no está terminado. Y también que las preguntas de arquitectura rara vez tienen una respuesta única: dependen de lo que se quiera optimizar, como vimos en el tema 23.

6. Los kernels híbridos

Entre los dos extremos hay una posición intermedia, que es la de los dos sistemas comerciales más extendidos.

Un kernel híbrido está organizado internamente como un microkernel, con un núcleo mínimo y servicios separados, pero ejecuta la mayoría de esos servicios en modo kernel, en el mismo espacio de direcciones, para evitar el costo de los mensajes.

Windows NTXNU (macOS, iOS)
OrigenDiseñado a fines de los ochenta, bajo la influencia de las ideas de microkernelConstruido sobre el microkernel Mach, al que se añadió el código de un Unix de la familia BSD
Núcleo mínimoUna capa pequeña que maneja interrupciones y planificaciónMach: mensajes, memoria y planificación
Servicios en modo kernelMemoria, procesos, archivos, red, drivers y, desde 1996, parte del sistema gráficoLa capa BSD —archivos, red, procesos— y el sistema de drivers
En modo usuarioSubsistemas de entorno y una parte de los driversUna cantidad creciente de drivers

El nombre de XNU es una declaración de origen: son las siglas de «X is Not Unix». Combina el paso de mensajes de Mach con la interfaz POSIX de BSD, y ambas partes conviven en modo kernel.

La categoría es polémica. Sus críticos, Torvalds entre ellos, sostienen que «híbrido» es una palabra de mercadotecnia: si los servicios comparten espacio de direcciones y privilegios, un error en uno afecta a todos, y a efectos prácticos el kernel es monolítico, por muy ordenado que esté por dentro. Es una objeción atendible. Lo que el término sí describe es una diferencia de estructura interna, y una tendencia: los dos sistemas llevan años trasladando drivers al modo usuario, que es precisamente lo que proponía el microkernel.

El caso de Windows tiene una lección concreta. En 1996, el sistema gráfico se movió del modo usuario al modo kernel para ganar velocidad. Durante las dos décadas siguientes fue una de las fuentes principales de fallos de seguridad del sistema. Es la tabla de la sección 2, leída en un caso real.

7. Linux: monolítico y modular

Linux es monolítico, y resuelve con otro recurso uno de los inconvenientes prácticos de ese diseño. Un kernel de una sola pieza que debe funcionar en cualquier equipo tendría que incluir los drivers de todo el hardware existente: sería enorme, y la mayor parte no se usaría nunca. La solución son los módulos cargables, que presentamos en el tema 11.

Un módulo del kernel es un archivo de código compilado que puede incorporarse a un kernel en ejecución, y retirarse de él, sin reiniciar el sistema. Una vez cargado, forma parte del kernel: se ejecuta en modo kernel, en su espacio de direcciones y con todos sus privilegios.

La última frase es la que importa, y la que se confunde con más facilidad. Los módulos no son servidores de un microkernel. La semejanza es superficial: en ambos casos hay piezas separadas. La diferencia es de fondo:

Módulo de LinuxServidor de un microkernel
Se ejecuta enModo kernelModo usuario
Espacio de direccionesEl del kernel, compartido con todo lo demásPropio y protegido
Se comunica con el resto medianteLlamadas a funciónMensajes
Si tiene un errorPuede detener el sistemaTermina él solo
La separación esDe organización: en el disco y al cargarDe protección: la impone el hardware

Los módulos aportan flexibilidad, no aislamiento. Permiten un kernel base pequeño, que crece según el hardware presente; es lo que hizo necesario el initramfs del tema 21. Y permiten añadir un driver sin recompilar ni reiniciar. Casi todo lo que no es imprescindible para arrancar se distribuye así: drivers, sistemas de archivos, protocolos de red.

Windows y macOS tienen mecanismos equivalentes: los archivos .sys y las extensiones del kernel.

8. Gestionar los módulos

Cinco comandos cubren todo el trabajo habitual con módulos:

ComandoFunción
lsmodLista los módulos cargados.
modinfo móduloMuestra la información de un módulo: archivo, descripción, licencia, dependencias, parámetros.
modprobe móduloCarga un módulo por su nombre, junto con los que necesite.
modprobe -r móduloLo descarga, si nadie lo está usando.
insmod archivo.ko, rmmod móduloCargan y descargan un archivo concreto, sin resolver dependencias. Se usan al desarrollar.
terminal
$ lsmod | wc -l
143
$ lsmod | grep -E '^Module|^nvme'
Module                  Size  Used by
nvme                   61440  2
nvme_core             212992  3 nvme

De los más de seis mil módulos disponibles que contamos en el tema 11, este equipo tiene cargados unos ciento cuarenta. La columna Size es la memoria que ocupa cada uno, y Used by indica cuántos usuarios tiene y qué otros módulos dependen de él: nvme_core no puede descargarse mientras nvme siga cargado.

Casi nunca hace falta cargar un módulo a mano. Cuando el kernel descubre un dispositivo durante la enumeración del tema 8, publica sus identificadores, y un servicio del sistema busca qué módulo declaró ser capaz de manejarlos. Esa declaración está en el propio módulo:

terminal
$ modinfo nvme | grep -E '^(filename|license|depends)|bc01sc08'
filename:       /lib/modules/6.8.0-45-generic/kernel/drivers/nvme/host/nvme.ko.zst
license:        GPL
alias:          pci:v*d*sv*sd*bc01sc08i02*
depends:        nvme-core

La línea alias es un patrón. Dice: «sirvo para cualquier dispositivo PCI, de cualquier fabricante y modelo, cuya clase sea 01, subclase 08 e interfaz 02», que es la identificación normalizada de un disco NVMe. Es la manera en que un equipo recién instalado reconoce su hardware sin que nadie le indique qué drivers cargar.

9. Experimento: cargar y descargar un módulo

Para ver el mecanismo sin riesgo sirve dummy, un módulo que viene con el kernel. Crea una interfaz de red ficticia, que descarta todo lo que se le envía; se usa para pruebas.

terminal
$ lsmod | grep dummy                 # no está cargado
$ ip link show dummy0
Device "dummy0" does not exist.

$ sudo modprobe dummy                # incorporarlo al kernel
$ lsmod | grep dummy
dummy                  12288  0
$ ip link show dummy0
5: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 6a:1f:3c:92:0b:e4 brd ff:ff:ff:ff:ff:ff

$ sudo modprobe -r dummy             # retirarlo
$ ip link show dummy0
Device "dummy0" does not exist.

Entre el segundo y el cuarto comando, el kernel en ejecución cambió: adquirió doce kilobytes de código nuevo, y con ellos la capacidad de manejar un tipo de dispositivo que un instante antes no conocía. No hubo reinicio, y ningún programa en marcha lo notó. Al retirarlo, el dispositivo desapareció con él.

Hizo falta sudo, y por una buena razón. Cargar un módulo es añadir código que se ejecutará con privilegio total. Es la operación que mencionamos en el tema 12 al separar root del modo kernel: el administrador no se ejecuta en el anillo 0, pero puede pedirle al kernel que acepte código en él.

En WSL

El kernel de WSL trae casi todo incorporado y muy pocos módulos, de modo que lsmod muestra una lista breve y modprobe dummy probablemente falle. Para esta sección y la siguiente conviene una máquina virtual.

10. Escribir un módulo

Un módulo es código C, con las particularidades que enumeramos en el tema 11: no tiene función main, no dispone de la biblioteca de C y se ejecuta en modo kernel. El más pequeño posible tiene dos funciones, una que el kernel llama al cargarlo y otra al descargarlo. A este le añadimos la línea de ensamblador de anillo.c, el programa del tema 12 que consultaba su nivel de privilegio.

Hazlo en una máquina virtual

Este código es inofensivo, pero se ejecuta dentro del kernel. Un error al modificarlo no produce un fallo de segmentación: puede colgar el sistema. Es precisamente lo que el experimento pretende hacer sentir. Usa un equipo donde reiniciar no importe.

hola.c
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Modulo minimo de ejemplo");

/* El kernel llama a esta función al cargar el módulo. */
static int __init hola_inicio(void)
{
    unsigned long cs;

    asm volatile ("mov %%cs, %0" : "=r"(cs));       /* igual que en anillo.c */

    pr_info("hola: modulo cargado, en el anillo %lu\n", cs & 3);
    return 0;                                       /* 0 = carga correcta */
}

/* Y a esta, al descargarlo. */
static void __exit hola_fin(void)
{
    pr_info("hola: modulo descargado\n");
}

module_init(hola_inicio);
module_exit(hola_fin);
Makefile (las líneas de comandos empiezan con un tabulador)
obj-m += hola.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

Un módulo no se compila con una orden directa a gcc, sino con el sistema de construcción del propio kernel, que aplica las mismas opciones con que se compiló el kernel en uso. Para eso hace falta instalar sus archivos de cabecera.

terminal
$ sudo apt install linux-headers-$(uname -r)     # en Fedora: sudo dnf install kernel-devel
$ make
…
  LD [M]  /home/diego/modulo/hola.ko

$ sudo insmod hola.ko
$ lsmod | grep hola
hola                   12288  0
$ sudo dmesg | tail -3
[ 5123.118822] hola: loading out-of-tree module taints kernel.
[ 5123.118870] hola: module verification failed: signature and/or required key missing - tainting kernel
[ 5123.119412] hola: modulo cargado, en el anillo 0

$ sudo rmmod hola
$ sudo dmesg | tail -1
[ 5140.002133] hola: modulo descargado

La línea resaltada es el resultado del experimento. En el tema 12, el mismo cálculo dentro de un programa daba 3, incluso ejecutado con sudo. Aquí da 0. El código es nuestro, lo escribimos y compilamos como cualquier otro, y se está ejecutando en el nivel de privilegio del kernel.

Varios detalles del listado y de la salida confirman lo dicho sobre los módulos:

La segunda advertencia se vuelve un obstáculo si el equipo tiene activado el arranque seguro. En ese caso insmod falla con el mensaje Key was rejected by service. Es el último eslabón de la cadena de confianza del tema 20: el firmware verifica al cargador, el cargador al kernel, y el kernel a cada módulo. Un módulo sin firma válida no entra.

11. Sacar código del kernel, sin cambiar de arquitectura

Los sistemas monolíticos no se convirtieron en microkernels, pero tomaron de ellos la idea central: lo que pueda ejecutarse fuera, mejor fuera. Linux ofrece varios mecanismos para hacerlo caso por caso:

MecanismoQué permiteEjemplos
FUSEEscribir un sistema de archivos como un programa común. El kernel conserva un módulo pequeño que reenvía cada operación a ese programa.Montar por red el directorio de otro equipo; leer discos con formato de Windows.
Drivers en espacio de usuarioManejar un dispositivo desde un proceso, al que el kernel concede acceso solo a ese dispositivo.Impresoras, escáneres y la mayoría de los dispositivos USB poco comunes.
Reparto del trabajoDejar en el kernel solo la parte que toca el hardware, y lo demás en bibliotecas.Los drivers de video: la mayor parte de su código está en espacio de usuario.
eBPFLo contrario: cargar en el kernel programas pequeños, que el kernel analiza antes de aceptar para comprobar que no pueden dañarlo.Las herramientas de observación que mencionamos en el tema 16; filtros de red.

FUSE es, literalmente, la arquitectura de microkernel aplicada a un componente: un servidor de archivos en modo usuario, con el que el kernel se comunica por mensajes. Tiene el costo que calculamos en la sección 4, y por eso se usa donde el rendimiento importa poco y la comodidad mucho. Un error en un sistema de archivos así termina con un proceso caído y un directorio inaccesible, no con un pánico.

La última fila ataca el problema desde el otro lado. En lugar de sacar código del kernel para que sus errores no lo alcancen, admite código dentro con la condición de poder demostrar de antemano que es inofensivo: que termina, que no accede a memoria indebida. Es una tercera vía entre confiar en todo y aislarlo todo.

12. Otras ideas

Las tres arquitecturas vistas cubren casi todos los sistemas en uso. Hay otras propuestas que conviene reconocer cuando aparecen:

ArquitecturaIdeaDónde se la encuentra
Por capasEl sistema se organiza en niveles; cada uno usa solo los servicios del inferior.Fue un diseño de los años sesenta. Hoy es un criterio de orden interno, no una arquitectura completa.
ExokernelEl kernel solo reparte el hardware de forma segura, sin ofrecer abstracciones. Los archivos y los procesos los implementa una biblioteca dentro de cada aplicación.Investigación. Sus ideas influyeron en las técnicas de red de alto rendimiento.
UnikernelLa aplicación y las partes del sistema operativo que necesita se compilan juntas en una sola imagen, que arranca directamente sobre un hipervisor. No hay modo usuario ni otros procesos.Servicios en la nube muy especializados, donde el hipervisor ya proporciona el aislamiento.

El unikernel cierra un círculo. Es la misma estructura del RTOS del tema 23, donde el kernel y la aplicación se enlazaban en una sola imagen sin protección entre ellos, trasladada del microcontrolador al centro de datos. Y hace explícito algo que este tema muestra desde la primera sección: el aislamiento tiene que estar en algún nivel, pero no hace falta pagarlo en todos.

13. Cuadro comparativo

MonolíticoMicrokernelHíbrido
En modo kernelTodos los serviciosMensajes, planificación y memoria básicaCasi todos los servicios
Drivers y sistemas de archivosDentro del kernelProcesos en modo usuarioDentro, en su mayoría
Comunicación internaLlamadas a funciónMensajes entre procesosLlamadas a función, con estructura de mensajes
RendimientoEl mejorMenor, por los cambios de contextoCercano al monolítico
Un driver defectuosoPuede detener el sistemaSe reinicia el driverPuede detener el sistema
Código con privilegio totalMillones de líneasDecenas de milesMillones de líneas
EjemplosLinux, FreeBSDQNX, seL4, MINIX 3Windows NT, XNU
Predomina enServidores, escritorio, móvilesAutomóviles, equipos médicos, procesadores de seguridadEscritorio y móviles comerciales

La tabla presenta tres columnas; la realidad es una línea continua. Linux es monolítico y usa servidores en modo usuario cuando conviene. Windows y macOS son híbridos y cada año sacan más drivers del kernel. Los microkernels incorporan atajos para acercarse al rendimiento de los monolíticos. Todos los diseños se mueven hacia el mismo punto medio, empujados por dos fuerzas que ya conocemos: el costo de cruzar la frontera, que medimos en el tema 13, y el costo de un error del lado privilegiado, que vimos en el tema 18.

En el tema 25 dejamos el interior del kernel y miramos los sistemas completos: las grandes familias y lo que cambia para quien programa sobre cada una.

14. Resumen

15. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Qué decide la arquitectura de un kernel?

Qué componentes del sistema operativo se ejecutan en modo kernel, compartiendo un espacio de direcciones, y cuáles se ejecutan como procesos en modo usuario, cada uno con su espacio protegido.

2. «Monolítico» ¿significa que el código no está organizado?

No. El código está dividido en subsistemas con interfaces definidas. Significa que entre ellos no hay barreras de protección: todos se ejecutan en modo kernel, en el mismo espacio de direcciones, y se llaman con llamadas a función.

3. ¿Qué tres cosas conserva un microkernel en modo kernel?

La gestión básica de los espacios de direcciones, la planificación de los procesos y la comunicación entre ellos mediante mensajes.

4. En un microkernel, ¿qué ocurre si el driver del disco tiene un puntero nulo?

El driver es un proceso en modo usuario, de modo que recibe una señal y termina, como cualquier programa. El sistema puede reiniciarlo. El kernel y los demás servidores no se ven afectados.

5. ¿Por qué leer un archivo cuesta más en un microkernel?

Porque la aplicación, el servidor de archivos y el driver son procesos distintos. Cada pedido y cada respuesta entre ellos es un mensaje que pasa por el kernel y requiere un cambio de contexto. En un kernel monolítico, esas mismas interacciones son llamadas a función.

6. ¿Qué sostenía cada parte en el debate de 1992, y cómo resultó?

Tanenbaum sostenía que el diseño monolítico era anticuado y que el futuro eran los microkernels. Torvalds, que en la práctica los microkernels existentes rendían menos y que su kernel funcionaba. En equipos de uso general ganó el diseño monolítico; en sistemas críticos, los microkernels.

7. ¿Qué es un kernel híbrido, y por qué algunos objetan el término?

Es un kernel con estructura interna de microkernel que ejecuta la mayoría de sus servicios en modo kernel. La objeción es que, si los servicios comparten privilegios y espacio de direcciones, un error en uno afecta a todos, y en la práctica se comporta como un kernel monolítico.

8. ¿En qué se diferencia un módulo de Linux de un servidor de un microkernel?

El módulo, una vez cargado, se ejecuta en modo kernel, dentro del espacio de direcciones del kernel, y un error suyo puede detener el sistema. El servidor se ejecuta en modo usuario, en su propio espacio protegido, y un error suyo solo lo afecta a él.

9. Entonces, ¿qué ventaja aportan los módulos?

Flexibilidad. Permiten un kernel base pequeño, que incorpora solo los drivers del hardware presente, y añadir o quitar funciones sin recompilar ni reiniciar.

10. ¿Cómo sabe el sistema qué módulo cargar para un dispositivo recién detectado?

Cada módulo declara, en sus líneas alias, los identificadores de los dispositivos que sabe manejar. Cuando el kernel detecta un dispositivo, publica sus identificadores, y el sistema carga el módulo cuyo patrón coincide.

11. El módulo hola informa «anillo 0», y anillo.c con sudo informaba 3. ¿Por qué?

Porque anillo es un programa: se ejecuta en modo usuario, sea cual sea el usuario que lo lance. El módulo se incorpora al kernel y su código se ejecuta en modo kernel. Ser administrador no cambia el modo; permite pedirle al kernel que cargue código.

12. ¿Por qué el kernel se marca como «contaminado» al cargar hola?

Para dejar constancia de que se le incorporó código que no pertenece a su distribución y no está firmado. Si después ocurre un fallo, esa marca indica que el estado del kernel pudo ser alterado por código ajeno.

13. ¿En qué sentido FUSE es «un microkernel para un solo componente»?

El sistema de archivos se ejecuta como un proceso en modo usuario, y el kernel le reenvía cada operación como un mensaje. Tiene las propiedades del microkernel para esa pieza: un error no detiene el sistema, y cada operación cuesta cambios de contexto.

Para practicar

  1. Cuenta los módulos cargados en tu equipo y ordénalos por tamaño con lsmod | sort -k2 -n | tail. ¿Qué son los cinco mayores? Usa modinfo -d para ver la descripción de cada uno.
  2. Elige un módulo cargado que tenga dependientes en la columna Used by y dibuja el árbol de dependencias, ayudándote con modinfo -F depends.
  3. Intenta descargar el módulo de tu disco con sudo modprobe -r. ¿Qué responde el sistema, y por qué es una suerte que lo haga?
  4. Repite el experimento de la sección 9 con sudo modprobe dummy numdummies=3. ¿Qué cambia? Busca ese parámetro en la salida de modinfo dummy.
  5. Compila y carga hola en una máquina virtual. Comprueba la marca con cat /proc/sys/kernel/tainted antes y después.
  6. En esa misma máquina, añade asm volatile ("hlt"); a la función de inicio. En el tema 12 esa instrucción terminó con el programa. ¿Qué ocurre ahora? Explica el resultado recordando qué hace hlt y qué llega a continuación.
  7. Lista los sistemas de archivos de tipo FUSE montados en tu sistema con findmnt -t fuse,fuseblk,fuse.portal. Si tienes un entorno gráfico, es probable que haya alguno.
  8. Para cada uno de estos casos, indica qué arquitectura elegirías y por qué: el sistema de frenado de un automóvil, un servidor de base de datos, un reloj inteligente, el procesador que guarda las huellas dactilares de un teléfono.