Mecanismos de extensión del núcleo Yolanda Becerra Fontal Juan José Costa Prats Facultat d'Informàtica de Barcelona Universitat Politècnica de Catalunya BarcelonaTech 2014-2015QT SO2/SOA Índice •Concepto •Mecanismos – Estáticos – Dinámicos • Módulos de Linux – Funcionalidad básica – Operaciones disponibles – Ejemplo de uso: Driver de dispositivo SO2/SOA Qué se entiende por extender el núcleo? • Añadir nuevas funcionalidades o características a un sistema operativo – Cambiar política planificación – Arreglar un error de seguridad – Añadir soporte para nueva tarjeta de red • Añadir código (rutinas) y datos (estructuras y variables) • Básicamente hay 2 mecanismos – Estáticos – Dinámicos SO2/SOA Mecanismos •Estáticos – – – – En tiempo de compilación Necesitamos acceso al código fuente Añadir el nuevo código y los datos Y generar nueva imagen del sistema operativo •Dinámicos – En tiempo de ejecución – En algunos casos sin la necesidad de reiniciar la máquina – Nuevo código y datos encapsulados en algun formato binario que permita su inserción dinámica – No todos los sistemas lo permiten SO2/SOA Linux modules •Linux permite inserción dinámica de código y datos a traves de los módulos – La alternativa es recompilar el sistema • Los módulos tienen las mismas limitaciones que cualquier otro desarrollo dentro del sistema: – Solo se pueden acceder/modificar los símbolos públicos del kernel (aquellos que han estado exportados explícitament) – No hay acceso a la libreria de C – Herramientas de depuración limitadas (p.ej: chivatos con printk) • Finalmente obtenemos un fichero binario (kernel object) que podemos insertar / quitar dinámicamente SO2/SOA Operaciones sobre módulos •Instalar un módulo – # insmod mymodule.ko [param=value][, param=value]* •Quitar un módulo – # rmmod mymodule.ko •Instalar un módulo resolviendo dependencias – # modprobe moduloA.ko – # cat /lib/modules/version/modules.dep /path_completo/moduloA.ko: /path_completo/moduloB.ko /path_completo/moduloB.ko: SO2/SOA Operaciones sobre módulos •Listar modulos instalados en el sistema – # lsmod – # cat /proc/modules • Listar información sobre un modulo – # modinfo module.ko SO2/SOA Desarrollo de un módulo de Linux •Programar los ficheros para implementar el módulo – Funciones de inicialización y finalización • Se invocan al instalar/desinstalar un módulo – Código/datos a incluir en el sistema – Exportar variables/funciones que vayan a ser usadas fuera del módulo • EXPORT_SYMBOL( f ) → “ksyms -a” o “cat /proc/kallsyms” – Puede usar cualquier variable/función exportada por el kernel u otros módulos • Compilar los ficheros – Es necesario disponer de los fuentes del sistema – Produce un fichero objeto (.ko = kernel object) • Insertarlo en el sistema – Cargar el módulo y sus dependencias – Pasar parametros de inicialización • Usar el módulo SO2/SOA Desarrollo de un módulo de Linux • Desde el módulo podeis acceder a muchas funciones para la gestión de estructuras de datos: – – – – find_task_by_pid for_each_process … Antes de implementar algo, mirad que no exista! • Desde el módulo accedeis al espacio de direcciones de usuario: unsigned long copy_from_user(void *to, const void *from, unsigned long count); unsigned long copy_to_user(void *to, const void *from, unsigned long count SO2/SOA Ejemplo de un módulo #include <linux/module.h> #include <linux/kernel.h> /* * Module initialization. */ static int __init Mymodule_init(void) { ... } /* * Finalization module. */ static void __exit Mymodule_exit(void) { ... } module_init(Mymodule_init); module_exit(Mymodule_exit); SO2/SOA Macros varias para módulos • module_param (parameter name and type) – int pid=1; – module_param (pid, int, 0); • MODULE_PARM_DESC (parameter description) – MODULE_PARM_DESC (pid, "Process ID to monitor (default 1)"); • MODULE_AUTHOR (author list) • MODULE_DESCRIPTION • MODULE_LICENSE (GPL, BSD, …) • Información visible con 'modinfo' SO2/SOA Contador de referencias • Un módulo puede descargarse, sí y solo sí, ningún proceso lo esta usando – Hay un contador de referencias, para quantificar el uso del módulo • Pero la gestión la tiene que hacer el programador del módulo :( • try_module_get( THIS_MODULE ) – Incrementa contador: Alguien esta usando el módulo. • module_put( THIS_MODULE ) – Decrementa contador: Alguien ha dejado de usar el módulo • Hay que programar esta gestión en todas las rutinas públicas SO2/SOA Device Driver • Los módulos son usados típicamente para cargar drivers de dispositivos SO2/SOA Device Driver • El driver es un conjunto de variables y funciones para manejar un dispositivo (logico o físico) • Utiliza una API estandar – Interna (no visible para el usuario) – Basado en el struct file_operations • Solo hay que proporcionar las funciones requeridas por el dispositivo (p.ej: open, read) SO2/SOA Operaciones del dispositivo Tabla Canales read(fd) fd Tabla Ficheros Abiertos Caract. dinámicas Tabla Inodos Caract. Estáticas (DD) DRIVER file_operations open read write close SO2/SOA Open(..){ } Read(…){ } Write(…){ } Identificación del dispositivo • Identificados por un major y un minor – Simples números • dev_t MKDEV (major, minor) – Históricamente, el major identificaba la clase de dispositivo (p.ej: una impresora) y el minor diferentes dispositivos dentro de la misma clase (p.ej: modelos diferentes de la misma impresora) • Este identificador permite al kernel saber que driver tiene que usar para comunicarse con el dispositivo • Hay un fichero visible para el usuario con ese major y minor SO2/SOA Registro de device drivers • Los device drivers se tienen que registrar en el sistema asociándole su identificador int register_chrdev_region (dev_t first, unsigned int count, const char *name); • Y para eliminar su registro: void unregister_chrdev_region (dev_t first, unsigned int count); • El interfaz depende del tipo de dispositivo SO2/SOA Asignar operaciones a dispositivos • Primero, crear una estructura cdev: struct cdev * cdev_alloc() • Segundo, inicializar sus campos: – owner: con THIS_MODULE • Para que el kernel se encargue de la gestion de los contadores – ops: con la estructura file_operations del dispositivo • Finalmente, asignar la estructura al dispositivo/s: int cdev_add (struct cdev *dev, dev_t num, unsigned int count); • Para borrarlo: void cdev_del (struct cdev *dev); SO2/SOA Operaciones del dispositivo • La estructura file_operations esta en <linux/fs.h> • struct file_operations my_operations = { owner: THIS_MODULE, read: my_read, ioctl: my_ioctl, open: my_open, release: my_release, }; • El campo owner automatiza gestión contadores SO2/SOA Device Driver's API • Cuando usuario hace open/close sobre dispositivo: – int my_open (struct inode * i, struct file * f); – int my_release (struct inode * i, struct file * f); • ssize t my_read (struct file * f, char * buffer, size t_size, loff_t * offset); – Es necesario usar copy_to_user para acceder al buffer – offset es un parám. de entrada/salida. Posición actual en “file” • int my_ioctl(struct inode * i, struct file * f, unsigned int request, unsigned long argp); – Usado para controlar las operaciones dentro del dispositivo SO2/SOA Insertar un device driver con un modulo • • • • Variable de tipo dev_t con el major i minor del disp. Funciones del dispositivo Variable de tipo struct file_operations inicializada Variable de tipo cdev para ligar las operaciones y el dispositivo • Al inicializar el módulo: – Registrar el dispositivo dentro del kernel y asociar las operaciones • Al finalizar el módulo: – Eliminar el registro y borrar el cdev SO2/SOA Como puede el usuario usar un nuevo dispositivo? • Administrador crea el dispositivo con el comando 'mknod', usando el identificador del dispositivo: – mknod <filename> <type> <major> <minor> – p.ej: mknod mydevice c 255 1 • Crea un fichero 'mydevice' que usará el driver de caracteres identificado con el major 255 y minor 1 • Ahora el usuario puede acceder a este fichero con el API de entrada/salida estandar SO2/SOA