Linux设备驱动程序 之 内核符号表

insmod使用公共内核符号表来解析模块中未定义的符号。功能内核符号表中包含了所有全局内核项(函数和变量)的地址,这是实现模块化驱动程序所必须的。当模块装载到内核后,它所导出的任何符号都会变成内核符号表的一部分。通常情况下,模块只需要实现自己的功能,无需导出任何符号,但是如果其他模块想要使用该模块的某函数或变量,就需要导出符号;通过导出符号,在可以在其他模块上层叠新的模块。通过模块层叠可将模块划分为多个层,简化每个层可缩短开发时间。

Linux提供了一个方法来管理符号对模块以外部分的可见性,从而减少了可能造成的名字空间污染,并且适当的隐藏信息。如果一个模块需要导出符号,则应该使用下面的宏:

EXPORT_SYMBOL(name);

EXPORT_SYMBOL_GPL(name);

这两个宏均用于给定的符号导出到模块外部。_GPL版本导出的符号只能被GPL许可证下的模块使用。符号必须在模块文件的全局部分导出,不能在函数中导出,这是因为上面两个宏将被扩展为一个特殊变量的声明,而该变量必须是全局的。该变量将在模块可执行文件的特殊部分(一个”ELF段”)中保存,在装载时,内核通过这个段来寻找模块导出的变量;

上面两个宏定义在include/linux/export.h中

 1 /* For every exported symbol, place a struct in the __ksymtab section */
 2 #define ___EXPORT_SYMBOL(sym, sec)                    \
 3     extern typeof(sym) sym;                        \
 4     __CRC_SYMBOL(sym, sec)                        \
 5     static const char __kstrtab_##sym[]                \
 6     __attribute__((section("__ksymtab_strings"), aligned(1)))    \
 7     = VMLINUX_SYMBOL_STR(sym);                    \
 8     static const struct kernel_symbol __ksymtab_##sym        \
 9     __used                                \
10     __attribute__((section("___ksymtab" sec "+" #sym), used))    \
11     = { (unsigned long)&sym, __kstrtab_##sym }
12 
13 #if defined(__KSYM_DEPS__)
14 
15 /*
16  * For fine grained build dependencies, we want to tell the build system
17  * about each possible exported symbol even if they're not actually exported.
18  * We use a string pattern that is unlikely to be valid code that the build
19  * system filters out from the preprocessor output (see ksym_dep_filter
20  * in scripts/Kbuild.include).
21  */
22 #define __EXPORT_SYMBOL(sym, sec)    === __KSYM_##sym ===
23 
24 #elif defined(CONFIG_TRIM_UNUSED_KSYMS)
25 
26 #include <generated/autoksyms.h>
27 
28 #define __EXPORT_SYMBOL(sym, sec)                \
29     __cond_export_sym(sym, sec, __is_defined(__KSYM_##sym))
30 #define __cond_export_sym(sym, sec, conf)            \
31     ___cond_export_sym(sym, sec, conf)
32 #define ___cond_export_sym(sym, sec, enabled)            \
33     __cond_export_sym_##enabled(sym, sec)
34 #define __cond_export_sym_1(sym, sec) ___EXPORT_SYMBOL(sym, sec)
35 #define __cond_export_sym_0(sym, sec) /* nothing */
36 
37 #else
38 #define __EXPORT_SYMBOL ___EXPORT_SYMBOL
39 #endif
40 
41 #define EXPORT_SYMBOL(sym)                    \
42     __EXPORT_SYMBOL(sym, "")
43 
44 #define EXPORT_SYMBOL_GPL(sym)                    \
45     __EXPORT_SYMBOL(sym, "_gpl")
46 
47 #define EXPORT_SYMBOL_GPL_FUTURE(sym)                \
48     __EXPORT_SYMBOL(sym, "_gpl_future")

 

测试代码

两个测试模块分别为hello.ko和world.ko,hello模块导出函数,world模块使用该函数;

hello.c

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 
 4 MODULE_LICENSE("GPL");
 5 
 6 void print_hello(void)
 7 {
 8         printk(KERN_INFO "Hello");
 9 }
10 EXPORT_SYMBOL(print_hello);
11 
12 static int hello_init(void)
13 {
14         printk(KERN_INFO "hello init!\n");
15         return 0;
16 }
17 
18 static void hello_exit(void)
19 {
20         printk(KERN_INFO "hello exit!\n");
21 }
22 
23 module_init(hello_init);
24 module_exit(hello_exit);

 

world.c

 1 #include <linux/init.h>
 2 #include <linux/module.h>
 3 
 4 MODULE_LICENSE("GPL");
 5 
 6 extern void print_hello(void);
 7 
 8 static void print_world(void)
 9 {
10         printk(KERN_INFO "World!\n");
11 }
12 
13 static int world_init(void)
14 {
15         printk(KERN_INFO "world init!\n");
16 
17         print_hello();
18         print_world();
19 
20         return 0;
21 }
22 
23 static void world_exit(void)
24 {
25         printk(KERN_INFO "world exit!\n");
26 }
27 
28 module_init(world_init);
29 module_exit(world_exit);

 

执行结果:

1 [13912.081180] hello init!
2 [13917.863471] world init!
3 [13917.863477] Hello
4 [13917.863479] World!

 

posted @ 2019-10-29 16:31  AlexAlex  阅读(1014)  评论(0编辑  收藏  举报