GNU/Linux >> Tutoriales Linux >  >> Linux

¿Qué significa EXPORT_SYMBOL en el código del kernel de Linux?

Hace que un símbolo sea accesible para módulos cargados dinámicamente (siempre que dichos módulos agreguen un extern declaración).

No hace mucho, alguien preguntó cómo usarlo.


Aquí hay una buena explicación.

https://www.quora.com/Cuál-es-la-diferencia-entre-extern-y-EXPORT_SYMBOL-in-Linux-kernel-codes

Extern es una palabra clave de clase de almacenamiento C. En el kernel, como en cualquier otro Ccode, le dice al compilador que la definición de la variable o función que califica está implementada en otro "archivo", o mejor dicho, más exactamente Unidad de traducción (programación) - Wikipedia. La unidad de traducción que lo define no debe usar el calificador estático. Por lo tanto, la tabla de símbolos tiene una entrada correspondiente. En el momento del enlace, el símbolo se resuelve normalmente. No hay nada específico del kernel sobre "externo".

EXPORT_SYMBOL() es una macro que definen los encabezados del kernel de Linux. No tiene mucho en común con extern. Le dice al mecanismo kbuild que el símbolo al que se hace referencia debe ser parte de la lista global de símbolos del núcleo. Eso, a su vez, permite que los módulos del kernel accedan a ellos. El código integrado en el propio kernel (a diferencia de un módulo) puede, por supuesto, acceder a cualquier símbolo no estático a través de una declaración externa, de acuerdo con C normal. El mecanismo EXPORT_SYMBOL() nos permite exportar un símbolo para que lo utilicen los módulos cargables como bien. ¡Una cosa interesante es que un símbolo así exportado por un módulo se vuelve accesible a otro módulo que puede depender de él!

Para resumir, extern no es específico del kernel. Se usa para calificar una declaración a un símbolo no estático de otra unidad de traducción. EXPORT_SYMBOL() es específico del kernel de Linux. Se utiliza en la unidad de traducción de la definición para que el símbolo esté disponible para módulos cargables.

Entonces, EXPORT_SYMBOL es solo un mecanismo como externo, pero es para referencia entre módulos cargables, no para archivos.

Para avanzar, podemos suponer que lo logra el externo porque el externo es la forma C, que es la base.

Aquí hay una pista.

https://elixir.bootlin.com/linux/v4.6.7/source/include/linux/export.h#L56

#define EXPORT_SYMBOL(sym)                  \
    __EXPORT_SYMBOL(sym, "")

/* For every exported symbol, place a struct in the __ksymtab section */
#define __EXPORT_SYMBOL(sym, sec)               \
    extern typeof(sym) sym;                 \
    __CRC_SYMBOL(sym, sec)                  \
    static const char __kstrtab_##sym[] __attribute__((section("__ksymtab_strings"), aligned(1)))  = VMLINUX_SYMBOL_STR(sym);               \
    extern const struct kernel_symbol __ksymtab_##sym;  \
    __visible const struct kernel_symbol __ksymtab_##sym    __used __attribute__((section("___ksymtab" sec "+" #sym), unused)) = { (unsigned long)&sym, __kstrtab_##sym }

Primero declara un símbolo externo.

Luego una cadena __kstrtab_##sym ==VMLINUX_SYMBOL_STR(sym).

Última una estructura externa kernel_symbol __ksymtab_##sym ={ (largo sin firmar)&sym , __kstrtab_##sym }. &símbolo registrar la dirección real del sym como una función o variable, _kstrtab ##sím grabar la cadena de nombre.


No es una respuesta en sí, sino una demostración, como prometí en mi comentario, de que los símbolos exportados no son requiere que sea no estático. Los siguientes 2 módulos demuestran esto:

/* mod1.c */
#include <linux/module.h>

static int mod1_exp_func(int i)
{
    pr_info("%s:%d the value passed in is %d\n",
            __func__, __LINE__, i);

    return i;
}
EXPORT_SYMBOL(mod1_exp_func); /* export static symbol */

static int __init mod1_init(void)
{
    pr_info("Initializing simple mod\n");
    return 0;
}

static void __exit mod1_exit(void)
{
    pr_info("This module is exiting\n");
}

module_init(mod1_init);
module_exit(mod1_exit);
MODULE_LICENSE("GPL v2");

Y el segundo módulo

/* mod2.c */
#include <linux/module.h>

extern int mod1_exp_func(int);

static int __init mod2_init(void)
{
    pr_info("Initializing mod2\n");
    pr_info("Calling exported function in mod1\n");
    mod1_exp_func(3);
    return 0;
}

static void __exit mod2_exit(void)
{
    pr_info("mod2 exiting\n");
}

module_init(mod2_init);
module_exit(mod2_exit);
MODULE_LICENSE("GPL v2");

Estos se probaron en CentOS 6 y CentOS 7:kernels 2.6.32 y 3.10 (respectivamente). Cargar mod1.ko y luego mod2.ko dará como resultado que el valor pasado a mod1_exp_func() se imprima en los búferes de registro del kernel.


Linux
  1. ¿Qué son los códigos de salida de Bash en Linux?

  2. ¿Qué significa decir que el kernel de Linux es preventivo?

  3. ¿Qué significa __init en el código del kernel de Linux?

  4. ¿Cuál es el significado de los guiones bajos iniciales y finales en los identificadores del kernel de Linux?

  5. ¿Cuál es la fuente actual del kernel de Linux?

Qué hacer en caso de pánico en el kernel de Linux

¿Qué es mejor int 0x80 o syscall en código de 32 bits en Linux?

¿Cómo codificar un módulo del kernel de Linux?

Significado de rc5 en linux kernel 2.6.37-rc5

¿Qué es una resolución de jiffie en Linux Kernel?

Requisitos para portar Linux a otra plataforma