Linux内核动态加载自定义模块

毕设是做一个网络方面的项目,需要自己写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模块。


版权声明:本文为qq_39006318原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。