在做Linux
驱动开发及调试的过程中,经常会遇到需要手动修改及编译驱动源代码、加载编译出来的oot
驱动的情况。刚开始做Linux
驱动开发的时候,老员工告诫我在调试驱动的时候,最好用insmod
来加载自己编译的oot
驱动,这样既方便又安全。
为什么这么说呢?因为我在用modprobe nfp
加载自己编译的oot
驱动时,每次发现其加载的驱动文件均为/lib/modules/$(uname -r)/kernel/drivers/net/ethernet/netronome/nfp/
文件夹下面由内核自带的in-tree
驱动文件,而并非我手动编译出来的oot
驱动文件。于是,查询了一下modprobe
用法,发现modprobe
默认会去/lib/modules/$(uname -r)/
下面查找待加载的module
,因此,就想到将/lib/modules/$(uname -r)/kernel/drivers/net/ethernet/netronome/nfp/
下面的驱动文件每次替换为自己编译出来的oot
驱动文件,这样就可以成功加载自己编译的驱动了。
于是,就对/lib/modules/$(uname -r)/kernel/drivers/net/ethernet/netronome/nfp/
路径下的in-tree
驱动文件备份,并用我手动编译出来的驱动文件替换该路径下的驱动文件。本以为这种方式很nice
,结果老员工却告诉我当我编译出来的oot
驱动文件有bug
的时候,即使重新启动系统,原本的驱动也无法恢复,导致设备工作异常;因为该路径下原有的in-tree
驱动文件已不存在,而使用insmod
直接加载我编译出来的oot
驱动时,即使该驱动文件有bug
,系统重启后,依然可以通过使用/lib/modules/$(uname -r)/kernel/drivers/net/ethernet/netronome/nfp/
路径下的in-tree
驱动文件正常启动系统。
想一想,好像确实是这个道理,于是,在日后的工作中,但凡加载自己手动编译的oot
驱动,我都是使用insmod
命令。但是,后续的工作中,又遇到了一个问题,就是使用insmod
加载的驱动,在系统重新启动后,又变成了系统自带的in-tree
驱动。于是,只能再次手动卸载in-tree
驱动,再加载oot
驱动。如果该主机上部署了与该oot
驱动模块紧密相关的业务环境,意味着一切将需要重新手动设置一遍,不可谓不麻烦。于是乎,系统重启后自动加载oot
驱动就成了一个亟待解决的问题。
因此,遂考虑可能是因为使用insmod
加载驱动的缘故,导致系统重新启动后无法自动加载oot
驱动。于是,便研究如何在工作中通过modprobe
来加载oot
驱动。
注意:其实导致系统重新启动后无法自动加载
oot
驱动,与使用insmod
或modprobe
加载没有关系,后续内容会讲解。
【拓展】:模块依据代码编写与编译时的位置可分:内部模块和外部模块,即
in-tree
模块 和out-of-tree
(即oot
)模块,在内核树外部编写并构建的模块就是外部模块。动态加载的模块包括in-tree
模块和out-of-tree
(oot
)模块。in-tree
模块是Linux
内核树的内部自带的模块,即它们已经是内核的一部分。树外模块是 来自Linux
内核树的外部。它们通常是为开发和测试目的编写的,例如测试树级或处理不兼容的内核模块的新版本。往往oot
模块虽然安装成功了,但是会提示loading out-of-tree module taints kernel
,可通过dmesg
查看到。
经过针对 modprobe
的研究和实验,本文总结三种方法:拷贝法、软链接法和配置 external
路径法。
注意:本文所有实验均在
CentOS Stream 8
系统上进行,一切配置和命令仅适用于CentOS
系列机器,Ubuntu
与其他机器具体配置指令可能有所区别。
这里有关modprobe
的使用总结的三种方法,均需要依赖/etc/depmod.d/
下的配置文件来实现自动识别驱动加载路径。/etc/depmod.d/
下通常会含有如下配置文件:
[root@localhost ~]# ls -l /etc/depmod.d/
total 8
-rw-r--r--. 1 root root 116 Jun 5 2021 dist.conf
-rw-r--r--. 1 root root 115 Nov 10 2021 kvdo.conf
接下来,让我们一起了解一下有关使用modprobe
的三种方法。
将自己编译生成的oot
驱动文件拷贝到/lib/modules/$(uname -r)/extra/
目录下,再执行echo "override xxx * extra" > /etc/depmod.d/xxx.conf
命令,确保当Linux
内核中存在多个同名的内核模块时,优先安装/lib/modules/$(uname -r)/extra/
目录下的文件。随后,再执行depmod -a
进行模块依赖的更新(驱动如果存在依赖项,必须先加载依赖项后才能进行驱动的安装)。
注意:
echo "override xxx * extra" > /etc/depmod.d/xxx.conf
命令表示在/etc/depmod.d/
目录下创建一个xxx.conf
(名字任意)文件,在里面添加override xxx * extra
(其中,xxx
表示待加载的驱动名字(不带.ko
后缀),*
表示匹配任何内核版本,也可以指定具体的内核版本),通过/etc/depmod.d
目录内的配置文件,设置override
命令,确保安装在/lib/modules/$(uname -r)/extra/
(或其他模块位置)下的任何匹配模块名称将优先于内核已经提供的任何类似名称的模块。外部开发的模块一般我们都放在/lib/modules/$(uname -r)/extra
目录下使用。
【拓展】:
depmod
配置文件的override
命令格式和解析如下:
override modulename kernelversion modulesubdirectory
This command allows you to override which version of a specific module will be used when more than one module sharing the same name is processed by the depmod command. It is possible to specify one kernel or all kernels using the * wildcard. modulesubdirectory is the name of the subdirectory under /lib/modules (or other module location) where the target module is installed. For example, it is possible to override the priority of an updated test module called kmod by specifying the following command: "override kmod * extra". This will ensure that any matching module name installed under the extra subdirectory within /lib/modules (or other module location) will take priority over any likenamed module already provided by the kernel.
实战演示如下:
# 将自己编译的oot nfp驱动拷贝到/lib/modules/$(uname -r)/extra/路径下
[root@localhost ~]# cp /home/xxx/nfp-drv-kmods-private/src/nfp.ko /lib/modules/$(uname -r)/extra/nfp.ko
[root@localhost ~]# ls -l /lib/modules/$(uname -r)/extra
total 50900
-rw-r--r--. 1 root root 52121240 Nov 15 19:49 nfp.ko
# 如果/etc/下不存在depmod.d目录,则执行如下命令创建depmod.d目录,否则无需执行
[root@localhost ~]# mkdir -p /etc/depmod.d
# 该命令确保在depmod命令处理多个共享相同名称的内核模块时,优先安装/lib/modules/$(uname -r)/extra/下的模块
[root@localhost ~]# echo "override nfp * extra" > /etc/depmod.d/nfp.conf
# 探测所有模块。如果在命令行中没有给出文件名,则默认启用此选项。
[root@localhost ~]# depmod -a
# depmod创建一个模块依赖列表,并确定它导出了什么符号以及它需要什么符号。默认情况下,该列表被写入modules.dep
[root@localhost ~]# cat /lib/modules/$(uname -r)/modules.dep | grep nfp
extra/nfp.ko:
[root@localhost ~]#
# 系统自带in-tree nfp驱动,显示大小为442368字节
[root@localhost ~]# lsmod | grep nfp
nfp 442368 0
tls 110592 1 nfp
# 卸载系统自带的in-tree nfp驱动
[root@localhost ~]# rmmod nfp
# 直接modprobe nfp就会加载手动编译的oot nfp驱动
[root@localhost ~]# modprobe nfp
# 手动编译的oot nfp驱动,显示大小为585728字节
[root@localhost ~]# lsmod | grep nfp
nfp 585728 0
# 将depmod -a更新后的结果保存到initramfs中,这一步可选
[root@localhost ~]# dracut -f
根据上面的结果,可以看出来拷贝法需要将手动编译的oot
驱动复制到/lib/modules/$(uname -r)/extra
目录下,创建depmod
的配置文件,即/etc/depmod.d/nfp.conf
,使用depmod
创建模块依赖列表,再使用modprobe
加载驱动的时候,就可以成功安装我们手动编译的oot
驱动了。这里之所以将oot
驱动复制到extra
目录下,是为了更新依赖列表后就可以使用modprobe
成功安装,因为执行了echo "override nfp * extra" > /etc/depmod.d/nfp.conf
这一步,override
命令确保安装在 /lib/modules/$(uname -r)/extra/
下的nfp
驱动将优先于内核已经提供的任何同名的模块。
上面实战演示中的dracut -f
是可选的步骤,可以执行,也可以不执行。具体作用后面再详细介绍。
软链接法与拷贝法比较相似,区别仅在于将自己编译生成的 oot
驱动文件通过建立软链接链接到 /lib/modules/$(uname -r)/extra/
目录下,而非拷贝到 extra
目录下,其余步骤与拷贝法基本相同。
注意:
uname -r
是获取系统当前使用的内核版本,不同内核版本有各自的内核目录。
实战演示如下:
[root@localhst ~]# rm -f /lib/modules/$(uname -r)/extra/nfp.ko
[root@localhost ~]# ls -l /lib/modules/$(uname -r)/extra
total 0
# 建立软链接
[root@localhost ~]# ln -sf /home/xxx/nfp-drv-kmods-private/src/nfp.ko /lib/modules/$(uname -r)/extra/nfp.ko
[root@localhost ~]# ls -l /lib/modules/$(uname -r)/extra
total 0
lrwxrwxrwx. 1 root root 42 Nov 16 12:20 nfp.ko -> /home/xxx/nfp-drv-kmods-private/src/nfp.ko
# 因拷贝法中已创建并配置该文件,因此,这里直接使用即可。否则,需要创建并配置该文件内容如下
[root@localhost ~]# cat /etc/depmod.d/nfp.conf
override nfp * extra
# 创建依赖列表
[root@localhost ~]# depmod -a
# 卸载掉之前安装的驱动模块
[root@localhost ~]# rmmod nfp
# 安装手动编译的oot nfp驱动
[root@localhost ~]# modprobe nfp
# 查看新加载的oot nfp驱动
[root@localhost ~]# lsmod | grep nfp
nfp 585728 0
# 将depmod -a更新后的结果保存到initramfs中,这一步可选
[root@localhost ~]# dracut -f
根据上面的结果,可以看出来软链接法需要将手动编译的oot
驱动链接到/lib/modules/$(uname -r)/extra
目录下,创建并配置depmod
的配置文件,再使用depmod
创建模块依赖列表。当使用modprobe
加载驱动的时候,就可以成功安装我们手动编译的oot
驱动了。这里之所以将oot
驱动软链接到extra
目录下,更新依赖列表后就可以使用modprobe
成功安装,是因为创建并配置了depmod
的配置文件,override
命令确保安装在 /lib/modules/$(uname -r)/extra/
下的nfp
驱动将优先于内核已经提供的任何同名的模块。
上面实战演示中的dracut -f
是可选的步骤,可以执行,也可以不执行。具体作用后面再详细介绍。
【解析】使用
external
关键字,可以指定系统上的任意编译生成oot
驱动的目录作为modprobe
安装驱动的路径。
配置external
路径法无需将自己编译生成的oot
驱动文件拷贝或者建立软链接到/lib/modules/$(uname -r)/extra/
目录下,但是需执行echo "external * /home/xxx/nfp-drv-kmods-private/src" > /etc/depmod.d/xxx.conf
命令以及配置/etc/depmod.d/dist.conf
文件。随后,再执行depmod -a
进行模块依赖表的更新,即更新modules.dep
和modules.dep.bin
文件。这时,再进行modprobe
的时候,直接安装的则是通过external
命令指定的路径下的oot
驱动。
注意:
echo "external * /home/xxx/nfp-drv-kmods-private/src" > /etc/depmod.d/xxx.conf
命令表示在/etc/depmod.d/
目录下创建一个xxx.conf
(名字任意)文件,在里面添加external * /home/xxx/nfp-drv-kmods-private/src
(其中,/home/xxx/nfp-drv-kmods-private/src
为编译出oot
驱动的路径(xxx
表示一个实际的路径名字而已),*
表示匹配任何内核版本,也可以指定具体的内核版本),通过/etc/depmod.d
目录内的配置文件,设置external
命令,表示这是一个外部的路径。/etc/depmod.d/dist.conf
的内容则表示搜索路径的优先级,如果想优先匹配external
指定的路径下的文件,则需将external
放在extra
和built-in
之前。这样external
指定的路径下的任何匹配模块名称将优先于extra
内保存的同名模块和内核已经提供的同名模块。
【拓展】:
depmod
配置文件的external
命令格式和解析如下:
external kernelversion absolutemodulesdirectory...
This specifies a list of directories, which will be checked according to the priorities in the search command. The order matters also, the first directory has the higher priority. The kernelversion is a POSIX regular expression or * wildcard, like in the override.
实战演示如下:
# 创建并配置depmod配置文件auto_define.conf
[root@localhost ~]# echo "external * /home/xxx/nfp-drv-kmods-private/src" > /etc/depmod.d/auto_define.conf
[root@localhost ~]# cat /etc/depmod.d/auto_define.conf
external * /home/xxx/nfp-drv-kmods-private/src
[root@localhost ~]# ls -l /etc/depmod.d/
auto_define.conf dist.conf kvdo.conf
# 编辑dist.conf文件内容,使external指定的路径优先级高于extra和built-in,即将external放在extra和built-in前面
[root@localhost ~]# vi /etc/depmod.d/dist.conf
# 查看dist.conf文件内容
[root@localhost ~]# cat /etc/depmod.d/dist.conf
#
# depmod.conf
#
# override default search ordering for kmod packaging
search updates external extra built-in weak-updates
# 创建依赖列表
[root@localhost ~]# depmod -a
# 查看modules.dep内容,根据检索结果可知,加载驱动使用的是/home/xxx/nfp-drv-kmods-private/src/路径下的oot nfp驱动
[root@localhost ~]# cat /lib/modules/4.18.0-500.el8.x86_64/modules.dep | grep nfp.ko
/home/xxx/nfp-drv-kmods-private/src/nfp.ko:
# 卸载掉系统上原有已加载的驱动
[root@localhost ~]# rmmod nfp
# 重新加载驱动
[root@localhost ~]# modprobe nfp
[root@localhost ~]#
# oot nfp驱动加载成功
[root@localhost ~]# lsmod | grep nfp
nfp 585728 0
# 将depmod -a更新后的结果保存到initramfs中,这一步可选
[root@localhost ~]# dracut -f
需要注意的是:因为在/etc/depmod.d/dist.conf
中,external
的优先级高于extra
,因此使用modprobe
的时候,优先加载的是external
指定路径下的驱动文件。否则,如果dist.conf
的内容如下所示:
[root@localhost ~]# cat /etc/depmod.d/dist.conf
#
# depmod.conf
#
# override default search ordering for kmod packaging
search updates extra external built-in weak-updates
即extra
位于external
之前,则modprobe
的时候,优先加载的将是/lib/modules/$(uname -r)/extra/
目录下的驱动文件。
注意:
built-in
表示/lib/modules/$(uname -r)/kernel/drivers/net/ethernet/netronome/nfp/
目录。
在日常Linux
驱动开发及调试的过程中,经常遇到需要手动加载oot
驱动成功的情况下,一旦重启系统以后,机器上重启前已成功加载的驱动就不见了,又需要重新再手动加载一遍,要是上面部署着复杂的业务,则全部都需要再重新部署一遍,就极其麻烦了。于是乎,系统重启后自动加载oot
驱动就成了一个亟待解决的问题。针对这个问题,小编经过学习以及工作中实战,终于摸索出这里面的规律,总结如下:
规律一:无论是使用insmod
或modprobe
加载驱动,如果未执行dracut --force
指令,均有可能会出现重启系统后系统中原来加载的oot
驱动消失,被in-tree
驱动替代的现象。
规律二:如果initramfs
中包含相关驱动且系统硬盘驱动库目录下存在同名的驱动,则系统重启时,会加载initramfs
中的该驱动(in-tree
或oot
均适用)。
规律三:如果initramfs
中包含多个同名驱动(in-tree
或oot
),则会根据这些同名驱动的优先级(/etc/depmod.d/dist.conf
中含有优先级),系统启动时选择优先级最高的驱动加载。
规律四:如果initramfs
中不包含相关驱动,而系统硬盘驱动库下存在同名的驱动,则系统启动时会选择系统硬盘驱动库目录下的该同名驱动加载。
【拓展】
initramfs
即initram file system
,翻译成中文意思就是 初始 ram 文件系统,基于tmpfs
,是一种大小灵活,直接作用在内存中的文件系统。initramfs
包含的工具和脚本,在正式的根文件系统的初始化脚本init
启动之前,就被挂载。Linux
系统开机后,首先加载initramfs
文件中包含的驱动程序,如果相应的设备对应的驱动不在initramfs
文件包含范围内,那么会去系统硬盘存储的驱动库中去寻找匹配的驱动进行加载;系统硬盘驱动库的位置即为:/lib/modules/$($uname -r)/
(下面讲系统硬盘驱动库时,即表示该目录);所以相应的驱动只要在系统硬盘的驱动库下或者initramfs
中至少存在一个就可以正常加载,一旦在initramfs
中加载成功,无论系统硬盘的驱动库中存在的驱动版本是否相同都不会重新去加载。initramfs
中包含的驱动ko
文件在目录lib/modules/
下,具体包含的ko
可依次查看。
针对上述总结的规律,让我们通过实战演示来验证一下。
无论是使用insmod
或modprobe
加载驱动,如果未执行dracut --force
指令,均有可能会出现重启系统后系统中原来加载的oot
驱动消失,被in-tree
驱动替代的现象。
实战演示如下:
# 卸载掉系统中原来安装的nfp驱动
[root@localhost ~]# rmmod nfp
[root@localhost ~]# lsmod | grep nfp
[root@localhost ~]# ls -l /home/xxx/nfp-drv-kmods-private/src/ | grep nfp.ko
-rw-r--r--. 1 root root 52121240 Nov 17 19:10 nfp.ko
# insmod安装自己编译的oot nfp驱动
[root@localhost ~]# insmod /home/xxx/nfp-drv-kmods-private/src/nfp.ko
[root@localhost ~]# lsmod | grep nfp
nfp 585728 0
# insmod安装完oot nfp驱动后,直接重启系统
[root@localhost ~]# reboot
# 重启系统后虽然加载了nfp驱动,但是并非自己编译的oot nfp驱动,而是系统内核自带的in-tree nfp驱动
[root@localhost ~]# lsmod | grep nfp
nfp 442368 0
tls 110592 1 nfp
通过对比系统重启前后,加载的nfp
驱动的大小可以知道,前后加载的驱动并非同一个驱动。果然,如果未执行dracut --force
指令,系统启动后,系统内核自带的in-tree
驱动会取代了我自己编译的oot
驱动。那dracut --force
指令的作用是什么呢?其实这个命令的作用就是将当前系统硬盘驱动库中包含的驱动写入到initramfs
镜像中,该镜像会在系统启动过程中,将里面含有的相关驱动加载到系统中。让我们执行这个命令后,再看一下结果:
[root@localhost ~]# rmmod nfp
[root@localhost ~]# insmod /home/xxx/nfp-drv-kmods-private/src/nfp.ko
[root@localhost ~]# lsmod | grep nfp
nfp 585728 0
# dracut -f为 dracut --force的缩写
[root@localhost ~]# dracut -f
[root@localhost ~]#
[root@localhost ~]# reboot
# 重启后,加载的是in-tree nfp驱动
[root@localhost ~]# lsmod | grep nfp
nfp 442368 0
tls 110592 1 nfp
明明前面说oot
驱动加载后,未执行dracut --force
,系统启动后,系统内核自带的in-tree
驱动会取代了我自己编译的oot
驱动。为什么我现在执行了这个命令,我编译的 oot
驱动却还是被系统内核自带的in-tree
驱动取代了呢?因为dracut -f
这个命令的作用是将当前系统硬盘驱动库中包含的驱动写入到initramfs
镜像中,而我使用 insmod
加载的是我自己编译目录下的驱动,而系统硬盘驱动库中并未含有我编译的 oot
驱动,让我将自己编译的oot
驱动放置到系统硬盘驱动库下属的extra
目录下,再验证一下结果会是怎么样。
[root@localhost ~]# cd /lib/modules/$(uname -r)/extra/
[root@localhost extra]# ls
[root@localhost extra]# cp /home/xxx/nfp-drv-kmods-private/src/nfp.ko .
[root@localhost extra]# ls
nfp.ko
[root@localhost extra]# pwd
/lib/modules/4.18.0-500.el8.x86_64/extra
[root@localhost extra]# rmmod nfp
# insmod加载编译路径下的oot驱动
[root@localhost extra]# insmod /home/xxx/nfp-drv-kmods-private/src/nfp.ko
[root@localhost extra]# lsmod | grep nfp
nfp 585728 0
#建立模块依赖列表
[root@localhost extra]# depmod -a
# 将oot驱动复制到extra目录下以后,执行depmod -a果然更新了模块依赖列表
[root@localhost extra]# cat /lib/modules/$(uname -r)/modules.dep | grep nfp.ko
extra/nfp.ko:
# 更新initramfs镜像
[root@localhost extra]# dracut -f
# 拷贝initramfs到新创建的test1目录下
[root@localhost test1]# cp /boot/initramfs-4.18.0-500.el8.x86_64.img .
[root@localhost test1]# ls
initramfs-4.18.0-500.el8.x86_64.img
# 解压缩initramfs到新创建的test1目录下
[root@localhost test1]# /usr/lib/dracut/skipcpio ./initramfs-4.18.0-500.el8.x86_64.img | zcat | cpio -ivd
.
bin
dev
dev/console
dev/kmsg
dev/null
dev/random
dev/urandom
etc
......
[root@localhost test1]# ls
bin etc init lib opt root sbin sys tmp var
dev home initramfs-4.18.0-500.el8.x86_64.img lib64 proc run shutdown sysroot usr
# initramfs镜像的lib/modules/4.18.0-500.el8.x86_64/extra/目录下含有nfp.ko
[root@localhost test1]# ls ./lib/modules/4.18.0-500.el8.x86_64/extra/
nfp.ko
# 重新启动系统
[root@localhost test1]# reboot
#重启系统后,加载的是我自己编译的oot驱动
[root@localhost ~]# lsmod | grep nfp
nfp 585728 0
果然,将自己编译的oot
驱动放置到系统硬盘驱动库下属的extra
目录下,执行depmod -a
,再执行dracut -f
,即会更新initramfs
镜像,使其在initramfs
镜像内的驱动库下属的extra
目录下含有我编译的oot
驱动,这样系统再重启以后,就会自动加载我编译的oot
驱动了。因此,无论是使用insmod
或modprobe
加载oot
驱动,重点是将自己编译的oot
驱动放置到硬盘驱动库下属的相关目录下,执行depmod -a
,再执行dracut -f
,更新initramfs
镜像,使其在initramfs
镜像内的驱动库下属相关目录下含有我编译的oot
驱动,这样系统再重启以后,就会自动加载我编译的oot
驱动了。
这里,modprobe
加载oot
驱动的演示就不再给出,感兴趣的小伙伴可以自行尝试。
如果initramfs
中包含相关驱动且系统硬盘驱动库目录下存在同名的驱动,则系统重启时,会加载initramfs
中的该驱动(in-tree
或oot
均适用)。
实战演示如下:
# 创建test2目录,并将dracut -f生成的initramfs镜像拷贝到该目录下
[root@localhost test2]# cp /boot/initramfs-4.18.0-500.el8.x86_64.img .
[root@localhost test2]# ls
initramfs-4.18.0-500.el8.x86_64.img
# 解压initramfs镜像到当前目录下
[root@localhost test2]# /usr/lib/dracut/skipcpio ./initramfs-4.18.0-500.el8.x86_64.img | zcat | cpio -ivd
.
bin
dev
dev/console
dev/kmsg
......
[root@localhost test2]# ls
bin etc init lib opt root sbin sys tmp var
dev home initramfs-4.18.0-500.el8.x86_64.img lib64 proc run shutdown sysroot usr
# 解压的initramfs镜像的lib/modules/4.18.0-500.el8.x86_64/extra/目录下含有nfp.ko,该驱动为oot驱动
[root@localhost test1]# ls ./lib/modules/4.18.0-500.el8.x86_64/extra/
nfp.ko
# 系统硬盘驱动库目录下属目录中存在同名的驱动,该驱动为in-tree驱动
[root@localhost ~]# ls /lib/modules/$(uname -r)/kernel/drivers/net/ethernet/netronome/nfp/
nfp.ko.xz
# 重启系统
[root@localhost ~]# reboot
# 根据nfp驱动的大小,可知重启后加载的驱动为oot驱动
[root@localhost ~]# lsmod | grep nfp
nfp 585728 0
根据实战演示的结果可知,当initramfs
镜像和系统硬盘驱动库目录下存在同名的驱动时,重启操作系统后,加载的是位于initramfs
镜像内的oot
驱动。
如果initramfs
中包含多个同名驱动(in-tree
或oot
),则会根据这些同名驱动的优先级(/etc/depmod.d/dist.conf
中含有优先级),系统启动时选择优先级最高的驱动加载。
实战演示如下:
[root@localhost ~]# vi /etc/depmod.d/dist.conf
[root@localhost ~]# vi /etc/depmod.d/auto_define.conf
[root@localhost ~]# cat /etc/depmod.d/auto_define.conf
external * /home/xxx/nfp-drv-kmods-private/src
# external位于extra和built-in之前,表示其优先级高于后两者
[root@localhost ~]# cat /etc/depmod.d/dist.conf
#
# depmod.conf
#
# override default search ordering for kmod packaging
search updates external extra built-in weak-updates
#更新模块依赖列表
[root@localhost ~]# depmod -a
# 查看模块依赖列表,优先级最高的是external设定的/home/xxx/nfp-drv-kmods-private/src/目录下的nfp驱动
[root@localhost ~]# cat /lib/modules/$(uname -r)/modules.dep | grep nfp
/home/xxx/nfp-drv-kmods-private/src/nfp.ko:
# 更新initramfs镜像
[root@localhost ~]# dracut -f
# 创建test3目录,并将dracut -f生成的initramfs镜像拷贝到该目录下
[root@localhost test3]# cp /boot/initramfs-4.18.0-500.el8.x86_64.img .
[root@localhost test3]# ls
initramfs-4.18.0-500.el8.x86_64.img
# 解压initramfs镜像到当前目录下
[root@localhost test3]# /usr/lib/dracut/skipcpio ./initramfs-4.18.0-500.el8.x86_64.img | zcat | cpio -ivd
.
bin
dev
dev/console
dev/kmsg
......
[root@localhost test3]# ls
bin etc init lib opt root sbin sys tmp var
dev home initramfs-4.18.0-500.el8.x86_64.img lib64 proc run shutdown sysroot usr
# initramfs镜像中含有/etc/depmod.d/auto_define.conf中定义的external目录下含义oot驱动
[root@localhost test3]# ls -l ./home/xxx/nfp-drv-kmods-private/src/ | grep nfp.ko
-rw-r--r--. 1 root root 52119640 Nov 18 21:51 nfp.ko
# initramfs镜像中含有驱动库目录下的in-tree驱动
[root@localhost test3]# ls ./lib/modules/$(uname -r)/kernel/drivers/net/ethernet/netronome/nfp/
nfp.ko.xz
# 重启系统
[root@localhost ~]# reboot
# 由已加载驱动的大小与前面均不同可知,加载的为external指定的目录下加载的oot驱动
[root@localhost ~]# lsmod | grep nfp
nfp 589824 0
根据实战演示结果可知,当initramfs
中包含多个同名驱动(in-tree
或oot
)时,则会根据这些同名驱动的优先级,选择优先级最高的驱动加载,无论其是in-tree
或oot
驱动。
如果initramfs
中不包含相关驱动,而系统硬盘驱动库下存在同名的驱动,则系统启动时会选择系统硬盘驱动库目录下的该同名驱动加载。
实战演示如下:
# 创建一个临时目录test4,存放initramfs镜像及解压后的文件
[root@localhost tmp]# mkdir test4
# 构造系统上不包含任何nfp驱动的当前内核对应的initramfs镜像并拷贝到test4目录下
[root@localhost test4]# cp /boot/initramfs-$(uname -r).img .
[root@localhost test4]# ls
initramfs-4.18.0-500.el8.x86_64.img
# 使用skipcpio解压缩当前目录下的initramfs镜像
[root@localhost test4]# /usr/lib/dracut/skipcpio ./initramfs-4.18.0-500.el8.x86_64.img | zcat | cpio -ivd
.
bin
dev
dev/console
dev/kmsg
dev/null
dev/random
dev/urandom
etc
etc/centos-release
etc/cmdline.d
etc/conf.d
etc/conf.d/systemd.conf
...
# 查看当前目录解压后包含的文件
[root@localhost test4]# ls
bin etc init lib opt root sbin sys tmp var
dev home initramfs-4.18.0-500.el8.x86_64.img lib64 proc run shutdown sysroot usr
# 查看解压后的initramfs中是否存在nfp驱动及依赖
[root@localhost test4]# cat ./lib/modules/4.18.0-500.el8.x86_64/modules.dep | grep nfp
# 解压后的initramfs中,连extra目录都不存在
[root@localhost test4]# ls -l ./lib/modules/4.18.0-500.el8.x86_64/extra/
ls: cannot access './lib/modules/4.18.0-500.el8.x86_64/extra/': No such file or directory
# 解压后的initramfs中,连lib/modules/4.18.0-500.el8.x86_64/kernel/drivers/net/ethernet/netronome/nfp/目录都不存在
[root@localhost test4]# ls -l ./lib/modules/4.18.0-500.el8.x86_64/kernel/drivers/net/ethernet/netronome/nfp/
ls: cannot access './lib/modules/4.18.0-500.el8.x86_64/kernel/drivers/net/ethernet/netronome/nfp/': No such file or directory
# 解压后的initramfs中,完全检索不到nfp.ko文件
[root@localhost test4]# find ./ -name "nfp.ko"
[root@localhost test4]#
# 查看当前系统驱动库下模块依赖列表/lib/modules/$(unaem -r)/modules.dep中是否包含nfp驱动
# 只有该列表中包含nfp驱动记录且硬盘驱动库下存在nfp驱动,在系统重启后,才能够成功加载硬盘上驱动库目录下的nfp驱动,
# 否则将不会加载nfp驱动。
[root@localhost ~]# cat /lib/modules/$(uname -r)/modules.dep | grep nfp
kernel/drivers/net/ethernet/netronome/nfp/nfp.ko.xz: kernel/net/tls/tls.ko.xz
# 检查系统硬盘驱动库下是否存在nfp驱动
[root@localhost ~]# ls -l /lib/modules/$(uname -r)/kernel/drivers/net/ethernet/netronome/nfp/
total 176
-rw-r--r--. 1 root root 176228 Jun 28 08:17 nfp.ko.xz
# 卸载掉系统上当前安装的nfp驱动
[root@localhost ~]# rmmod nfp
# 重启操作系统
[root@localhost ~]# reboot
# 查看重启后的系统中是否成功加载nfp驱动,果然加载的是in-tree nfp驱动,即硬盘驱动目录下的nfp驱动
[root@localhost ~]# lsmod | grep nfp
nfp 442368 0
tls 110592 1 nfp
上述实战结果证明,initramfs
镜像中如果不包含相关驱动,而系统驱动库下的模块依赖列表中含有驱动依赖记录,且系统硬盘驱动库下属目录(即/lib/modules/$(uname -r)/kernel/drivers/net/ethernet/netronome/nfp/
)存在同名的驱动,则系统启动时会选择系统硬盘驱动库目录下属目录包含的该同名驱动进行加载。然而,如果系统驱动库下的模块依赖列表中不包含驱动依赖记录,则即使系统硬盘驱动库下属目录中含有相关驱动,操作系统亦不会加载。如下结果能够证明该结论。
[root@localhost ~]# cat /lib/modules/$(uname -r)/modules.dep | grep nfp
[root@localhost ~]#
[root@localhost ~]# ls -l /lib/modules/$(uname -r)/kernel/drivers/net/ethernet/netronome/nfp/
total 176
-rw-r--r--. 1 root root 176228 Jun 28 08:17 nfp.ko.xz
[root@localhost ~]# rmmod nfp
[root@localhost ~]# lsmod | grep nfp
[root@localhost ~]# reboot
# 重启系统后,检查系统中是否含有nfp驱动,结果显示没有加载nfp驱动
[root@localhost ~]# lsmod | grep nfp
[root@localhost ~]#
至此,有关modprobe
加载oot
驱动与重启系统后自动加载oot
驱动的分析就总结完毕了。俗话讲:“好记性不如烂笔头”;将相关知识总结成文,等日后有需要的时候可以拿出来快速回顾一下,当然这也有助于加深对这方面知识的理解。
最后,希望对于从事Linux
驱动的朋友们有所帮助和启发。