Skip to content

LV015-怎么加载

一、加载命令

前面我们已经拿到了 ko 文件,现在我们来学习一下怎么加载 ko 驱动。先来学习几个命令:

1. file——查看文件格式

file 命令可用于查看指定的.ko 文件可以用于哪种平台,一般使用格式如下:

shell
file module_name.ko

输出信息中,带 x86 字样的适用于主机 ubuntu linux,带 arm 字样的适用于开发板 linux。

image-20241117163610305

2. dmesg——内核日志查看

dmesg 命令可用于查看内核的打印的提示信息,主要是在 Ubuntu 中加载模块的使用后使用,我们的开发板会实时在串口终端界面打提示信息,一般使用格式如下:

shell
sudo dmesg -c  # 清除内核已打印的信息
dmseg          # 查看内核的打印信息

3. 模块相关的命令

3.1 insmod——加载指定模块

insmod 命令可用于加载.ko 文件,一般使用格式如下:

shell
insmod module_name.ko

3.2 lsmod——查看已经加载的模块

lsmod 命令可用显示已经加载的模块,一般使用格式如下:

shell
lsmod module_name.ko

3.3 rmmod——卸载已经加载的模块

rmmod 命令可用于卸载已经加载的模块,一般使用格式如下:

shell
rmmod module_name

4. 加载测试

4.1 获取内核模块文件

上一节我们编译了两个内核模块文件,一个是在内核源码中编译的,另一个是在内核源码外编译的,这两个文件可以在这里找:02_module_load/module_load_test

image-20260117113309333

4.2 拷贝到根文件系统

我们前面配置的开发环境是从 nfs 挂载根文件系统,所以我们直接把这两个内核模块文件拷贝到根文件系统去就行了,linux 系统启动后,我们就可以看到。

shell
mkdir ~/4nfs/imx6ull_rootfs/driver_demo
cp -rvf module_load_test ~/4nfs/imx6ull_rootfs/drivers_demo/

我们启动开发板,然后看一下/driver_demo 目录:

image-20241117165539155

需要注意的事这里并不是所有的移植完的根文件系统都有 tree 命令,有的话就能用。另外我发现好像加载有些延迟,拷贝完,linux 开发板上还没有这些文件,等待一小会就好了。

4.3 模块测试

注意,两个测试模块名字一样,就不要同时加载了,一个测试完卸载后再加载另一个测试。命令都是一样的。

shell
cd /drivers_demo

insmod hello_world_demo.ko # 加载模块
lsmod                      # 查看已加载模块
rmmod hello_world_demo     # 卸载已加载的模块
image-20241117171145909

二、modprobe

还有一个很强大的命令就是 modprobe,使用它,我们可以不进入驱动所在的目录。

1. 一个错误的解决

1.1 原因分析

还记得前边使用 buildroot 构建根文件系统的时候,有一个报错:

image-20241117171806402

这里的这个目录起始就是为 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 命令无法加载驱动模块。

所以我们先自行在根文件系统创建这个目录,这个目录名怎么确定?我们可以看开机打印信息:

image-20241117171932832

或者制作的根文件系统支持 uname 命令的话也可以使用这个命令来看:

shell
uname -r
image-20241117172034406

我们手动创建这个目录

shell
mkdir -p /lib/modules/4.19.71-00007-g51f3cd8ec-dirty

之后就会发现这个报错就会消失,但是又会报另一个错:

image-20241117172158173

可以看出, modprobe 提示无法打开“modules.dep”这个文件,因此驱动挂载失败了。我们不用手动创建 modules.dep 这个文件,直接输入 depmod 命令即可自动生成 modules.dep。

1.2 添加 depmod 命令

有些根文件系统可能没有 depmod 这个命令,如果没有这个命令就只能重新配置 busybox 了,所以我们可以进入 buildroot 源码(我用的是这个)中,执行以下命令打开 busybox 的配置窗口:

shell
cd ~/7Linux/buildroot-2023.05.1
sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- busybox-menuconfig

这里要加个 sudo,不然有个地方会报没有权限,反正我的是这样的。然后就会打开 busybox 的配置窗口。我们按以下配置项路径找一下 depmod 的配置项:

shell
Linux Module Utilities  ---> 
	[*] depmod (27 kb)

勾选这个 depmod:

shell
 BusyBox 1.36.1 Configuration
 ──────────────────────────────────────────────────────────────────────────────
  ┌──────────────────────── Linux Module Utilities ─────────────────────────┐
  Arrow keys navigate the menu.  <Enter> selects submenus --->.  
  Highlighted letters are hotkeys.  Pressing <Y> includes, <N> excludes,  
  <M> modularizes features.  Press <Esc><Esc> to exit, <?> for Help, </>  
  for Search.  Legend: [*] built-in  [ ] excluded  <M> module  < >
 ┌─────────────────────────────────────────────────────────────────────┐  
 │[ ] Simplified modutils  
 │[*] depmod (27 kb)                                                   │ │  
 │[*] insmod (22 kb)                                                   │ │  
 │[*] lsmod (1.9 kb)                                                   │ │  
 │[*]   Pretty output  
 │[ ] modinfo (24 kb)                                                  │ │  
 │[*] modprobe (28 kb)                                                 │ │  
 │[ ]   Blacklist support  
 │[*] rmmod (3.3 kb)                                                   │ │  
 │--- Options common to multiple modutils  
 │[*] Accept module options on modprobe command line  
 │[ ] Support version 2.2/2.4 Linux kernels  
 │[*] Support tainted module checking with new kernels  
 │[ ] Try to load module from a mmap'ed area                           │ │  
  │ │[*] Support module.aliases file                                      │ │  
  │ │[*] Support module.symbols file                                      │ │  
  │ │(/lib/modules) Default directory containing modules                  │ │  
  │ │(modules.dep) Default name of modules.dep                            │ │  
  │ └─────────────────────────────────────────────────────────────────────┘ │  
  ├─────────────────────────────────────────────────────────────────────────┤  
  │                    <Select>    < Exit >    < Help >                     │  
  └─────────────────────────────────────────────────────────────────────────┘

看最后两个配置项,我们应该是可以在这里自定义这个模块默认存放的目录以及 modules.dep 的默认文件名,不过这里还是默认好了。最后我们保存退出。然后我们执行以下命令重新编译:

shell
sudo make # 在图形界面已经配置过编译工具链了

1.3 重启测试

最后重启,就会发现还在报,原来是还需要用这个命令自动生成一些文件,我们直接在终端执行 depmod,就会生成下图这些文件:

shell
depmod
image-20241117174639337

然后我们再重启,应该就不会有这些报错了:

2. 加载驱动

我们前边知道可以用 insmod 命令加载驱动,但是这个命令有一个缺点,就是驱动若是依赖其他驱动的话,就需要先加载其他依赖的驱动,然后这个驱动才能加载成功,这样是有些麻烦的,而且还需要指定驱动路径或者到驱动存放目录下才能加载。我们可以吧驱动放在上面创建的目录下:

shell
cp drivers_demo/module_load_test/outside /lib/modules/4.19.71-00007-g51f3cd8ec-dirty/ -r

然后我们直接在根目录执行:

shell
modprobe hello_world_demo.ko

然后会有以下信息:

image-20241117174936411

从图中可以看出,报错了,第一次并未加载成功,这个时候我们需要执行一次 depmod 命令重新生成一下那几个文件:

shell
ls /lib/modules/`uname -r` -F
image-20241117175752872

更新完毕后重新加载就可以了:

image-20241117175015311

重新生成后,就可以成功加载驱动啦。

3. 命令说明

上面我们已经把问题解决,也体验了这个命令,接下来我们来学习一下这个命令的使用。

3.1 命令格式

我们可以直接在 ubuntu 下看一下 man 手册:

shell
NAME
       modprobe - Add and remove modules from the Linux Kernel

SYNOPSIS
       modprobe [-v] [-V] [-C config-file] [-n] [-i] [-q] [-b] [modulename] [module parameters...]

       modprobe [-r] [-v] [-n] [-i] [modulename...]

       modprobe [-c]

       modprobe [--dump-modversions] [filename]

modprobe 会从 linux 内核中智能地添加或者移除模块。modprobe 需要一个实时更新的 modules.dep 文件,这个文件位于这里:

shell
/lib/modules/`uname -r`/modules.dep

这个文件由 depmod 命令生成。这个文件列出了每个模块还需要依赖哪些其他的模块。 modprobe 利用这个文件来自动解决添加和删除模块时候的依赖关系。我们可以看一下 bulidroot 创建的根文件系统中这个命令的手册:

shell
BusyBox v1.36.1 (2024-11-02 20:28:53 CST) multi-call binary.

Usage: modprobe [-alrqvsD] MODULE [SYMBOL=VALUE]...

        -a      Load multiple MODULEs
        -l      List (MODULE is a pattern)
        -r      Remove MODULE (stacks) or do autoclean
        -q      Quiet
        -v      Verbose
        -s      Log to syslog
        -D      Show dependencies

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

这个命令可以列出内核中所有已经或者未挂载的所有模块

shell
modprobe -l
image-20241117180953845

3.2 modprobe -r

前面我们知道了怎么加载驱动,怎么卸载呢?可以用这个命令:

shell
modprobe -r MODULE

移除已加载的模块,和 rmmod 功能类似。注意:模块名是不能带有后缀的,我们通过 modprobe -l 所看到的模块,都是带有 .ko.o 后缀。前面已经说了,当要卸载的模块依赖于别的模块的时候可能就会有问题,所以卸载的时候其实不是很常用这个命令,主要还是用的 rmmod。

3.3 modprobe -D

这个命令是显示依赖:

shell
modprobe -D MODULE
image-20241117181338149

4. 搜索目录

在 Linux 中,modprobe 命令会在下面的路径搜索可加载的内核模块:

shell
/lib/modules/`uname -r` #(内核模块所在的默认目录)

uname -r 为当前系统所使用的内核版本。当我们使用 modprobe 命令加载一个内核模块时,系统首先会在上面的路径中搜索是否存在该模块,若存在,系统会将其加载到内存中。若以上三个路径都没有找到该模块,则系统会返回“找不到该模块”的错误。

我们可以通过 modprobe 配置文件来设置搜索路径。Linux 中 modprobe 的配置文件为/etc/modprobe.conf 和/etc/modprobe.d/,其中/etc/modprobe.conf 为全局配置文件,而/etc/modprobe.d/为目录,其中存放的是单独的模块配置文件。

在打开的 modprobe.conf 文件中,我们可以设定模块搜索路径,例如:

shell
install 模块名 路径

其实在前面打开 depmod 命令的时候,我们看到是可以直接在 buildroot 中配置目录的:

image-20241117182244699

这里我就没做具体尝试了,感觉目前好像也不是很重要,后面有需要再补充吧。