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

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

EXPORT_SYMBOL(name);

EXPORT_SYMBOL_GPL(name);

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

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

 /* 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())) \
= VMLINUX_SYMBOL_STR(sym); \
static const struct kernel_symbol __ksymtab_##sym \
__used \
__attribute__((section("___ksymtab" sec "+" #sym), used)) \
= { (unsigned long)&sym, __kstrtab_##sym } #if defined(__KSYM_DEPS__) /*
* For fine grained build dependencies, we want to tell the build system
* about each possible exported symbol even if they're not actually exported.
* We use a string pattern that is unlikely to be valid code that the build
* system filters out from the preprocessor output (see ksym_dep_filter
* in scripts/Kbuild.include).
*/
#define __EXPORT_SYMBOL(sym, sec) === __KSYM_##sym === #elif defined(CONFIG_TRIM_UNUSED_KSYMS) #include <generated/autoksyms.h> #define __EXPORT_SYMBOL(sym, sec) \
__cond_export_sym(sym, sec, __is_defined(__KSYM_##sym))
#define __cond_export_sym(sym, sec, conf) \
___cond_export_sym(sym, sec, conf)
#define ___cond_export_sym(sym, sec, enabled) \
__cond_export_sym_##enabled(sym, sec)
#define __cond_export_sym_1(sym, sec) ___EXPORT_SYMBOL(sym, sec)
#define __cond_export_sym_0(sym, sec) /* nothing */ #else
#define __EXPORT_SYMBOL ___EXPORT_SYMBOL
#endif #define EXPORT_SYMBOL(sym) \
__EXPORT_SYMBOL(sym, "") #define EXPORT_SYMBOL_GPL(sym) \
__EXPORT_SYMBOL(sym, "_gpl") #define EXPORT_SYMBOL_GPL_FUTURE(sym) \
__EXPORT_SYMBOL(sym, "_gpl_future")

测试代码

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

hello.c

 #include <linux/init.h>
#include <linux/module.h> MODULE_LICENSE("GPL"); void print_hello(void)
{
printk(KERN_INFO "Hello");
}
EXPORT_SYMBOL(print_hello); static int hello_init(void)
{
printk(KERN_INFO "hello init!\n");
return ;
} static void hello_exit(void)
{
printk(KERN_INFO "hello exit!\n");
} module_init(hello_init);
module_exit(hello_exit);

world.c

 #include <linux/init.h>
#include <linux/module.h> MODULE_LICENSE("GPL"); extern void print_hello(void); static void print_world(void)
{
printk(KERN_INFO "World!\n");
} static int world_init(void)
{
printk(KERN_INFO "world init!\n"); print_hello();
print_world(); return ;
} static void world_exit(void)
{
printk(KERN_INFO "world exit!\n");
} module_init(world_init);
module_exit(world_exit);

执行结果:

 [13912.081180] hello init!
[13917.863471] world init!
[13917.863477] Hello
[13917.863479] World!

Linux设备驱动程序 之 内核符号表的更多相关文章

  1. Linux设备驱动程序 之 内核定时器

    综述 如果需要在将来的某个时间点调度执行某个动作,同时在该时间点到达之前不会阻塞当前进程,则可以使用内核定时器: 内核定时器是一个数据结构,它告诉内核在用户定义的时间点使用用户定义的参数来执行一个用户 ...

  2. linux内核符号表

    我们已经看到 insmod 如何对应共用的内核符号来解决未定义的符号. 表中包含了全局内 核项的地址 -- 函数和变量 -- 需要来完成模块化的驱动. 当加载一个模块, 如何由模块 输出的符号成为内核 ...

  3. 嵌入式Linux设备驱动程序:编写内核设备驱动程序

    嵌入式Linux设备驱动程序:编写内核设备驱动程序 Embedded Linux device drivers: Writing a kernel device driver 编写内核设备驱动程序 最 ...

  4. linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)

    原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...

  5. LINUX设备驱动程序的注意事项(两)建设和执行模块

             <一>:设置測试系统 首先准备好一个内核源代码树,构造一个新内核,然后安装到自己的系统中.           <二>:HelloWorld模块 #inclu ...

  6. Linux设备驱动程序 第三版 读书笔记(一)

    Linux设备驱动程序 第三版 读书笔记(一) Bob Zhang 2017.08.25 编写基本的Hello World模块 #include <linux/init.h> #inclu ...

  7. 【Linux设备驱动程序】Chapter 2 - 构造和运行模块

    Hello World 模块 #include <linux/init.h> #include <linux/module.h> MODULE_LICENSE("Du ...

  8. Linux设备驱动程序学习----3.模块的编译和装载

    模块的编译和装载 更多内容请参考Linux设备驱动程序学习----目录 1. 设置测试系统 第1步,要先从kernel.org的镜像网站上获取一个主线内核,并安装到自己的系统中,因为学习驱动程序的编写 ...

  9. Linux设备驱动程序学习----目录

    目录 设备驱动程序简介 1.设备驱动程序简介 构造和运行模块 2.内核模块和应用程序的对比 3.模块编译和装载 4.模块的内核符号表  5.模块初始化和关闭  6.模块参数  7.用户空间编写驱动程序 ...

随机推荐

  1. 关于EF数据迁移的个人总结 简单有效

    有用的拿走,没用的嘴下留情!

  2. luogu2858奶牛零食题解--区间DP

    题目链接 https://www.luogu.org/problemnew/show/P2858 一句话题意: https://cn.vjudge.net/problem/POJ-3186#autho ...

  3. JS实现旋转的魔方

    js <script> window.onload = function () { let cube = document.querySelector('.cube') let timer ...

  4. 最简单的SAP云平台开发教程 - 如何开发UI5应用并运行在SAP云平台上

    选择Services Catalog,根据关键字搜索到WebIDE服务,点击超链接打开WebIDE: 进入workspace,选择Git->Clone Repository: 从我的github ...

  5. 通过ABAP代码判断当前系统类型,BYD还是S4 OP还是S4 Cloud

    用工具类 CL_COS_UTILITIES IS_BYD 如果是BYD系统,这个方法的实现会硬编码返回一个true, 在其他系统里则返回false,如图: IS_SUITE 原理同上,suite系统里 ...

  6. SSISDB7:当前正在运行的Package及其Executable

    PM问:“Vic,现在ETL Job跑到哪一个Package了,正在执行哪个Task?”,第一次遇到这个问题时,一下就懵逼了,只能硬着头皮说:“我看看”. 在做项目开发时,这个问题很常见,但是,被很多 ...

  7. 【ASE高级软件工程】第一次结对作业

    问题定义 具体规则见:讲义.大致规则如下: N个同学(N通常大于10),每人写一个0~100之间的有理数 (不包括0或100),交给裁判,裁判算出所有数字的平均值,然后乘以0.618(所谓黄金分割常数 ...

  8. dedeampz 套件关于PHP开启curl方法

    php开启curl方法主要用到三个文件libeay32.dll,php_curl.dll,ssleay32.dll 打开dede的安装目录,更改对应版本PHP中的php.ini文件,在 ; exten ...

  9. IDEA常见问题和设置

    1.查看方法的文档:快捷键 Ctrl-Q(Ctrl-J(mac)) 2.历史记录 3.IDEA控制台输出中文乱码问题 4.修改Idea默认的maven等全局设置 5.idea 启动时报错javax.i ...

  10. C# 运算符和类型强制转换(6) 持续更新

    C#支持的运算符 https://msdn.microsoft.com/zh-cn/library/6a71f45d(v=vs.140).aspx checked 和 unchecked ; b++; ...