jueves, 12 de julio de 2012

Programación de Módulos para el Kernel de Linux (II)

En una entrada anterior realicé una introducción a la programación de módulos para el kernel del Linux: los comandos básicos para gestionarlos (lsmod, insmod, rmmod, etc), su estructura básica, compilación, alguno que otro detalle y por supuesto, el típico hola mundo.

Particularmente para la compilación, utilicé un Makefile sencillo para utilizarlo en cualquier directorio y contra la versión actual del kernel.

Ahora queremos compilar un kernel vanilla, con nuestros propios módulos. Esto es bastante parecido a lo que hicimos en la entrada anterior con algunas consideraciones adicionales.

Partimos del hecho de que tenemos el kernel vanilla sobre el que trabajaremos, y más importante aún, que sabemos compilarlo, así que no tocaremos detalles de la compilación per se.

Agregaremos el hola mundo que ya hemos hecho, al directorio del kernel en el que se almacena el Linux Virtual Server, que en la versión sobre la cual estoy trabajando (la 3.0.1), sería net/netfilter/ipvs/.

Bien, vamos al directorio net/netfilter/ipvs y agregamos nuestro módulo que llamaremos ip_vs_hola.c. Dentro de él, colocaremos el código tal cual como el ejemplo final de la entrada anterior:

#include <linux/module.h>  /* utilizada por todos los modulos */
#include <linux/init.h>    /* utilizada para poder usar macros */
#include <linux/kernel.h>  /* utilizada por printk */

int init_func()
{
    printk(KERN_INFO "holamundo: cargando...\n");
    return 0;
}

void exit_func()
{
    printk(KERN_INFO "holamundo: descargando...\n");
}

module_init(init_func);
modue_exit(exit_func);

Bien, si queremos que nuestro módulo aparezca entre las opciones cuando hacemos make menuconfig, por ejemplo, debemos agregarlo al archivo Kconfig del directorio en el cual se encuentra el módulo (net/netfilter/ipvs/Kconfig en este caso).

config CONFIG_IP_VS_HOLA
    tristate "Hola Mundo"
    help
      Modulo hola mundo para el ip_vs.

Acá le hemos puesto un nombre para ser usado en el ámbito de la configuración, hemos dicho que puede ser seleccionado como módulo, built-in o ninguno, y hemos especificado una ayuda corta. Para más detalles, revisar la documentación oficial de kconfig aquí.

El siguiente paso es modificar el Makefile (también del directorio donde se encuentra el módulo) con las reglas necesarias para su compilación. En este caso, dada la sencillez del problema, basta con agregar obj-$(CONFIG_IP_VS_HOLA) += ip_vs_hola.o en alguna parte del Makefile. Así estaríamos usando la variable con la cual identificamos a nuestro módulo, que al momento de compilar se transformará en "y" o "m" si configuramos para ser compilada buit-in o como módulo, respectivamente.

Finalmente nos queda completar los pasos normales para una instalación de un kernel a partir de sus fuentes (compilar el kernel, compilar los módulos, instalar los módulos e instalar el kernel).

Podemos hacer el ejercicio de seleccionar nuestra opción como built-in, en vez de módulo, y veremos como luego de instalado el kernel, cada vez que lo iniciemos, tendremos en el log el mensaje que imprimimos en la función de inicialización. Si por el contrario, seleccionamos como módulo, tendremos que hacer un modprobe para que se ejecute dicha función.


EXPORT_SYMBOL

Una de las múltiples razones por las cuales quisieramos agregar nuestros módulos a un kernel, es agregar funcionalidades de algún tipo. Eventualmente podríamos requerir utilizar en un módulo, una función que hemos definido en otro módulo.

Ahora vamos a agregar una función saludar() a nuestro módulo ip_vs_hola, que también imprima algo con printk(). Si quisieramos utilizar esta función dentro de otro módulo ip_vs_hola_2, utilizamos EXPORT_SYMBOL de la siguiente manera en ip_vs_hola:

#include <linux/module.h>  /* utilizada por todos los modulos */
#include <linux/init.h>    /* utilizada para poder usar macros */
#include <linux/kernel.h>  /* utilizada por printk */

int saludar()
{
 printk(KERN_INFO "la funcion saludar ha sido invocada \n");
 return 1;
}
EXPORT_SYMBOL(saludar);

int init_func()
{
    printk(KERN_INFO "holamundo: cargando...\n");
    return 0;
}

void exit_func()
{
    printk(KERN_INFO "holamundo: descargando...\n");
}

module_init(init_func);
modue_exit(exit_func);

Y luego, en ip_vs_hola_2 bastaría con una referencia extern a dicha función.
#include <linux/module.h>  /* utilizada por todos los modulos */
#include <linux/init.h>    /* utilizada para poder usar macros */
#include <linux/kernel.h>  /* utilizada por printk */

extern int saludar(void);

int init_func()
{
    saludar();
    return 0;
}

void exit_func()
{
    printk(KERN_INFO "holamundo: descargando...\n");
}

module_init(init_func);
modue_exit(exit_func);

Y eso es todo. No es nada del otro mundo, pero hay que tomar un par de cosillas adicionales en cuenta. 

No hay comentarios: