Linux内核代码中的“EXPORT_SYMBOL”是什么意思?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9836467/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 05:21:34  来源:igfitidea点击:

What's meaning of "EXPORT_SYMBOL" in Linux kernel code?

clinuxlinux-kernel

提问by Jeegar Patel

from here

这里

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

I am not getting whats the meaning of it and why that is used. I tried to search about it but not understanding the meaning of that.

我不明白它的含义是什么以及为什么使用它。我试图搜索它,但不明白它的含义。

采纳答案by cnicutar

It makes a symbol accessible to dynamically loaded modules (provided that said modules add an externdeclaration).

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

Not long ago, someone asked how to use it.

不久前,有人问如何使用它

回答by Andrew Falanga

Not an answer per se but a demonstration, as promised from my comment, that exported symbols are notrequired to be non-static. The below 2 modules demonstrate this:

不是从我的评论许诺本身的答案,但演示的是,导出的符号被非静态的要求。以下 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");

And the second 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");

These were tested on CentOS 6 & CentOS 7: kernels 2.6.32 and 3.10 (respectively). Loading mod1.ko and then mod2.ko will result in the value passed to mod1_exp_func() being printed to the kernel log buffers.

这些已在 CentOS 6 和 CentOS 7 上进行了测试:内核 2.6.32 和 3.10(分别)。加载 mod1.ko 然后加载 mod2.ko 将导致传递给 mod1_exp_func() 的值被打印到内核日志缓冲区。

回答by Victor Choy

Here is a good explanation.

这是一个很好的解释。

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

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

Extern is a C storage class keyword. In the kernel, as in any other C code, it tells the compiler that the definition of the variable or function it qualifies is implemented in another “file”, or rather, more accurately Translation unit (programming) - Wikipedia. The translation unit that does define it should not use the static qualifier. Therefore, the symbol table has an entry corresponding to it. At link time, the symbol is resolved as normal. There is nothing kernel specific about “extern”.

EXPORT_SYMBOL() is a macro the Linux kernel headers define. It has not much in common with extern. It tells the kbuild mechanism that the symbol referred to should be part of the global list of kernel symbols. That, in turn allows kernel modules to access them. Code that is built into the kernel itself (as opposed to a module) can, of course, access any non-static symbol via an extern declaration, in accordance with regular C. The EXPORT_SYMBOL() mechanism allows us to export a symbol for use by loadable modules as well. An interesting thing is that a symbol thus exported by one module becomes accessible to another module that may depend on it!

To summarise, extern is not kernel specific. It is used to qualify a declaration to a non-static symbol from another translation unit. EXPORT_SYMBOL() is specific to the Linux kernel. It is used in the translation unit of the definition to make the symbol available to loadable modules.

Extern 是 C 存储类关键字。在内核中,就像在任何其他 C 代码中一样,它告诉编译器它所限定的变量或函数的定义是在另一个“文件”中实现的,或者更准确地说,是翻译单元(编程)——维基百科。定义它的翻译单元不应使用静态限定符。因此,符号表有一个与之对应的条目。在链接时,符号会正常解析。没有任何关于“extern”的内核特定内容。

EXPORT_SYMBOL() 是 Linux 内核头文件定义的宏。它与 extern 没有太多共同之处。它告诉 kbuild 机制所引用的符号应该是内核符号全局列表的一部分。这反过来又允许内核模块访问它们。内置于内核本身(而不是模块)的代码当然可以根据常规 C 通过 extern 声明访问任何非静态符号。 EXPORT_SYMBOL() 机制允许我们导出符号以供使用也通过可加载模块。有趣的是,一个模块导出的符号可以被另一个依赖它的模块访问!

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

So EXPORT_SYMBOL is just a mechanism like extern, but it's for reference between loadable modules not file.

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

To move forwards, we can guess it's achived by the extern because extern is form C which is the foundation.

往前走,我们可以猜测它是由 extern 实现的,因为 extern 是形式 C,这是基础。

Here is a clue.

这是一个线索。

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

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 }

First declare a extern sym.

首先声明一个extern sym。

Then a string __kstrtab_##sym = = VMLINUX_SYMBOL_STR(sym).

然后是一个字符串 __kstrtab_##sym == VMLINUX_SYMBOL_STR(sym)。

Last a extern struct kernel_symbol __ksymtab_##sym = { (unsigned long)&sym, __kstrtab_##sym}. &symrecord the real address of the sym such as a function or variable, _kstrtab##symrecord the name string.

最后一个 extern struct kernel_symbol __ksymtab_##sym = { (unsigned long) &sym, __kstrtab_##sym}。 &sym记录函数或变量等sym的真实地址,_ kstrtab##sym记录名称字符串。