GNU/Linux >> Tutoriels Linux >  >> Linux

Que signifie EXPORT_SYMBOL dans le code du noyau Linux ?

Il rend un symbole accessible aux modules chargés dynamiquement (à condition que lesdits modules ajoutent un extern déclaration).

Il n'y a pas si longtemps, quelqu'un a demandé comment l'utiliser.


Voici une bonne explication.

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

Extern est un mot-clé de la classe de stockage C. Dans le noyau, comme dans tout autre Ccode, il indique au compilateur que la définition de la variable ou de la fonction qu'il qualifie est implémentée dans un autre "fichier", ou plutôt, plus précisément Unité de traduction (programmation) - Wikipédia. L'unité de traduction qui le définit ne doit pas utiliser le qualificateur statique. Par conséquent, la table de symboles a une entrée qui lui correspond. Au moment de la liaison, le symbole est résolu normalement. Il n'y a rien de spécifique au noyau à propos de "extern".

EXPORT_SYMBOL() est une macro définie par les en-têtes du noyau Linux. Il n'a pas grand-chose en commun avec externe. Il indique au mécanisme de kbuild que le symbole référencé doit faire partie de la liste globale des symboles du noyau. Cela permet à son tour aux modules du noyau d'y accéder. Le code intégré au noyau lui-même (par opposition à un module) peut, bien sûr, accéder à n'importe quel symbole non statique via une déclaration externe, conformément au C normal. Le mécanisme EXPORT_SYMBOL() nous permet d'exporter un symbole à utiliser par des modules chargeables comme bien. Chose intéressante, un symbole ainsi exporté par un module devient accessible à un autre module qui peut en dépendre !

Pour résumer, extern n'est pas spécifique au noyau. Il est utilisé pour qualifier une déclaration à un symbole non statique d'une autre unité de traduction. EXPORT_SYMBOL() est spécifique au noyau Linux. Il est utilisé dans l'unité de traduction de la définition pour rendre le symbole disponible dans les modules chargeables.

Donc EXPORT_SYMBOL est juste un mécanisme comme extern, mais c'est pour référence entre les modules chargeables et non les fichiers.

Pour avancer, on devine que c'est réalisé par l'externe car extern c'est la forme C qui est le fondement.

Voici un indice.

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 }

Déclarez d'abord un sym externe.

Puis une chaîne __kstrtab_##sym ==VMLINUX_SYMBOL_STR(sym).

Last a extern struct kernel_symbol __ksymtab_##sym ={ (unsigned long)&sym , __kstrtab_##sym }. &sym enregistrer l'adresse réelle du sym comme une fonction ou une variable, _kstrtab ##sym enregistrer la chaîne de nom.


Pas une réponse en soi mais une démonstration, comme promis dans mon commentaire, que les symboles exportés ne le sont pas doit être non statique. Les 2 modules ci-dessous le démontrent :

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

Et le deuxième module

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

Ceux-ci ont été testés sur CentOS 6 et CentOS 7 :noyaux 2.6.32 et 3.10 (respectivement). Le chargement de mod1.ko puis de mod2.ko entraînera l'impression de la valeur transmise à mod1_exp_func() dans les tampons du journal du noyau.


Linux
  1. Linux - Parties propriétaires ou fermées du noyau ?

  2. Qu'est-ce qui rend un serveur Linux noyau fondamental ?

  3. Qu'est-ce que cela signifie de dire que le noyau Linux est préemptif ?

  4. Quelle est la source actuelle du noyau Linux ?

  5. Quelle est la signification de fork() et grep sous Linux ?

Que faire en cas de panique du noyau Linux

Quel est le meilleur int 0x80 ou syscall en code 32 bits sous Linux ?

Comment coder un module du noyau Linux ?

Compiler GNU/Linux avec l'optimisation -O3

Signification de rc5 dans le noyau Linux 2.6.37-rc5

qu'est-ce qu'une résolution de jiffie dans le noyau Linux