ubuntu版本:ubuntu-gnome-16.04-desktop-amd64,gnome版
---------------------------------------------------------------------------------
本文主要介绍如何在内核外编译内核模块,即:
how to build an out-of-tree kernel module.
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. Makefile
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. 编译测试
内核信息如下:
[156596.317933] hello world.
[156604.933930] hello exit!
4. Makefile分析
4.1 多个源文件
如果你有一个模块名为 module.ko, 是来自 多个源文件( 姑且称之为, file1.c 和 file2.c ), 正确的书写应当是:
obj-m := module.o
module-objs := file1.o file2.o
4.2 clean
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
可以换成:
make -C $(KDIR) M=$(PWD) clean
4.3 KERNELRELEASE
这个 makefile 在一次典型的建立中要被读 2 次。
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 obj-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 :=
The kbuild system will build
.o from .c, and, after linking, will result in the kernel module
.ko. The above line can be put in either a "Kbuild" file or a "Makefile."
When the module is built from multiple sources, an additional line is
needed listing the files:
-y := .o .o ...
4.5 $(KDIR)
/lib/modules/$(shell uname -r)/build 是内核top makefile所在路径。
$(shell uname -r):是调用shell命令显示内核版本,在我系统上是4.4.0-109-generic
4.6 kbuild makefile
Kbuild系统使用Kbuild Makefile来编译内核或模块。当Kernel Makefile被解析完成后,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 and M=$PWD
以下在内核文档/kbuild/modules.txt中有介绍。
-C $KDIR
The directory where the kernel source is located.
"make" will actually change to the specified directory
when executing and will change back when finished.
M=$PWD
Informs kbuild that an external module is being built.
The value given to "M" is the absolute path of the
directory where the external module (kbuild file) is
located.
4.8 modules
When building an external module, only a subset of the "make"
targets are available.
make -C $KDIR M=$PWD [target]
The default will build the module(s) located in the current
directory, so a target does not need to be specified. All
output files will also be generated in this directory. No
attempts are made to update the kernel source, and it is a
precondition that a successful "make" has been executed for the
kernel.
modules
The default target for external modules. It has the
same functionality as if no target was specified. See
description above.
modules_install
Install the external module(s). The default location is
/lib/modules/
/extra/, but a prefix may be added with INSTALL_MOD_PATH (discussed in section 5).
clean
Remove all generated files in the module directory only.
help
List the available targets for external modules.