GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Was bedeutet EXPORT_SYMBOL im Linux-Kernel-Code?

Es macht ein Symbol für dynamisch geladene Module zugänglich (vorausgesetzt, dass diese Module einen extern hinzufügen Erklärung).

Vor nicht allzu langer Zeit fragte jemand, wie man es benutzt.


Hier ist eine gute Erklärung.

https://www.quora.com/What-is-the-difference-between-extern-and-EXPORT_SYMBOL-in-Linux-kernel-codes

Extern ist ein Schlüsselwort der C-Speicherklasse. Im Kernel, wie in jedem anderen Ccode, teilt es dem Compiler mit, dass die Definition der Variablen oder Funktion, die es qualifiziert, in einer anderen „Datei“ implementiert ist, oder genauer gesagt, Übersetzungseinheit (Programmierung) – Wikipedia. Die Übersetzungseinheit, die sie definiert, sollte den statischen Qualifizierer nicht verwenden. Daher hat die Symboltabelle einen entsprechenden Eintrag. Zur Verbindungszeit wird das Symbol wie gewohnt aufgelöst. Es gibt nichts Kernel-spezifisches über „extern“.

EXPORT_SYMBOL() ist ein Makro, das die Header des Linux-Kernels definieren. Es hat nicht viel mit extern gemeinsam. Es teilt dem kbuild-Mechanismus mit, dass das Symbol, auf das verwiesen wird, Teil der globalen Liste der Kernelsymbole sein soll. Das wiederum erlaubt Kernelmodulen, auf sie zuzugreifen. Code, der in den Kernel selbst eingebaut ist (im Gegensatz zu einem Modul), kann natürlich gemäß regulärem C über eine externe Deklaration auf jedes nichtstatische Symbol zugreifen. Der EXPORT_SYMBOL()-Mechanismus erlaubt uns, ein Symbol zur Verwendung durch ladbare Module als zu exportieren Gut. Eine interessante Sache ist, dass ein so von einem Modul exportiertes Symbol einem anderen Modul zugänglich wird, das davon abhängen kann!

Zusammenfassend ist extern nicht Kernel-spezifisch. Es wird verwendet, um eine Deklaration zu einem nichtstatischen Symbol aus einer anderen Übersetzungseinheit zu qualifizieren. EXPORT_SYMBOL() ist spezifisch für den Linux-Kernel. Es wird in der Übersetzungseinheit der Definition verwendet, um das Symbol für ladbare Module verfügbar zu machen.

EXPORT_SYMBOL ist also nur ein Mechanismus wie extern, aber es dient der Referenz zwischen ladbaren Modulen, nicht der Datei.

Um voranzukommen, können wir vermuten, dass es von extern erreicht wird, da extern Form C ist, das die Grundlage darstellt.

Hier ist ein Hinweis.

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 }

Deklarieren Sie zuerst ein externes Sym.

Dann eine Zeichenfolge __kstrtab_##sym ==VMLINUX_SYMBOL_STR(sym).

Zuletzt ein externes struct kernel_symbol __ksymtab_##sym ={ (unsigned long)&sym , __kstrtab_##sym }. &sym Zeichnen Sie die tatsächliche Adresse des Symbols auf, z. B. eine Funktion oder Variable, _kstrtab ##sym Notieren Sie die Namenszeichenfolge.


Keine Antwort an sich, aber eine Demonstration, wie in meinem Kommentar versprochen, dass exportierte Symbole nicht sind muss nicht statisch sein. Die folgenden 2 Module demonstrieren dies:

/* 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");

Und das zweite Modul

/* 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");

Diese wurden auf CentOS 6 und CentOS 7 getestet:Kernel 2.6.32 bzw. 3.10. Das Laden von mod1.ko und dann von mod2.ko führt dazu, dass der an mod1_exp_func() übergebene Wert in die Kernel-Protokollpuffer gedruckt wird.


Linux
  1. Linux – Proprietäre oder geschlossene Teile des Kernels?

  2. Was macht einen Kernel-Linux-Server grundlegend?

  3. Was sind Bash-Exit-Codes in Linux

  4. Was bedeutet es zu sagen, dass der Linux-Kernel präventiv ist?

  5. Wie lade ich Linux-Kernel-Module aus C-Code?

Was tun bei einer Linux-Kernel-Panik?

Was bedeuten führende und nachgestellte Unterstriche in Linux-Kernel-Identifikatoren?

Was ist besser int 0x80 oder syscall in 32-Bit-Code unter Linux?

Wie kodiere ich ein Linux-Kernel-Modul?

Bedeutung von rc5 im Linux-Kernel 2.6.37-rc5

Anforderungen für die Portierung von Linux auf eine andere Plattform