A fondo: Tu primer módulo cargable para el Linux kernel
Esta entrada pretende profundizar los temas aprendidos en el anterior artículo (Tu primer módulo cargable para el Linux kernel), con el fin de clarificar dudas, atar algunos cabos que dejamos sueltos y prepararnos para los siguientes artículos.
¿Puedo usar directamente el árbol del código fuente del kernel?
Sí, por supuesto. Como ya sabes, no es posible compilar un módulo cargable sin al menos parte del código fuente del kernel – esto es, la parte que contiene la infraestructura de construcción general y los archivos de cabecera esenciales. Solo por recordar, un archivo header (con extensión .h), contiene, normalmente, una declaración directa de clases, subrutinas, variables, u otros identificadores; sin la definición de estas, no es posible compilar código que las use.
En cualquier caso, es siempre útil tener una copia completa del código del kernel, y la manera más fácil de obtenerla es usando git:
Además que puedes actualizarlo fácilmente:
Nótese que, al contrario de instalar el paquete oficial de desarrollo del kernel como hicimos en el anterior artículo, usando git no es necesario tener privilegios administrativos para poner todo el código en algún lugar de tu directorio home. Además, si por alguna razón no puedes usarlo, puedes descargarte un archivo tar y funcionará igual; claro, usar git es mucho más cool
Urgando en el código fuente: examinando los archivos de cabecera (Header Files)
Recordemos que en el ejemplo del artículo anterior, nuestro código contiene referencias a archivos de cabecera del kernel:
#include <linux/module.h> // para todos los módulos #include <linux/init.h> // para entrada y salida de macros #include <linux/kernel.h> // para los macros de prioridad de printk #include <asm/current.h> // procesar información, solo por diversión #include <linux/sched.h> // para usa la estructura task_struct
Tales referencias son siempre relativas al nivel superior del directorio include/ en el código fuente del kernel, así que un include de, por ejemplo, <linux/module.h>, se refiere al archivo de cabecera include/linux/module.h, y así sucecivamente.
Sin embargo, si miras un poco más de cerca, notarás que no hay un directorio include/asm/, lo cual es bastante fácil solucionar. Incluso si no planeas construir algo contra el código fuente que descargaste, deberías prepararlo de tal manera que refleje de una manera más precisa lo que piensas hacer con él:
$ make modules_prepare
Los comandos anteriores hacen bastantes cosas, aunque lo único que nos interesa en este momento es que crea algunos enlaces simbólicos en el directorio include/, que reflejan la arquitectura de nuestro sistema.
Antes de ejecutar los comandos:
drwxrwxr-x. … include/asm-arm
drwxrwxr-x. … include/asm-generic
drwxrwxr-x. … include/asm-x86
Después de ejecutarlos:
lrwxrwxrwx. … include/asm -> asm-x86 <– aja!
drwxrwxr-x. … include/asm-arm
drwxrwxr-x. … include/asm-generic
drwxrwxr-x. … include/asm-x86
Una vez que esto es hecho, los includes de preprocesador tendrán sentido, y podemos usar los nombres genéricos para referirnos al archivo de cabecera apropiado de ahora en adelante. Por supuesto, esto que hicimos no es necesario hacerlo con los paquetes oficiales de desarrollo del kernel, puesto que ya vienen preparados.
¿Es posible generar mensajes de salida con un módulo?
No, no lo es. Bueno, no realmente. Como un autor de módulos novato, es necesario que entiendas que tu módulo será ejecutado en el kernel space, no en el user space, así que debes dejar de pensar en impresiones de mensajes en la consola. Olvídate de ello. La manera canónica de generar mensajes de depuración desde tu módulo es con llamadas printk:
printk(KERN_INFO "Hola, el modulo esta siendo cargado.\n"); printk(KERN_INFO "El user space del proceso es '%s'\n", current->comm); printk(KERN_INFO "El PID es %i\n", current->pid);
Corriendo el riesgo de sobre-simplificar el asunto, la salida que genera printk acabará en el archivo /var/log/messages así que, si estás insertando y removiendo tu módulo, es útil tener una terminal aparte mostrando en tiempo real todo lo que sea escrito en dicho archivo (para lo cual necesitas privilegios administrativos):
Para los más ambiciosos, es útil fijarse en la definición de los niveles de log en el archivo de cabecera <linux/kernel.h>:
#define KERN_EMERG "<0>" /* system is unusable */ #define KERN_ALERT "<1>" /* action must be taken immediately */ #define KERN_CRIT "<2>" /* critical conditions */ #define KERN_ERR "<3>" /* error conditions */ #define KERN_WARNING "<4>" /* warning conditions */ #define KERN_NOTICE "<5>" /* normal but significant condition */ #define KERN_INFO "<6>" /* informational */ #define KERN_DEBUG "<7>" /* debug-level messages */
Si algunos de ellos lucen vagamente familiares, no es de sorprenderse – son los niveles de depuración (debugging) soportados por syslog, así que eres libre de personalizar el syslog si quieres redireccionar la salida de tu módulo a donde quieras basado en los niveles de log, lo cual está más allá del objetivo de este artículo.
printk es simplemente una cadena de texto como “<0>” y así sucesivamente, lo cual explica el porqué no usar comas al momento de usarlo – todo lo que el preprocesador está haciendo es concatenando dos cadenas de caracteres, así que hubiera sido totalmente equivalente escribir cualquiera de estos:
printk(KERN_INFO "Hola, el modulo esta siendo cargado.\n");
printk("<6>" "Hola, el modulo esta siendo cargado.\n");
printk("<6>Hola, el modulo esta siendo cargado.\n");
Aun así, es mejor hacerlo como se muestra en la primera línea. Incluso programando cosas del kernel, la estética importa.
Cargando tu módulo lleno de información
Como te diste cuenta la última vez, puedes cargar tu módulo con información bastante útil como:
MODULE_AUTHOR("Robert P. J. Day");
MODULE_AUTHOR("Cristian Castiblanco [solo lo puse en castellano]");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("Aqui puedes poner una descripcion de tu modulo");
que puede examinarse con el comando modinfo:
filename: hola.ko
description: Aqui puedes poner una descripcion de tu modulo
license: Dual BSD/GPL
author: Cristian Castiblanco [solo lo puse en castellano
author: Robert P. J. Day
depends:
vermagic: 2.6.29-gentoo-r5 SMP mod_unload CORE2
Algunas cosas útiles acerca de esta característica:
- El conjunto completo de estos macros está definido en el archivo de cabecera
<linux/module.h>, en donde verás macros relacionados con firmware, tablas de dispositivos y más. - Además de esos macros en específico, está el menos conocido macro genérico MODULE_INFO, el cual puedes usar dentro del módulo en el lugar que quieras. Curiosamente, muy pocos programadores toman ventaja de este.
- La única macro que realmente debes poner es la licencia. Si no especificas alguna de las variaciones de la licencia GPL, entonces el módulo “contaminará” el kernel, un tema que trataremos en otro artículo. El conjunto completo de licencias válidas está definido en el mismo archivo de cabecera.
Compilando contra el código fuente del kernel
Finalmente, podrías querer construir tu módulo, no contra el código instalado por el paquete de desarrollo, sino contra el código que descargaste tú mismo. Si ese es tu plan, es bastante simple.
Primero, como decíamos arriba, necesitas preparar tu código fuente para construir el módulo:
$ make modules_prepare
Todo lo que falta es configurar el Makefile de tal manera que compile el módulo contra nuestro código fuente. Recordemos parte del archivo Makefile del artículo anterior:
ifeq ($(KERNELRELEASE),) KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd)
Lo que hace la segunda línea es asignar la ubicación del código fuente del kernel a ser usado. Para cambiar esta variable, podemos asignarle el valor deseado con este comando:
y aquí es donde comienzas a notar algunas diferencias. Asumiendo que la versión del código que descargué es 2.6.31-rc1, esto es lo que sucede en mi sistema:
make -C /usr/src/git-kernel M=/tmp/modulo modules
make[1]: se ingresa al directorio `/usr/src/git-kernel’
WARNING: Symbol version dump /usr/src/git-kernel/Module.symvers
is missing; modules will have no dependencies and modversions.
Building with KERNELRELEASE = 2.6.31-rc1
CC [M] /tmp/modulo/hola.o
Building modules, stage 2.
Building with KERNELRELEASE = 2.6.31-rc1
MODPOST 1 modules
CC /tmp/modulo/hola.mod.o
LD [M] /tmp/modulo/hola.ko
make[1]: se sale del directorio `/usr/src/git-kernel’
Puesto que estoy compilando contra un código fuente que no concuerda con el que estoy corriendo actualmente, pierdo la tabla de símbolos actual, pero la compilación funciona.
El comando modinfo también muestra el árbol contra el cual el módulo fue compilado:
filename: hola.ko
description: Aqui puedes poner una descripcion de tu modulo
license: Dual BSD/GPL
author: Cristian Castiblanco [solo lo puse en castellano
author: Robert P. J. Day
depends:
vermagic: 2.6.31-rc1 SMP mod_unload 686
Ahora algunas preguntas capciosas – ¿es posible cargarlo? ¿no dará problemas por la versión? Eso depende de si el kernel que se está ejecutando fue configurado para permitir diferencias de versiones, y hay una manera rápida de comprobarlo. En mi Gentoo por ejemplo:
insmod: error inserting ‘hi.ko’: -1 Invalid module format
Mala suerte. Tal parece que el kernel no fue compilado con las opciones necesarias. Si miramos en el archivo /var/log/messages:
[ 4908.680123] hola: version magic ‘2.6.31-rc1 SMP mod_unload 686 \
‘ should be ‘2.6.29-gentoo-r5 SMP mod_unload CORE2 ‘…
Por lo pronto entonces, de ahora en adelante, jugaremos un poco dentro de la seguridad que nos ofrece compilar contra el kernel que concuerda con la versión que corremos. No hay motivo para hacerlo más difícil de lo que debe ser. En el próximo artículo veremos algo acerca de las rutinas entry y exit.
3 Comentarios | deja el tuyo



Muy buen articulo (Y)