Linux内核头文件提供了一个方便的方法用来管理符号的对模块外部的可见性,因此减少了命名空间的污染(命名空间的名称可能会与内核其他地方定义的名称冲突),并且适当信息隐藏。 如果你的模块需要输出符号给其他模块使用,应当使用下面的宏定义:

EXPORT_SYMBOL(name);

EXPORT_SYMBOL_GPL(name);   //只适用于包含GPL许可权的模块;

这两个宏均用于将给定的符号导出到模块外. _GPL版本的宏定义只能使符号对GPL许可的模块可用。 符号必须在模块文件的全局部分导出,不能在函数中导出,这是因为上述这两个宏将被扩展成一个特殊用途的声明,而该变量必须是全局的。这个变量存储于模块的一个特殊的可执行部分(一个"ELF段" ),在装载时,内核通过这个段来寻找模块导出的变量(感兴趣的读者可以看<linux/module.h>获知更详细的信息)。

一、宏定义EXPORT_SYMBOL分析

1、源码

[cpp] view plaincopy
  1. <include/linux/moudule.h>
  2. …….
  3. #ifndef MODULE_SYMBOL_PREFIX
  4. #define MODULE_SYMBOL_PREFIX ""
  5. #endif
  6. …….
  7. struct kernel_symbol       //内核符号结构
  8. {
  9. unsignedlong value;  //该符号在内存地址中的地址
  10. constchar *name;     //该符号的名称
  11. };
  12. ……
  13. #define __EXPORT_SYMBOL(sym,sec)                                 \
  14. externtypeof(sym) sym;                                                        \
  15. __CRC_SYMBOL(sym,sec)                                            \
  16. staticconst char __kstrtab_##sym[]                                 \
  17. __attribute__((section(“__ksymtab_strings”),aligned(1)))   \
  18. =MODULE_SYMBOL_PREFIX#sym;                      \
  19. staticconst struct kernel_symbol __ksymtab_##sym         \
  20. __used                                                                          \
  21. __attribute__((section(“__ksymatab”sec),unused))                   \
  22. ={(unsignedlong)&sym,_kstrab_#sym}
  23. #define    EXPORT_SYMBOL(sym)                   \
  24. __EXPOTR_SYMBOL(sym,””)
  25. #define    EXPORT_SYMBOL_GPL(sym)           \
  26. __EXPOTR_SYMBOL(sym,”_gpl”)
  27. #define    EXPORT_SYMBOL(sym)                   \
  28. __EXPOTR_SYMBOL(sym,”_gpl_future”)

在分析前,先了解如下相关知识:

1)#运算符,##运算符

通常在宏定义中使用#来创建字符串 #abc就表示字符串”abc”等。

##运算符称为预处理器的粘合剂,用来替换粘合两个不同的符号,

如:#define xName (n)  x##n

则xName(4)  则变为x4

2)gcc的 __attribute__ 属性:

__attribute__((section(“section_name”)))的作用是将指定的函数或变量放入到名为”section_name”的段中

__attribute__属性添加可以在函数或变量定义的时候直接加入在定义语句中。

如:

int myvar__attribute__((section("mydata"))) = 0;

表示定义了整形变量myvar=0;并且将该变量存放到名为”mydata”的section中

关于gcc_attribute详解可以参考:http://blog.sina.com.cn/s/blog_661314940100qujt.html


2、EXPORT_SYMBOL的作用是什么?

EXPORT_SYMBOL标签内定义的函数或者符号对全部内核代码公开,不用修改内核代码就可以在您的内核模块中直接调用,即使用EXPORT_SYMBOL可以将一个函数以符号的方式导出给其他模块使用。

这里要和System.map做一下对比:System.map 中的是连接时的函数地址。连接完成以后,在2.6内核运行过程中,是不知道哪个符号在哪个地址的。

EXPORT_SYMBOL的符号,是把这些符号和对应的地址保存起来,在内核运行的过程中,可以找到这些符号对应的地址。而模块在加载过程中,其本质就是能动态连接到内核,如果在模块中引用了内核或其它模块的符号,就要EXPORT_SYMBOL这些符号,这样才能找到对应的地址连接。

二、 EXPORT_SYMBOL使用方法

第一、在模块函数定义之后使用EXPORT_SYMBOL(函数名)

第二、在调用该函数的模块中使用extern对之声明

第三、首先加载定义该函数的模块,再加载调用该函数的模块

要调用别的模块实现的函数接口和全局变量,就要导出符号 /usr/src/linux-headers-2.6.32-33-generic/Module.symvers

A B
static int num =10;
static void show(void)
{
printk("%d  \n",num);
}

EXPORT_SYMBOL(show);
extern void show(void);

函数A先将show() 函数导出,函数B 使用extern 对其声明,要注意:

a -- 编译a模块后,要将 Module.symvers 拷贝到b模块下

b -- 然后才能编译b模块

c -- 加载:先加载a模块,再加载b模块

d -- 卸载:先卸载b模块,再卸载a模块


三、示例
代码a ,hello.c
[cpp] view plaincopy
  1. #include <linux/module.h>
  2. static int num =10;
  3. static void show(void)
  4. {
  5. printk("show(),num = %d\n",num);
  6. }
  7. static int hello_init(void)
  8. {
  9. printk("hello_init");
  10. return 0;
  11. }
  12. static void hello_exit(void)
  13. {
  14. printk("hello_exit \n");
  15. }
  16. EXPORT_SYMBOL(show);
  17. MODULE_LICENSE("GPL");
  18. module_init(hello_init);
  19. module_exit(hello_exit);

代码b show.c

[cpp] view plaincopy
  1. #include <linux/module.h>
  2. extern void show(void);
  3. static int show_init(void)
  4. {
  5. printk("show_init");
  6. show();
  7. return 0;
  8. }
  9. static void show_exit(void)
  10. {
  11. printk("show_exit \n");
  12. }
  13. MODULE_LICENSE("GPL");
  14. module_init(show_init);
  15. module_exit(show_exit);<strong>
  16. </strong>

编译后加载模块,卸载模块,可以用 dmesg 查看内核打印信息。

Linux 驱动开发之内核模块开发(四)—— 符号表的导出相关推荐

  1. Linux驱动篇之内核模块

    Linux驱动篇之内核模块 1.基本概念 模块与驱动: Linux中,将设备分为三种基本的类型. 字符设备 块设备 网络接口 Linux中还有一个很重要的概念,模块.可在运行时添加到内核中的代码被称为 ...

  2. Linux驱动开发(二)内核符号表

    内核符号表是内核中一个全局的总表,这个表中声明了一些全局的函数.内核中的驱动程序只需要用 extern 声明后就可以直接调用这些函数了. 查看内核中的符号表: cat /proc/kallsyms 将 ...

  3. Linux 驱动开发之内核模块开发 (二)—— 内核模块编译 Makefile 入门

    一.模块的编译        我们在前面内核编译中驱动移植那块,讲到驱动编译分为静态编译和动态编译:静态编译即为将驱动直接编译进内核,动态编译即为将驱动编译成模块. 而动态编译又分为两种: a -- ...

  4. Linux 驱动开发之内核模块开发 (三)—— 模块传参

    一.module_param() 定义 通常在用户态下编程,即应用程序,可以通过main()的来传递命令行参数,而编写一个内核模块,则通过module_param() 来传参. module_para ...

  5. Linux驱动学习--多图层开发(一)--lcdc/framebuffer的注册(RK平台)

    目录 一.引言 Android图形系统系统篇之HWC的介绍 二.源码分析 ------>framebuffer框架 ------>2.RK平台下多图层代码分析 三.多图层的硬件lcdc支持 ...

  6. 嵌入式linux驱动开发实战教程,嵌入式Linux驱动开发实战视频教程

    嵌入式Linux驱动开发实战教程(内核驱动.看门狗技术.触摸屏.视频采集系统) 适合人群:高级 课时数量:109课时 用到技术:嵌入式 Linux 涉及项目:驱动开发.看门狗技术.触摸屏.视频采集 咨 ...

  7. 《Android深度探索(卷1):HAL与驱动开发》——6.4节使用多种方式测试Linux驱动...

    本节书摘来自异步社区<Android深度探索(卷1):HAL与驱动开发>一书中的第6章,第6.4节使用多种方式测试Linux驱动,作者李宁,更多章节内容可以访问云栖社区"异步社区 ...

  8. Linux内核开发_内核模块

    内核模块是什么? Linux下的内核模块类似于Windows下的DLL动态链接库技术,和我们平常所使用的一些动态链接的SDK库一样,只是调用者是内核而已,不是用户态的程序. 内核模块拥有的的权限是和用 ...

  9. 嵌入式学习之Linux驱动篇-迅为视频更新了

    想学习Linux驱动但是无从下手的同学,学习Linux驱动但是一直不能入门的同学,学习了很多视频和资料还是很懵的同学快来学习拉 https://www.bilibili.com/video/BV1Vy ...

最新文章

  1. OpenCV新手入门,如何用它平移缩放和旋转图片
  2. PMCAFF原创作者人气榜,快来看看你排第几?
  3. 2020牛客暑期多校训练营(第六场)
  4. rabbitmq延迟队列相关
  5. 创建一个dynamics 365 CRM online plugin (三) - PostOperation
  6. D-News|英特尔首推融合现实,亚马逊云服务市场占比超3成
  7. typescript 判断异步执行已经结束_vue进阶系列——用typescript玩转vue和vuex
  8. Matplotlib——基本用法
  9. spring data mongodb CURD
  10. JDK11即将来临,新特性了解一下
  11. 4K标准---电视显示标准(ITU-R BT.2020)
  12. HttpHeaders()无法调用
  13. 鹰眼系统原理_飞思卡尔智能车一:山外鹰眼摄像头使用原理
  14. 中文简繁转换项目 OpenCC
  15. linux tar zcxf,tar/gzip/zip文件打包、压缩命令
  16. HTML内镶svg编辑器!后续改进,记录过程!
  17. 养蚕日记软件测试,科学观察养蚕日记(精选3篇)
  18. 【mysql】mysql查询结果添加固定值
  19. 刷屏!日本内政部向国民宣传5G的短视频:未来将是这样!
  20. 硬件工程师成长之路(0)----认识元件

热门文章

  1. hdu5424 Rikka with Graph II
  2. IOS UIPageController
  3. 关于如何使用的MSCS(微软群集服务器)中的iSCSI Guest Initiators (客户机启动器)...
  4. Forms身份验证基本原理
  5. [转载]使用命名管道实现进程间通信
  6. nessuss中文使用手册
  7. mysql常用的存储引擎_Mysql存储引擎
  8. opencv:畸变矫正:透视变换算法的思想与实现
  9. 5827. 检查操作是否合法
  10. leetcode 191. 位1的个数(位运算)