一、linux内核模块
Linux模块是一些可以作为独立程序来编译的函数和数据类型的集合。之所以提供模块机制,是因为Linux本身是一个单内核。单内核由于所有内容都集成在一起,效率很高,但可扩展性和可维护性相对较差,模块机制可弥补这一缺陷。
Linux模块可以通过静态或动态的方法加载到内核空间,静态加载是指在内核启动过程中加载;动态加载是指在内核运行的过程中随时加载。
一个模块被加载到内核中时,就成为内核代码的一部分。模块加载入系统时,系统修改内核中的符号表,将新加载的模块提供的资源和符号添加到内核符号表中,以便模块间的通信。
内核提供了接口函数来注册我们自己的模块:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// linux/init.h /** * module_init() - driver initialization entry point * @x: function to be run at kernel boot time or module insertion * * module_init() will either be called during do_initcalls() (if * builtin) or at module insertion time (if a module). There can only * be one per module. */ #define module_init(x) __initcall(x); /** * module_exit() - driver exit entry point * @x: function to be run when driver is removed * * module_exit() will wrap the driver clean-up code * with cleanup_module() when used with rmmod when * the driver is a module. If the driver is statically * compiled into the kernel, module_exit() has no effect. * There can only be one per module. */ #define module_exit(x) __exitcall(x); |
module_init
和module_exit
分别用于加载和卸载模块,其中的x的函数声明为:
1 |
int f(void); |
二、编译一个自己的模块
2.1 编写一个模块
首先下载内核源码:yum groupinstall "Development Tools"
,、安装好后源码在/usr/src/kernels/2.6.32-754.9.1.el6.x86_64/
,注意最后面的一串内核版本,可能和uname -r
不一致。
然后准备测试代码test.c
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <linux/module.h> #include <linux/init.h> static int test_init(void) { printk("===Test Module Start!===\n"); return 0; } static int test_exit(void) { printk("===Test Module Has Been Closed!==="); return 0; } module_init(test_init); module_exit(test_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("MaQian"); |
其中linux/module.h
和linux/init.h
是必须包含的两个问头文件,MODULE_LICENSE
是模块许可证声明,一般设置为GPL,可选。MODULE_AUTHOR
是模块作者,可选。
2.2 Makefile编写
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# test是模块的名字 obj-m := test.o # 内核代码的位置 KERNEL := /usr/src/kernels/2.6.32-754.9.1.el6.x86_64/ # 当前模块的目录 PWD := $(shell pwd) modules: $(MAKE) -C $(KERNEL) M=$(PWD) modules .PHONY: clean clean: rm -rf *.o *.ko |
如果不出意外,执行make
即可生成我们要的模块文件test.ko
,但是一般情况下,第一次编译是会失败的,会报错,错误的解决方案在最下面。
2.3 挂载模块到系统
挂载驱动使用insmod
,卸载使用rmmod
,先开启另外一个窗口开始打印调试信息:
1 |
> while :; do dmesg -c; sleep 1; done |
插入模块到系统:
1 |
> insmod test.ko |
输出:
1 |
===Test Module Start!=== |
卸载:
1 |
rmmod test.ko |
输出:
1 |
===Test Module Has Been Closed!=== |
三、错误处理
3.1 Kernel configuration is invalid
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
make -C /usr/src/kernels/2.6.32-754.6.3.el6.x86_64/ M=/home/maqian/code/hello_netfilter modules make[1]: Entering directory `/usr/src/linux-2.6.32' ERROR: Kernel configuration is invalid. include/linux/autoconf.h or include/config/auto.conf are missing. Run 'make oldconfig && make prepare' on kernel src to fix it. WARNING: Symbol version dump /usr/src/linux-2.6.32/Module.symvers is missing; modules will have no dependencies and modversions. Building modules, stage 2. /usr/src/linux-2.6.32/scripts/Makefile.modpost:42: include/config/auto.conf: No such file or directory make[2]: *** No rule to make target `include/config/auto.conf'. Stop. make[1]: *** [modules] Error 2 make[1]: Leaving directory `/usr/src/linux-2.6.32' make: *** [modules] Error 2 |
进入到内核代码目录,执行sudo make oldconfig && sudo make prepare
,一路回车确认。
3.2 Symbol version dump xxx is missing
1 2 3 4 5 6 7 8 9 10 11 12 13 |
make -C /usr/src/kernels/2.6.32-754.6.3.el6.x86_64/ M=/home/maqian/code/hello_netfilter modules make[1]: Entering directory `/usr/src/linux-2.6.32' WARNING: Symbol version dump /usr/src/linux-2.6.32/Module.symvers is missing; modules will have no dependencies and modversions. Building modules, stage 2. MODPOST 0 modules /bin/sh: scripts/mod/modpost: No such file or directory make[2]: *** [__modpost] Error 127 make[1]: *** [modules] Error 2 make[1]: Leaving directory `/usr/src/linux-2.6.32' make: *** [modules] Error 2 |
还是在刚刚的目录执行:sudo make scripts
。
当一个模块编译完成之后,使用insmod
加载,使用rmmod
加载,lsmod
可以查看已经挂载的模块,modinfo
查看一个模块的详细信息。
3.3 insmod: error inserting 'test.ko': -1 Invalid module format
插入驱动报错:
1 |
insmod: error inserting 'test.ko': -1 Invalid module format |
dmesg错误:
1 |
test: no symbol version for module_layout |
检查驱动内核版本是否一致,最开始出现这个问题是因为内核代码是自己手动下载的,后面改成yum下载就好了。
评论