Ubuntu版本:ubuntu-gnome-16.04-desktop-amd64,gnome版本
-------------------------------------------------- ----------------------------------
本文主要介绍如何在内核外编译内核模块,即:
如何构建树外内核模块。
1. 代码hello.c
#include //所有模块都需要的头文件
#include // init&exit相关宏
#include
MODULE_LICENSE("GPL");
MODULE_AUTHOR("baoli");
MODULE_DESCRIPTION("hello world module");
static int __init hello_init(void)
{
printk(KERN_WARNING "hello world.n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_WARNING "hello exit!n");
}
module_init(hello_init);
module_exit(hello_exit);
2. 生成文件
ifneq ($(KERNELRELEASE),)
obj-m :=hello.o
else
KDIR :=/lib/modules/$(shell uname -r)/build
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
endif
3. 编译和测试
makesudo insmod hello.kodmesgsudo rmmod 你好
内核信息如下:
[156596.317933]你好世界。
[156604.933930]你好退出!
4.Makefile分析
4.1 多个源文件
如果你有一个名为 module.ko 的模块,它来自多个源文件(我们称它们为 file1.c 和 file2.c),正确的写法应该是:
obj-m := 模块.o
模块-objs := file1.o file2.o
4.2 清洁
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
可以替换为:
使 -C $(KDIR) M=$(PWD) 干净
4.3 内核释放
在典型的构建中该 makefile 会被读取两次。
KERNELRELEASE 是在内核源代码的顶层 Makefile 中定义的变量。 第一次(一开始)读取并执行Makefile时,KERNELRELEASE没有定义(空),所以make会在执行完else后读取内容。 如果make的目标是clean,则直接执行clean操作,然后结束。 当make的目标为all时,-C(KDIR)表示跳转到内核源码目录去读取那里的Makefile,M=(PWD)表示返回当前目录继续读取并执行当前的Makefile。 从内核源码目录返回时,KERNELRELEASE已经被定义,kbuild也已经开始解析kbuild句型的句子,make会继续读取else之前的内容(指obj-m:=hello.o) 。 else之前的内容是一句kbuild句型,表示模块源码中各个文件的依赖关系,以及要生成的目标模块的名称。
4.4 对象-m
obj-m :=hello.o 表示编译连接后会生成hello.o模块。
module-objs := file1.o file2.o file3.o 表示 module.o 是由 file1.o、file2.o 和 file3.o 链接生成的。
在modules.txt中提到:
obj-m := .o
kbuild系统将从.c构建.o,
链接后,将生成内核模块 .ko。
上面的行可以放入“Kbuild”文件或“Makefile”中。
当模块是从多个源构建时,需要添加一行
需要列出文件:
-y := .o .o ...
4.5 $(KDIR)
/lib/modules/$(shell uname -r)/build 是内核顶层makefile 所在的路径。
$(shell uname -r):调用shell命令显示内核版本。 在我的系统上它是 4.4.0-109-generic
4.6 kbuild 生成文件
Kbuild系统使用Kbuild Makefile来编译内核或模块。 解析Kernel Makefile后linux内核源码编译安装,Kbuild将读取相关的Kbuild Makefile来编译内核或模块。 Kbuild Makefile 有特定的句型来指定哪些内容编译到内核中,哪些内容编译成模块,以及对应的源文件是什么。 内核和驱动程序开发人员需要编译此 Kbuild Makefile。
目标定义是Kbuild Makefile的主要部分和核心部分。 主要定义了要编译的文件、所有选项以及执行递归操作的子目录。 最简单的 Kbuild makefile 仅包含一行:
示例:obj-y += foo.o
这个例子告诉Kbuild这个目录中有一个名为foo.o的目标文件。 foo.o 将从 foo.c 或 foo.S 文件编译。 如果要将 foo.o 编译成模块,则必须使用 obj-m。 使用的方法如下:
示例: obj-$(CONFIG_FOO) += foo.o
$(CONFIG_FOO) 可以是 y(编译到内核)或 m(编译到模块)。 如果CONFIG_FOO不是y和m,那么该文件将不会被编译和连接。
4.7 -C $KDIR 和 M=$PWD
下面是内核文档/kbuild/modules.txt中介绍的。
-C$KDIR
内核源码所在的目录。
“make”实际上会更改到指定目录
执行时会变回来。
M=$PWD
通知 kbuild 正在构建外部模块。
赋予“M”的值是绝对路径
外部模块(kbuild 文件)所在目录
位于。
4.8 模块
构建外部模块时,仅使用“make”的子集
目标是可用的。
make -C $KDIR M=$PWD [目标]
默认情况下将构建位于当前位置的模块
目录linux内核源码编译安装,因此不需要指定目标。 全部
输出文件也将在此目录中生成。 不
尝试更新内核源代码,这是一个
前提是已经执行了成功的“make”
核心。
模块
外部模块的默认目标。 它有
与未指定目标的功能相同。 看
上面的描述。
模块安装
安装外部模块。 默认位置是
/lib/modules//extra/,但前缀可能
添加 INSTALL_MOD_PATH(第 5 节中讨论)。
干净的
仅删除模块目录中所有生成的文件。
帮助
列出外部模块的可用目标。