驱动学习(三)符号导出
文章目录
1.什么是符号?
主要是指全局变量和函数
2.为什么要导出符号?
linux内核采用的是模块化的形式管理内核代码。内核中每个模块之间是相互独立的,也就是说A模块的全局变量和函数,B模块是无法访问的。若B模块想要使用A模块中的已有符号,那么必须将A模块中的符号做符号导出,导出到模块符号表中,然后B模块可以使用A模块导出的符号。
3.如何导出符号?
linux内核给我们提供了两个函数宏,用他们做符号导出。
3.1 EXPORT_SYMBOL(符号)
3.2 EXPORT_SYMBOL_GPL(符号)
4.符号表的类型
4.1 本地符号表
用户自定义模块导出的符号表,这些符号表保存在本模块目录下Module.symvers
4.2 全局符号表
内核已经导出的符号表,在 /proc/kallsyms
sudo cat /proc/kallsyms |grep printk 0xc15cd833
5.注意
安装时,先安装符号导出模块,再安装使用符号的模块;
卸载时,先卸载使用符号的模块,再卸载导出符号的模块。
6.测试
6.1 测试思路
-
分别创建两个驱动模块文件夹,分别编写符号导出驱动模块(export_symbols.c)和符号使用驱动模块(use_symbols.c)。
-
在export_symbols.c中使用EXPORT_SYMBOL()宏函数和EXPORT_SYMPOL_GPL()宏函数测试导出符号。
-
编译export_symbols.c会生成本地符号表Module.symvers。
-
将生成的本地符号表Module.symvers拷贝到usr_symbols驱动模块文件夹下,编译use_symbols.c。
-
查看内核打印消息
-
验证对错
6.2 本地符号表符号导出实际操作
创建文件夹,每个文件夹下有一个.c源码和Makefile工程管理文件
分别编写export_symbols.c和use_symbols.c
1 #include <linux/module.h>
2
3 int test = 1;
4
5 void function(void)
6 {
7 printk("*********export test = %d\n",test);
8 }
9
10 int driver_init(void)
11 {
12 printk("*********export driver init\n");
13 return 0;
14 }
15 void driver_clear(void)
16 {
17 printk("*********export driver clear\n");
18 }
19
20
21 module_init(driver_init);
22 module_exit(driver_clear);
23
24 EXPORT_SYMBOL(test);
25 EXPORT_SYMBOL_GPL(function);
26
27 MODULE_LICENSE("GPL");
28 MODULE_AUTHOR("cfy");
29 MODULE_ALIAS("liangzai");
30 MODULE_DESCRIPTION("2022-7-6");
export_symbols.c
1 ifeq ($(KERNELRELEASE),)
2 KERNELDIR ?=/lib/modules/$(shell uname -r)/build
3 PWD := $(shell pwd)
4 modules:
5 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
6 else
7 obj-m :=export_symbols.o
8 endif
9
10 clean:
11 rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
Makefile
1 #include <linux/module.h>
2
3 extern int test;
4 extern void function(void);
5
6 int driver_init(void)
7 {
8 printk("*********use test = %d\n",test);
9 function();
10 return 0;
11 }
12 void driver_clear(void)
13 {
14 printk("*********use driver clear\n");
15 }
16
17
18 module_init(driver_init);
19 module_exit(driver_clear);
20
21 MODULE_LICENSE("GPL");
22 MODULE_AUTHOR("cfy");
23 MODULE_ALIAS("liangzai");
24 MODULE_DESCRIPTION("2022-7-6");
use_symbols.c
1 ifeq ($(KERNELRELEASE),)
2 KERNELDIR ?=/lib/modules/$(shell uname -r)/build
3 PWD := $(shell pwd)
4 modules:
5 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
6 else
7 obj-m :=use_symbols.o
8 endif
9
10 clean:
11 rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
Makefile
编译export_symbols.c
生成Module.symvers
将Module.symvers拷贝到use_symbols下
编译use_symbols.c,出现了未定义的问题。
观察了一下,发现每次编译之后,之前复制进来的本地符号表文件会“消失”,翻了一下解决方案,还真给找到了,https://blog.csdn.net/lu_embedded/article/details/51440817
修改use_symbols的Makefile,添加了第10行
1 ifeq ($(KERNELRELEASE),)
2 KERNELDIR ?= /lib/modules/$(shell uname -r)/build
3 PWD := $(shell pwd)
4 modules:
5 $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
6 else
7 obj-m := use_symbols.o
8 endif
9
10 KBUILD_EXTRA_SYMBOLS = /home/cfy/hqyj/drivers/sysbols_export/export_symbols/Module.symvers
11
12 clean:
13 rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* modules*
重新编译
安装导出模块
查看内核打印信息,因为测试了好几次,前面的信息为之前测试打印信息
安装导入模块
可以看到export_symbols被use_symbols使用,再查看内核打印信息
倒数第二条为变量导入结果,最后一条为函数导入结果,验证完毕,全局符号导出还有点问题,后续解决后再发布。接下来是字符设备。