LV06-02-内核模块-02-内核模块的编译
本文主要是内核模块——内核模块编译的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
PC端开发环境 | Windows | Windows11 |
Ubuntu | Ubuntu20.04.2的64位版本 | |
VMware® Workstation 17 Pro | 17.6.0 build-24238078 | |
终端软件 | MobaXterm(Professional Edition v23.0 Build 5042 (license)) | |
Win32DiskImager | Win32DiskImager v1.0 | |
Linux开发板环境 | Linux开发板 | 正点原子 i.MX6ULL Linux 阿尔法开发板 |
uboot | NXP官方提供的uboot,使用的uboot版本为U-Boot 2019.04 | |
linux内核 | linux-4.19.71(NXP官方提供) |
点击查看本文参考资料
分类 | 网址 | 说明 |
官方网站 | https://www.arm.com/ | ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档 |
https://www.nxp.com.cn/ | NXP官方网站 | |
https://www.nxpic.org.cn/ | NXP 官方社区 | |
https://u-boot.readthedocs.io/en/latest/ | u-boot官网 | |
https://www.kernel.org/ | linux内核官网 |
点击查看相关文件下载
分类 | 网址 | 说明 |
NXP | https://github.com/nxp-imx | NXP imx开发资源GitHub组织,里边会有u-boot和linux内核的仓库 |
nxp-imx/linux-imx/releases/tag/v4.19.71 | NXP linux内核仓库tags中的v4.19.71 | |
nxp-imx/uboot-imx/releases/tag/rel_imx_4.19.35_1.1.0 | NXP u-boot仓库tags中的rel_imx_4.19.35_1.1.0 | |
I.MX6ULL | i.MX 6ULL Applications Processors for Industrial Products | I.MX6ULL 芯片手册(datasheet,可以在线查看) |
i.MX 6ULL Applications ProcessorReference Manual | I.MX6ULL 参考手册(下载后才能查看,需要登录NXP官网) | |
Source Code | https://elixir.bootlin.com/linux/latest/source | linux kernel源码 |
https://elixir.bootlin.com/u-boot/latest/source | uboot源码 |
一、内核模块怎么编写?
这里我们以一个最简单的字符设备为例,先来了解一下大概的流程,后续再继续详细学习。
1. 模块源文件
我们需要编写一个模块源文件用于测试:
1 | cd ~/7Linux/imx6ull-kernel |
可以看到这里有很多的源文件:
我们就在这里新建hello_world_demo.c源文件,并添加以下内容:
1 |
|
2. Kconfig修改
此步骤可以向linux的图形配置界面添加新功能配置选项。
1 | cd driver/char/ # 进入hello_world_demo.c的同级目录 |
找到如下语句:
1 | source "drivers/tty/Kconfig" |
在该语句下边添加以下内容:
1 | config MY_HELLO |
然后保存退出即可,我们可以试一下有什么效果,我们回到顶层源码目录下,打开图形配置界面:
1 | cd ~/7Linux/imx6ull-kernel |
我们按以下层级找到我们新添加的配置项:
1 | Device Drivers ---> |
如下图所示:
我们在这里,光标移动到新增加的配置项,按空格键,前面尖括号中的符号就会发生改变,*
表示编译进内核,M
表示编译成内核模块。这个分别对应两种加载方式,后面我们再讨论。
3. Makefile修改
我们添加了新源码文件,自然要参与编译的,我们打开hello_world_demo.c同级目录下的Makefile:
1 | vim drivers/char/Makefile |
我么在这里面添加一句:
1 | obj-$(CONFIG_MY_HELLO) += hello_world_demo.o |
【注意】这里需要是CONFIG_xxx
,这个xxx
就是上边修改Kconfig
的时候添加的config MY_HELLO
的MY_HELLO
。这样的话后面我们需要在内核的图形配置界面打开这个模块,才能进行编译。
二、模块的编译
模块的编译有两种方式:一种是直接编译进内核,这样就只会生成一个中间文件.o
,另一种是编译成单独的内核模块,会生成一个.ko
文件。
1. 编译进内核
1.1 配置编译方式
这个我们在kernel的图形配置界面进行配置,我们这里打开linux
内核图形配置界面:
1 | cd ~/7Linux/imx6ull-kernel |
然后就会打开图形配置界面,我们按照下面的配置项路径找到刚才新增的配置项,选中后按空格键,将新功能前面尖括号的标识改为*
,这就表示将新功能编译到内核中:
1 | Device Drivers ---> |
效果是这样的:
1 | .config - Linux/arm 4.19.71 Kernel Configuration |
然后我们选择保存,最后退出即可。然后我们可以看一下.config文件的变化:
1.2 编译内核源码
我们不需要清空之前的配置文件,直接重新编译即可,注意这里不要执行这个使用默认配置文件的命令:
1 | make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_alpha_emmc_defconfig |
若是执行了,我们前面的配置就会被清空,需要重新在图形界面配置。
1 | make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean # 这个清理的命令可以不执行 |
编译完毕后如下:
我们看一下drivers/char目录,可以看到这里只生成了一个.o文件:
1.3 启动测试
因为这个是直接编译进内核源码的,在启动的时候我们不需要做任何操作,不需要像后面独立模块一样需要用一些加载命令去加载,内核就会自动加载这个新功能,这里我们可以直接试一下:
1 | cp arch/arm/boot/zImage ~/3tftp/ #将编译出来带有hello_world_demo功能的镜像拷贝到tftp服务器 |
然后我们启动开发板,之前设置的从tftp下载zImage,所以这里我们直接拷贝到tftp服务器目录下即可,启动后我们可以看到打印日志:
2. 编译成独立模块
新功能源码与内核其它源码不一起编译,而是独立编译成内核的插件(被称为内核模块)文件.ko
。然后可以通过一些命令进行插入或者卸载这个内核模块。
这种编译方式又有两种形式,主要是根据我们的模块源码位置来区分,我们可以在linux内核源码中编写模块的源码然后编译,这种方式显然不利于我们对多个模块源码的管理。内核模块的源码可以在linux内核源码外,通过调用内核源码相关的内容完成对独立模块的编译,第二种显然更利于我们对驱动模块源码的管理。接下来我们就来看一看这两种编译方式怎么实现。
2.1 模块源码在linux内核源码中
2.1.1 配置编译方式
我们还是使用上边的源文件hello_world_demo.c,该文件还是包含在linux
内核源码中,这一次我们配置编译方式为另外一种:
修改新功能编译方式
1 | cd ~/7Linux/imx6ull-kernel |
然后将新功能前边配置改为M
,表示编译成独立的模块:
1 | Device Drivers ---> |
最后效果是这样的:
1 | .config - Linux/arm 4.19.71 Kernel Configuration |
我们来看一下配置文件.config的变化:
2.1.2 编译内核
这里我们需要重新编译内核,主要是去掉之前内核镜像中带的hello_world_demo模块,到后面编译成独立模块的时候,就不需要吧镜像完整的编译一遍了。
1 | cd ~/7Linux/imx6ull-kernel # 回到顶层源码目录下 |
用重新编译过的这个镜像的话,再在开发板上启动就不会有我们demo中打印的相关信息了。
2.1.3 编译模块
接下来我们编译模块:
1 | cd ~/7Linux/imx6ull-kernel # 回到顶层源码目录下 |
不出意外的话我们应该可以看到以下打印信息:
会发现这里是在生成各种ko文件。我们自己的模块呢?这里也有:
我们来看一下drivers/char目录:
会发现这里生成了一个ko文件,这就是我们最后要的独立模块。
2.2 模块源码独立在其他目录
这样每次都要使用 linux 源码目录下操作,文件也太多了,而且,这样会把所有的内核模块都编译一遍,还是很麻烦的。我们现在尝试在其他的目录单独创建模块驱动源码文件,然后调用linux
源码进行编译,生成我们所需要的.ko
内核模块文件。
注意:这种方式就不需要linux的图形界面的各种配置了。
2.2.1 源码编写
- (1)新建一个目录用于存放我们的模块源码
1 | mkdir ~/imx6ull-driver-demo/02_module_load/out_of_source # 新建一个目录 |
这里随便自定义一个目录就是了,都一样的。
- (2)编写模块源码文件
这里我直接拷贝之前的文件到这里来:
1 | cp ~/7Linux/imx6ull-kernel/drivers/char/hello_world_demo.c . |
2.2.2 Makefile文件编写
点击查看 Makefile 文件内容
1 | # 模块名和模块测试APP名称 |
2.2.3 编译模块
1 | make # 编译模块 |
不出意外的话应该会有这些提示信息出现:
然后我们看一下当前目录下都有哪些文件:
发现这里生成了一堆的中间文件,同样生成了我们所需要的ko文件。