CentOS 7网卡驱动安装实战:从诊断、编译到持久化加载
新装的CentOS 7开机之后网卡灯不亮ip addr看了一圈只有一个lo连eth0都不存在。这种“网卡失踪”的问题十次里有七八次都出在驱动没有正确安装或没有正确加载上。CentOS 7网卡驱动安装这件事听起来好像很简单不就是下载源码、make、make install嘛但实际走一遍就会发现卡住你的往往不是编译本身而是编译之前的判断、环境准备和装完之后的持久化配置。这篇文章我按自己处理过的真实场景来写从“怎么确认网卡真的是缺驱动”开始再到编译安装的每一步、重启后如何不失效最后把最常见的错误和排查过程展开讲。不管你是刚入门的小白还是被驱动折腾过几次的运维都应该能找到对应的一环。1. 先判断是不是缺驱动别急着下载源码我见过不少人一上来就搜“CentOS7网卡驱动安装”然后直接编译折腾了半小时发现根本不是驱动问题是/etc/sysconfig/network-scripts/ifcfg-eth0里ONBOOTno没改。所以第一步不是装驱动是定位问题。1.1 三分钟快速诊断用几条命令看清网卡状态先打开终端执行下面这几条命令把输出记录下来ip link show lspci | grep -i ethernet dmesg | grep -i ethip link show看的是系统里已经注册的网络接口如果里面只有lo说明内核根本没有识别出你的物理网卡。lspci | grep -i ethernet看的是PCI设备列表里有没有网卡存在只要硬件被主板识别这里一定会有输出。dmesg | grep -i eth则能看到内核启动时对网卡设备的枚举和驱动加载日志有时候驱动加载失败了错误原因就在这里。这三条命令的组合能帮你把“设备存在但没驱动”和“设备压根没被识别”分开。我最常遇到的情况是lspci能看到网卡但ip link里没有接口这种情况基本可以确认是驱动没有加载或没有编译。反过来如果lspci里都看不到网卡那就不能只怪驱动了得先检查主板BIOS里网卡有没有被禁用或者PCIe插槽本身有没有问题。1.2 两个经常被误判为“驱动问题”的原因在判断成缺驱动之前我建议顺手确认两件事一是检查 NetworkManager 和 network 服务到底谁在管理接口。CentOS 7 默认用 NetworkManager但很多服务器场景会把 network 脚本也启用两个服务一起管同一个接口时偶尔会出现接口状态异常。执行systemctl status NetworkManager和systemctl status network看看。二是直接手写一个 ifcfg 文件测试不要依赖图形界面。有时候网卡驱动其实装好了但系统里根本没有对应的配置文件接口自然起不来。比如我知道网卡是eth0就直接在/etc/sysconfig/network-scripts/下建ifcfg-eth0内容按最小化写BOOTPROTOdhcp、ONBOOTyes、DEVICEeth0然后systemctl restart network试一下。如果接口能起来说明驱动没问题问题出在配置如果依然起不来再往驱动方向查。1.3 驱动、内核模块、接口三者的关系这里需要先把概念理清后面编译时才不会迷糊。硬件网卡和一个网卡接口之间隔着一层内核模块也就是驱动。内核模块负责把硬件行为翻译成网络栈能理解的操作。模块加载成功接口才会被注册出来接口被注册出来之后还需要用户态的配置工具比如 NetworkManager 或 network 脚本给它分配IP和启动状态。很多时候你觉得“驱动没装”实际上内核里有对应模块但模块没有被自动加载。这种情况在系统里存在多个候选驱动时特别常见。比如一个网卡芯片同时有两个驱动模块都声称支持它内核自动选择时选错了接口就出不来。所以后面的章节里我会专门讲如何确认当前实际绑定的是哪个模块以及怎么用modprobe手动干预。理解这条链路你就知道每一步操作到底在解决哪一环的问题。2. 编译前的准备工作内核版本、编译工具链与源码获取确认网卡确实缺驱动之后下一步不是马上编译而是先确认你这台机器的内核版本、编译工具链和驱动源码三者是否匹配。这里不匹配后面百分之百会报错。2.1 为什么驱动源码必须匹配内核版本Linux驱动在CentOS 7上通常以内核模块.ko文件形式存在模块是直接加载进内核地址空间里运行的所以它的编译必须是针对当前内核版本进行的。你可以把内核理解成一套固定的API接口驱动模块则是调用这套接口的程序。内核升级了接口细节变了旧模块自然不能直接加载。实操里最典型的体现就是make的时候系统会去/lib/modules/$(uname -r)/build下找内核头文件。如果你的内核版本是3.10.0-1160.el7.x86_64那依赖的头文件也必须是这个版本。用uname -r把当前内核版本记下来后面所有操作都以这个为准。uname -r输出类似3.10.0-1160.el7.x86_64这就是当前运行的内核版本。2.2 gcc、make、kernel-devel一个都不能少编译内核模块你需要三样东西C编译器gcc、构建工具make、内核开发包kernel-devel。很多人以为只装gcc就够了等到编译时发现找不到linux/version.h或generated/autoconf.h才明白缺了kernel-devel。检查是否已安装rpm -qa | grep gcc rpm -qa | grep make rpm -qa | grep kernel-devel如果没装在能联网的情况下用 yum 安装yum install -y gcc make kernel-devel-$(uname -r)注意kernel-devel版本要跟uname -r严格对应。有时候你执行yum install kernel-devel安装的可能是仓库里最新的内核版本而你正在运行的是旧内核这样版本就对不上了。所以我习惯写成kernel-devel-$(uname -r)的形式确保安装的就是当前内核对应的开发包。这里特别提醒一下kernel-devel和kernel-headers不是一回事。kernel-headers主要给用户态程序编译用路径在/usr/include/linuxkernel-devel才是给内核模块编译用的路径在/lib/modules/$(uname -r)/build。缺少后者驱动源码里的Makefile会因为找不到头文件而直接报错。2.3 离线环境下的依赖准备服务器经常是不允许上外网的。这种情况我一般分两种思路处理。第一种找一台同系统版本、同内核版本的机器用yumdownloader把 gcc、make、kernel-devel 以及所有依赖的 rpm 包下载下来拷到U盘里带到目标机器上然后rpm -Uvh *.rpm本地安装。第二种用CentOS 7的系统安装光盘或ISO镜像做本地 yum 源安装源里的开发工具组。离线安装时要注意 rmp 依赖顺序问题稳妥的做法是把所有 rpm 放在同一个目录里一次性rpm -Uvh *.rpm让系统自动处理依赖。如果你只是拿了个 kernel-devel 的 rpm 过去发现装不上多半是因为缺了elfutils-libelf-devel之类的依赖把这些包也一并带上。2.4 去哪里下载驱动源码渠道和辨别方法下载驱动源码的渠道最靠谱的是网卡芯片厂商的官网支持页面按芯片型号搜索对应的Linux驱动包。这里强调“芯片型号”而不是“网卡品牌”是因为同一块网卡上用的芯片可能来自不同厂商驱动是跟芯片走的不是跟网卡牌子走的。怎么确认芯片型号还是用lspci -v查看输出里会包含芯片厂商和Device ID。另外lspci -n可以只列出PCI vendor ID和device ID比如常见设备ID8086开头的是某家的芯片10ec开头的是另一家。你在厂商官网搜索框里输入这些ID往往能直接定位到对应驱动。如果没有设备ID把lspci -vvv完整输出记下来再配合dmesg | grep -i pci的信息综合判断。源码下载后不要急着解压编译。我习惯先看压缩包里的README或INSTALL文件确认它支持的内核版本范围。有些老驱动对3.10内核支持良好有些则停更在了旧版本硬编译会一路报错。README 里一般会写明确的适用环境多花两分钟看文档能省下后面一小时。3. 驱动源码编译安装每一个命令的意图和坑环境准备好、源码也拿到手之后就可以开始编译了。我只是拿最常见的 tar.gz 源码包来示范具体文件名以你下载到的为准。3.1 解压源码包并阅读说明文件tar -zxvf driver.tar.gz cd driver ls -l解压后第一时间读README或INSTALL。这两个文件里写明了编译前是否要修改Makefile、目标内核版本、需要额外定义的宏等还可能提到依赖的其他模块。我遇到过一次某老驱动在3.10内核编译时需要手动在Makefile里加一个CFLAGS选项README 写在第4页不细看根本发现不了。3.2 编译前确认Makefile里的内核路径驱动源码的Makefile里有几行很关键的配置决定了编译时去哪个目录找内核头文件KSRCDIR ? /lib/modules/$(shell uname -r)/build好的Makefile会动态获取当前内核版本。但也有写死路径的比如KSRCDIR ? /usr/src/kernels/2.6.32-xxx这种老掉牙的东西直接编译必挂。手动打开看一下如果发现路径写死了内核版本号改成上面这种动态形式。这一步很少有人提但确实会发生在从旧项目里翻出来的驱动源码上。3.3 make和make install到底做了什么make make installmake的工作是把源码编译成.ko文件。编译内核模块时Makefile 会自动调用kernel-devel里的编译脚本把头文件、配置信息全部打包进模块里。这个过程耗时长短取决于源码量从几秒到几分钟不等。成功后当前目录下会出现.ko文件。make install则是把编译好的.ko文件复制到系统的模块目录通常是/lib/modules/$(uname -r)/extra/或/lib/modules/$(uname -r)/kernel/drivers/net/ethernet/。如果执行完make install却看不到文件可以试试手动复制cp xxx.ko /lib/modules/$(uname -r)/extra/手动复制完之后必须运行depmod更新模块依赖关系让系统在modprobe xxx时能找到这个模块depmod -adepmod会扫描/lib/modules/$(uname -r)/下所有.ko文件生成modules.dep等依赖映射。不执行这一步modprobe极可能提示Module xxx not found。3.4 加载模块modprobe和insmod用哪个更好加载驱动有两个命令insmod和modprobe。insmod只负责把一个具体的.ko文件塞进内核不会解析依赖modprobe则会根据depmod生成的依赖信息自动加载所有需要的模块。所以日常操作优先用modprobemodprobe xxx lsmod | grep xxxmodprobe成功了lsmod | grep xxx应该能看到模块已经加载。此时再用ip link查看接口是不是已经出现了。这一步会让人很有成就感但别急接口出现了只是第一步还有很多细节要收尾。如果modprobe报了错先把错误信息抄下来。常见的有Unknown symbol、Invalid module format、No such file or directory三种我在第5章会逐个展开讲。这里先记住一个原则错误信息永远比感觉管用别靠猜。3.5 验证驱动真正绑定到了硬件上接口出现后用下面两条命令确认驱动确实在干活lspci -k | grep -A 3 -i ethernet ethtool -i eth0lspci -k会显示“内核驱动正在使用”的模块名ethtool -i eth0则直接告诉你接口绑定的驱动名和固件版本。如果这两个输出的模块名和你刚安装的一致说明驱动绑定成功。如果lspci -k里显示的还是原来的老模块名说明内核优先加载了旧驱动后面需要用黑名单机制把旧驱动禁掉。4. 重启不失效驱动持久化加载与旧驱动冲突处理编译安装成功只是第一步真正的考验是重启。很多人在这里功亏一篑重启之后网卡又消失了是因为新驱动模块没有被自动加载。4.1 用modprobe.conf实现开机自动加载最简单可靠的持久化方式是把模块名写进/etc/modules-load.d/下的配置文件。CentOS 7 默认会读取这个目录里的配置并在开机时自动加载对应模块。比如创建一个文件echo xxx /etc/modules-load.d/xxx.conf光这样还不够因为驱动模块还可能依赖别的模块。比如某些网卡模块依赖mdio、phy之类的公共模块modprobe会自动处理依赖但开机阶段的modules-load.d机制不一定帮你完整解析依赖。更稳妥的做法是同时把依赖模块也写进去或者直接使用modprobe.conf里的install指令。另一种传统方式是编辑/etc/modprobe.conf或/etc/modprobe.d/xxx.conf加入alias eth0 xxx options xxx paramvalue这个alias写法不仅是“开机加载”还告诉内核当系统要使用名为eth0的接口时应该加载xxx模块。在老系统中很管用。4.2 新驱动和旧驱动冲突用blacklist禁用不用的模块如果系统里同时存在旧驱动和新驱动且两个模块都声称支持同一个硬件内核加载时通常依据模块内的设备ID表来决定用谁。但实际会出现在/lib/modules/$(uname -r)/kernel/drivers/net/下的旧模块被优先加载新模块根本没机会绑定硬件。解决办法是给旧驱动上黑名单让内核启动时忽略它echo blacklist old_driver /etc/modprobe.d/blacklist-old-driver.conf黑名单只影响“自动加载”如果你手动执行modprobe old_driver它依然能加载。所以排查时如果你不确定到底是谁绑定了网卡优先用lspci -k看实际绑定的模块名再决定黑名单谁。别平白无故把系统里一堆模块都禁了。4.3 把initramfs也刷新一下如果你改动了/etc/modprobe.d/下的配置还需要重新生成一下 initramfs。因为在CentOS 7启动时initramfs 里会包含一部分必须提前加载的驱动模块如果你的网卡驱动要在早期启动阶段就生效不刷新 initramfs 可能不生效。执行mv /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.bak dracut /boot/initramfs-$(uname -r).img $(uname -r)刷新并重启后用lsmod和ip link验证一下模块是否自动加载。这一步虽然不是每次都必须做但遇到重启后驱动不生效时它是优先怀疑对象。4.4 完整验证重启效果配置完成后先别急着收工。执行reboot然后重新登录按下面的顺序检查ip link show lsmod | grep xxx ethtool -i eth0如果接口存在、模块加载、驱动绑定正确说明持久化搞定。如果接口还是没出现先把dmesg里和网卡相关的输出抓出来看内核启动时会打印驱动加载日志和错误信息。这一步比瞎改配置更有价值。5. 实战排错我遇到过的四类典型错误和处理链路驱动编译安装过程中的报错种类其实不多。我整理出四类最常见的每类我都按“错误现象、排查过程、最终解决”的结构来写你可以直接对照。5.1 make时报错找不到头文件kernel-devel版本不对错误信息通常长这样make[1]: *** /lib/modules/3.10.0-1160.el7.x86_64/build: No such file or directory. Stop.这个报错最直接的含义是/lib/modules/$(uname -r)/build这个软链接指向的目录不存在。执行ls -l /lib/modules/$(uname -r)/build如果显示build - /usr/src/kernels/3.10.0-1160.el7.x86_64而右侧目录不存在说明 kernel-devel 没装或版本不对。解决办法就是装对应版本的 kernel-devel。注意有时候你在/usr/src/kernels/下能看到一个内核版本目录但和你uname -r不一致这也是版本不匹配的典型情况。我遇到过一次更隐蔽的build软链接指向存在但里面的Makefile是空的。原因是之前有人手动删过内核源码目录。这种情况重装一遍同一个版本的kernel-devel就能解决。5.2 make时报错Unknown symbol或编译错误内核头文件与源码不匹配比如ERROR: xxx [/root/driver/xxx.ko] undefined!或者错误未知的类型名‘foo’undefined类型的报错通常说明驱动源码里引用了一个当前内核版本不存在的函数或结构体。老旧驱动在新内核上编译时这是常见的兼容性问题。处理思路有三个方向一是确认你是不是在两个不同内核版本之间来回编译过残留的.o文件会污染构建先make clean再重新make二是到源码官网找有没有针对新内核的补丁或更新版本三是手动修改源码里对应的函数调用改成新内核的API。第三个方向比较硬核适合熟悉内核编程的人。以我的经验大多数时候换个新版本的官方驱动就能解决厂商通常会在新版里适配新内核。5.3 modprobe提示Module not found没跑depmod或安装路径不对modprobe: FATAL: Module xxx not found.这个报错只有两种可能.ko文件不在/lib/modules/$(uname -r)/下的任何目录里或者modules.dep里没有对应记录。先确认文件位置find /lib/modules/$(uname -r)/ -name xxx.ko如果找不到说明make install没把文件复制到正确位置手动复制后再执行depmod -a。如果文件存在但modprobe依然报错很可能是路径不在标准扫描范围。比如你自己放在了/root/下系统当然找不到。把.ko文件统一放进/lib/modules/$(uname -r)/extra/是最省心的选择。5.4 insmod提示Invalid module format内核版本不一致如果你用insmod ./xxx.ko方式加载报错insmod: ERROR: could not insert module xxx.ko: Invalid module format这句话的含义是模块的 vermagic 和当前内核不一致。每个内核模块在编译时会带一个魔术字符串包含它针对的内核版本和默认编译选项。系统加载时会校验这个字符串不一致就拒绝加载。最典型的场景是你在旧内核下编译了模块然后升级了内核但没重启仍然用旧内核跑此时你加载的模块是旧版本号编译的会报格式不匹配或者你在内核A下编译后来重启到了内核B也会报错。解决办法是切换到编译时对应的内核或者干脆在当前内核下重新编译一遍。用modinfo xxx.ko可以查看模块的 vermagic 信息modinfo xxx.ko | grep vermagic和uname -r对比一下一眼就能看出是否匹配。5.5 驱动装好了但接口还是没出现查 dmesg 和 lspci 绑定情况最后一种情况最让人沮丧模块加载成功了lsmod也能看到但ip link里依然没有新接口。此时按顺序排查dmesg | tail -50 lspci -k | grep -A 3 -i ethernetdmesg里能看到驱动注册时的日志如果提示no such device说明驱动模块声称支持的设备ID和你实际网卡的设备ID对不上也就是驱动和硬件不匹配。lspci -k则能看到到底是谁占用了这个设备。如果显示其他模块正在使用你的驱动即使加载了也没机会绑定硬件这时候就用第4章讲的黑名单机制处理。还有一种容易被忽略的情况网卡被ip link set down了当然看不到状态。别笑我确实见过驱动正常、配置正常最后发现是接口被手动down掉了。ip link set ethX up一下立刻恢复。6. 这些操作背后的几条经验总结驱动安装做完收尾之前把几个经验记下来省得下次再踩。6.1 能联网就联网源配置弄好别用离线硬扛CentOS 7 的软件源配置是件小事但关系重大。如果机器能上外网先把/etc/yum.repos.d/里的源配好装什么依赖都简单。如果必须离线我强烈建议在第一次部署时就准备一个完整离线rpm包目录里面放好 gcc、make、kernel-devel、elfutils-libelf-devel 等常见依赖。不要等到缺了再到处找那会非常被动。6.2 装了驱动一定要把“持久化”和“冲突”一起考虑很多新手只做到make install modprobe就以为完事了。实际上生产环境里重启验证是必须的。我处理过的案例中至少三成是“当前能通、重启必挂”原因不外乎模块没配置开机自动加载、黑名单没写、initramfs没刷新。记住重启是驱动安装的最终裁判。6.3 报错信息比感觉可靠记录比记忆可靠处理驱动问题的时候把每一步终端输出保存下来尤其是dmesg和modprobe的报错。我习惯用tee把输出同时写入日志文件make 21 | tee make.log modprobe xxx 21 | tee modprobe.log这样排查的时候可以反复看完整的错误上下文有时候一个报错的前几行信息才是关键。6.4 谨慎升级内核升级后驱动要重编译最后一条经验关于内核升级。CentOS 7 系统在线升级内核之后之前手动编译的网卡驱动模块基本都会失效因为内核API变了、模块目录也变了。升级完内核后请主动检查一下网卡是否正常不正常就进入/lib/modules/新内核版本/目录重新编译安装一次驱动。这是很多人忽略的维护项。CentOS 7网卡驱动安装这件事原理并不复杂但每一步都有讲究。从判断缺不缺驱动到编译环境准备再到装完之后的持久化和冲突处理环环相扣。你按照这个流程走一遍再遇到类似问题基本都能自己定位到具体环节。真到了某个报错卡住的时候记住一句老话回到报错本身去它已经告诉你答案了。