linux 内核模块编程之内核符号导出(五)

时间:2021-09-14 15:46:36

/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


分别编译完之后我们会得到一个hello.ko文件和calculate.ko文件

linux 内核模块编程之内核符号导出(五)

然后我们就可以将我们的模块拷贝到我们的开发板上运行测试了。

2个模块我们得先安装calculate.ko ,不然的话hello.ko会因为找不到函数的实现而报错,测试结果如下

linux 内核模块编程之内核符号导出(五)


内核符号导出的使用

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 内核模块编程之内核符号导出(五)

前面几行就是他的版本号,这种方法很容易出问题,所以不推荐这种方法。


内核打印优先级

在<linux/kernel.h>中定义了8种记录级别。按照优先级别递减的顺序分别是:

KERN_EMERG "<0>" 用于紧急消息,常常是那些崩溃前的消息。

KERN_ALERT "<1>"  需要立刻行动的消息

KERN_CRIT "<2>" 严重情况

KERN_ERR "<3>" 错误情况

如果没有指定消息的级别,printk()会使用默认的DEFAULT_MESSAGE_LOGLEVEL,他是一个在kernel/printk.c中定义的整数