Configuración y Compilación del Kernel
Autor: PinkX
La intención de este documento es orientar a quienes quieren, por primera vez, compilar ellos mismos el kernel de su máquina. Comenzaré por explicar conceptos básicos como qué es el kernel, y describiré algunas de las características más comunes e importantes.
En ningún caso pretendo describir la funcionalidad de cada una de las opciones que forman parte del kernel, sino únicamente servir de guía en este importante paso.
¿Qué es el Kernel?
Debemos comenzar por respondernos esta pregunta. El Kernel en sí es el corazón o núcleo del Sistema Operativo. En nuestro caso, el kernel se llama Linux, y el Sistema Operativo en sí lo compone el kernel junto con una serie de programas y aplicaciones.
¿Para qué compilar el Kernel?
Una pregunta que a veces los usuarios principiantes se hacen es justamente esta: para qué compilar el kernel si ya tienen su máquina funcionando. La respuesta es simple, y es que entre una versión y otra de los distintos kernel se agregan caracterísitcas nuevas al mismo además de una serie de mejoras. Por otro lado, el kernel que acompaña a las distribuciones es un kernel genérico, esto quiere decir que no tiene ningún tipo de optimización para nuestro hardware específico, viene por defecto para procesadores i386, y lo más probable que tenga soporte para una gran cantidad de dispositivos que es innecesaria puesto que no los poseemos.
Por último, el compilar el kernel es un paso altamente educativo y didáctico para comprender en mayor profundidad el funcionamiento del sistema, y siempre es bueno saberlo por si alguien lo pregunta ;-)
¿Qué necesito para poder compilar el kernel?
En primer lugar, un compilador (gcc). Necesitamos además las librerías de desarrollo correspondientes al sistema (glibc), y en algunos casos requeriremos también del ensamblador as que se distribuye en el paquete binutils. Otros ensambladores disponibles para Linux son gasm o nasm.
Una vez que confirmemos la existencia de todas estas herramientas en nuestro sistema, es necesario contar, obviamente, con las fuentes del kernel.
En la mayoría de las distribuciones estas se incluyen en el disco de instalación, pero no se instalan por defecto. De todas maneras es recomendable bajar siempre la última versión puesto que la mayoría de los fabricantes de distribuciones incluye código adicional en su kernel que no forma parte del original.
Si tienes la seguridad que cuentas con los fuentes 100% originales sin modificaciones, es posible que te actualices a la última versión a través de parches.
Los parches son archivos que contienen las diferencias entre un árbol de fuentes y otro. Por ende, es mucho más conveniente actualizar nuestro kernel a través de este metodo sobre todo si contamos con una conexión a internet relativamente lenta, puesto que los parches son de muy pequeño tamaño.
Parchando el kernel
Si tienes una versión completa del kernel, no es necesario que leas esta sección, aunque puedes hacerlo para fines educativos.
Los parches se aplican en forma consecutiva. Esto quiere decir que, si por ejemplo, tu versión actual del kernel es la 2.2.13 y deseas actualizarte a la 2.2.16, necesitas los parches 2.2.14, 2.2.15 y 2.2.16. Estos deben ser aplicados en ese orden, uno después del otro, y no es necesario realizar una compilación en cada paso. Es necesario remarcar que no es posible parchar una versión mayor del kernel: del 2.0.36 no se puede parchar al 2.2.
Siguiendo el ejemplo anterior en que actualizaremos el kernel 2.2.13 al 2.2.16, supondremos que los parches se encuentran en nuestro directorio home para el usuario usuario, en formato gzip. El árbol de nuestro kernel actual se encuentra en /usr/src/linux, que a su vez debe ser un link simbólico a /usr/src/linux-2.2.13. Dentro de ese directorio, llamamos el siguiente comando:
localhost:/usr/src/linux# gzip -cd /home/usuario/patch-2.2.14.gz | patch -p0
localhost:/usr/src/linux# gzip -cd /home/usuario/patch-2.2.15.gz | patch -p0
localhost:/usr/src/linux# gzip -cd /home/usuario/patch-2.2.16.gz | patch -p0
Esta serie de comandos puede ser reemplazada por el siguiente, siempre y cuando tengamos solo aquellos parches en dicha ubicación:
localhost:/usr/src/linux# gzip -cd /home/usuario/patch-2.2.1* | patch -p0
Si después de lo anterior no obtenemos ningún mensaje de error (y espero que así sea), todo está bien y ahora contamos con un árbol actualizado con la última versión del kernel. Entonces actualizamos el link simbólico, renombrando el directorio primero:
localhost:/usr/src# mv linux-2.2.13 linux-2.2.16
localhost:/usr/src# ln -sf linux-2.2.16 linux
Finalmente tenemos todo listo para configurar y compilar.
El proceso de configuración
Como mencioné en un principio, una de las razones por la cual compilar el kernel es para ajustarlo y optimizarlo a nuestro hardware. Esto se hace por medio de la configuración, un proceso con una gran cantidad de opciones. Acá trataré de describir solo aquellas más comunes e importantes.
Antes de comenzar, es necesario tomar en cuenta lo siguiente: muchas de las características propias del kernel pueden ser compiladas dentro del mismo o como módulos. La diferencia radica principalmente en que, mientras más modularizado sea nuestro kernel, más pequeño será el tamaño de su imagen, lo que por ende nos lleva a una mejor utilización de la memoria.
Por otro lado, la utilización de módulos tiene otra gran ventaja: un kernel dinámico hace más facil la tarea de agregar o eliminar hardware, puesto que solamente es necesario compilar e instalar o eliminar el módulo correspondiente, respectivamente, y no reconfigurar todas las opciones del sistema y compilar un nuevo kernel. Además, los módulos son cargados en memoria y eliminados de ella en demanda, haciendo nuevamente un manejo más eficiente de los recursos.
Por último, cabe decir que hay quienes ven en la existencia de módulos un gran riesgo de seguridad: un módulo malicioso puede ser capaz de esconder procesos, usuarios, etc. (por ejemplo el módulo heroine). Por supuesto, los módulos requieren de privilegios root para ser instalados, por lo que la máquina debe tener un compromiso de seguridad previo para que esto suceda (cosa que un buen administrador no permitirá ;-)).
Dicho todo esto, vamos a lo nuestro. La configuración del kernel se lleva a cabo a través de un programa interactivo que muestra todas las posibles opciones. En este sentido, existen tres posibilidades: un sencillo programa de consola que consta en una serie de preguntas (no recomendado, es muy tedioso y no da la posibilidad de retroceder), otro es un programa al igual de consola pero con una interfaz basada en ncurses lo que lo hace mucho más amigable para el usuario (definitivamente la mejor alternativa), y por último un programa similar pero para X11 (no recomendado puesto que puede presentar problemas de estabilidad). Estos programas se ejecutan, respectivamente, de la siguiente manera:
localhost:/usr/src/linux# make config
localhost:/usr/src/linux# make menuconfig
localhost:/usr/src/linux# make xconfig
Todos estos cuentan con pantallas de ayuda para la gran mayoría de cada una de las opciones de configuración. Como mencioné más arriba, la segunda alternativa es la más segura, y es según la cual me guiaré en el resto de este documento.
Al ejecutar este comando, nos encontraremos luego de un instante frente a una pantalla con una serie de opciones.
¿Qué elegir?
Las opciones a elegir pueden presentarse de la siguiente manera:
[*] Caracteristica que solo es posible compilar dentro del kernel
< > Opcion que puede ser compilada como modulo independiente
Como la primera, las opciones que presentan ese tipo de paréntesis solamente se pueden compilar como parte del kernel y las opciones son "Y" o "N", mientras que en la segunda se nos da la posibilidad de implantarlas como módulo donde las opciones son "Y", "M" (módulo, o también interpretado como maybe, quizás) o "N".
A continuación, describiré brevemente las categorías y opciones más importantes:
Code maturity level options --->
[*] Prompt for development and/or incomplete code/drivers
Al seleccionar esta opción se nos preguntará durante el proceso de configuración si queremos incluir en nuestro kernel características muy nuevas y/o experimentales, incrementando la cantidad de posibilidades.
Processor type and features --->
(386) Processor family
(1GB) Maximum Physical Memory
[ ] Math emulation
[*] MTRR (Memory Type Range Register) support
[ ] Symmetric multi-processing support
En esta sección se indica el tipo de procesador y sus características: primero seleccionamos la familia de la CPU (386, 486, 586, 686, etc.), es importante elegir la que corresponde y no una mayor a la nuestra.
El resto de las opciones debieran de ser comunes para la mayoría de los usuarios: la cantidad máxima de memoria física (1 o 2 Gb), emulación matemática (no es necesario a menos que tengamos un 386 sin coprocesador), MTRR (muy importante para quienes poseen aceleradores gráficos), y SMP (sólo para quienes tienen procesadores duales/múltiples).
Loadable module support --->
[*] Enable loadable module support
[*] Set version information on all symbols for modules
[*] Kernel module loader
Definitivamente queremos soporte para las 3 ;-)
General setup --->
Opciones generales de configuración: soporte para redes, PCI, entre otros. De suma importancia:
[*] System V IPC
[*] BSD Process Accounting
[*] Sysctl support
<*> Kernel support for a.out binaries
<*> Kernel support for ELF binaries
Recomendado leer las pantallas de ayuda para cada una de las opciones ("?")
Plug and Play support --->
Sorpresa: soporte para dispositivos PnP (plug 'n pray)
Block devices --->
Configuración de dispositivos adicionales: de suma importancia, no olvidar: Si contamos con un disco duro IDE desde el cual booteamos Linux, el soporte para este tipo de dispositivos NO debe ser configurado como módulo; esto es motivo para un kernel panic al momento de iniciar el sistema. Lo mismo va para quienes poseen discos SCSI.
<*> Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support
<*> Include IDE/ATA-2 DISK support
El resto de las opciones son para dispositivos específicos en su mayoría, nuevamente "?".
Networking options --->
Opciones de red: soporte para firewall, IP aliasing, masquerading, entre otros. No olvidar "?".
SCSI support --->
Soporte para dispositivos SCSI, desde controladoras hasta una variedad de periféricos.
Network device support --->
Soporte para diversos dispositivos de red, como adaptadores Ethernet, Token Ring, X.25, etc. ("?").
ISDN subsystem --->
Configuración ISDN. "?".
Character devices --->
Diversas opciones de dispositivos tales como soporte para terminales virtuales, consolas, puertos seriales, autodetección de IRQs, mouse, joysticks, adaptadores de captura de video y otros. "?".
Filesystems --->
Soporte para diversos sistemas de archivos: Ni se te ocurra compilar el soporte para ext2 como módulo. Como siempre, "?".
Console drivers --->
Drivers para la consola de texto, desde el normal VGA hasta extensión framebuffer. Nuevamente "?".
Sound --->
Soporte para sonido, junto con módulos para una gran cantidad de tarjetas. Muy importante ver la ayuda correspondiente a la tuya ("?") además de la documentación existente en /usr/src/linux/Documentation/sound.
Kernel hacking --->
La única opción que encontraremos dentro de esta categoría es Magic SysRq key, la cual nos permite, si la habilitamos, poder acceder a pantallas informativas y otras funciones muy útiles (sobre todo si se cae el sistema) a través de combinaciones de teclas. Una vez más recomiendo leer la ayuda correspondiente ("?").
Y a continuación, ¿qué?
Una vez finalizada la configuración del kernel (y estando seguros de que todo está como corresponde), debemos salir del programa y guardar los cambios.
Hecho esto, procedemos a generar las dependencias y limpiar los objetos residentes anteriores que puedan andar por ahi:
localhost:/usr/src/linux# make dep && make clean
Pregunta (para quienes no saben): ¿qué significa el &&? Respuesta: es un operador lógico que significa and (y), en términos prácticos, separa dos comandos, y siempre y cuando la salida del primero haya sido true (verdadero), o sea, haya tenido éxito, ejecutará el siguiente. De esta forma, nos aseguramos que solo se continuará si es que no hay errores.
A continuación, la parte más larga: la compilación de la imagen del kernel:
localhost:/usr/src/linux# make zImage
Mientras esto ocurre, podemos disfrutar de una taza de café, aunque en los procesadores actuales esto es cada vez menos posible ;-)
Si al finalizar este proceso obtenemos un mensaje diciéndonos que el sistema es demasiado grande (system too big) tenemos dos posibilidades: tratar de compilar la imagen en un formato comprimido reemplazando el comando anterior por:
localhost:/usr/src/linux# make bzImage
o bien modularizando en mayor forma nuestro sistema, esto es, eliminando aquellas opciones innecesarias y utilizar la mayor cantidad de módulos posibles. Luego repetir el paso anterior.
Si, por el contrario, el sistema no nos arrojó ningún mensaje de error, estamos listos para el siguiente paso: compilar los módulos, e instalarlos:
localhost:/usr/src/linux# make modules && make modules_install
Otra pequeña espera (quizás mayor que la primera). Una vez terminado, estamos listos para nuestro siguiente paso.
La instalación del nuevo kernel
Si estás aquís es porque (espero) durante todo el proceso no has sufrido ningún fallo. Nuestro siguiente paso puede ser el más complejo y es instalar de forma definitiva el nuevo kernel en el sistema.
Aunque, según me han dicho, esto se puede hacer fácilmente en forma automática a través del siguiente comando:
localhost:/usr/src/linux:# make install
El asunto es que nunca lo he probado yo personalmente, ni me llama la atención hacerlo. Si eres de esas personas como yo, quienes prefieren hacer todo manualmente y entender un poco mejor como funciona todo esto, entonces sigue las instrucciones que se dan a continuación (en caso contrario y asumiendo que el comando anterior funcionó puedes dejar de leer):
Lo primero es copiar la imagen generada al directorio /boot del sistema. La ubicacion original de la imagen depende de la arquitectura, En este caso utilizaremos la del x86:
localhost:/usr/src/linux:# cp arch/i386/boot/zImage /boot/vmlinuz-2.2.16
Donde, por supuesto, reemplazaremos zImage con bzImage en caso de que hayamos generado la imagen con ese procedimiento, y tambien cambiamos el nombre del destino a la versión del kernel correspondiente. A modo de idea: yo mantengo links simbólicos a dos versiones del kernel, de esta manera al actualizarlo solo debo además actualizar los links, y no reconfigurar LILO:
localhost:/:# ln -sf /boot/vmlinuz-2.2.16 /boot/vmlinuz
localhost:/:# ln -sf /boot/vmlinuz-2.2.13 /boot/vmlinuz-old
Esto es, por supuesto, siguiendo nuestro primer ejemplo en el cual deseamos actualizar desde la versión 2.2.13 a la 2.2.16. Paso siguiente: actualizar el LILO.
Como mencioné anteriormente, si utilizas el sistema de links simbólicos descrito arriba solo será necesario que el LILO lo modifiques una primera vez. Lo que a continuación se muestra es un extracto del archivo de configuración de LILO, ubicado en /etc/lilo.conf correspondiente a la sección de la imagen de Linux:
image = /boot/vmlinuz
root = /dev/hda1
label = Linux
read-only
La idea es agregar una nueva sección, correspondiente a la nueva imagen, y cambiar el nombre de etiqueta de la actual, por lo que esta parte de nuestro archivo debiera de quedar de la siguiente manera:
image = /boot/vmlinuz
root = /dev/hda1
label = Linux
read-only
image = /boot/vmlinuz-old
root = /dev/hda1
label = Linux-old
read-only
No es mi intención en este caso describir la sintaxis de la configuración del LILO sino citarlo para lo que es necesario. Si deseas más detalles al respecto puedes referirte a la página del manual del lilo.conf(5).
El último paso: actualizar el LILO. Para eso, simplemente, lo ejecutamos:
localhost:/# lilo
Added Linux *
Added Linux-old
Lo olvidaba. Recomiendo también crear un link simbólico para el archivo System.map con el fin de no tener que copiarlo cada vez que compilemos un nuevo kernel (este archivo contiene información específica de la versión sobre los símbolos en los módulos):
localhost:/# ln -sf /usr/src/linux/System.map /boot/System.map
Fin
Eso es todo. Fin. Reinicia el nuevo kernel. Si funciona, felicitaciones. Si no, revisa cual pudo haber sido tu error.