Linux内核如何访问另外一个模块的函数和变量

系统 Linux
有粉丝问我Linux内核如何访问另外一个模块的函数和变量,今天就这个问题给大家介绍一下内核中两个模块,一个A,一个B,A模块中有操作函数,B模块要调用A模块的函数。

[[386922]]

一、问题整理

内核中两个模块,一个A,一个B,A模块中有操作函数,B模块要调用A模块的函数。


二、分析

这是一个驱动工程师经常遇到的一个问题,该问题其实是模块符号导出问题,实现该功能比较简单,借助EXPORT_SYMBOL()即可。

1. 什么是符号?

这里的符号主要指的是全局变量和函数,静态全局变量其实也可以被另外一个模块访问到。

2. 为什么要导出符号?

↓ Linux内核采用的是以模块化形式管理内核代码。内核中的每个模块相互之间是相互独立的,也就是说A模块的全局变量和函数,B模块是无法直接访问的。

有些时候,我们写一些模块代码的时候,发现部分函数功能别人已经实现了,此时我们就想如果我们可以调用他们已经实现好的函数接口就好了。那如何才能做到这点呢?

就靠符号导出了,也就是说你可以把你实现的函数接口和全局变量导出,以供其他模块使用。

在Linux内核的世界里,如果一个模块已经以静态的方式编译进的内核,那么它导出的符号就会出现在全局的内核符号表中。

在Ubuntu 14.04系统中,Linux内核的全局符号表存放在以下文件:

  1. /usr/src/linux-headers-3.2.0-29-generic-pae/Module.symvers 

如果打开这个文件,可以发现里面的内容就是:

  1. Addr------->符号名------>模块名------>导出符号的宏 

 

3. 如何导出符号?

Linux内核给我们提供了两个宏:

  1. EXPORT_SYMBOL(name); 
  2. EXPORT_SYMBOL_GPL(name); 

上面宏定义的任一个使得给定的符号在模块外可用;GPL版本的宏定义只能使符号对GPL许可的模块可用;符号必须在模块文件的全局部分输出,在任何函数之外,因为宏定义扩展成一个特殊用途的并被期望是全局存取的变量的声明。

4. 模块编译时,如何寻找使用的符号?

  • a.在本模块中符号表中,寻找符号(函数或变量实现)
  • b.在内核全局符号表中寻找
  • c.在模块目录下的Module.symvers文件中寻找

5. 案例演示

模块A导出全局变量global_var和函数show两个符号供模块B使用。

A模块

  1. #include <linux/init.h> 
  2. #include <linux/module.h> 
  3. static int global_var = 100; 
  4. static void show(void) 
  5.  printk("show():  global_var =%d \n",global_var); 
  6. static int hello_init(void) 
  7.  printk("module b :global_var=%d\n",global_var); 
  8.  return 0; 
  9. static void hello_exit(void) 
  10.  printk("hello_exit \n"); 
  11.  return
  12. }EXPORT_SYMBOL(global_var); 
  13. EXPORT_SYMBOL(show); 
  14. MODULE_AUTHOR("yikoulinux"); 
  15. MODULE_LICENSE("GPL"); 
  16. module_init(hello_init); 
  17. module_exit(hello_exit);  

B模块

  1. #include <linux/init.h> 
  2. #include <linux/module.h> 
  3.  
  4. extern int global_var; 
  5. extern  void show(void); 
  6. static int hello_init(void) 
  7.  printk("module a: global_var= %d\n",global_var); 
  8.  show(); 
  9.  return 0; 
  10. static void hello_exit(void) 
  11.  printk("hello_exit \n"); 
  12.  return
  13. MODULE_AUTHOR("yikoulinux"); 
  14. MODULE_LICENSE("GPL"); 
  15. module_init(hello_init); 
  16. module_exit(hello_exit); 

调试步骤:

1.编译模块A,然后加载模块A,在模块A编译好后,在它的当前目录会看到一个Module.symvers文件,这里存放的就是我们模块A导出的符号。

2.将模块A编译生成的Module.symvers文件拷贝到模块B目录下,然后编译模块B,加载模块B。

3.通过dmesg查看模块打印的信息。打印信息如下:


由结果可知,我们在B模块中访问到了模块A的全局变量global_var以及函数show。

 

责任编辑:姜华 来源: 一口Linux
相关推荐

2021-07-06 14:36:05

RustLinux内核模块

2014-07-24 14:35:26

Linux内核模块

2018-10-15 10:10:41

Linux内核补丁

2011-08-29 15:12:24

UbuntuLinux模块

2009-09-11 08:44:36

2020-11-10 07:11:23

Linux内核补丁

2021-05-18 14:25:30

Linux运维Linux系统

2021-05-26 07:53:58

Linux运维Linux系统

2022-10-10 17:00:19

地址内核函数

2018-12-06 08:34:15

Linux访问控制安全拦截技术

2018-06-19 09:07:57

Linux内核模块

2023-07-25 15:17:38

Linux操作系统开发

2019-02-11 14:35:59

Linux硬件监控工具命令

2009-08-18 11:01:51

2013-08-01 10:58:53

2023-09-28 13:21:32

2023-02-27 12:53:14

Linux内核

2021-02-20 11:34:43

Linux内核指针

2010-01-22 11:01:04

linux内核模块

2023-05-08 08:05:42

内核模块Linux
点赞
收藏

51CTO技术栈公众号