上一篇文章介绍了 Linux 驱动编程需要了解的基础知识: 这篇文章来介绍一下,如何构建一个驱动模块。构建一个模块,可以在两个地方完成:
除此之外,还需要编写相应的构建脚本文件 makefile。 接下来,我们逐步进行介绍。 模块构建的makefilemakefile 是用来执行一组操作的特殊文件,其中最重要的操作是程序的编译。专用工具 make 用于解析makefile。 在说明整个make文件之前,先介绍一下 obj-<X> kbuild 变量。<X> 可以取值为 y、m、n或者空白。 例如: obj-y += mymodule.o 告诉 kbuild 在当前目录中有一个名为 mymodule.o 的对象。mymodule.o 将从 mymodule.c 或 mymodule.S 构建。<X> 的值决定了如何构建以及是否构建或链接 mymodule.o
如果后边跟着是某个目录,例如 obj-<X> += onedir/ kbuild 应该进入 onedir 目录,查找其中所有的 makefile 并处理它们,从而决定应该构建哪些对象。 一份完整的模块构建Makefile 示例: KERNELDIR ?= /lib/modules/$(shell uname -r)/build obj-m := helloworld.o all: $(MAKE) -C $(KERNELDIR) M=$(shell pwd) modules; clean: $(MAKE) -C $(KERNELDIR) M=$(shell pwd) clean;
内核树内的模块在内核树中构建驱动程序,需要把驱动程序的代码文件放在特定的目录中。驱动程序中的每个子目录都有 makefile 和 kconfig。 例如,驱动文件 mychardev.c 为字符驱动程序源码,则应该把他放在内核源码的 drivers/char 目录中。 一个 kconfig 的示例文件如下: config PACKT_MYCDEV 同时,在这个目录下的 makefile 文件中添加一下语句: obj-$(CONFIG_PACKT_MYCDEV) += mychardev.o 注意,.o 文件名称必须与 .c 文件名完全一致。 配置完成后,可以分别使用 make 和 make modules 构建内核和模块。 内核源码树中包含的模块安装在 内核树外的模块在构建外部模块之前,需要有一个完整的、预编译的内核源代码树。内核源码树版本必须与将加载和使用模块的内核相同。 有两种方法可以获得预构建的内核版本:
sudo apt-get update 这将只安装头文件,而不是整个源代码树。 头文件将被安装在 有一个符号链接 以上内容准备完成之后,就可以进行构建驱动模块。 构建模块处理完 makefile 后,只需要切换到源码目录并运行 make 命令,即可开始构建模块。 一个简单模块程序 helloworld.c #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> /* 模块入口点函数 */ static int helloworld_init(void) { pr_info('Hello world!\n'); return 0; } /* 模块出口点函数 */ static void helloworld_exit(void) { pr_info('End of the world\n'); } /* 指定函数用途 */ module_init(helloworld_init); module_exit(helloworld_exit); MODULE_AUTHOR('zsky'); MODULE_LICENSE('GPL'); 构建脚本文件 Makefile KERNELDIR ?= /lib/modules/`uname -r`/build 开始构建: $ make make -C /lib/modules/`uname -r`/build M=/home/user/learn/drivers/chap2 modules; make[1]: Entering directory '/usr/src/linux-headers-5.15.0-73-generic' CC [M] /home/user/learn/drivers/chap2/helloworld.o MODPOST /home/user/learn/drivers/chap2/Module.symvers CC [M] /home/user/learn/drivers/chap2/helloworld.mod.o LD [M] /home/user/learn/drivers/chap2/helloworld.ko BTF [M] /home/user/learn/drivers/chap2/helloworld.ko Skipping BTF generation for /home/user/learn/drivers/chap2/helloworld.ko due to unavailability of vmlinux make[1]: Leaving directory '/usr/src/linux-headers-5.15.0-73-generic' 构建完成后,在当先目录下会生成一些文件,其中 交叉编译 上面的例子使用的是本地构建,在 x86 机器上为x86 机器编译。交叉编译怎么实现? 这个过程是在机器 A(称为宿主机)上编译,该代码要运行在机器 B(称为目标机)上;宿主机和目标机具有不同的体系结构。 常见的交叉编译是在 x86 机器上构建的代码要运行在 ARM 架构上。交叉编译内核模块时,构建 makefile 需要指定两个变量:ARCH 和 CROSS_COMPILE,它们分别表示目标体系结构和编译器的前缀名称。 因此,内核模块本地编译和交叉编译之间的差别在于 makefile 构建文件。 另外,还需要一份目标机器正在使用的内核源码,编译模块的时候需要用到。 模块装载和卸载模块构建完成后,可以通过 $ sudo insmod helloworld.ko 指令执行完,看不到任何信息。 加载内核模块,模块的打印信息需要通过 卸载模块,通过 sudo rmmod helloworld 同样出口函数打印的信息, 另外,用 $ modinfo helloworld.ko
|
|