一、导出符号表机制
二、内核中为什么可以导出符号表
1.因为内核空间共用3-4G空间
2.通过上面可知,B模块只要拿到A模块add'函数地址就可以调用A模块的add函数
3.A模块:提供者
4.B模块:调用者
三、内核中导出符号表的好处
1.解决程序冗杂问题
2.驱动工程师不需要关心内核中驱动源码的实现,只需要内核中导出的对应的驱动的符号表信息,就可以调用内核中任意函数
四、内核中如何导出符号表
EXPORT_SYMBOL_GPL(name)
函数功能:内核中导出对应函数的符号表信息
参数:
name:函数名
五、代码实现
提供者:
#include <linux/init.h> #include <linux/module.h> int add(int a,int b){ return a+b; } EXPORT_SYMBOL_GPL(add);//导出符号表 //入口函数 static int __init demo_init(void) { //static:限定函数demo_init只可以在当前文件中使用 //initial:函数返回值类型 //__init:#define __init __section(".init.text"),将demo_init入口函数存放在.init.text段中 //demo_init:入口函数名 //void:函数参数为空 printk(KERN_CRIT "入口函数\n");//消息级别大于终端级别,消息可在终端打印 return 0;//函数返回值为0 } //出口函数 static void __exit demo_exit(void) { //__exit:#define __exit __section(".exit.text"),将demo_exit出口函数存放在.exit.text段中 printk(KERN_CRIT "出口函数\n");//消息级别大于终端级别,消息可在终端打印 } module_init(demo_init);//指定入口地址 module_exit(demo_exit);//指定出口地址 MODULE_LICENSE("GPL");//许可证,遵循GPL协议
调用者:
#include <linux/init.h> #include <linux/module.h> extern int add(int a,int b); //入口函数 static int __init demo_init(void) { //static:限定函数demo_init只可以在当前文件中使用 //initial:函数返回值类型 //__init:#define __init __section(".init.text"),将demo_init入口函数存放在.init.text段中 //demo_init:入口函数名 //void:函数参数为空 printk(KERN_CRIT "入口函数\n");//消息级别大于终端级别,消息可在终端打印 printk("sum=%d\n",add(3,4)); return 0;//函数返回值为0 } //出口函数 static void __exit demo_exit(void) { //__exit:#define __exit __section(".exit.text"),将demo_exit出口函数存放在.exit.text段中 printk(KERN_CRIT "出口函数\n");//消息级别大于终端级别,消息可在终端打印 } module_init(demo_init);//指定入口地址 module_exit(demo_exit);//指定出口地址 MODULE_LICENSE("GPL");//许可证,遵循GPL协议
调用者Makefile:
#arm架构linux内核路径 #KERNEDDIR := /home/ubuntu/linux-5.10.61/ #x86架构linux内核路径 KERNEDDIR := /lib/modules/$(shell uname -r)/build #打开终端,执行pwd命令 PWD := $(shell pwd) #指定提供提供者符号表信息路径 KBUILD_EXTRA_SYMBOLS := /home/ubuntu/DC24031/02_symbol_gpl/demoA/Module.symvers all: @#-C:跳转到指定的目录下,读取当前目录下的Makefile文件 @#M:回到当前驱动目录下,读取当前目录下的Makefile文件 make -C $(KERNEDDIR) M=$(PWD) modules clean: make -C $(KERNEDDIR) M=$(PWD) clean #添加驱动被编译文件 obj-m := demoB.o