print last know how linux-kernel kernel-module kernel

linux-kernel - last - version del kernel centos



¿Se puede retrasar la carga de símbolos exportados? (2)

Si conoce el tipo / prototipo del símbolo, intente usar kallsyms_lookup_name() para obtener un puntero al símbolo deseado en tiempo de ejecución en lugar de vincularlo como un símbolo externo (lo que significa que el cargador lo buscará en el momento de la carga) . Se pueden encontrar ejemplos usando su motor de búsqueda favorito.

Estoy trabajando en algunos módulos del kernel de Linux y tengo una pregunta relacionada con un problema de carga circular.

El módulo A carga primero y exporta muchos símbolos para el uso del módulo B o C. El Módulo B o C luego se cargan después y los símbolos existen para su uso.

Sin embargo, ahora encuentro que el módulo A requiere un símbolo del módulo B o C, pero solo durante el tiempo de ejecución, y no es necesario para inicializar el módulo. Así que, por supuesto, cuando A carga, descubre que el símbolo aún no existe. Incluso tuve el símbolo marcado como externo en el módulo A, pero tampoco funcionó.

¿Es posible retrasar la carga de un símbolo después de que se haya cargado el módulo A, aunque todavía no existe hasta que se haya cargado B o C?


Tales situaciones a menudo se resuelven usando devoluciones de llamada.

Supongamos que el módulo A exporta las funciones para registrar / anular el registro de las devoluciones de llamada. B y / o C utilizan estas funciones y proporcionan las devoluciones de llamada adecuadas a A. Cuando es necesario, A comprueba si las devoluciones de llamada están establecidas y las llama.

Algo como esto (sin manejo de errores y bloqueo por simplicidad):

/* Module A */ struct a_ops /* Better to define struct a_ops in a header file */ { void (*needed_func)(void); void (*another_needed_func)(void); }; ... struct a_ops ops = { .needed_func = NULL; .another_needed_func = NULL; }; ... int a_register_needed_funcs(struct a_ops *a_ops) { ops.needed_func = a_ops->needed_func; ops.another_needed_func = a_ops->another_needed_func; } EXPORT_SYMBOL(a_register_needed_funcs); void a_unregister_needed_funcs() { ops.needed_func = NULL; ops.another_needed_func = NULL; } EXPORT_SYMBOL(a_unregister_needed_funcs); ... /* Call the specified callbacks when needed: */ void do_something(void) { if (ops.needed_func != NULL) { ops.needed_func(); } else { /* the callback is not set, handle this: report error, ignore it or * do something else */ ... } } ... /* Modules B and C */ /* Their code #includes the file where struct a_ops is defined. * The module registers the callbacks, for example, in its init function * and unregister in exit function. */ ... static void func(void) { ... } static void another_func(void) { ... } struct a_ops my_funcs = { .needed_func = func; .another_needed_func = another_func; }; int __init my_module_init(void) { ... result = a_register_needed_funcs(&my_funcs); ... } void __exit my_module_exit(void) { ... a_unregister_needed_funcs(); ... }

Esto es similar a las operaciones de archivos y muchas otras operaciones de devolución de llamada en el kernel. Supongamos que un usuario quiere leer, por ejemplo, un dispositivo de caracteres mantenido por un controlador personalizado. El kernel propiamente dicho (VFS, para ser exactos) recibe la solicitud pero no puede manejarla ella misma. Reenvía la solicitud a ese controlador personalizado que ha registrado sus devoluciones de llamadas de operación de dicho dispositivo. A su vez, el controlador utiliza las funciones exportadas por el kernel propiamente dicho, como cdev_add() , etc.