一. 内核模块加载情况查询
Linux提供了三种方法查询加载到内核的模块,一种方法是直接访问proc虚拟文件系统获取,一种方法则是比较常用的lsmod方法获取,而lsmod的输出其实是基于/proc/modules。另外还有一种就是查看/sys/module/目录下是否生成已加载模块的目录。
1、/proc/modules文件
Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。
/proc 中的大多数虚拟文件都可以使用cat、more和less等命令查看。其中,/proc/modules列出了所有load进入内核的模块列表,里面的内容会随着系统使用和配置的变化而变化。/proc/modules内模块加载情况查询如下:
[root@localhost]# cat /proc/modules
hellomod 16384 0 - Live 0xffffffffc1072000 (OE)
openvswitch 212992 0 - Live 0xffffffffc103d000
nf_conncount 24576 1 openvswitch, Live 0xffffffffc1036000
xt_nat 16384 15 - Live 0xffffffffc1022000
vhost_net 40960 1 - Live 0xffffffffc102b000
vhost 65536 1 vhost_net, Live 0xffffffffc0ea4000
...
intel_uncore 258048 0 - Live 0xffffffffc0d39000
pcspkr 16384 0 - Live 0xffffffffc05ad000
mei_me 57344 0 - Live 0xffffffffc0d2a000
ipmi_ssif 49152 0 - Live 0xffffffffc0d16000
mei 184320 1 mei_me, Live 0xffffffffc0cd8000
i2c_i801 36864 0 - Live 0xffffffffc0c46000
...
#hellomod是小编自定义的模块,加载后可在/proc/modules中查询到
[root@localhost]# cat /proc/modules | grep hellomod
hellomod 16384 0 - Live 0xffffffffc1072000 (OE)
上述查询结果大家看起来可能会比较懵,接下来让我们解析下各列的含义:
-
第一列:模块的名字 -
第二列:模块的内存大小,单位是bytes -
第三列:被load的次数,0以为着没有被load过 -
第四列:是否依赖第三方moudle,列出这些module -
第五列:模块的状态,有Live, Loading, Unloading三种状态 -
第六列:模块当前的内核内存偏移位置。这些信息,debug的时候会非常有用。例如使用诊断工具 addr2line时就可能会用到该内存偏移位置
2、lsmod命令
Linux lsmod命令用于显示已经加载到内核中的模块的状态信息,原理就是将/proc/modules 中的信息调整一下格式输出。
执行lsmod命令后会列出所有已载入系统的模块,lsmod 输出列表中有一列 Used by
,它表明此模块正在被其他模块使用。Linux操作系统的核心具有模块化的特性,因此在编译核心时,务须把全部的功能都放入核心。您可以将这些功能编译成一个个单独的模块,待需要时再分别载入。
[root@localhost]# lsmod
Module Size Used by
hellomod 16384 0
openvswitch 212992 0
tun 69632 4 vhost_net
bridge 393216 0
...
mei 184320 1 mei_me
i2c_i801 36864 0
ioatdma 69632 0
lpc_ich 28672 0
...
dm_mirror 28672 0
dm_region_hash 28672 1 dm_mirror
dm_log 24576 2 dm_region_hash,dm_mirror
dm_mod 204800 12 dm_log,dm_mirror
#hellomod是小编自定义的模块,加载后可通过lsmod查询到
[root@localhost]# lsmod | grep hellomod
hellomod 16384 0
上述查询结果虽然比第一种方法输出列少,但是可能大家依然不知道什么意思,接下来让我们解析下各列的含义:
-
第一列:模块的名字 -
第二列:模块的大小 -
第三列:被其他模块所依赖的次数 -
第四列:依赖该模块的模块名称
3、/sys/module 目录
该目录下有系统中所有的模块信息,不论这些模块是以内联(inlined) 方式 编译到内核镜像文件中,还是编译为外部模块(.ko),均会在/sys/module目录下生成以模块名命名的目录 。
[root@localhost]# ls -l /sys/module
total 0
drwxr-xr-x. 3 root root 0 Feb 23 19:32 8250
drwxr-xr-x. 3 root root 0 Feb 23 19:32 acpi
drwxr-xr-x. 5 root root 0 Feb 23 19:32 acpi_ipmi
...
drwxr-xr-x. 3 root root 0 Feb 23 19:32 firmware_class
drwxr-xr-x. 5 root root 0 Feb 23 19:32 ghash_clmulni_intel
drwxr-xr-x. 3 root root 0 Feb 23 19:32 gpiolib_acpi
drwxr-xr-x. 5 root root 0 Feb 23 19:32 grace
drwxr-xr-x. 3 root root 0 Feb 23 19:32 haltpoll
drwxr-xr-x. 5 root root 0 Feb 23 19:34 hellomod
drwxr-xr-x. 3 root root 0 Feb 23 19:32 hid
...
[root@localhost]# ls -l /sys/module | grep hellomod
drwxr-xr-x. 5 root root 0 Feb 23 19:34 hellomod
当我们加载驱动程序之后,我们可以通过 调用cat /proc/modules、lsmod命令或者查看 /sys/module/,查看我们刚加载的模块有没加载成功。
二. 内核模块加载与卸载工具
1、insmod
将指定模块加载到内核,insmod命令完全由用户自行加载一个完整文件名的模块,不会主动分析模块依赖性,需要自己手动加载。
语法格式:
insmod [ 文件名 ] [ 模块参数... ]
例如:
insmod /path/xxx.ko
其中,path表示ko文件的绝对路径或相对路径。
2、depmod
分析可加载模块的依赖性,生成
modules.dep
文件和modules.dep.bin
文件,以便modprobe加载模块时根据modules.dep.bin
进行依赖模块加载。
语法格式:
[root@localhost]# depmod [-adeisvV][-m<文件>][--help][模块名称]
-a 分析所有可用的模块
-d 执行排错模式
-e 输出无法参照的符号
-i 不检查符号表的版本
-m<文件> 使用指定的符号表文件
-n 不写入 modules.dep ,而是将结果输出到萤幕上(standard out);
-s 在系统记录中记录错误
-v 执行时显示详细的信息
-V 显示版本信息
--help 显示帮助
Linux内核模块可以为其它模块提供提供服务(在代码中使用EXPORT_SYMBOL),这种服务被称作”symbols”。若第二个模块使用了这个symbol,则该模块很明显依赖于第一个模块。这些依赖关系是非常繁杂的。
depmod 通过读取 /lib/modules/$(uname -r) /
下的每个模块并确定它导出的符号和需要的符号,创建一个模块依赖关系列表。默认情况下,此列表将写入modules.dep
和名为modules.dep.bin
的二进制哈希版本,这两个文件位于同一目录中。如果在命令行中给出了文件名,则只检查这些模块(除非列出了所有模块,否则这些模块很少有用)。
depmod 还创建由 modules.symbols
文件及其二进制散列版本 modules.symbols.bin
中的模块提供的符号列表。
最后,如果模块提供了特殊的设备名(devname),则 depmod 将输出一个名为 modules.devname
的文件,该设备名应在启动时填充在 /dev
中(由 udev
等实用程序填充)。
3、modprobe
加载或卸载内核模块;
语法格式:
modprobe [ 参数] [模块名称] [模块参数...]
modprobe需要根据modules.dep.bin文件的内容进行加载操作,可以自动解决模块间的依赖关系表,一次性将有依赖关系的驱动全部加载到内核,不需要驱动的具体地址,但需要将驱动拷贝或设置软链接到/lib/modules/$(uname -r)/
或者/lib/modules/$(uname -r)/extra/
目录下。modprobe加载模块时并不需要指定ko文件的具体目录,直接使用modprobe 模块名字即可,我们不仿测试下:
#加载hellomod.ko模块之前,默认的模块信息如下
[root@localhost 6.2.0-rc5+]# modinfo hellomod
filename: /lib/modules/6.2.0-rc5+/hellomod.ko
license: Dual BSD/GPL
srcversion: DEA7EE7031439C7A120C77C
depends:
retpoline: Y
name: hellomod
vermagic: 6.2.0-rc5+ SMP preempt mod_unload modversions
#删除调/lib/modules/$(uname -r)/目录下的hellomod软链接
[root@localhost 6.2.0-rc5+]# rm -f hellomod.ko
[root@localhost 6.2.0-rc5+]# ls -l
total 3800
lrwxrwxrwx. 1 root root 27 Feb 20 11:17 build -> /usr/src/kernels/6.2.0-rc5+
drwxr-xr-x. 13 root root 141 Feb 24 10:46 kernel
-rw-r--r--. 1 root root 943175 Feb 24 19:26 modules.alias
-rw-r--r--. 1 root root 899138 Feb 24 19:26 modules.alias.bin
-rw-r--r--. 1 root root 8920 Jan 30 10:22 modules.builtin
-rw-r--r--. 1 root root 11118 Feb 24 19:26 modules.builtin.bin
-rw-r--r--. 1 root root 69179 Jan 30 10:22 modules.builtin.modinfo
-rw-r--r--. 1 root root 313395 Feb 24 19:26 modules.dep
-rw-r--r--. 1 root root 429116 Feb 24 19:26 modules.dep.bin
-rw-r--r--. 1 root root 405 Feb 24 19:26 modules.devname
-rw-r--r--. 1 root root 106408 Jan 30 10:22 modules.order
-rw-r--r--. 1 root root 833 Feb 24 19:26 modules.softdep
-rw-r--r--. 1 root root 494353 Feb 24 19:26 modules.symbols
-rw-r--r--. 1 root root 593333 Feb 24 19:26 modules.symbols.bin
lrwxrwxrwx. 1 root root 27 Feb 20 11:17 source -> /usr/src/kernels/6.2.0-rc5+
#复制源文件路径下的hellomod.ko到/lib/modules/$(uname -r)路径下
[root@localhost 6.2.0-rc5+]# cp /tmp/28/hellomod.ko .
[root@localhost 6.2.0-rc5+]# ls -l
total 4080
lrwxrwxrwx. 1 root root 27 Feb 20 11:17 build -> /usr/src/kernels/6.2.0-rc5+
-rw-r--r--. 1 root root 285744 Feb 25 17:18 hellomod.ko
drwxr-xr-x. 13 root root 141 Feb 24 10:46 kernel
-rw-r--r--. 1 root root 943175 Feb 24 19:26 modules.alias
-rw-r--r--. 1 root root 899138 Feb 24 19:26 modules.alias.bin
-rw-r--r--. 1 root root 8920 Jan 30 10:22 modules.builtin
-rw-r--r--. 1 root root 11118 Feb 24 19:26 modules.builtin.bin
-rw-r--r--. 1 root root 69179 Jan 30 10:22 modules.builtin.modinfo
-rw-r--r--. 1 root root 313395 Feb 24 19:26 modules.dep
-rw-r--r--. 1 root root 429116 Feb 24 19:26 modules.dep.bin
-rw-r--r--. 1 root root 405 Feb 24 19:26 modules.devname
-rw-r--r--. 1 root root 106408 Jan 30 10:22 modules.order
-rw-r--r--. 1 root root 833 Feb 24 19:26 modules.softdep
-rw-r--r--. 1 root root 494353 Feb 24 19:26 modules.symbols
-rw-r--r--. 1 root root 593333 Feb 24 19:26 modules.symbols.bin
lrwxrwxrwx. 1 root root 27 Feb 20 11:17 source -> /usr/src/kernels/6.2.0-rc5+
#直接执行modprobe会报错并提示无法插入hellomod.ko文件,需要执行depmod命令更新modules.dep和modules.dep.bin文件,
#将hellomod.ko的路径写入modules.dep和modules.dep.bin文件中
[root@localhost 6.2.0-rc5+]# modprobe hellomod
modprobe: ERROR: could not insert 'hellomod': Unknown symbol in module, or unknown parameter (see dmesg)
[root@localhost 6.2.0-rc5+]#
#depmod指令会自动分析/lib/modules/$(uname -r)/目录下的可加载模块,并按照固定的格式填入modules.dep和modules.dep.bin中。
#因此,我们可以先将需要加载的ko文件拷贝到对应的目录,再执行depmod指令。
[root@localhost 6.2.0-rc5+]# depmod
#执行depmod后,在modules.dep和modules.dep.bin中已经存在有我们需要加载的ko文件名了,注意,不要手工的去编辑modules.dep文件
[root@localhost 6.2.0-rc5+]# cat modules.dep | grep hellomod
hellomod.ko:
#再执行modprobe指令,即可加载模块了,modprobe是根据modules.dep.bin文件中的路径信息加载ko文件。
[root@localhost 6.2.0-rc5+]# modprobe hellomod
[root@localhost 6.2.0-rc5+]# lsmod | grep hellomod
hellomod 16384 0
注意:在加载自己制作的ko文件时,我们最好使用insmod命令;因为modprobe使用的模块加载路径一般是系统启动时的默认加载路径,存放的一般是系统原始的ko文件。
如果要使用modprobe进行加载,则建议不用将默认加载路径下的模块文件替换掉,一旦将原始ko模块文件替换掉,假如自己制作的模块文件存在bug,可能导致系统出现故障,即使重新启动机器,系统默认加载的仍然是自己制作的ko文件,这时就会导致系统无法正常启动。因此,假如要使用modprobe命令,则建议在与默认加载路径不同的
/lib/modules/$(uname -r)/
或者/lib/modules/$(uname -r)/extra/
路径下生成源模块文件的软链接,再分别执行depmod, modprobe xxx两个命令,这样能成功加载自己制作的模块文件,且即使自制模块有bug也不影响系统重启后成功启动。因为这样操作过后,系统重启后该模块文件未加载到内核,读者可以自己实验。
有上述可知,无论使用modprobe或者insmod加载自制的模块,在重启后均会失效;对于insmod,系统重启后仍然会加载默认路径下的内核模块。对于modprobe,系统重启后则既不会加载自制模块也不会加载默认模块。
好多文章或博客里面写到modprobe是根据modules.dep文件的内容进行加载模块操作的,其实是不正确的。使用 strace 追踪 modprobe 的调用过程,发现 modprobe 并不会使用 modules.dep 文件,而是调用modules.dep.bin文件。
strace 的相关信息记录如下:
[root@localhost]# strace /sbin/modprobe hellomod 2>&1 | grep "modules"
open("/lib/modules/6.2.0-rc5+/modules.softdep", O_RDONLY|O_CLOEXEC) = 3
open("/lib/modules/6.2.0-rc5+/modules.dep.bin", O_RDONLY|O_CLOEXEC) = 3
open("/lib/modules/6.2.0-rc5+/modules.alias.bin", O_RDONLY|O_CLOEXEC) = 3
open("/lib/modules/6.2.0-rc5+/modules.symbols.bin", O_RDONLY|O_CLOEXEC) = 3
open("/lib/modules/6.2.0-rc5+/modules.builtin.bin", O_RDONLY|O_CLOEXEC) = 3
open("/lib/modules/6.2.0-rc5+/hellomod.ko", O_RDONLY|O_CLOEXEC) = 3
由上面结果可知,modprobe在加载模块时,它访问了 modules.dep.bin
、modules.alias.bin
、modules.symbols.bin
、modules.builtin.bin
等文件,它并没有访问 modules.dep
文件。
接下来让我们使用man modules.dep看下有关该文件的说明:
DESCRIPTION
modules.dep.bin is a binary file generated by depmod listing the dependencies for every module in the directories under /lib/modules/version. It is used by kmod tools such as modprobe
and libkmod.
Its text counterpar is located in the same directory with the name modules.dep. The text version is maintained only for easy of reading by humans and is in no way used by any kmod
tool.
These files are not intended for editing or use by any additional utilities as their format is subject to change in the future. You should use the modinfo(8) command to obtain
information about modules in a future proof and compatible fashion rather than touching these files.
从man的描述来看,modules.dep
只是供人查看的,kmod相关的命令使用的是modules.dep.bin
这个文件,如modprobe和libkmod。
4、modinfo
查看某个模块的详细信息。
语法格式:
[root@localhost]# modinfo [-adlpn0Fkbvh] <模块文件>
-a 或–author 显示模块开发人员。
-d 或–description 显示模块的说明。
-l 或—license 显示版本信息
-p 或–parameters 显示模块所支持的参数。
-0 或–null 用