Linux内核代码中“EXPORT_SYMBOL”的含义是什么?

时间:2012-03-23 09:08:31

标签: c linux linux-kernel

来自here

 48 struct snd_card *snd_cards[SNDRV_CARDS];
 49 EXPORT_SYMBOL(snd_cards);

我没有了解它的含义以及使用它的原因。我试图搜索它,但没有理解它的含义。

3 个答案:

答案 0 :(得分:40)

它使动态加载的模块可以访问符号(前提是所述模块添加extern声明)。

不久前,someone asked how to use it

答案 1 :(得分:5)

本身不是答案,而是根据我的评论所承诺的演示,即导出的符号不是必须是非静态的。以下2个模块证明了这一点:

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

第二个模块

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

这些是在CentOS 6&amp; CentOS 7:内核2.6.32和3.10(分别)。加载mod1.ko然后mod2.ko将导致传递给mod1_exp_func()的值被打印到内核日志缓冲区。

答案 2 :(得分:2)

这是一个很好的解释。

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

  

Extern是C存储类关键字。与其他C语言一样,在内核中   代码,它告诉编译器变量的定义或   它合格的功能是在另一个“文件”中实现的,或者说,   更准确地翻译单位(编程)-维基百科。的   确实定义了翻译单元的翻译单元不应使用静态   限定词。因此,符号表具有对应于   它。在链接时,该符号将正常解析。什么也没有   特定于“外部”的内核。

     

EXPORT_SYMBOL()是Linux内核头文件定义的宏。它没有   与extern有很多共同之处。它告诉kbuild机制   引用的符号应该是内核全局列表的一部分   符号。进而允许内核模块访问它们。编码   内置于内核本身(相对于模块而言)可以   当然,可以通过extern声明访问任何非静态符号   符合常规C。EXPORT_SYMBOL()机制使我们能够   导出符号以供可加载模块使用。一个有趣的   问题是一个模块导出的符号可以访问   到另一个可能依赖它的模块!

     

总而言之,extern不是特定于内核的。它用于限定一个   从另一个翻译单元声明为非静态符号。   EXPORT_SYMBOL()特定于Linux内核。它用于   定义的翻译单位,以使符号可用于   可加载的模块。

所以EXPORT_SYMBOL只是一种类似于extern的机制,但是它是供可加载模块之间的参考而不是文件。

要向前迈进,我们可以猜测它是extern实现的,因为extern是形式C,它是基础。

这是一个线索。

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 }

首先声明一个外部符号。

然后输入一个字符串__kstrtab _ ## sym = = VMLINUX_SYMBOL_STR(sym)。

最后一个外部结构kernel_symbol __ksymtab _ ## sym = {(无符号长)&sym __ kstrtab _ ## sym }。 &sym 记录符号的真实地址,例如函数或变量,_ kstrtab ## sym 记录名称字符串。

相关问题