Práctica 1. Módulos del núcleo de Linux.

Anuncio
Arquitectura y Tecnologı́a de Ordenadores Personales (IS37 - II37)
Ingenierı́a Técnica en Informática de Sistemas - Ingenierı́a Informática
Práctica 1. Módulos del núcleo de
Linux.
1.
Objetivos
Con el desarrollo de esta práctica se podrá ver la estructura de los módulos de Linux
–versión 2.6.x del núcleo–, sus caracterı́sticas, su función y la forma de acceder a los dispositivos gestionados por módulos –un módulo actuando de manejador o driver del dispositivo.
2.
Introducción
El núcleo del sistema operativo Linux presenta una arquitectura monolı́tica, es decir que todos sus componentes –sistema de ficheros, gestión de memoria, manejadores de
dispositivos- están enlazados en una sola imagen –normalmente el fichero vmlinuz– que se
carga en memoria y ejecuta durante el arranque del sistema.
Esta estructura es poco flexible dado que requiere bien que todos los posibles manejadores
de los distintos dispositivos –estén o no presentes en el hardware del sistema– estén incluı́dos
en el código del núcleo, haciéndolo innecesariamente grande, bien que el núcleo deba ser
recompilado cada vez que se produce una modificación en el soporte material del sistema.
Para evitar estos inconvenientes, desde la versión 2.0 del núcleo de Linux, se ha incorporado soporte para la carga y descarga dinámica de fragmentos de código del núcleo, los
módulos que se van a estudiar en esta práctica.
2.1.
Módulos del núcleo
Los módulos son fragmentos de código del sistema operativo en un formato objeto especial –.ko– que pueden ser vinculados dinámicamente –esto es, insertados– en el núcleo o
eliminados de él durante el funcionamiento normal del sistema. El código del módulo insertado pasa a ser una parte del núcleo del sistema y por lo tanto se ejecuta con el máximo nivel
de privilegio –modo supervisor–, tiene acceso a todas las funciones exportadas por el núcleo
o por otros módulos cargados anteriormente y a todos los recursos fı́sicos de la máquina. La
única diferencia con el código estático –el que se carga al arrancar el sistema– del núcleo es
que un módulo se puede descargar, desvinculándose del núcleo, liberando todos los recursos
utilizados –la memoria consumida por el propio módulo– e invalidando las referencias que
hubiera podido exportar al sistema.
2.2.
Estructura y creación de los módulos
Los módulos se pueden generar como cualquier otro programa pero deben seguir una
serie de normas para adecuarse a la estructura que el núcleo espera. En esta práctica se
compilará un fichero fuente en lenguaje C indicando, mediante el fichero Makefile adjunto,
las opciones de compilación adecuadas.
La primera función que se ejecuta de un módulo es init module(), que tiene por objeto
realizar las comprobaciones adecuadas –por ejemplo, detectar un dispositivo– y crear las
estructuras de datos necesarias para la ejecución de su código. Cuando un módulo es descargado del sistema se invoca la función cleanup module() cuyo objeto es liberar los recursos
consumidos por aquél. Estas dos funciones son invocadas por el núcleo y su ejecución termina tras llevar a cabo las tareas indicadas. El código del módulo que realiza trabajo útil para
el sistema se invoca a través de funciones de gestión de ficheros –open, close, read, write,
ioctl . . . – para las cuales el módulo incorpora código. Este código es añadido al núcleo
al invocar la función register chrdev() y ejecutado cada vez que se realiza la llamada al
sistema correspondiente sobre el dispositivo gestionado por el módulo.
Otra caracterı́stica importante de la estructura de los módulos es que, siendo código del
núcleo, no puede incluir funciones de las librerı́as compartidas –funciones estandarizadas
en la programación en C– que utilizan las aplicaciones de usuario. Sı́ pueden utilizar sin
embargo llamadas a todas las funciones que el núcleo exporta. En el ejemplo que se va a
seguir se verá el uso de la función del núcleo printk() como método habitual de salida de
información del módulo.
2.3.
Acceso al código de los módulos desde aplicaciones de usuario
Las aplicaciones de usuario acceden al código exportado por los módulos mediante llamadas al sistema sobre dispositivos creados en /dev. Al registrar el módulo como se ha indicado
anteriormente, el sistema devuelve un entero que constituye el número de referencia del dispositivo, el major number que le corresponde. Si se crea una entrada en /dev mediante la
orden del sistema mknod utilizando este número –y el minor number que se desee, consultar
la descripción del manual man mknod para ver el uso correcto de la orden – se accederá a
las funciones del módulo a través de ella utilizando la semántica de gestión de ficheros que
se ha comentado. En estos casos el minor number se utiliza para distinguir entre distintos
dispositivos gestionados por el mismo módulo.
2.4.
Gestión de módulos y órdenes asociadas
Para cargar y descargar el código de un módulo en el núcleo el sistema dispone de
un conjunto de órdenes a tal efecto que deben ejecutarse con permisos de superusuario.
Estas órdenes son insmod y modprobe para cargar módulos y rmmod para descargarlos.
También existe lsmod para consultar los módulos cargados en un momento dado. Estas
órdenes residen en el directorio de ejecutables del sistema, /sbin. Si no está incluido en el
PATH del superusuario, se ejecutan indicando esa ruta, por ejemplo /sbin/insmod.
Para observar los mensajes generados por los módulos y, en general, por el núcleo mediante la función printk() se puede utilizar la orden dmesg. Otra información importante
acerca de los módulos se puede observar ejecutando cat /proc/modules y acerca de los
sı́mbolos exportados por el núcelo mediante cat /proc/kallsyms
3.
Trabajo a desarrollar
1. Descargar la última versión del fichero mi pci.tgz adjunto, descomprimirlo y compilar el módulo mi pci.ko. Instalar el módulo en el núcleo y observar los mensajes
generados mediante la orden dmesg. Antes habrá que editar el archivo de cabecera
mi pci.h y eliminar el comentario que protege la constante que identifica el chipset
de los ordenadores del laboratorio.
2. Crear, como superusuario y con los permisos de lectura y escritura, un dispositivo tipo
carácter llamado /dev/mi pci y ejecutar el programa mi pci prueba.
3. Descargar el módulo, recompilarlo para mostrar información de depuración –definiendo
DEBUG en el fichero fuente del módulo o en el fichero de cabecera– y observar los
mensajes generados. Verficar que el major number no ha cambiado o modificar el
punto de acceso en /dev/mi pci adecuadamente. Ejecutar nuevamente la aplicación
mi pci prueba y observar los mensajes generados por el módulo.
4. Modificar el módulo para que devuelva información acerca de los recursos del sistema
utilizados por cada dispositivo. Consultar las estructuras de datos adecuadas mediante
el navegador del código de linux. Programar una aplicación que acceda al módulo
modificado y probar su funcionamiento.
4.
Bibliografı́a
Se puede encontrar más información acerca de los módulos, su creación y la forma de
acceder a ellos en el documento The Linux Kernel Module Programming Guide disponible
en http://www.tldp.org o en la web de la asignatura.
Descargar