资讯动态

lsmod命令详解:内核模块管理与驱动排查实战指南

发布时间:2026/10/20 3:23:29 来源:尧图企业网站定制
刚接手一台新服务器发现板载网卡怎么都起不来dmesg里并没有网卡相关的报错网络接口也少了一个。排查了一圈最后用lsmod一看——驱动模块压根没加载前面全白忙活了。这种经历可能不少运维朋友都遇到过lsmod看起来简单就是列一下已加载的内核模块但真正把它的输出读透再把与它配套的命令串起来用很多系统底层问题几分钟就能定位。这篇文章我就从自己的实际使用经验出发把这个命令和它周围的工具链彻底聊透希望能帮你省点排查时间。1. 读透lsmod的输出Size、Used by和Tainted列的真正含义先看最常见的执行结果。在终端敲下lsmod输出大致长这样$ lsmod Module Size Used by nf_nat 24576 3 nf_conntrack 114688 23 nf_nat,iptable_nat,xt_conntrack vfat 16384 1 fat 65536 1 vfat这个输出格式是从/proc/modules文件读出来后再格式化排列的。你可以对比一下cat /proc/modules字段完全对应只是lsmod把列宽整理了看起来更舒服。第一列 Module是模块的名字它对应/lib/modules/$(uname -r)/kernel/目录下某个.ko文件的文件名但加载进内核后名字可以去掉.ko后缀。比如vfat.ko加载后模块名就叫vfat。这里有个容易忽略的细节有些模块名和文件名不一致比如binfmt_misc这个模块modinfo查询时要输入的是模块名而不是文件名。第二列 Size表示该模块在内存中占用的字节数。注意这不是磁盘上.ko文件的大小而是加载后代码段、数据段等实际占用的内存。在嵌入式设备或内存受限的服务器上这一列非常值得关注。我曾经在一台内存只有 512MB 的ARM 设备上排查过 OOM 问题发现某个用于远程管理的驱动模块自己一个就吃了 200MB解决方案就是确认业务不需要后重新编译内核把它从模块列表里去掉内存瞬间宽裕了很多。第三列 Used by代表这个模块当前的引用计数以及被哪些模块或进程引用。引用计数为 0 表示现在没人用它理论上可以卸载。如果引用计数不为 0执行rmmod会直接报Module is in use。如果这一列显示的是-1情况比较特殊意思是该模块正被其他模块请求加载但它自身没有维护合理的引用计数最常见的就是某些老驱动或者未遵循规范的第三方模块。后面我会专门展开讲这部分。另外lsmod有时还会出现第四列 Tainted不过默认显示可能看不到。如果你执行cat /proc/modules最后一列可能会有大写字母比如$ cat /proc/modules | head -3 nf_nat 24576 3 - Live 0xffffffffc0000000 vfat 16384 1 - Live 0xffffffffc0003000这里出现字母就是内核被“污染”了Tainted。Tainted 是一个状态位标记当前内核运行环境是否掺杂了非标准元素。常见情况包括加载了带专有协议的第三方模块、模块使用了非 GPL 兼容许可、发生了内核警告但系统没有宕机等。后续排查问题时如果确认内核对某个模块标记了 Tainted那么出现诡异现象时优先怀疑该模块是更明智的选择。实际工作中lsmod最常见的用法就是配合grep判断某个模块是否存在$ lsmod | grep ext4 $ lsmod | grep -i usb也常用wc -l统计当前加载了多少模块$ lsmod | wc -l这样能快速感受一台机器的基础软件生态复杂度。通常普通服务器加载几十到一两百个模块是正常范围如果看到三五百甚至更多建议留意一下是否有大量不必要的驱动被加载这些驱动不仅占用内存还可能带来安全面扩大的问题。1.1 使用 lsmod 前必须想明白的问题lsmod输出的是“当前已经加载进内核的模块”它不会告诉你“系统里存在哪些模块”。很多新手把lsmod | grep xxx没有输出理解成“这个驱动不存在”这就不对了。它只能说明“此刻没加载”至于模块文件在不在、为什么没加载还需要后面讲到的modinfo、modprobe、dmesg一起配合才能下结论。2. 模块依赖关系为什么按下葫芦浮起瓢卸载一个模块牵连一串内核模块并不是孤立存在的。最典型的例子就是文件系统驱动你插入一个 FAT32 格式的 U 盘挂载时需要vfat模块而vfat本身又依赖fat模块提供底层 FAT 文件系统的通用处理逻辑。所以你会看到$ lsmod | grep fat fat 65536 1 vfat vfat 16384 1这里fat的 Used by 是 1引用来源是vfat。如果你试图直接卸载fat$ sudo rmmod fat rmmod: ERROR: Module fat is in use by vfat这就是依赖关系的直观体现。如果不先卸载vfatfat是卸不掉的。依赖关系的完整信息并没有直接写进lsmod的输出里它实际上来自模块文件自身的元数据通过modinfo可以看到$ modinfo vfat depends: fat更详细的依赖树关系则写在/lib/modules/$(uname -r)/modules.dep和对应的.bin文件里。这个文件是内核模块加载工具获取依赖关系的关键索引。如果你手动拷贝了模块文件但忘记运行depmod那么modprobe会告诉你找不到模块或者依赖错误。某种意义上modules.dep就是模块世界的地图lsmod是当前现状的体检报告两者配合才能分析清楚“谁依赖谁”以及“现在谁在用谁”。循环依赖是另一个值得注意的情况。A 依赖 BB 又依赖 A理论上这是不允许的内核模块加载器不会让这种状态正常成立。如果出现类似问题模块会加载失败dmesg里能查到circular dependency之类的提示。自己开发内核模块时一定要注意这一点宁可把公共代码抽到第三个模块里也不要设计成循环依赖。2.1 Used by 为 -1 的迷思前面提到过 Used by 为 -1 的情况。我做一个不太严谨但容易理解的比喻普通模块的引用计数就像一件工具被人借走的次数借出一次加一还回来减一。而 -1 就像是这个工具被塞进了一个借还记录混乱的仓库仓库管理员告诉你“这件东西一定被用着但具体被谁用、用了几次系统记录不下来”。出现 -1 时直接rmmod xxx基本都会被拒绝内核会认为有引用方存在。遇到这类模块如果不是为了调试不建议强行处理而是要找到真正引用它的上层模块或者用户态程序。我印象很深的一次是某个 WiFi 驱动模块一直显示 -1后来发现是驱动自己创建了一个无法正常释放的定时器导致引用计数异常。这种问题只能从驱动源码层面修靠命令行硬刚没有意义。2.2 如何查看完整的模块依赖链想看清某个模块的整个依赖链可以逐步用modinfo -F depends递归查询。例如$ modinfo -F depends nf_nat nf_conntrack $ modinfo -F depends nf_conntrack不过这只能看一层。想看多层依赖一个简单的 shell 循环就派上用场了。把模块名当作参数传入不断补查依赖的依赖#!/bin/bash dep_list func_dep() { local m$1 local d$(modinfo -F depends $m 2/dev/null | tr , ) for item in $d; do if [[ $dep_list ! *$item* ]]; then dep_list$dep_list $item func_dep $item fi done } func_dep $1 echo 依赖模块$dep_list这样整个依赖树就一目了然了。排查“为什么某些模块会被一起加载”或者“禁用某个模块会不会影响其他功能”时这个脚本能省去不少手工查表的功夫。3. 相关命令的分工与合作modinfo、modprobe、insmod、rmmod、depmod 使用要点lsmod只是体检报告真正动手加载、卸载、查详情的是下面这条命令链。它们之间的分工可以用一张表说清楚命令核心职责是否自动处理依赖典型场景modinfo查看模块元信息、参数、许可证否加载前确认模块信息与可用参数modprobe智能加载/卸载模块自动解决依赖是日常装载和卸载最常用insmod直接加载指定 .ko 文件否调试自己编译的模块绕过 dep 索引rmmod按模块名卸载否卸载已知模块depmod重建模块依赖索引否新增模块文件后必须执行lsmod列出已加载模块及状态否确认加载结果、引用关系3.1 modinfo加载前先看清“身份证”modinfo的完整用法是modinfo [选项] 模块名。最常用的参数包括$ modinfo e1000e filename: /lib/modules/$(uname -r)/kernel/drivers/net/ethernet/intel/e1000e.ko license: GPL description: Intel(R) PRO/1000 Network Driver depends: alias: pci:v00008086d00001518sv*sd*bc*sc*i* parm: debug:Debug level (int) parm: copybreak:Copy breakpoint for copy-only-tx-frags (int)这几个字段里parm列出的就是模块支持的加载参数后面会讲到如何传参。depends告诉你它依赖谁。alias则是设备和模块之间的匹配关系系统通过设备的 vendor ID 和 device ID 找对应模块时靠的就是这些 alias。还有一个容易被忽略的用法用modinfo -F只提取某个字段方便脚本处理。比如$ modinfo -F filename vfat /lib/modules/$(uname -r)/kernel/fs/fat/vfat.ko如果系统里找不到某个模块先别急着说“内核不支持”用modinfo查一下往往能区分出是模块缺失、模块内置于内核、还是模块文件存在但没被索引。3.2 modprobe日常加载卸载的首选modprobe区别于insmod的最大特性就是自动解析依赖。比如加载vfat它会自动先把fat也加载上来。$ sudo modprobe vfat之后用lsmod看vfat和fat会同时出现。modprobe不仅是加载工具也承担卸载职责$ sudo modprobe -r vfat它会智能判断如果vfat卸载后fat没有其他使用者就一起卸掉。这比rmmod一个一个手工卸要安全得多。还有一个参数值得记住modprobe -D可以“试运行”只显示会加载哪些模块但不真正执行加载。在需要确认模块依赖链时非常好用$ sudo modprobe -D vfat它会明确输出vfat以及依赖的fat的加载计划。3.3 insmod 和 rmmod手工操作的特例场景insmod的参数必须是模块文件的完整路径而且不会解析依赖加载失败就直接报错。它适用的场景很单一你自己编译了一个不在标准目录下的.ko不想拷贝到系统目录、也不想跑depmod纯粹为了调试。比如$ sudo insmod /home/user/test.ko如果模块加载时依赖别的模块而那个模块没加载insmod会报Unknown symbol in module。这时要么先手动加载依赖要么干脆切到modprobe。我有一次调试一个实验性的字符驱动模块里引用了一个不存在的导出符号折腾很久才发现是自己头文件版本不一致导致的。用insmod的报错信息配合dmesg能很清晰地看到是哪一步断了。rmmod则要求目标模块的引用计数为 0否则会报 in use。日常使用中和modprobe -r最大的区别是rmmod只卸载指定的模块不会顺带清理它的依赖模块。3.4 depmod新模块入场后的“地图重建”如果你往/lib/modules/$(uname -r)/目录里拷贝了新的.ko文件比如某个厂商提供的闭源驱动直接modprobe大概率会失败因为依赖索引里还没有它的记录。此时跑一次$ sudo depmod -a生成新的modules.dep索引后再执行modprobe就能正常识别了。这也是所有自编译驱动安装过程里必不可少的一步。顺手提一下depmod是分析模块间符号依赖的工具它会把每个模块导出的符号和依赖的符号写进索引所以内核模块加载时才能快速判断依赖是否满足。4. 一次网卡驱动排查实录lsmod 在这个过程中的关键定位作用拿一个真实的排查流程举个例子。假设手头有一台服务器新装了一张 PCIe 网卡但ip link里看不到对应的网卡接口。第一步用lspci确认系统是否识别到了硬件$ lspci | grep -i ethernet能列出设备说明 PCI 总线层面识别成功。如果lspci都看不到那问题可能在硬件插槽或者 BIOS 设置和驱动还没关系。第二步用lspci -k看内核给这个设备配了什么驱动$ lspci -k -s 01:00.0-k会显示“Kernel driver in use”如果内核认为设备没有驱动通常会提示no driver has been assigned to this device。这时候lsmod就该登场了。第三步判断对应驱动模块是否加载。假设这张网卡用的是某厂商的xyz系列驱动模块名可能叫xyz或xyz_eth$ lsmod | grep xyz如果grep没结果说明模块确实没加载。但先别急着判断“模块不存在”用modinfo确认$ modinfo xyz这里会出现两种情况情况一modinfo 能查到模块。说明驱动文件存在但出于某种原因没有自动加载。这时候执行$ sudo modprobe xyz然后立刻再看一次lsmod$ lsmod | grep xyz看到模块已经被加载再回到ip link检查是否多出新的网卡接口问题大概率就解决了。如果modprobe报错或加载后lsmod里仍然没有就需要看dmesg了$ dmesg | tail -30常见报错包括Unknown symbol、Invalid argument、firmware not found。其中firmware not found是很多网卡驱动特有的问题驱动代码本身在但固件文件缺失需要到/lib/firmware下补齐对应的.bin文件。情况二modinfo 找不到模块。说明内核里根本没有对应驱动模块。这时要查内核自身是否集成了该驱动$ modprobe -D xyz 2/dev/null $ grep -r xyz /lib/modules/$(uname -r)/modules.builtin如果modules.builtin里有这个名字说明驱动被编译进了内核镜像而不是独立模块。这种情况下lsmod永远看不到它但设备应该能被正常驱动。反之如果两处都查不到就需要考虑安装厂商提供的官方驱动或者 DKMS 包安装后再depmod -a并重新加载。整条链路走完核心就是三个问题硬件在不在总线上、驱动模块有没有加载、驱动模块存在不存在。lsmod恰好是回答第二个问题的第一手工具而且它比dmesg更直接——dmesg可能被大量其他日志刷屏而lsmod永远只给你当下的模块状态。4.1 为什么硬件插上了lsmod 里却没有对应模块很多人不理解设备明明插着为什么系统没有自动加载驱动模块自动加载依赖 udev 和内核的模块别名匹配机制。设备插入时内核通过设备 vendor ID 和 device ID 匹配/lib/modules/$(uname -r)/modules.alias里的规则找到对应的模块名然后调用modprobe加载。如果匹配失败模块就不会自动加载。匹配失败的原因一般有几种模块文件存在但是 alias 信息缺失安装驱动时没有正确注册 alias模块被加入了黑名单模块加载失败导致系统自动放弃。遇到这类情况modinfo打印出的alias行就是排查依据拿实际的 PCI ID 跟它对比就能看出到底匹配不匹配。4.2 模块加载后被自动卸载怎么定位还有一种阴间情况模块加载后几秒钟又不见了。通常是因为它被另一个模块依赖而那模块加载失败连带把它也卸载了或者模块自身的probe函数返回错误设备没有被成功绑定系统会把它清理掉。此时dmesg里一定有线索比如$ dmesg | grep -i xyz配合lsmod | grep xyz反复观察能看到模块加载后又被移除的全过程。这种问题没法靠反复modprobe解决必须根据日志里的具体错误去定位驱动问题或硬件兼容问题。5. 开机自加载、模块参数与黑名单lsmod 看不到的幕后配置lsmod只能反映当前状态但模块为什么是这个状态往往由/etc/modprobe.d/、/etc/modules-load.d/这些目录里的配置决定。想真正驾驭模块管理这几块内容绕不开。5.1 让模块开机自动加载如果某个模块需要在系统启动时就加载比如某些特殊磁盘驱动、加密模块、网络过滤模块常见做法是在/etc/modules-load.d/下新建一个.conf文件内容直接写模块名$ cat /etc/modules-load.d/xyz.conf xyz写完以后重启或者用systemctl restart systemd-modules-load.service就能生效。系统启动时systemd 会读取这些配置并调用modprobe加载。这个机制比传统写在/etc/modules里的方式更规范也更容易按项目区分配置。验证是否生效很简单重启后执行lsmod | grep xyz看模块是否存在。这也是lsmod最经典的验收场景。5.2 给模块传参数有些模块支持运行参数通过modinfo的parm字段能看到全部可配置项。加载时传参的语法是$ sudo modprobe xyz debug1如果希望参数固化下来就在/etc/modprobe.d/下写配置文件$ cat /etc/modprobe.d/xyz.conf options xyz debug1之后无论谁触发加载xyz都会自动带上debug1。调试完记得去掉这个参数否则生产环境会一直输出大量调试日志。模块加载后部分参数还能在运行中查看路径在/sys/module/xyz/parameters/下。比如$ cat /sys/module/e1000e/parameters/debug这给了我们一个不用重启就能核对模块实际生效状态的途径。5.3 黑名单把不想要的模块拦在门外最常见的黑名单需求有两类一是系统里有冲突的驱动比如两个驱动争抢同一设备二是某些硬件带有安全功能但对应的驱动会自动加载希望禁用它。配置黑名单也是在/etc/modprobe.d/下写配置$ cat /etc/modprobe.d/blacklist-xyz.conf blacklist xyz写完以后执行$ sudo update-initramfs -u或者根据发行版重建 initramfs重启后生效。这里有个非常容易踩的坑blacklist关键字只对模块加载器生效。如果目标模块不是独立.ko而是被直接编译进了内核镜像built-in那么黑名单对它无效。举个例子某驱动以内置形式编译进了内核开机后设备直接就被驱动了lsmod里根本看不到它。此时在/etc/modprobe.d/里写blacklist一点用都没有因为在模块加载器介入之前驱动已经在内核里工作了。判断方法是执行$ modinfo xyz输出里的filename如果指向.ko文件说明是可加载模块如果显示类似(builtin)或者路径不是.ko结尾就说明它被编译进内核了。想禁掉这种驱动只能改内核启动参数或者在系统层面屏蔽设备比如借助 udev 规则。5.4 模块依赖的“搭便车”加载很多时候你没主动加载某个模块但lsmod里它在跑大概率是因为某个你常用的功能触发了它的依赖加载。比如插入 U 盘触发vfat蓝牙设备触发一堆蓝牙协议栈模块。这种自动加载是内核模块设计的优点不需要用户手工维护。但反过来也提醒我们想真正禁用某个模块光黑名单它自己还不够还要找出哪些模块会把它拉起来。用前面提到的依赖链脚本把父模块、祖父模块逐个拉出来列黑名单才算彻底。我之前就见过有人禁用了某个安全模块但没禁它的父模块结果开机后父模块自动加载又带着子模块一起加载了lsmod里那个模块还是堂而皇之出现了。排查这个问题的思路就很依赖依赖链分析。6. 我长期使用 lsmod 积累的几个小习惯文章写到最后分享几个我实际工作中养成的习惯算不上什么高深技巧但确实帮我少踩过不少坑。第一个习惯每次装驱动、改模块配置之后都用lsmod前后对比一次。操作前先记录当前状态操作后再看差异能立刻确认改动是否生效。命令简单但要养成习惯尤其是在你执行了depmod、modprobe、update-initramfs这一套组合拳之后光看命令返回码还不够lsmod的确认最直观。第二个习惯用lsmod | grep排查问题时同时列出整行内容而不是只看模块名是否出现。因为 Used by 列很可能带着你需要的额外信息它是被谁引用的、引用计数是多少。很多时候你以为模块加载成功了实际上它是被别的模块顺带拉起来的这就意味着卸载目标模块时你得先处理那个“别的模块”。第三个习惯在写脚本自动化处理模块时优先用modprobe而不是insmod。insmod只是一次性加载指定文件完全不考虑依赖脚本跑起来很容易因为环境差异而失败。modprobe加上-D做预演、-v看过程既安全又可控。第四个习惯也是最重要的遇到内核层面的诡异问题先把lsmod结果和dmesg放在一起看。lsmod告诉你“现在是什么”dmesg告诉你“刚才发生了什么”。两者结合绝大多数驱动问题都能在五分钟内判断出方向而不是靠重启碰运气。这算是我从一次次深夜排查里总结出来的第一条经验分享给你希望能帮你少熬几个夜。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑