Linux内核模块管理工具总结

嵌入式技术

1372人已加入

描述

一. 内核模块加载情况查询

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 用 \\0 代替 \\n -F 或–field=FIELD 仅打印提供的字段
-k 或–set-version=VERSION 用 VERSION 代替uname -r
-b 或–basedir=DIR 使用 DIR 作为/lib/modules 的文件系统根目录
-V 或–version 显示版本信息
-h 或–help 显示帮助信息

modinfo命令通过/lib/modules/$(uname -r)/modules.dep 文件获取模块(ko文件)的实际存放路径,之后,通过调用modinfo()函数打开模块(ko文件)从而获取模块的详细信息并打印出来。因此,存在这样一种情况,即模块实际并未加载,但是通过modinfo却可以读取到模块的详细信息。

注:这就告诫我们,当使用modinfo读出模块的详细信息时,不要就误以为模块已加载到内核。

这里,我们演示一下模块未加载,但是modinfo却能显示模块的详细信息的例子,如下:

#lsmod显示的内容中未检索到hellomod模块,说明hellomod模块未加载到内核
[root@localhost]# lsmod | grep hellomod
[root@localhost]#

#直接执行modinfo命令可以显示hellomod模块的详细信息
[root@localhost]# 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
[root@localhost]#

#查看/lib/modules/$(uname -r)/modules.dep文件内容,包含hellomod路径
[root@localhost]# cat /lib/modules/$(uname -r)/modules.dep | grep hellomod
hellomod.ko:

[root@localhost]# ls -l /lib/modules/$(uname -r)
total 3800
lrwxrwxrwx.  1 root root     27 Feb 20 11:17 build - > /usr/src/kernels/6.2.0-rc5+
lrwxrwxrwx.  1 root root     19 Feb 23 19:26 hellomod.ko - > /tmp/28/hellomod.ko
drwxr-xr-x. 13 root root    141 Feb 24 10:46 kernel
-rw-r--r--.  1 root root 943175 Feb 24 14:17 modules.alias
-rw-r--r--.  1 root root 899138 Feb 24 14:17 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 14:17 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 14:17 modules.dep
-rw-r--r--.  1 root root 429116 Feb 24 14:17 modules.dep.bin
-rw-r--r--.  1 root root    405 Feb 24 14:17 modules.devname
-rw-r--r--.  1 root root 106408 Jan 30 10:22 modules.order
-rw-r--r--.  1 root root    833 Feb 24 14:17 modules.softdep
-rw-r--r--.  1 root root 494353 Feb 24 14:17 modules.symbols
-rw-r--r--.  1 root root 593333 Feb 24 14:17 modules.symbols.bin
lrwxrwxrwx.  1 root root     27 Feb 20 11:17 source - > /usr/src/kernels/6.2.0-rc5+

上面示例说明,hellomod模块未加载时,modinfo通过读取modules.dep文件中hellomod模块的实际路径,找到hellomod.ko文件,读取该文件,从而获取hellomod模块的详细信息。

接下来,我们通过modprobe加载hellomod模块,再通过modinfo查看,发现和未加载hellomod模块时显示的一样。

[root@localhost]# modprobe hellomod
[root@localhost]# lsmod | grep hellomod
hellomod               16384  0
[root@localhost]# 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
[root@localhost]#

注:modinfo只有在其后接xxx.ko时,才是读取该模块的详细信息,即只有带.ko后缀时,读取的才是当前路径下的模块文件;不带.ko后缀时,读取的则是/lib/modules/$(uname -r)/modules.dep文件内保存的路径下的模块的信息。

5、rmmod

rmmod命令来自于英文词组”remove module“的缩写,其功能是用于移除内核中已加载的模块。

语法格式:

rmmod [参数] 模块名称

常用参数如下:

-a 删除所有目前不需要的模块
-s 把信息输出至日志服务中
-v 显示指令执行的详细信息
-f 强制移除模块
-w 确认模块能被删除时再操作
[root@localhost 6.2.0-rc5+]# lsmod | grep hellomod
hellomod               16384  0
[root@localhost 6.2.0-rc5+]#
[root@localhost 6.2.0-rc5+]# rmmod hellomod
[root@localhost 6.2.0-rc5+]# lsmod | grep hellomod
[root@localhost 6.2.0-rc5+]#

#查看dmesg日志,可以看到hellomod模块在卸载后的日志
[root@localhost ~]# dmesg | tail -2
[181935.423616] Hello, World!
[181944.217893] Goodbye, World!
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分