Núcleo del Sistema

De GLASS

Tabla de contenidos

Introducción

Después de leer este capítulo tendrá una idea clara del lo que es el kernel y cómo adaptarlo a sus necesidades. Aprenderá también qué son los módulos y cómo utilizarlos.

Como ya se explicó en el capítulo 2, el kernel o núcleo del sistema actúa como un intermediario entre el hardware del sistema y las aplicaciones, poniendo a su disposición una serie de recursos que permiten su ejecución, entre los que destacan, la asignación de la memoria de los programas que se están ejecutando, el reparto del tiempo de CPU entre los diferentes procesos, o los mecanismos para la intercomunicación de procesos. Por otra parte, el núcleo proporciona una interface que le permite a las aplicaciones hablar con los diferentes dispositivos hardware. Dada la enorme cantidad de recursos hardware disponibles, y la constante aparición de nuevos dispositivos, es poco práctico que el kernel tenga soporte directo para todos estos dispositivos, pues esto requeriría de un kernel excesivamente grande y poco eficiente, ya que se daría soporte dispositivos de los carece el sistema. La solución a este problema llego con la aparición en 1995 de la version 1.2 del kernel de Linux, la cual disponía de soporte para módulos cargables en el kernel (Loadable Kernel Modules). Un módulo dinámico o cargable es una parte del kernel Linux que se compila por separado y puede ser incorporada al núcleo que ya esta corriendo.


Compilar el Kernel

Al iniciarse el sistema, se carga en memoria la imagen del kernel, que no es más que el resultado que se obtiene al compilar el código fuente del mismo. Esta imagen esta compuesta por código binario (ejecutable) que se ha creado con unas determinadas características, especificadas en el momento de la compilación.

Todo sistema operativo GNU/Linux dispone de una imagen del kernel, creada a partir de una determinada versión del código fuente del kernel Linux. Slacware 9.0 viene con varias imágenes precompiladas a partir del código fuente de la versión 2.4.20 de Linux. Pese a que el sistema es totalmente funcional sin necesidad de compilar una nueva versión del kernel, existen diferentes motivos por los que es muy recomendable, en ocasiones imprescindible, compilar una nueva imagen.

Motivos para compilar el kernel

Compilar el kernel no es algo estrictamente necesario, ya que las distribuciones utilizan uno precompilado que puede adaptarse a un gran numero de configuraciones posibles, pues el kernel se acompaña de un buen numero de módulos precompilados que permiten dar soporte a gran variedad de hardware. Sin embargo, existen poderosas razones por las cuales resulta muy recomendable generar (compilar) un nuevo kernel, por ejemplo: en ocasiones es necesario compilar el kernel para poder añadir hardware nuevo a un sistema, en otras ocasiones es justo lo contrario, nuestro kernel por defecto tiene soporte para hardware del cual no disponemos, en este caso es posible quitar dicho soporte del kernel para así conseguir un núcleo mas pequeño, y rápido. Si la maquina que ha de gobernar el kernel esta pensada para ofrecer servicios de red, utilizar un kernel “por defecto“ es la manera mas sencilla para facilitar la detección del mismo desde el exterior, y por lo tanto de explotar las vulnerabilidades conocidas para ese kernel. En otras ocasiones, algunas aplicaciones avanzadas pueden requerir aplicar un parche en el kernel, de esta manera podrá compilarse posteriormente con funcionalidades nuevas, para las que a priori no estaba preparado. También por motivos de seguridad, o para poder acceder a las nuevas características, puede ser interesante actualizar el kernel a una versión mas reciente.

Obtener las fuentes

La iso de Slackware 9.0 disponible en www.slackware.com viene sin las fuentes del kernel de Linux. Para compilar un nuevo kernel es posible utilizar el CD-ROM extra de Slackware o bien bajarse las fuentes de la versión del kernel que se deseen utilizar. Las fuentes del kernel se encuentran disponibles en www.kernel.org o en cualquiera de sus muchos mirrors, el comando siguiente muestra como bajarse las fuentes desde el mirror de Rediris:

wget -c ftp.rediris.es/mirror/kernel/v2.4/linux-2.4.21.tar.gz

Las fuentes residen en el directorio /usr/src/linux. Si no se han instalado las fuentes, este directorio no existirá. Lo más sencillo es descomprimir el archivo con las fuentes en /usr/src y crear luego un enlace simbólico llamado linux que apunte al directorio que contiene las fuentes de la versión descargada.

tar -zxvf linux-2.4.21.tar.gz

Este comando creará un directorio con las fuentes llamado linux-2.4.21 ya que al descomprimir el archivo de las fuentes linux-x.y.z.tar.gz se crea un directorio llamado linux-x.y.z. Para crear el enlace simbólico al directorio que contiene las fuentes.

ln -s linux-2.4.21 linux
Nota: Consultar la documentacion: Al descomprimir el tarball que contiene las fuentes se crea el directorio /usr/src/linux-x.y.z/Documentation. Es muy recomendable consultar esta documentación para ver los cambios introducidos en esta versión del kernel o para saber mas acerca de cualquier tema relacionado con la compilación del kernel.

El archivo de configuración

El siguiente paso consiste en crear el archivo de configuración del kernel. Este archivo especifica las características que ha de tener el kernel que se va a compilar. Existen tres métodos para crear este archivo, son estos: el comando make config, el comando make menuconfig y el comando make xconfig. El primero es poco recomendable debido a que implica responder a infinidad de preguntas una por una, el segundo es el más adecuado si se trabaja desde la shell, el último método sólo es posible utilizarlo si se ha iniciado una sesión en un entrono X-Window. Por ello este texto se centra en la opción make menuconfig, si bien la mayoría de opciones descritas son aplicables a make xconfig.

Para crear el archivo de configuración con cualquiera de los métodos anteriores es necesario ser root y estar situado en el directorio de las fuentes, /usr/src/linux.

root@super8:~# cd /usr/src/linux
root@super8:/usr/src/linux# make menuconfig

Aparece entonces la pantalla siguiente:

Imagen:menu-config.png
Menú Principal del programa de configuración del kernel en modo consola.

Tal como explica el menú principal, podemos desplazarnos a lo largo de las diferentes categorías de opciones mediante los cursores. Con la tecla <ENTER> desplegamos los submenus de opciones para cada categoría. Si deseamos incluir una característica en el kernel se utiliza la tecla <Y>, para incluirla dentro del kernel (buit-in). Con la tecla <M> se añade esta funcionalidad mediante módulos (compilados por separado).

Dada la gran cantidad de opciones disponibles, es probable que en ocasiones tenga dudas acerca de que opciones ha de seleccionar y cuales no. Cuando esto sucede, basta con pulsar la tecla <?> para desplegar una completa descripción sobre la opción en cuestión.

El archivo de configuración del kernel se llama (.config) y se crea tras ejecutar por primera vez make menuconfig, make xconfig o make config. Este archivo contiene todas las opciones que se han ido seleccionando desde el menú de configuración del kernel.

Cuando se compila el kernel para actualizarlo a una versión nueva, puede ser interesante utilizar el archivo de configuración de un kernel anterior, en lugar de crear un archivo de configuración nuevo, basta entonces con copiar el archivo de configuración que se desee utilizar a /usr/src/linux y saltarse el paso de de configuración del kernel. Otra opción puede ser modificar un archivo de configuración existente, para ello es posible cargar un archivo de configuración alternativo al archivo (.config) cargado por defecto. Para ello se ha de seleccionar la opción Load an Alternate Configuration File e introducir el archivo de configuración deseado. También es posible salvar el archivo de configuración con un nombre alternativo al utilizado por defecto, para ello se utiliza la opción Save Configuration to an Alternate File.

Compilar el kernel

Una vez creado o copiado el archivo de configuración del kernel, los pasos para compilarlo y crear así una nueva imagen, son los siguientes: make dep make clean make bzImage

Otra forma de ejecutar estos comandos mediante una sola orden es esta: make dep && make clean && make bzImage

make dep

Este comando chequea que se cumplen todas las dependencias, es decir, que se dispone de todos los archivos include necesarios para compilar el kernel.

make clean

Elimina una serie de archivos intermedios que una compilación anterior haya podido dejar. Si no hemos compilado un kernel anteriormente no es imprescindible ejecutar este comando, pero si se esta recompilando un kernel se deberá ejecutar. En cualquier caso y dado que este comando no requiere mucho tiempo de ejecución, lo mas recomendable es ejecutarlo siempre.

Es posible realizar una limpieza mas exhaustiva mediante el comando make mrproper. Este tipo de limpieza es recomendable antes de proceder a actualizar un kernel mediante un parche. Se ha de tener en cuenta que esta orden elimina hasta el archivo de configuración del kernel (.config), por lo cual es recomendable tener una copia de seguridad del mismo antes de realizar este tipo de limpieza.

make bzImage

Una vez comprobadas las dependencias y eliminados los archivos intermedios que pudieran existir, se utiliza make bzImage para compilar las fuentes del kernel y crear una imagen nueva. Este proceso consume bastante tiempo, especialmente si se trata de un equipo antiguo.

Finalizado el proceso de compilación, se crea la nueva imagen comprimida en /usr/src/linux/arch/i386/boot llamada bzImage. El siguiente paso consiste en instalar esta nueva imagen para que el equipo pueda arrancar el sistema con el nuevo kernel.

Existe también la opción de crear el kernel en un disquete y arrancar con él el sistema. Este método permite comprobar el funcionamiento del nuevo kernel sin tener que instalarlo, para ello, en lugar de ejecutar make bzImage, se ha de insertar un disquete en la unidad y ejecutar make bzdisk.

Instalar el kernel

Una vez que se dispone de una imagen del kernel, el siguiente paso consiste en instalarla o, dicho de otro modo, hacer que sea la nueva imagen la utilizada para arrancar y gobernar el sistema. Cuando el sistema arranca, ejecuta la imagen del kernel que se encuentra en el directorio /boot y que por defecto se llama vmlinuz, por lo tanto el paso lógico seria copiar la imagen recién compilada al directorio /boot y realizar los cambios necesarios en el gestor de arranque del sistema.

# cd /usr/src/linux/arch/i386/boot/
# cp bzImage /boot/vmlinuz

Posteriormente tan sólo se tendría que volver a compilar el gestor de arranque LILO, ver [Linux Loader, LILO]. El problema de hacerlo de este modo es que, sino realizamos una copia de la imagen antigua, esta se pierde al ser sobreescrita por la nueva. Otra opción sería hacer una copia de la imagen antigua y editar el archivo de configuración de LILO (/etc/lilo.conf) y compilarlo con la orden lilo. Sin embargo la opción más sencilla y la utilizada en Slackware consiste en copiar la imagen a /boot con un nombre diferente del tipo linux-bzImage-x.y.z o algo similar y posteriormente crear un enlace simbólico llamado vmlinuz que apunte a la nueva imagen.


# cp /usr/src/linux/arch/i386/boot/bzImage /boot/linux-bzImage-2.4.21
# ln -sf linux-bzImage-2.4.21 vmlinuz 
# lilo

De esta manera no hace falta hacer una copia de seguridad de la nueva imagen, dado que al cambiar el nombre de destino no se sobreescribe la imagen del kernel actual. Por otro lado tampoco es necesario modificar /etc/lilo.conf, dado que éste sigue estando configurado para arrancar la imagen /boot/vmlinuz. En cualquier caso, resulta siempre interesante añadir una entrada al final de archivo /etc/lilo.conf que, en caso de fallo, permita arrancar fácilmente el antiguo kernel. Tras realizar estos pasos el sistema ya esta listo para arrancar con el nuevo kernel.

Para finalizar correctamente la instalación es necesario asegurarse de que las aplicaciones que requieren del archivo System.map, sepan encontrar la versión correcta del mismo, ver [System.map]. La forma más sencilla de asegurarse de que esto sucede es copiar el archivo System.map, creado al compilar el nuevo kernel, al directorio /boot. El primer sitio en el que las aplicaciones buscaran este archivo es en /boot/System.map, este archivo puede ser un enlace simbólico a la versión correcta del archivo System.map. Lo mismo sucede con el archivo de configuración del kernel.

Dado que /usr/src/linux es un enlace simbólico a las fuentes del kernel actual, se asegura que las aplicaciones que requieren del archivo System.map encuentran la versión correcta del mismo. En cualquier caso siempre podemos actualizar los enlaces simbólicos de /boot/System.map, asegurando que se está utilizando la versión correcta del archivo.

cp /usr/src/linux/System.map /boot/System.map-2.4.21
cp /usr/src/linux/.config /boot/config-2.4.21
ls -sf /boot/System.map-2.4.21 /boot/System.map
ls -sf /boot/config-2.4.21 /boot/config

System.map

El archivo System.map es utilizado por aplicaciones como klogd (kernel log daemon) para traducir las señales y códigos de error enviadas por el kernel, en algo más comprensible para los humanos. Cada kernel genera su propio archivo System.map, incluso dos compilaciones de una misma versión generan archivos System.map diferentes. Klogd y otras aplicaciones buscarán este archivo en los siguientes directorios:

  • /boot/system.map
  • /System.map
  • /usr/src/linux/System.map

Es por eso que, aunque no copiemos el archivo actual System.map a /boot éste será encontrado por klogd al buscar en /usr/src/linux/, dado que este directorio es un enlace simbólico a las fuentes del kernel actual.

Módulos

Los módulos o Loadable Kernel Modules (LKMs) permiten añadir código ejecutable al kernel en ejecución. De esta forma es posible añadir nuevas funcionalidades al kernel sin necesidad de recompilarlo. Existen muchos tipos de módulos, desde drivers de dispositivos hardware a controladores para trabajar con diferentes sistemas de ficheros.

Los módulos aportan muchas ventajas: por un lado permiten crear un kernel más reducido y rápido, por otro evitan tener que recompilar el kernel constantemente. Además ayudan a diagnosticar fallos en el sistema: un fallo en un driver que forma parte de la base del kernel (se compilo dentro del kernel, no como módulo), puede hacer que el sistema no llegue a arrancar, dificultando mucho la tarea de detectar el fallo y solventarlo. Si el fallo se produce en un driver en forma de módulo, el sistema arrancará, y simplemente fallará en el momento de cargar dicho módulo. Bastará entonces con no cargar ese módulo, para solucionar los problemas de inestabilidad del kernel, hasta que este disponible una actualización para ese módulo o se tenga una solución al problema.

Existen dos tipos de módulos, los módulos que forman parte de la distribución del kernel Linux y los que no. Los módulos se crean como parte del proceso de compilación de un nuevo kernel. Recuerde que en el momento de creación del archivo de configuración del kernel, se seleccionan una serie de funcionalidades como módulos y otras como partes del kernel base. Aquellas funcionalidades que se seleccionaron como módulos serán compiladas como tales cuando se generen los módulos mediante el comando make modules.

Los módulos no son otra cosa que un archivo u objeto ELF cuyo nombre tiene la forma nombre_modulo.o. Los módulos se cargan dinámicamente cuando son requeridos por el kernel para satisfacer las necesidades del sistema. Cuando esto sucede, el kernel se lo notifica a kmod (Kernel Module Loader, sustituto del anterior kerneld) y éste los carga mediante la orden insmod o modprobe. Para que los módulos sean cargados dinámicamente por kmod, se ha de seleccionar la opción Kernel module loader en el momento de configurar el kernel.

Los módulos que no forman parte de la distribución del kernel de Linux (módulos externos), disponen de su propio proceso de generación, que puede variar para cada uno de ellos, en cualquier caso, el resultado de dicho proceso será siempre un archivo u objeto ELF, el cual podrá insertarse en el kernel en ejecucion mediante los comandos insmod o modprobe, cuando sea necesario.

Compilar e instalar los módulos

Tras compilar el kernel, llega el momento de generar los módulos, para ello se utiliza el comando make modules. Este comando crea los archivos “.o“ que más tarde podrán ser cargados en el kernel. Tras crear los módulos, éstos se instalan con el comando make modules_install.

make modules
make modules_ install

Los módulos del kernel se almacenan en el directorio /lib/modules, y debido a que los módulos son específicos para cada versión del kernel, se crea un directorio /lib/modules/x.y.z para cada versión del kernel. El comando make modules_install crea el directorio para la versión del kernel que se esta compilando y copia los módulos creados al lugar que le corresponden.

Tras realizar estos dos pasos los módulos se encuentran en condiciones de ser utilizados. A continuación se presentan algunas de las utilidades que permiten manejar los módulos.

Utilidades para trabajar con los módulos

A continuación se describen algunas de las herramientas que proporciona el sistema para manejar los módulos. Estas herramientas están incluidas en el paquete modutils.

root@super8:~# cat /var/log/packages/`ls /var/log/packages |grep modutils`|grep
modutils:
modutils: modutils (kernel module utilities)
modutils:
modutils: Utilities to use kernel modules.  These tools are used for loading
modutils: and unloading chunks of kernel code on the fly, to add support for a
modutils: CD-ROM drive or ethernet card, for instance.
modutils:
modutils: Many Linux drivers are provided as kernel modules, and some packages
modutils: (like the PCMCIA package) require the module utilities to work, so you
modutils: probably want to install these.
modutils:
modutils:

lsmod

Muestra la lista de módulos cargados en el kernel.

root@super8:~# lsmod
Module                  Size  Used by    Not tainted
8139too                15848   1
vfat                   10924   1  (autoclean)
fat                    33624   0  (autoclean) [vfat]

La información mostrada es la misma que la contenida en el /proc/modules.

rmmod

Permite descargar un módulo cargado en el kernel.

insmod

Permite cargar un módulo en el kernel.

insmod nombre_modulo

Donde nombre_modulo es el nombre del archivo correspondiente a dicho módulo sin la extensión “.o“, o su versión comprimida “o.gz“ Si el módulo que se pretende cargar depende de otros no sera cargado.

root@super8:~# insmod bttv
Using /lib/modules/2.4.20/kernel/drivers/media/video/bttv.o.gz
/lib/modules/2.4.20/kernel/drivers/media/video/bttv.o.gz: unresolved symbol i2c_master_send
/lib/modules/2.4.20/kernel/drivers/media/video/bttv.o.gz: unresolved symbol i2c_bit_del_bus
/lib/modules/2.4.20/kernel/drivers/media/video/bttv.o.gz: unresolved symbol video_register_device
/lib/modules/2.4.20/kernel/drivers/media/video/bttv.o.gz: unresolved symbol video_unregister_device
/lib/modules/2.4.20/kernel/drivers/media/video/bttv.o.gz: unresolved symbol i2c_bit_add_bus
/lib/modules/2.4.20/kernel/drivers/media/video/bttv.o.gz: unresolved symbol i2c_master_recv

modprobe

Carga módulos en el kernel de manera inteligente, por ejemplo, si para cargar el módulo mod-A.o se requiere cargar antes el modulo mod-B.o, modprobe cargará automáticamente el módulo mod-A.o. Este era el motivo por el cual, en el ejemplo anterior fallaba la orden insmod bttv. Con modprobe en cambio, el módulo y los módulos de los cuales depende son cargados automáticamente. root@super8:~# modprobe bttv root@super8:~# lsmod Module Size Used by Not tainted tuner 9696 1 (autoclean) tvaudio 12188 0 (autoclean) (unused) bttv 67936 0 (unused) i2c-algo-bit 6984 1 [bttv] i2c-core 12708 0 [tuner tvaudio bttv i2c-algo-bit] videodev 5632 2 [bttv] parport_pc 14724 0 parport 23264 0 [parport_pc] usbcore 58144 1 soundcore 3332 0 [bttv] ide-scsi 8048 0 8139too 15272 1 mii 2240 0 [8139too]

depmod

Determina las interdependencias entre módulos. Es utilizado por modprobe para determinar que módulos ha de cargar.

modinfo

Muestra el contenido de la sección .modinfo de los módulos o archivos “.o“. El formato para esta orden es:

modinfo <opciones> <modulo>

Algunas opciones disponibles son: -a (muestra el autor del modulo), -d (muestra la descripción), o -p (muestra los parámetros que se le pueden pasar). Utilizado sin parámetros muestra toda la información disponible para ese modulo.

root@super8:~# modinfo sound
filename:    /lib/modules/2.4.20/kernel/drivers/sound/sound.o.gz
description: "OSS Sound subsystem"
author:      "Hannu Savolainen, et al."
license:     "GPL"
parm:        dmabuf int
parm:        dmabug int

pcimodules

Muestra la lista de módulos disponibles para los dispositivos PCI de los que dispone el sistema. Este comando es ejecutado es ejecutado durante la inicialización del sistema para determinar que módulos han de ser cargados.

lspci

Muestra información de los buses PCI del sistema y de los dispositivos conectados a ellos.

/etc/rc.d/rc.modules

Este archivo se utiliza para cargar módulos adicionales al iniciar el sistema. En un kernel “hotplug“ en el cual kmod se encarga de cargar los módulos necesarios, este archivo es irrelevante, si bien los módulos descomentados en este archivo serán igualmente cargados en el sistema.

LILO

LILO es la aplicación encarga de de instalar el gestor de arranque del sistema, de su correcta configuración depende el que se arranque la imagen de kernel apropiada. Mediante LILO es posible también arrancar otros sistemas operativos. A continuación se muestra un archivo de configuración de LILO.

# Configuracion de ejemplo de LILO
boot = /dev/hda
message = /boot/boot_message.txt
prompt
timeout = 1200
default=FreeBSD
image="/boot/vmlinuz"
       root="/dev/hda7"
       label="Slackware"
read-only
image="/boot/linux-2.4.19"
       root="/dev/hda7"
       label="OldKernel"
read-only  
other=/dev/hda3
       label="FreeBSD"
other = /dev/hda1
       label="Windows"
       table = /dev/hda

La linea que deberemos editar para arrancar la imagen del kernel apropiada es image=/boor/imagen_deseada.

Para saber mas:

man lilo
man lilo.conf
Nota: Compilar LILO: Es importante remarcar que no es suficiente con editar el archivo /etc/lilo.conf. Recuerde que para que los cambios tengan efecto debe compilar LILO ejecutando el comando lilo.

Parchear el kernel

Aplicar un parche al kernel consiste en añadir modificaciones en las fuentes del kernel. Estas modificaciones permiten aplicar parches “oficiales“ para actualizar las fuentes a versiones más nuevas, o bien aplicar parches “no oficiales“ que permitan añadir funciones nuevas al kernel.

Para actualizar el kernel a una versión nueva, en primer lugar se ha de descargar el parche apropiado. Suponiendo que se dispone de las fuentes de la versión 2.4.20 y que se desea actualizar a la versión 2.4.21 se han de seguir los siguientes pasos:

Antes de proceder es recomendable realizar una copia de seguridad de las fuentes actuales:

cd /usr/src
tar zcvf fuentes-antiguas.tar.gz linux

Descarga del parche apropiado:

wget ftp://ftp.rediris.es/mirror/kernel/v2.4/patch-2.4.21.gz
cd /usr/src

Aplicar el parche (suponiendo que el parche esta comprimido y en /usr/src):

cd /usr/src
zcat patch-2.4.21.gz | patch -p0

O también:

unzip patch-2.4.21.gz
patch -p0  patch-2.4.21.gz

Compilar normalmente:

cd /usr/src/linux

Copiar el archivo de configuración (.config)

cp archivo_de_configuracion /usr/src/linux

O generar un nuevo archivo de configuración y/o modificar uno existente:

make menuconfig

Compilar el kernel:

make dep && make clean && make bzImage

Compilar e instalar los módulos:

make modules && make modules_install

Existen otros parches no oficiales que permiten introducir funcionalidades, aun no presentes en el kernel de Linux. Un ejemplo de esto sería el parche IP Virtual Server, el cual implementar un balanceador de carga (Linux Virtual Server). Tras descargar el parche correspondiente a la versión del kernel y aplicarlo, aparecen en el programa de configuración del kernel nuevas opciones que permiten configurar el kernel para que se comporte como un balanceador de carga.

Los pasos para parchear el kernel serían los siguientes: Descargar el parche linux-2.4.20-ipvs-1.0.9.patch.gz desde el sitio web www.linuxvirtualserver.org y copiarlo al directorio de las fuentes.

cp  linux-2.4.20-ipvs-1.0.9.patch.gz /usr/src/2.4.20
zcat linux-2.4.20-ipvs-1.0.9.path.gz | patch -p1

La opción -p1 es necesaria para parchear unas fuentes que no están en /usr/src/linux

Una vez finalizado con éxito el parcheado, aparecerán las nuevas opciones en el menú de configuración del kernel.

Herramientas personales