简介:面向CentOS 7离线环境下的运维人员,压缩包内完整覆盖R8125网卡驱动安装所需的全部组件。包内不仅包含r8125-9.007.01驱动源码、autorun.sh一键安装脚本及编译生成的ko模块,还专门收集了14个rpm离线依赖包,从gcc、gcc-c++到glibc、kernel-headers等一应俱全,并针对官网驱动在CentOS 7下的编译报错做了修正,省去因缺失开发工具链而反复折腾的麻烦。整个压缩包共52个文件,大小约46MB,除rpm外还有c/h源码、Makefile、命令记录、txt说明及模块符号文件等,类型齐全,目录结构也按驱动源码与gcc依赖分开整理,便于对照检查。目前已有3268人学习下载,适合需要在内网或无外网环境中快速为R8125网卡装载驱动的用户,按包内说明依次执行即可完成离线编译安装,并能理解常见依赖错误的解决方法。
1. 装完CentOS 7才发现板载网卡不亮:R8125离线安装到底卡在哪
新买的板子自带2.5G网口,系统装好后发现ip link里只有一个lo,网卡灯根本不亮。查lspci能看到设备,但dmesg里没有任何驱动加载记录,这类板载2.5G网卡绝大多数用的就是R8125芯片,CentOS 7自带内核里只有老旧的r8169模块,它对R8125的支持极不完整。更要命的是,目标是台离线机器,yum没有网络可用,想临时装个编译工具链都成了问题。
这篇笔记解决的就是这个完整链路:离线状态下把gcc和 kernel-devel 给你备齐,拿到官网R8125驱动源码后现场改掉两个编译坑,最终让网卡稳定撑过多次内核升级。整个过程不需要外网,只需要一台同版本CentOS 7的在线机器帮你拉rpm包,加一个U盘。适合所有做内网部署、机房交付,或者手里只剩一台断网服务器的运维工程师参考。
2. 离线环境装R8125的三个前置条件:内核驱动、依赖链和工具链
2.1 内核里没有R8125,只有R8152:先确认你的网卡是PCIe还是USB
很多人在这一步就搞混了。内核里自带的r8152驱动是针对USB接口的2.5G网卡芯片的,比如USB转2.5G的网卡适配器,它和主板板载的PCIe网卡走的完全是两条路。R8125是PCIe接口的以太网控制器,内核自带模块列表中并不存在对应的原生驱动,所以系统装完自然找不到它。
确认网卡属于哪一类,直接用最朴素的命令:
# 查看PCI总线上所有以太网控制器 lspci -nn | grep -i ethernet # 查看当前设备被哪个驱动接管 lspci -k | grep -A2 -i ethernet第一行输出的设备名里如果明确写着RTL8125或R8125字样的,就是PCIe版R8125。第二行的Kernel driver in use如果显示的是r8169,说明当前是老驱动在硬扛,虽然能识别到设备,但无法发挥2.5G速率,甚至可能出现不定时断流。如果这一行是空的,说明没有任何驱动绑定它。
这个确认步骤不要跳过。曾经有人把USB的R8152和PCIe的R8125混为一谈,最后折腾了整整两天,还以为是gcc装坏了。网卡芯片型号不同,后续下载的驱动源码完全不同,官网针对R8152和R8125分别提供独立的驱动包,下错一个,编译到天亮也起不来。
2.2 在线能一条yum搞定,离线必须凑齐依赖链
在线安装R8125的标准做法非常简单:yum装上gcc和kernel-devel,解压驱动源码,make,insmod,完事。但离线环境的麻烦在于——目标机器连网卡驱动都没有,自然不联网,yum源形同虚设。你需要在另一台同版本CentOS 7的在线机器上,把编译驱动所需的全套rpm包拉下来,用U盘或内网传输到目标机,再本地安装。
这里的核心问题是依赖链。gcc本身依赖cpp、glibc-devel、libmpc等一系列包,kernel-devel又依赖kernel-headers、elfutils-libelf-devel。如果只是单独下载一个gcc的rpm拷过去,安装时会直接被依赖检查卡死。网上很多教程让你直接rpm -Uvh gcc.rpm --nodeps,这是能装上,但将来编译别的内核模块时大概率翻车,因为关键头文件根本没配齐。
所以离线安装的思路应该是:以编译R8125驱动为最终目标,反向列出完整工具链,用repotrack把所有依赖于包全部拉到一个目录里,整体搬运,整体安装。而不是缺一个装一个,那样依赖图永远补不完。
2.3 编译驱动真正需要的工具链与内核头文件清单
以下是我在CentOS 7.9 x86_64环境下编译R8125驱动实际用到的软件包清单,给离线环境用的机器上必须确保都有对应版本的rpm文件:
| 软件包 | 用途 | 是否必须 |
|---|---|---|
| gcc | C编译器,编译驱动主代码 | 必须 |
| gcc-c++ | 部分源码段含C++代码时使用 | 建议 |
| make | 执行Makefile构建流程 | 必须 |
| kernel-devel | 提供内核头文件、生成module所需build目录 | 必须 |
| kernel-headers | 提供用户态与内核交互头文件 | 必须 |
| elfutils-libelf-devel | 部分内核模块编译会用到libelf符号 | 建议 |
有一个关键点必须记住:kernel-devel的版本必须和目标机器上uname -r的输出完全一致。比如目标机内核是3.10.0-1160.el7.x86_64,那kernel-devel也必须是这个精确版本,连小版本号都不能差。原因是驱动编译时会到/lib/modules/$(uname -r)/build这个目录里找内核头文件,如果这个目录指向的版本和实际运行内核不一致,编出来的模块即使加载成功,也会因为符号表不匹配而在insmod时直接报Unknown symbol错误。
3. 离线构建gcc与开发工具链:rpm打包到本地安装
3.1 在一台同版本CentOS 7机器上把依赖rpm全部拉下来
准备一台能够联网、且系统版本与目标机一致的CentOS 7机器。版本必须一致,因为不同小版本的rpm可能有依赖差异,尤其glibc和libgcc这类底层包,混用版本轻则装不上,重则把系统搞坏。
在在线机器上执行下面的命令,把整套工具链打成一个目录:
# 安装yum-utils以获取repotrack工具 yum install -y yum-utils # 创建存放rpm的目录 mkdir -p /tmp/r8125-toolchain # 拉取所有依赖包,不安装,只下载 repotrack gcc gcc-c++ make kernel-devel kernel-headers elfutils-libelf-devel \ --arch=x86_64 --destdir=/tmp/r8125-toolchain # 查看拉下来的包数量 ls /tmp/r8125-toolchain | wc -l这里的repotrack比yumdownloader --resolve更彻底,它会把指定包及其所有依赖全部下载到--destdir指定目录,而且不做安装。--arch=x86_64限定架构,避免拉到i686的包造成混淆。如果在线机器的yum源里没有kernel-devel对应版本,可以提前在/etc/yum.repos.d/里配置[base]源指向镜像站,然后yum clean all && yum makecache刷新缓存。
下载完成后,把整个目录拷贝到U盘。注意最后复制到目标机的时候,保留目录内所有rpm文件,不要只挑其中几个,因为你无法预知目标机上哪些基础包已经是老版本、哪些缺失。全量搬运,到目标机再做取舍。
3.2 目标机本地安装gcc、make与kernel-devel
目标机上没有任何yum源可用,安装时不能走yum install,而要改用yum localinstall。这个命令会把当前目录下的rpm文件当作本地源,自动解析依赖关系,并在本地rpm集合中找依赖。如果依赖的包正好也在目录里,就能顺利完成安装。
# 进入存放rpm的目录 cd /tmp/r8125-toolchain # 先安装kernel相关包,确保内核头文件先就位 rpm -Uvh kernel-headers-*.rpm kernel-devel-*.rpm # 再安装gcc和make rpm -Uvh gcc-*.rpm gcc-c++-*.rpm make-*.rpm libmpc-*.rpm cpp-*.rpm # 验证工具链 gcc --version make --version先装kernel再装gcc是有讲究的。kernel-devel的rpm在安装时不会要求gcc一定存在,但反过来gcc的某些依赖包可能要求内核头文件版本匹配,这个顺序能减少不必要的依赖冲突。装完后检查/lib/modules/$(uname -r)/build是否存在且是一个软链接或目录,这一步决定了后续驱动编译能不能找到内核头文件。
如果机器之前装过旧版本gcc,这里可能提示file conflicts或package gcc-xxx already installed,先rpm -e gcc --nodeps再装新的,但代价是旧gcc的依赖也会被留下,不影响编译结果,只是目录里会多一些冗余文件。
3.3 升级到gcc 7.3.1:解决驱动对编译器版本的硬性要求
CentOS 7默认gcc版本是4.8.5,这个版本编译旧版R8125驱动没问题,但较新的官网驱动在Makefile里使用了部分新语法,或者对C标准要求更高,gcc 4.8.5下编译会冒出一堆warning: implicit declaration of function之类的告警,甚至直接报错中断。标题里提到的“gcc+”,实际工作中最常见的高版本gcc方案是用devtoolset-7,可以把gcc升到7.3.1,这个版本对内核模块编译非常稳定。
先在在线机器上准备devtoolset-7的rpm包:
# 启用SCL软件集仓库 yum install -y centos-release-scl-rh # 下载devtoolset-7相关包 mkdir -p /tmp/dts7 yumdownloader --destdir=/tmp/dts7 --resolve devtoolset-7-gcc devtoolset-7-gcc-c++然后把/tmp/dts7里的rpm全部拷到目标机,执行安装:
cd /tmp/dts7 rpm -Uvh *.rpm # 启用高版本gcc环境 source /opt/rh/devtoolset-7/enable # 确认当前gcc版本 gcc --version这里的关键点是source /opt/rh/devtoolset-7/enable只是临时修改当前shell的环境变量,把gcc、g++的路径切换到7.3.1。新开终端或者重启后,默认的gcc还是4.8.5。所以在后面编译R8125驱动之前,必须先执行这个source命令,或者直接把这一行写进/etc/profile.d/下新建的脚本里,让每次登录shell都自动启用。
选择devtoolset而不是下载gcc源码自己编的原因很简单:源码编译gcc需要gcc,这是一个鸡生蛋问题。更麻烦的是,编译gcc还依赖gmp、mpfr、mpc等库,离线状态下凑齐这些库的编译依赖,已经够跑完一遍R8125驱动了。devtoolset的rpm是官方预编译好的,依赖关系清晰,离线安装最省事。
4. 编译R8125驱动与官网源码的两个必改点:Werror和头文件
4.1 解压驱动源码并确认Makefile会用哪个内核目录
从官网下载对应R8125芯片的驱动源码包,文件名通常是r8125-<版本号>.tar.bz2的形式。把它拷贝到目标机,解压到/usr/src目录下:
# 解压源码包,注意文件名中的版本号要替换成实际值 tar xjf r8125-9.xxx.tar.bz2 -C /usr/src # 进入源码目录 cd /usr/src/r8125-9.xxx # 查看Makefile里的内核目录指向 grep -n "KSRC\|KERNELDIR\|KDIR" Makefile | head -20解压后第一件事不是急着执行make,而是确认Makefile支持的内核路径是/lib/modules/$(shell uname -r)/build这种动态写法,还是写死了某个路径。很多版本的官网驱动默认用KSRC := /lib/modules/$(shell uname -r)/build,这种写法最省心。但也看到过某些老版本写的是绝对路径/usr/src/kernels/2.6.32-xxx,这种路径在CentOS 7下完全不存在,必须手动改成上面的动态形式。
检查完Makefile后执行ls /lib/modules/$(uname -r)/build,确认目录存在。如果这一步报错,说明前面kernel-devel没装好,回去查uname -r的版本号与你安装的kernel-devel版本是否一致。这个目录是编译的核心依赖,它里面包含了Makefile、Kconfig、generated/autoconf.h等一整套内核构建系统文件。
4.2 用gcc 7.3.1编译时必改的-Werror问题
在正式编译前,先把编译器的版本切换到位:
source /opt/rh/devtoolset-7/enable # 执行编译 make clean make -j4如果这一段编译直接报错,最常见的是两类问题。第一类是与-Werror相关:官网驱动部分版本在Makefile里加了-Werror,把所有warning都升级为error。在gcc 7.3.1下,内核模块编译时经常会有少量的无害告警,比如变量未使用、隐式函数声明等,这些在gcc 4.8.5下只是warning,到gcc 7.3.1下依然只是warning,但只要Makefile带-Werror,就会直接中断编译。
修改方式是找到Makefile里MOD_CFLAGS相关的行,追加两个编译参数:
# 在Makefile中追加-Wno-error,让warning不再终止编译 sed -i 's/^MOD_CFLAGS.*/& -Wno-error -Wno-implicit-function-declaration/' Makefile # 确认修改结果 grep "MOD_CFLAGS" Makefile-Wno-error表示即使出现warning也继续编译,-Wno-implicit-function-declaration则专门屏蔽隐式函数声明的告警。这两个参数对内核模块编译是安全的,不会影响最终生成的模块质量。改完后重新执行make -j4,大部分情况下都能顺利通过。
4.3 头文件缺失或隐式声明:正确补include的两种姿势
第二类常见编译错误是头文件缺失导致的implicit declaration of function或unknown type name。这类错误在gcc 7.3.1下会显示具体是哪个函数或类型出了问题。比如内核模块源码用到vmalloc却没包含<linux/vmalloc.h>,或者用到VLAN相关接口却没包含<linux/if_vlan.h>。
处理办法是在驱动源码的主头文件(通常是src/r8125.h)顶部补齐include:
# 先看当前包含的头文件 head -50 src/r8125.h # 用vim或sed在#include区域追加缺失头文件 sed -i '/#include <linux\/kernel.h>/a #include <linux/vmalloc.h>\n#include <linux/if_vlan.h>' src/r8125.h # 再次编译 make -j4这里补include都要加在源码文件开头部分,确保在用到这些函数前都完成声明。不同内核版本对头文件的组织方式有差异,CentOS 7的内核比较老,很多在新内核里已经自动包含的头文件在这里需要手动加。这是老驱动放在新系统上编译时的普遍现象。
如果头文件缺失但不确定具体缺哪个,最直接的定位方式是把编译输出重定向到文件里,然后greperror: invalid use of undefined type这类关键字,每一个报错对应一个缺失的头文件。一次补齐后再编译,比反复试错效率高得多。
4.4 insmod加载驱动并让网卡名出现在ip link里
编译通过后会生成src/r8125.ko文件。先手动加载看效果:
# 加载驱动模块 insmod src/r8125.ko # 查看加载日志 dmesg | tail -20 # 查看网卡是否出现 ip link正常情况下,dmesg里会出现类似r8125: probe device with 00:xx:xx:xx:xx:xx的日志,ip link里会多出一张网卡,名称可能是eth0,也可能是enp3s0这类基于PCI总线位置的命名。如果ip link里没有任何新网卡,先别急,下一章会讲这个现象对应的排查点。
加载成功后再设置IP测试连通性:
# 将网卡启用并通过DHCP获取地址 ip link set dev eth0 up dhclient eth0 # 查看IP ip addr show eth0拿到IP说明驱动已经正常工作。但此时驱动还只是临时的,重启即失效,需要做持久化和后续的DKMS收尾,这些内容放在第5、6章展开。
5. R8125离线安装避坑:五条实操踩坑记录
5.1 insmod报Unknown symbol:内核开发包版本对不上
现象:编译一切顺利,执行insmod src/r8125.ko时却弹出insmod: ERROR: could not insert module r8125.ko: Unknown symbol in module。后面跟着一长串Unknown symbol列表。
原因:驱动编译时使用的内核头文件与当前实际运行的内核版本不匹配。最常见的是目标机uname -r显示3.10.0-1160.el7.x86_64,但安装的kernel-devel是3.10.0-1127.el7版本,编译和运行时内核模块符号表对不上。另一种可能是在线机器打包rpm时,顺手把自己的kernel-devel也打进去了,版本比目标机内核新很多。
解决:重新核对版本,必须完全一致后再重编。目标机器上执行uname -r,在线机器上按这个版本搜索对应的kernel-devel rpm重新打包。装好后检查ls -ld /lib/modules/$(uname -r)/build,确认软链接存在且指向正确目录,再重新make clean && make。
5.2 驱动装好网卡却不出现:r8169模块抢占的锅
现象:insmod成功了,没有报错,但ip link里死活看不到新网卡,或者看到网卡被识别为r8169且协商速率只有100Mbps。
原因:内核自带的r8169模块在系统启动时提前加载,并且成功绑定了网卡设备。R8125驱动无法同时接管已经被r8169占用的设备,两个驱动的probe顺序决定了谁最后控制网卡。
解决:把r8169加入黑名单,然后卸载并重新加载R8125驱动。
# 编辑黑名单配置 cat > /etc/modprobe.d/blacklist-r8169.conf <<'EOF' blacklist r8169 EOF # 卸载r8169,并移除当前已加载的r8125再重新加载 modprobe -r r8169 rmmod r8125 modprobe r8125这里注意modprobe和insmod的区别:modprobe会自动处理模块依赖,并在/lib/modules/$(uname -r)目录下搜索模块文件;insmod只加载指定路径文件,不做依赖解析。黑名单写入后需要重建模块依赖关系,执行depmod -a,然后重启再验证一次。
5.3 yum localinstall装依赖时版本冲突
现象:在目标机执行rpm -Uvh gcc-*.rpm时提示package gcc-4.8.5-44.el7.x86_64 conflicts with file from package gcc-4.8.5-44.el7.x86_64,或者报Protected multilib versions错误。
原因:目标机原本就带有一个老版本的gcc,本地rpm目录里又有一个不同小版本的gcc,两个包的文件互相覆盖。另一种情况是打包时混用了官方base源和EPEL源,导致依赖链内部的glibc版本要求冲突。
解决:先把目标机上已有的编译器相关包卸载干净,再统一安装新的一整套。用rpm -qa | grep -E '^(gcc|gcc-c++)'查出现有包,逐个rpm -e移除。之后重新安装。如果仍顽固报冲突,可以退一步用rpm -Uvh *.rpm --nodeps --force,但前提是你自己清楚这个目录里的所有依赖是完整配套的,否则后续编译时缺失符号会非常难排查。
5.4 重启后网卡回原形:没有持久化加载模块
现象:调试完一切正常,重启后网卡又消失,ip link只剩lo。需要手动insmod才能恢复。
原因:insmod加载的是临时模块,它没有进入内核模块系统管理范围。系统启动时不会自动加载这个模块,除非你把模块文件拷贝到标准模块目录并执行depmod。
解决:做一次完整的持久化操作。
# 拷贝模块到系统模块目录 cp src/r8125.ko /lib/modules/$(uname -r)/extra/ # 重建模块依赖 depmod -a # 创建自动加载配置 echo "r8125" > /etc/modules-load.d/r8125.conf # 如果网络服务依赖此网卡,需要把模块塞进initramfs dracut -f这里重点说一下dracut -f:CentOS 7默认网络服务启动时,内核模块需要在initramfs阶段就已经加载完成。如果没有把r8125放进initramfs,某些依赖网络文件系统启动的场景下还是会出现启动后网络起不来的情况。执行dracut -f会重建initramfs镜像,把当前内核模块目录里的所有模块重新打包进去,花费时间较长但值得做。
5.5 升级内核后驱动立刻失效:没有走DKMS
现象:后续某天执行了yum update kernel*,重启后网卡消失,modprobe r8125提示找不到模块。
原因:手动拷贝到/lib/modules/$(uname -r)/extra/的模块只匹配当时的那个内核版本。内核升级后uname -r变成了新版本,模块目录整体换新,旧的r8125.ko自然不在新目录里。
解决:长期维护的机器不应该靠手动拷贝模块过日子,应该把驱动注册进DKMS系统。DKMS会在每次内核安装后自动重新编译并安装外部模块,彻底告别手动维护。具体做法下一章展开。更保守的临时方案是在yum更新时排除内核包,yum update --exclude='kernel*',但这只是拖延,内核安全更新积累起来还是要面对。
6. 验证驱动生效并让它在内核升级后仍然存活:DKMS收尾
6.1 用dmesg与ethtool确认驱动真正接管网卡
装完驱动后,用几条命令就能确认是否真正接管了网卡。不要只看网卡能不能上网,2.5G速率能不能跑满才是关键。
# 确认模块加载和链路状态 dmesg | grep -i r8125 # 确认驱动名称与支持的速率 ethtool -i eth0 ethtool eth0ethtool -i eth0会输出driver: r8125,ethtool eth0会显示Speed: 2500Mb/s。如果速度显示的是1000Mb/s,先检查网线压制是否为4对8芯全通,很多六类线只通了4芯,2.5G协商不下会退回1G。再配合lspci -k确认Kernel driver in use后面是r8125而不是r8169,这几条全部通过,驱动才算真正接管。
6.2 用DKMS把驱动交给内核维护,避免每次升级后重编
DKMS(Dynamic Kernel Module Support)是解决内核升级后模块失效的标准手段。把R8125驱动注册到DKMS后,每次内核更新,DKMS都会在安装阶段自动编译并安装对应版本模块,不再需要手动干预。
# 先在目标机上安装dkms rpm -Uvh /tmp/r8125-toolchain/dkms-*.rpm 2>/dev/null # 如果没有提前打包dkms,可以从在线机器上用yumdownloader拉取 yumdownloader --destdir=/tmp/dkms-rpm --resolve dkms给驱动源码添加dkms.conf配置文件:
# 在源码目录下创建dkms.conf cat > /usr/src/r8125-9.xxx/dkms.conf <<'EOF' PACKAGE_NAME="r8125" PACKAGE_VERSION="9.xxx" BUILT_MODULE_NAME[0]="r8125" DEST_MODULE_LOCATION[0]="/kernel/drivers/net/ethernet/r8125" AUTOINSTALL="yes" EOF注册并编译安装到DKMS:
# 添加驱动源码到DKMS系统 dkms add -m r8125 -v 9.xxx # 编译并安装 dkms build -m r8125 -v 9.xxx dkms install -m r8125 -v 9.xxx这里PACKAGE_VERSION必须和dkms add -v后面的版本号严格一致,同时也要和源码目录名的版本号对应。AUTOINSTALL="yes"表示新内核安装后自动触发编译安装,这行参数不能漏。DEST_MODULE_LOCATION建议保持/kernel/drivers/net/ethernet/r8125,这是内核模块目录的标准层次结构。
做完DKMS注册后,之前的/etc/modules-load.d/r8125.conf和黑名单配置继续保留。验证是否生效的方法是执行dkms status,看到r8125/9.xxx: installed即表示注册成功。以后每次内核升级时盯一次dkms status,如果报Error! Build of r8125 failed,多半是编译环境缺了新内核版本的kernel-devel,补齐后运行dkms build -m r8125 -v 9.xxx即可。
以前我有一台跑着业务的内网服务器,就是吃了没做DKMS的亏,每次内核升级都要半夜爬起来重新编译网卡驱动,连编译环境都重新配一遍,那段时间一看到自动更新就神经紧张。后来花十分钟把驱动交给DKMS管,升级流程彻底安静了。希望这篇笔记能帮你少走这一段弯路,离线环境折腾网卡驱动,真正麻烦的不是编译本身,而是把工具链和模块维护机制一次想清楚。希望帮到你。
本文还有配套的精品资源,点击获取