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! "); 15 return 0; 16 } 17 18 static void hello_exit(void) 19 { 20 printk(KERN_INFO "hello exit! "); 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! "); 11 } 12 13 static int world_init(void) 14 { 15 printk(KERN_INFO "world init! "); 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! "); 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!