毕设是做一个网络方面的项目,需要自己写netfilter模块,今天研究了一下午怎么往Linux内核上加载自定义模块,遇到了很多问题,记录一下心得。简单起见,以一个HelloWorld模块为例来说。内核是3.10版本的。
1.编写hello.c文件
#include<linux/init.h>
#include<linux/printk.h>
static int hello_init(void)
{
printk(KERN_WARNING"Hello\n");
return 0;
}
static void hello_exit(void)
{
printk(KERN_INFO"Bye\n");
}
module_init(hello_init);
module_exit(hello_exit);
这里有几个需要注意的地方:
① hello_init函数返回值需要写成int类型,不然make时会报一个警告
② 函数就算没有参数也要写上void,不然会报警告function declaration isn’t a prototype
③ 理论上最后要加一个MODULE_AUTHOR和MODULE_LICENSE,不然内核会报一个警告说内核被污染= =
2.编写Makefile文件
要学习Makefile文件编写规则,推荐一个教程跟我一起写Makefile
本例中Makefile文件如下:
ifneq($(KERNELRELEASE),)
obj-m := hello.o
else
KDIR := /usr/src/kernels/3.10.0-1062.9.1.el7.x86_64
PWD := /root
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *mod.c *.symvers *.order
endif
先引用一段话
KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,在第一次读取执行此Makefile时,KERNELRELEASE没有被定义,所以make将读取执行else之后的内容,如果make的目标是clean,直接执行clean操作,然后结束。当make的目标为all时,-C $(KDIR)指明跳转到内核源码目录下读取那里的Makefile;M=$(PWD) 表明然后返回到当前目录继续读入、执行当前的Makefile。当从内核源码目录返回时,KERNELRELEASE已被定义,kbuild也被启动去解析kbuild语法的语句,make将继续读取else之前的内容。else之前的内容为kbuild语法的语句,指明模块源码中各文件的依赖关系,以及要生成的目标模块名。
ifneq是if not equal,意思就是判断KERNELRELEASE是否为NULL,obj-m代表将hello.o加载为模块,如果换成obj-y就是加载进内核。KDIR是你自己电脑内核源码目录,PWD是hello.c和Makefile文件所在目录(我的是/root)。注意,Makefile对缩进和空格很敏感,一个不小心就会报错,而且这种错很难发现。
3.执行make命令
在hello.c和Makefile文件路径下执行make命令,成功后如下图:
此时目录下的文件:
如果make过程中有报错stack-protector enabled but compiler support broken,请先安装gcc。
4.加载与卸载模块命令
加载:insmod hello.ko (install module)
卸载:rmmod hello (remove module)
5.查看模块是否加载成功
printk函数输出的信息在/var/log/messages中,加载模块后,用vim打开该文件即可看到自定义的输出消息:
卸载后的输出消息:
至此,小功告成,过段时间写自定义netfilter模块。