/proc/kallsyms 记录了内核中所有导出的符号的名字与地址
我们需要编译2个内核模块,然后其中一个内核模块去调用另一个内核模块中的函数
hello.c代码如下
#include <linux/module.h> #include <linux/init.h> MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Xie"); MODULE_DESCRIPTION("Hello World Module"); MODULE_ALIAS("a simplest module"); extern int add_integar(int a, int b); extern int sub_integer(int a, int b); static int __init hello_init() { int res = add_integar(1, 2); printk("add[%d]\n",res); return 0; } static void __exit hello_exit() { int res = sub_integer(2, 1); printk("sub[%d]\n",res); } module_init(hello_init); module_exit(hello_exit);
从这可以看出我们的hello.c中调用了2个函数 add_integar和 sub_integer,这2个函数我们在另一个模块中实现
calculate.c的代码如下
#include <linux/module.h> #include <linux/init.h> MODULE_LICENSE("GPL"); int add_integar(int a, int b) { return a+b; } int sub_integer(int a, int b) { return a-b; } static int __init sym_init() { return 0; } static void __exit sym_exit() { } module_init(sym_init); module_exit(sym_exit); EXPORT_SYMBOL(add_integar);//导出函数,供hello.c调用 EXPORT_SYMBOL(sub_integer);//导出函数,供hello.c调用
calculate.c对应的makefile 如下
ifneq ($(KERNELRELEASE),) obj-m := calculate.o else KERNELDIR ?= /home/grb/grb/arm/linux-2.6.38/ PWD := $(shell pwd) all: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: rm -f *.ko *.o *.mod.o *.mod.c *.symvers *~ *.order endif
然后我们就可以将我们的模块拷贝到我们的开发板上运行测试了。
2个模块我们得先安装calculate.ko ,不然的话hello.ko会因为找不到函数的实现而报错,测试结果如下
内核符号导出的使用
EXPORT_SYMBOL(符号名)
EXPORT_SYMBOL_GPL(符号名)
其中EXPORT_SYMBOL_GPL只能用于包含GPL许可证的模块
常见问题:版本不匹配
内核模块的版本由其所依赖的内核代码版本所决定,在加载内核模块时,insmod程序会将内核模块的版本与当前正在运行的内核版本比较,如果不一致,就会出现下面的错误:
insmod hello.ko
disagrees about version of symbol struct_module
insmod: error inserting 'hello.ko': -1 Invalid module format
解决方法:
1、使用modprobe --force-modversion强行插入(不推荐)
2、确保编译内核模块时,所依赖的内核代码版本等于当前正在运行的内核,可通过uname -r查看当前运行的内核版本
有一种投机取巧的方式,就是修改/home/grb/grb/arm/linux-2.6.38/Makefile 文件中的版本号,如下
前面几行就是他的版本号,这种方法很容易出问题,所以不推荐这种方法。
内核打印优先级
在<linux/kernel.h>中定义了8种记录级别。按照优先级别递减的顺序分别是:
KERN_EMERG "<0>" 用于紧急消息,常常是那些崩溃前的消息。
KERN_ALERT "<1>" 需要立刻行动的消息
KERN_CRIT "<2>" 严重情况
KERN_ERR "<3>" 错误情况
如果没有指定消息的级别,printk()会使用默认的DEFAULT_MESSAGE_LOGLEVEL,他是一个在kernel/printk.c中定义的整数