LV06-02-内核模块-03-内核模块的加载
本文主要是内核模块——内核模块的加载的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
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源码 |
一、加载命令
前面我们已经拿到了ko文件,现在我们来学习一下怎么加载ko驱动。先来学习几个命令:
1. file——查看文件格式
file命令可用于查看指定的.ko文件可以用于哪种平台,一般使用格式如下:
1 | file module_name.ko |
输出信息中,带x86字样的适用于主机ubuntu linux,带arm字样的适用于开发板linux。
2. dmesg——内核日志查看
dmesg命令可用于查看内核的打印的提示信息,主要是在Ubuntu中加载模块的使用后使用,我们的开发板会实时在串口终端界面打提示信息,一般使用格式如下:
1 | sudo dmesg -c # 清除内核已打印的信息 |
【注意】在主机Ubuntu中加载模块的时候需要加上sudo。
3. 模块相关的命令
3.1 insmod——加载指定模块
insmod命令可用于加载.ko文件,一般使用格式如下:
1 | insmod module_name.ko |
【注意】在主机Ubuntu中加载模块的时候需要加上sudo。
3.2 lsmod——查看已经加载的模块
lsmod命令可用显示已经加载的模块,一般使用格式如下:
1 | lsmod module_name.ko |
【注意】在主机Ubuntu中加载模块的时候不加sudo,也可以正常使用。
3.3 rmmod——卸载已经加载的模块
rmmod命令可用于卸载已经加载的模块,一般使用格式如下:
1 | rmmod module_name |
【注意】在主机Ubuntu中加载模块的时候需要加上sudo。
4. 加载测试
4.1 获取内核模块文件
上一节我们编译了两个内核模块文件,一个是在内核源码中编译的,另一个是在内核源码外编译的,这两个文件可以在这里找:02_module_load/module_load_test · 苏木/imx6ull-driver-demo - 码云 - 开源中国 (gitee.com)
4.2 拷贝到根文件系统
我们前面配置的开发环境是从nfs挂载根文件系统,所以我们直接把这两个内核模块文件拷贝到根文件系统去就行了,linux系统启动后,我们就可以看到。
1 | mkdir ~/4nfs/imx6ull_rootfs/driver_demo |
我们启动开发板,然后看一下/driver_demo目录:
需要注意的事这里并不是所有的移植完的根文件系统都有tree命令,有的话就能用。另外我发现好像加载有些延迟,拷贝完,linux开发板上还没有这些文件,等待一小会就好了。
4.3 模块测试
注意,两个测试模块名字一样,就不要同时加载了,一个测试完卸载后再加载另一个测试。命令都是一样的。
1 | cd /drivers_demo |
二、modprobe
还有一个很强大的命令就是modprobe,使用它,我们可以不进入驱动所在的目录。
1. 一个错误的解决
1.1 原因分析
还记得前边使用buildroot构建根文件系统的时候,有一个报错:
这里的这个目录起始就是为modprobe这个命令准备的,这个目录中将会存放驱动文件,然后可以不进入此目录,就直接使用modprobe命令加载指定的驱动。
“/lib/modules”这个目录是通用的,不管用的什么板子、什么内核,这部分是一样的。不一样的是这个目录下的子目录,会有一个以内核版本命名的目录,这个目录才是最终存放驱动的目录,例如“/lib/modules/4.19.71”,这里要根据我们所使用的 Linux 内核版本来设置,比如 ALPHA 开发板现在用的是4.19.71 版本的 Linux 内核,因此就是“/lib/modules/4.19.71”。如果使用的其他版本内核,比如 5.14.31,那么就应该创建“/lib/modules/5.14.31”目录,否则 modprobe 命令无法加载驱动模块。
所以我们先自行在根文件系统创建这个目录,这个目录名怎么确定?我们可以看开机打印信息:
或者制作的根文件系统支持uname命令的话也可以使用这个命令来看:
1 | uname -r |
我们手动创建这个目录
1 | mkdir -p /lib/modules/4.19.71-00007-g51f3cd8ec-dirty |
之后就会发现这个报错就会消失,但是又会报另一个错:
可以看出, modprobe 提示无法打开“modules.dep”这个文件,因此驱动挂载失败了。我们不用手动创建 modules.dep 这个文件,直接输入 depmod 命令即可自动生成modules.dep。
1.2 添加depmod命令
有些根文件系统可能没有 depmod 这个命令,如果没有这个命令就只能重新配置busybox了,所以我们可以进入buildroot源码(我用的是这个)中,执行以下命令打开busybox的配置窗口:
1 | cd ~/7Linux/buildroot-2023.05.1 |
这里要加个sudo,不然有个地方会报没有权限,反正我的是这样的。然后就会打开busybox的配置窗口:
我们按以下配置项路径找一下depmod的配置项:
1 | Linux Module Utilities ---> |
勾选这个depmod:
1 | BusyBox 1.36.1 Configuration |
看最后两个配置项,我们应该是可以在这里自定义这个模块默认存放的目录以及modules.dep的默认文件名,不过这里还是默认好了。最后我们保存退出。然后我们执行以下命令重新编译:
1 | sudo make # 在图形界面已经配置过编译工具链了 |
1.3 重启测试
最后重启,就会发现还在报,原来是还需要用这个命令自动生成一些文件,我们直接在终端执行depmod,就会生成下图这些文件:
1 | depmod |
然后我们再重启,应该就不会有这些报错了:
2. 加载驱动
我们前边知道可以用insmod命令加载驱动,但是这个命令有一个缺点,就是驱动若是依赖其他驱动的话,就需要先加载其他依赖的驱动,然后这个驱动才能加载成功,这样是有些麻烦的,而且还需要指定驱动路径或者到驱动存放目录下才能加载。我们可以吧驱动放在上面创建的目录下:
1 | cp drivers_demo/module_load_test/outside /lib/modules/4.19.71-00007-g51f3cd8ec-dirty/ -r |
然后我们直接在根目录执行:
1 | modprobe hello_world_demo.ko |
然后会有以下信息:
从图中可以看出,报错了,第一次并未加载成功,这个时候我们需要执行一次depmod命令重新生成一下那几个文件:
1 | ls /lib/modules/`uname -r` -F |
更新完毕后重新加载就可以了:
重新生成后,就可以成功加载驱动啦。
3. 命令说明
上面我们已经把问题解决,也体验了这个命令,接下来我们来学习一下这个命令的使用。
3.1 命令格式
我们可以直接在ubuntu下看一下man手册:
1 | NAME |
modprobe会从linux内核中智能地添加或者移除模块。modprobe需要一个实时更新的 modules.dep 文件,这个文件位于这里:
1 | /lib/modules/`uname -r`/modules.dep |
这个文件由 depmod 命令生成。这个文件列出了每个模块还需要依赖哪些其他的模块。 modprobe利用这个文件来自动解决添加和删除模块时候的依赖关系。我们可以看一下bulidroot创建的根文件系统中这个命令的手册:
1 | BusyBox v1.36.1 (2024-11-02 20:28:53 CST) multi-call binary. |
modprobe 命令在加载驱动模块的时候, 会同时加载该模块依赖的其他模块。 比如 helloworld.ko 依赖 before.ko,使用 insmod 加载的时候, 就必须先加载 before.ko, 然后在加载 helloworld.ko 才可以加载成功。 但是使用 modprobe 加载的时候, 他会自动分析模块的依赖关系, 然后将所有的依赖的模块都加载到内核当中。 比较“聪明” 。
同样, 在卸载驱动模块的时候, 如果模块存在依赖关系, 如果使用 insmod 命令, 需要手动卸载依赖的内核模块, 但是使用 modprobe 命令可以自动卸载驱动模块所依赖的其他模块。所以, 如果驱动模块是以“modprobe helloworld.ko”命令加载的, 卸载的时候使用“modprobe -r helloworld.ko” 命令卸载。但是使用 modprobe 卸载存在一个问题, 如果所依赖的模块被其他模块所使用, 这时候就不能使用 modprobe 卸载。 所以还是推荐使用 rmmod 命令来卸载。
3.1 modprobe -l
这个命令可以列出内核中所有已经或者未挂载的所有模块
1 | modprobe -l |
3.2 modprobe -r
前面我们知道了怎么加载驱动,怎么卸载呢?可以用这个命令:
1 | modprobe -r MODULE |
移除已加载的模块,和 rmmod 功能类似。注意:模块名是不能带有后缀的,我们通过 modprobe -l
所看到的模块,都是带有 .ko
或 .o
后缀。前面已经说了,当要卸载的模块依赖于别的模块的时候可能就会有问题,所以卸载的时候其实不是很常用这个命令,主要还是用的rmmod。
3.3 modprobe -D
这个命令是显示依赖:
1 | modprobe -D MODULE |
4. 搜索目录
在Linux中,modprobe命令会在下面的路径搜索可加载的内核模块:
1 | /lib/modules/`uname -r` #(内核模块所在的默认目录) |
uname -r
为当前系统所使用的内核版本。当我们使用modprobe命令加载一个内核模块时,系统首先会在上面的路径中搜索是否存在该模块,若存在,系统会将其加载到内存中。若以上三个路径都没有找到该模块,则系统会返回“找不到该模块”的错误。
我们可以通过modprobe配置文件来设置搜索路径。Linux中modprobe的配置文件为/etc/modprobe.conf和/etc/modprobe.d/,其中/etc/modprobe.conf为全局配置文件,而/etc/modprobe.d/为目录,其中存放的是单独的模块配置文件。
在打开的modprobe.conf文件中,我们可以设定模块搜索路径,例如:
1 | install 模块名 路径 |
其实在前面打开depmod命令的时候,我们看到是可以直接在buildroot中配置目录的:
这里我就没做具体尝试了,感觉目前好像也不是很重要,后面有需要再补充吧。
参考资料