003-编译、加载、卸载

上一篇的内核模块源码

#include "linux/init.h"
#include "linux/module.h"

static int __init hello_module_init(void)
{
	printk("Initialize module\n");
	return 0;
}

static void __exit hello_module_exit(void)
{
	printk("Exit module\n"); 
}

module_init(hello_module_init);
module_exit(hello_module_exit);

MODULE_LICENSE("GPL");

编译内核模块的Makefile

编译简单的应用程序只需要使用命令gcc 文件名即可。但内核模块由于需要依赖整个kernel的构建环境,因此需要结合kernel的编译方式进行编译。整个kernel的构建使用的是make工具,因此我们的内核模块也需要采用make进行编译。因此我们需要为我们的内核模块编写一个Makefile,放在源代码同一个目录下。如下:

KERNELPATH ?= /lib/modules/5.8.0-63-generic/build

hellokernel-objs := HelloKernelModule.o
obj-m := hellokernel.o

all:
	${MAKE} -C ${KERNELPATH} M=${PWD} modules;

clean:
	${MAKE} -C ${KERNELPATH} M=${PWD} clean;
	rm -f *.ko
  • KERNELPATH ?= /lib/modules/5.8.0-63-generic/build
    由于编译内核模块需要依赖内核,因此我们必须告诉编译器内核源码的位置,因此使用一个变量KERNELPATH来保存内核源码位置。变量名称可以根据自己喜好随便起。只需要在后面正确应用即可。
    PC上安装的Linux发行版都会带有该发行版使用的kernel源码,它的位置在lib/modules/版本号/build下,其中版本号可以使用命令uname -r获得。比如我的电脑上,该命令的返回信息是5.8.0-63-generic,因此KERNELPATH的值就被定义成/lib/modules/5.8.0-63-generic/build
  • hellokernel-objs := HelloKernelModule.o
    模块名称-objs 变量用于执行该模块依赖的.o文件,而Makefile会根据.o文件名自动识别.o文件依赖的.c文件,其原则就是文件名.o依赖文件名.c。因此hellokernel-objs := HelloKernelModule.o的最终含义是告诉make,名为hellokernel的内核模块依赖HelloKernelModule.o,而HelloKernelModule.o依赖HelloKernelModule.cHelloKernelModule.c正是我们编写的模块源代码。
  • obj-m := hellokernel.o
    obj-m := 模块名.o obj-m是告诉内核需要将模块名.o编译成动态加载的内核模块。这个变量的细节与内核的构建系统有关,我们暂时无需深入研究。
  • all: clean:
    all: clean:是Make的目标,当我们执行make时,make会读取Makefile,并执行all:后面的指令。我们也可以明确指定make的目标如make clean,这时make会执行clean:后面的指令。关于make和Makefile不懂的可以自行在网络上查找相关资料,因为它不是本次分享的主要内容,因此不过多阐述。
  • ${MAKE} -C K E R N E L P A T H M = {KERNELPATH} M= KERNELPATHM={PWD} modules;
  • ${MAKE} -C K E R N E L P A T H M = {KERNELPATH} M= KERNELPATHM={PWD} clean;
  • rm -f *.ko
    以上是构建和清除内核模块的指令,我们只需要按照格式写好即可。其中-C 用于表明内核源码的位置。M用于指定当前模块的位置。modules用于编译内核模块,clean用于清除内核模块
eniac@eniac-ThinkPad-E480:~/Test$ uname -r
5.8.0-63-generic

总之,到目前位置我们需要按照固定的格式写好Makefile,然后修改KERNELPATH 模块名-objs obj-m 三个变量的值即可。

编译模块

编译模块的指令很简单只需要在模块路径下执行make即可。
最终会在该目录下生成一堆文件,我们需要关注其中一个文件模块名.ko,在我的例子中是hellokernel.ko

加载和卸载模块

insmodrmmod 用于加载和卸载模块。
以我的例子为例 sudo insmod hellokernel.ko 便是加载模块此时会执行函数hello_module_init()sudo rmmod hellokernel便是卸载模块,此时会执行函数hello_module_exit()

关于输出

我们在hello_module_init()hello_module_exit() 分别调用了printk打印了信息,但是可以并没有直接输出到控制台上。这时你需要使用dmesg指令,该指令用于查看内核log信息。在该命令输出结果的最后会看到两个函数的输入内容。

在这里插入图片描述

你可能感兴趣的:(linux,嵌入式)