ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Linux驱动丢失排查指南:从DKMS到modprobe,一文搞定

Linux驱动丢失排查指南:从DKMS到modprobe,一文搞定 那种“昨晚还好好的今早开机全乱套”的崩溃感用过Linux的人多少都体会过。具体症状大家都熟有线网卡突然没IP了无线网卡直接消失屏幕分辨率糊成一坨或者外接显卡干脆不工作。重启一遍又一遍问题依旧进Windows却一切正常。圈子里管这叫“重启或更新后掉驱动”。我最初折腾Linux驱动的时候也在这个坑里摔过好几次。刚开始以为是硬件坏了后来发现是内核更新后模块没跟上再过一阵子发现重启掉驱动和更新掉驱动根本是两码事原因和修法完全不一样。这篇文章就把我这几年踩过的坑、总结出的排查链路一次性讲清楚。无论你是刚入门的Linux新手还是长期跑生产服务器的运维这套思路应该都能帮你少走很多弯路。那“掉驱动”到底是怎么发生的我自己的理解是Linux下的驱动不像Windows那样是一堆安装包它更像一套可以按需加载的“模块”系统。硬件要被驱动起来得走完一整条链路——内核识别硬件、匹配模块、加载模块、加载固件、注册设备。中间任何一环断了表现出来就是“驱动掉了”。而且这里有个容易忽略的点你平时说的“驱动”在Linux里其实分两个阶段。第一个阶段是引导早期也就是initramfs阶段。这个阶段内核要识别磁盘控制器、文件系统、启动必要的存储设备。如果这个阶段的驱动出问题系统大概率直接起不来或者报“No such device”。第二个阶段是系统完全启动后内核从根文件系统里加载各种模块——网卡、声卡、显卡、USB控制器都在这个阶段。用户平时遇到的“掉驱动”绝大多数属于后者硬件能被识别但对应的模块没有加载成功或者加载了但初始化失败。这就是为什么排查“掉驱动”时我第一件事永远是先看模块加载状态而不是急着重装驱动。1. 内核更新后掉驱动的头号元凶DKMS失效先说最常见的场景你执行了apt upgrade或pacman -Syu更新了一堆包然后重启发现NVIDIA显卡驱动没了、VirtualBox的vboxdrv没了或者某些第三方网卡驱动罢工了。这类问题的根子几乎都在于“内核版本变了但第三方模块用的还是旧内核编译出来的二进制”。Linux内核模块不是“写一次到处跑”的它必须和当前运行的内核版本精确匹配。你打个modinfo nvidia看一下输出里的vermagic那行刻的就是内核版本号。内核一升级旧模块的vermagic和新内核对不上系统就直接拒绝加载。那DKMS是干嘛的全称Dynamic Kernel Module Support它的作用就是维护一份驱动的源代码每当检测到新内核安装时自动重新编译模块保证新内核下也有对应版本的驱动可用。但DKMS不是银弹。我遇到过几次DKMS“失灵”的情况总结下来无非这几类原因第一内核头文件没装。DKMS编译模块需要linux-headers-$(uname -r)这个包很多精简安装的Linux发行版默认不带。没有头文件DKMS就只能报错模块自然编译不出来。第二DKMS编译本身失败。有的驱动源码太老和新内核的API对不上编译直接报错。这种一般要等驱动上游更新或者手动打补丁。第三Secure Boot签名问题。这个后面单独讲它害人的方式非常隐蔽。怎么判断是不是DKMS的问题三步走# 查看当前运行的内核版本 uname -r # 查看DKMS管理的模块及状态 dkms status正常状态下dkms status输出应该是类似这样的nvidia/550.120, 6.8.0-51-generic, x86_64: installed如果模块状态是built但没install或者干脆没有出现那就说明新内核下没有对应的模块。修复方式很直接# 以nvidia为例重新安装当前内核对应的DKMS模块 sudo dkms install -m nvidia -v 550.120 -k $(uname -r) # 然后把模块加载到当前内核 sudo modprobe nvidia如果dkms install报找不到内核头文件先装# Debian/Ubuntu系 sudo apt install linux-headers-$(uname -r) # RHEL/Fedora系 sudo dnf install kernel-devel kernel-headers我不想说“重装驱动”这种话但在这个场景下重装DKMS模块确实是标准解法。关键在于搞清楚为什么DKMS会在新内核上失效而不是每次出了问题才去补救。2. 重启后掉驱动的隐藏凶器模块配置与加载顺序和内核更新不同还有一种更让人抓狂的场景内核版本没变模块文件也好端端在/lib/modules/$(uname -r)/底下躺着但每次重启系统就是不去加载它。这种问题的根源通常藏在你平时根本不会注意的两个目录里/etc/modprobe.d/和/etc/modules-load.d/。2.1 /etc/modprobe.d/ 里的黑名单和金手指/etc/modprobe.d/是模块配置目录里面最常见的命运是blacklist.conf、*.conf里写了blacklist某某模块。这个blacklist的作用是告诉内核这个模块我不让你自动加载。问题就出在这。有些情况下你之前为了临时屏蔽一个坏驱动往blacklist.conf里加了一行后来忘了删。过了一阵子换了个新硬件或者换了个新内核偏偏就需要那个被你屏蔽的模块于是硬件怎么都不工作。我自己就栽过一次一块Realtek 8125网卡装上后系统默认加载了r8169驱动内核自带的通用版本但这个驱动对8125支持不完整网络时断时续。我当时随手在blacklist.conf里加了blacklist r8169然后又手动装了r8125模块问题当时解决了。结果过了几个月某次重启后网卡完全消失——原因就是r8125模块没被自动加载而r8169又被我拉黑了网卡直接处于无人接管状态。这个案例的教训是拉黑内置模块之前一定要确保替代模块有可靠的自动加载机制否则后患无穷。检查方法# 查看所有blacklist配置 grep -r blacklist /etc/modprobe.d/ # 查看所有modprobe配置 ls -la /etc/modprobe.d/2.2 模块没有被加入自动加载列表Linux有自动加载模块的机制内核识别到硬件时会根据设备的vendor ID和device ID去匹配模块。这个机制对绝大多数内置驱动是有效的。但第三方驱动、或者驱动模块依赖比较复杂的时候自动匹配可能失效。这时候就要靠/etc/modules-load.d/来指定“开机必须加载哪些模块”。这个目录下的.conf文件里每一行写一个模块名系统启动时会强制加载。我记得有一回帮朋友排查一个USB转串口的问题。CP2102芯片的驱动模块叫cp210x系统里模块文件都在但每次重启后串口设备就是不出来。查了半天发现是dmesg里根本没有任何cp210x的加载记录。解决方法就是在/etc/modules-load.d/自己写一个配置文件# 在/etc/modules-load.d/下面建一个文件 echo cp210x | sudo tee /etc/modules-load.d/cp210x.conf # 重新生成initramfsDebian系习惯性操作 sudo update-initramfs -u重启后设备就正常了。这个操作的本质就是手动指定了驱动的加载时机绕过了自动匹配可能存在的盲区。2.3 模块冲突与加载顺序还有一种情况比较难查两个模块都想接管同一个硬件设备。这时谁先加载谁就“占领”设备后加载的那个只能叹气。典型的例子就是前面提到的r8169和r8125。这类问题最阴险的地方在于你只在系统日志里看到奇怪的行为比如模块加载成功了但设备就是没被驱动起来。排查方式是先看dmesgdmesg | grep -i r8169\|r8125如果看到类似r8169 has no hardware那样的信息说明设备已经被别的模块认领了。解决办法通常是在modprobe.d里明确加载顺序或者直接blacklist掉不想要的模块。3. 固件文件与initramfs两个最容易被忽略的环节很多新手甚至一些老手都容易忽略的一点是驱动模块本身加载成功不等于硬件就能正常工作。很多硬件还要额外的固件文件firmware才能跑起来。如果说模块是“驱动程序”那固件就是设备自己的“操作系统”。内核把固件数据发送给硬件硬件才能初始化。所以固件缺失或版本不匹配效果等同于驱动没装好。3.1 检查固件加载是否成功固件加载失败的时候dmesg里通常会有非常明确的报错dmesg | grep -i firmware常见输出是这种iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-so-a0-hr-b0-86.ucode failed with error -2error -2的意思是“文件不存在”——内核在/lib/firmware目录下找不到对应的固件文件。处理方法分两步# 先安装固件包 sudo apt install linux-firmware # Debian/Ubuntu sudo dnf install linux-firmware # RHEL/Fedora # 如果固件包已经有了还不行更新initramfs sudo update-initramfs -u有些特殊设备还需要从设备厂商处手动下载固件放进/lib/firmware/后重新加载模块。Intel网卡、部分Realtek无线网卡都出过这种问题。3.2 initramfs为什么重生成能解决一堆怪问题initramfs之于Linux有点像飞机起飞前的检查清单——它在内核真正挂载根文件系统之前先把必要的驱动和工具加载到内存里保证后续系统能正常起来。问题在于如果你改动了/etc/modprobe.d/下的配置或者安装了会生成initramfs钩子的驱动比如NVIDIA、VirtualBox但你没有重新生成initramfs老旧的initramfs里仍然记录着旧的模块配置。开机时早期引导阶段的驱动加载还按老配置来自然容易出问题。所以Debian系发行版有个“万金油”操作sudo update-initramfs -u手动改过模块配置、更新过内核、装过第三方驱动之后最好都跑一遍这个命令。3.3 手动安装的驱动最怕内核头文件缺失说完固件再说说手动安装驱动的场景。很多人喜欢从GitHub上拉驱动源码自己编译安装但编译驱动的必要前提是当前内核的头文件headers必须存在。内核头文件包含了驱动编译时需要的各种API定义与结构体。如果你把旧内核的头文件删了然后又编译了对应版本的驱动模块那这个模块在这个内核查上就是残缺的插进去也不一定能用。还是那句话装驱动之前先确认当前内核版本的头文件是否已安装。4. 从故障现场到定位一份可以直接抄的排查手册前面都在讲原理和常见的坑这一节我直接给出一套完整的排查流程。你照着一步步走大部分“掉驱动”问题都能定位到根因。4.1 先分清是哪一类“掉驱动”拿到故障后先问自己三个问题是更新后重启才掉的还是什么都没动就是重启后掉的是装完新驱动重启就掉还是用了很久才掉所有硬件都掉了还是只有某一个设备掉了这三个问题的答案直接决定你该往哪个方向排查故障场景最大可能原因优先排查方向内核更新后显卡失效DKMS模块未编译/加载dkms status、modinfo更新后网络不可用固件缺失或模块冲突dmesg、modprobe.d重启后某个设备消失模块未被自动加载modules-load.d、dmesg所有设备都异常内核模块目录损坏/lib/modules/$(uname -r)升级后开机直接失败initramfs未更新update-initramfs4.2 现场取证五步法第一步确认当前内核版本和模块加载状态uname -r lsmod | head -30lsmod输出的每一行是当前内核里已加载的模块。看看目标设备的驱动模块有没有在里面。不在说明没加载在说明模块加载了但可能初始化失败。第二步翻内核日志# 本次启动的日志 dmesg -T | tail -100 | grep -iE fail|error|firmware|not found # 上次启动的日志系统曾正常时的对比 journalctl -b -1 -p errdmesg是排查驱动问题的核心阵地。硬件识别失败、模块加载失败、固件加载失败都会在这里留下痕迹。第三步确认模块文件是否还在# 检查当前内核的模块目录 ls /lib/modules/$(uname -r)/kernel/drivers/net/ethernet/realtek/如果模块文件不在了多半是手动删除或者某个清理操作误删了。重新安装对应驱动包即可。第四步直接查看目标模块的信息和依赖modinfo r8169 | head -20如果modinfo提示not found模块文件确实不存在。如果提示vermagic不匹配说明模块内核版本对不上。第五步尝试手动加载模块sudo modprobe r8169 dmesg | tail -20手动加载可以复现故障而且能直接看到报错。如果手动加载成功了问题大概率出在“开机自动加载”的环节。4.3 快速回滚先让系统恢复正常排查归排查但如果你正在生产环境时间不等人。最快的恢复手段是启动到旧内核。大多数发行版默认保留最近几个内核。重启时在GRUB菜单里选择“Advanced options for Ubuntu”或对应的入口就能看到历史内核列表。选上一个正常的内核系统就能恢复。恢复后怎么办两种选择如果确认是新内核导致的驱动不兼容锁定内核版本暂时不要升级到最新。如果想继续用新内核就按上面的步骤在新内核环境下重新编译/安装驱动模块。我个人的习惯是生产服务器上只保留一个最新内核和它的备用版本绝不把所有旧内核删光。谁也说不准什么时候需要回滚。5. 一劳永逸的防守策略把“掉驱动”扼杀在摇篮里前面讲的都是故障发生后的应对。但“掉驱动”这种东西等你撞上了损失可能已经造成了。我更想讲的是怎么让这个事根本不发生。5.1 内核更新前先看清楚更新清单Debian/Ubuntu系执行升级前先看有没有内核更新apt list --upgradable | grep -E linux-image|linux-headers如果确认有内核更新进入系统后第一时间跑sudo dkms status sudo update-initramfs -u systemctl reboot先看一眼dkms status确保第三方模块在新内核下是installed状态再更新initramfs把最新的模块配置打进引导镜像里。这两步花不了三分钟但能避免90%的更新后掉驱动问题。5.2 Secure Boot一个隐藏很深的坑现代Linux发行版默认启用Secure Boot这个机制会拒绝加载没有签名的内核模块。NVIDIA、VirtualBox、某些网卡驱动的DKMS模块通常没有在shim层的信任链里导致内核更新后驱动模块即使编译成功运行时也会被Secure Boot拦截。判断方法mokutil --sb-state如果输出SecureBoot enabled而你的驱动模块加载失败日志里又有这样一个信息Lockdown: modprobe: unsigned module loading is restricted那就是Secure Boot在拦路。解决办法有两种一是在BIOS里关闭Secure Boot简单直接但不是每台机器都方便二是把驱动模块签名加入MOKMachine Owner Key管理sudo mokutil --import /var/lib/shim-signed/mok/MOK.der然后重启按提示完成MOK登记。签名一次之后的内核更新需要重新签名。5.3 写好你的“开机驱动加载清单”前面提到过/etc/modules-load.d/。我强烈建议你在部署好一台机器后顺手把这个目录维护起来。尤其是那些手动编译安装的驱动把模块名写进modules-load.d相当于给系统立了一个规矩开机必须老老实实加载不要指望自动匹配机制每次都靠谱。参照我的习惯# 一次性写入多个模块 sudo tee /etc/modules-load.d/custom-drivers.conf EOF r8125 cp210x v4l2loopback EOF sudo update-initramfs -u5.4 建立“驱动快照”习惯所谓驱动快照就是在系统状态良好的时候把关键模块的加载状态、固件版本、DKMS状态记录下来。这样出了问题你能立刻对比出“之前好好的时候”和“现在坏掉的时候”到底差在哪儿。写个简单的巡检脚本定期跑一遍#!/bin/bash echo 内核版本 uname -r echo DKMS 状态 dkms status echo 关键模块 for mod in nvidia r8169 iwlwifi cp210x; do echo --- $mod --- modinfo $mod 2/dev/null | grep -E version|filename || echo not found done echo 固件错误 dmesg | grep -i firmware.*fail || echo (没有固件错误)这套快照本身不解决任何问题但它能在故障发生时帮你把排查范围缩小一大半尤其是在你记不清自己改过什么配置的时候。6. 我个人的几个“土办法”但每次都管用排查“掉驱动”这些年我总结出几个不像技术方案、但实战中特别管用的习惯在这里分享给大家。第一个习惯改配置之前先备份备份之后写注释。我见过太多人在/etc/modprobe.d/里随手加blacklist加完要么不记得要么忘了为什么加。给配置文件加注释是个好习惯写明“这条配置是为了解决什么而加的、什么时候加的”三个月后回来看你真的会感谢自己。第二个习惯遇到问题先把dmesg和journalctl完整保存下来再动手修。很多人跟我之前一样一看到驱动出了问题就开始试各种命令结果问题没修好原始报错也没了。保存完整的日志后面排查真能救命。第三个习惯永远保留一个“已知良好”的内核和initramfs。我吃过一次大亏一次删旧内核时手滑把最后一个可用内核也删了新内核驱动又没跟上系统直接起不来。从那以后我的服务器上永远至少保留两个版本的kernel一个负责“当前好用”一个负责“出问题时的后路”。最后再说说我对Linux驱动这事的整体感受它在Linux里确实比Windows繁琐但这套模块化机制的好处是——它把每个硬件驱动变成了一个你可以精确控制的独立单元。只要理解了模块从编译、加载到驱动设备这条完整链路绝大多数“掉驱动”问题都能在几分钟内定位和解决。别慌按链路查打开dmesg从底层找原因问题总比办法少。
返回列表