El kernel de Linux es técnicamente monolítico, pero en la práctica funciona como un sistema modular. La distinción importa: a diferencia de los microkernels, todo el código del kernel corre en el mismo espacio de memoria y con los mismos privilegios, pero ese código puede cargarse y descargarse en tiempo de ejecución sin reiniciar. Las extensiones cargables del kernel que hacen esto posible se llaman módulos del kernel (kernel modules), y su extensión es .ko (kernel object).
Un módulo es esencialmente código que se enlaza dinámicamente al kernel mientras el sistema está corriendo. Puede implementar un driver de hardware, un sistema de archivos, un protocolo de red, o un mecanismo de seguridad. El diseño existe por una razón pragmática: compilar en el kernel base todos los drivers posibles haría el binario gigantesco e inmanejable, y además lo haría más frágil porque no podrías reemplazar un componente problemático sin reiniciar. Con módulos, puedes cargar el driver de una tarjeta de red cuando se detecta el hardware y descargarlo si algo falla.
El punto crítico de seguridad operacional que hay que tener siempre presente: los módulos corren en ring 0, el mismo nivel de privilegio que el kernel. Un módulo mal escrito, corrupto, o malicioso tiene acceso completo al sistema. Esto es exactamente lo que hace que las distribuciones como Debian separen los módulos por versión de kernel: los módulos compilados para 6.1.0-21-amd64 no funcionan en 6.1.0-28-amd64. La estructura de directorios refleja esto directamente en /lib/modules/$(uname -r)/.
Cuando algo sale mal aquí, los síntomas son variados y a veces confusos: el hardware que no responde, un sistema de archivos que no monta, un protocolo de red que no arranca, o en el peor caso un kernel panic. La mayoría de esos problemas tienen solución sin tocar el hardware ni reinstalar nada, si sabes dónde mirar.
# Ver qué módulos están cargados ahora mismo lsmod # La salida tiene tres columnas: Module, Size, Used by # "Used by" es clave: si el contador > 0, modprobe -r fallará lsmod | grep -E '^(Module|drm|i915|e1000)' # Información detallada de un módulo — esto es lo primero que haces # antes de tocar cualquier opción de carga modinfo e1000e # Ver solo los parámetros disponibles (útil para afinar comportamiento) modinfo -p e1000e # Cargar un módulo con sus dependencias resueltas automáticamente # modprobe lee /lib/modules/$(uname -r)/modules.dep para el árbol sudo modprobe e1000e # Cargar con un parámetro específico (sin tocar ficheros de configuración) # Los parámetros disponibles los muestra modinfo -p sudo modprobe e1000e SmartPowerDownEnable=1 # Descargar el módulo — falla si Used by > 0 sudo modprobe -r e1000e # insmod carga directamente desde un archivo .ko sin resolver dependencias # Solo útil en desarrollo de módulos o situaciones de recuperación sudo insmod /lib/modules/$(uname -r)/kernel/drivers/net/ethernet/intel/e1000e/e1000e.ko # Ver mensajes del kernel inmediatamente después de una carga # (errores de inicialización, detección de hardware, etc.) journalctl -k --since "1 minute ago" # Caso práctico: silenciar el driver nouveau para instalar el propietario de NVIDIA # Primero verifica que nouveau está cargado y tiene dependencias lsmod | grep nouveau # Crea el fichero de blacklist — el nombre del fichero es arbitrario # pero la convención es descriptiva cat <<'EOF' | sudo tee /etc/modprobe.d/blacklist-nouveau.conf # Deshabilitar el driver open source de NVIDIA para usar el propietario blacklist nouveau # options evita que se cargue incluso como dependencia de otro módulo options nouveau modeset=0 EOF # Módulos que deben cargarse en cada boot (aquí: módulo de red para # una interfaz que udev no detecta automáticamente en este hardware) echo "bonding" | sudo tee /etc/modules-load.d/bonding.conf # Pasar opciones al módulo en cada carga (separado de modules-load.d) cat <<'EOF' | sudo tee /etc/modprobe.d/bonding.conf # max_bonds=0 permite crear interfaces bond dinámicamente sin reservar ninguna options bonding max_bonds=0 EOF # Regenerar initramfs para que los cambios en modprobe.d y modules-load.d # sean efectivos en el próximo arranque — imprescindible en Debian sudo update-initramfs -u -k all # Verificar que el blacklist funciona sin reiniciar (solo para módulos # que no están actualmente cargados como dependencia de otro) sudo modprobe --dry-run --verbose nouveau
Qué está pasando debajo de cada decisión
lsmod es básicamente un formateador de /proc/modules. La columna Used by es la que determina si puedes descargar el módulo: el kernel mantiene un contador de referencias y rechaza el rmmod si ese contador no es cero. Cuando ves algo como drm_kms_helper 204800 1 i915, significa que i915 tiene una referencia activa a drm_kms_helper, y tendrías que descargar i915 primero. modprobe -r resuelve este orden automáticamente; rmmod no lo hace, que es otra razón para no usar rmmod directamente en producción.
modinfo e1000e en el ejemplo muestra el campo vermagic, que contiene la versión exacta del kernel para la que fue compilado el módulo. El kernel verifica este campo al cargar y rechaza módulos que no coincidan. Esto protege contra el escenario silencioso y peligroso de cargar un módulo compilado para otra versión que podría tener interfaces internas incompatibles.
La separación entre /etc/modules-load.d/ y /etc/modprobe.d/ tiene lógica propia. El primero solo lista qué módulos cargar; el segundo define cómo configurarlos. Mezclar ambas responsabilidades en un solo mecanismo complicaría la gestión porque querrías cambiar opciones de un módulo sin afectar si se carga o no, y viceversa. En el ejemplo del bonding, ponemos bonding.conf en ambos directorios: uno para que systemd-modules-load.service lo cargue en boot, otro para que modprobe use las opciones correctas cada vez que alguien lo cargue manualmente.
El blacklist de nouveau usa dos directivas porque una sola no es suficiente en todos los escenarios. blacklist nouveau evita que modprobe lo cargue por nombre, pero si otro módulo lo requiere como dependencia, el sistema podría cargarlo igualmente. options nouveau modeset=0 deshabilita el mode-setting, que es lo que hace al driver funcional para mostrar imagen; es un seguro adicional. Tras editar cualquier fichero en /etc/modprobe.d/, el update-initramfs -u -k all es obligatorio en Debian porque la configuración de módulos se embebe en el initramfs, y si no regeneras, el sistema arrancará ignorando tus cambios.
insmod con la ruta completa al .ko en el ejemplo ilustra exactamente por qué no se usa en operación normal: tienes que conocer la ruta exacta, tienes que cargar manualmente cada dependencia en el orden correcto, y si te equivocas, el kernel te devuelve ENOENT o EINVAL sin demasiadas explicaciones. modprobe hace todo eso consultando el fichero modules.dep que depmod genera; si alguna vez un módulo no carga correctamente con modprobe pero sí con insmod directo, suele significar que depmod no se ejecutó tras instalar un módulo nuevo y el árbol de dependencias está desactualizado.
N° 93