/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中定义的整数