简介:在无外网依赖的服务器或内网环境中,为CentOS 7手动编译安装Realtek R8125网卡驱动,常常卡在缺少gcc/gcc-c++工具链以及官网源码编译报错的问题上。这套资源包正是针对这两个痛点设计,汇集了离线安装gcc与gcc-c++所需的全部rpm依赖包,并提供了修改后的驱动源码,可有效规避官方版本在CentOS 7下的编译错误。压缩包共计52个文件,以14个rpm离线包为核心,辅以C语言源文件、头文件、预编译的.o/ko模块、Makefile构建文件与自动化安装脚本,总大小46.09MB,目录划分清晰,便于按序操作。目前已有3268人次学习使用。通过阅读包内说明并执行autorun.sh,可快速完成依赖安装、驱动编译加载及网络启用等全流程,避免缺包、缺头文件等常见编译陷阱,大幅提高离线装网卡驱动的整体成功率,对运维人员、网络管理员及DIY玩家均具有实用参考价值。
1. 一台离线 CentOS 7 装 R8125:最难的从来不是驱动本身
一台刚装完 CentOS 7 的离线服务器,板载的 R8125 2.5G 网卡不出网。lspci 能看到设备,系统里却没有任何驱动,唯一的内网上行路径还进不去,想 yum 装什么都装不了。这是很多人第一次碰 R8125 时的翻车现场:难的根本不是芯片,而是你连编译驱动的工具链都没有。CentOS 7 自带的 3.10 内核没有现成的 R8125 模块,官方驱动源码得现场用 gcc 编成 .ko,可离线机器上 gcc 和 gcc-c++ 往往都没装,依赖包又卡在内网,成了死循环。这篇文章顺着这条链路走一遍:先用 repotrack 把离线编译工具链凑齐,拿到官方 R8125 驱动源码,处理它在较新内核下的编译错误,最后加载并固化到系统,重启不掉网。给正在机房、内网现场装机的你:新手照着命令走,熟手直接看第 4、5 章的报错与坑。
2. 离线装 gcc 与 gcc-c++:用 repotrack 打一个完整的依赖包
2.1 为什么要先装 gcc 和 gcc-c++:内核模块必须现场编译
驱动不是下载完就能用的,官方给的是 C 源码,需要在目标机器上编译成 .ko 内核模块。编译动作本身依赖 gcc、make、kernel-devel、kernel-headers 这一套东西。标题里的 gcc+ 我理解为 gcc-c++,虽然网卡驱动是纯 C 写的,但有些官方源码包里的辅助工具或 make 脚本会用到 C++ 编译器,装齐了省得二次跑腿。
离线环境最大的尴尬在于:目标机上rpm -ivh xxx.rpm一下,rpm 报一串Requires: libmpc.so.3之类的缺失。所以先解决的不是装包,而是怎么把依赖闭环保出来。常见做法是找一台和这台机器同版本、能联网的 CentOS 7,用它拉全依赖,再打包拷过去。
2.2 从联网机器全量拉依赖:repotrack 比 yumdownloader 更保险
yumdownloader 的--resolve会跳过本机已装的依赖包,这在联网机器上很容易漏包——因为它认为这台机器已经满足依赖了。所以我习惯用 repotrack,它不管本机装没装,把每个包的所有依赖层层展开全部下载。
yum install -y yum-utils mkdir -p /data/deps && cd /data/deps repotrack -p /data/deps gcc gcc-c++ make kernel-devel kernel-headers glibc-devel libstdc++-devel ls /data/deps | wc -l先装 yum-utils,repotrack 是它自带的工具。-p /data/deps指定下载目录,后面跟的包名列表就是要拉取的目标。gcc、gcc-c++、make 是编译链主体,kernel-devel 提供内核头文件和 Makefile,kernel-headers 提供用户态和内核态共用的头文件,glibc-devel 和 libstdc++-devel 是 gcc 和 g++ 运行时缺一不可的依赖层。
这里最关键的一步:先到目标机器上执行uname -r,把内核版本号抄下来。kernel-devel 必须和这个版本号完全一致,否则驱动编译时用的头文件和你 boot 的内核对不上,编出来的 .ko 加载时直接报 Exec format error。联网机器上这样下载指定版本:
repotrack -p /data/deps kernel-devel-3.10.0-1160.el7.x86_64目标机器的内核版本是多少,kernel-devel 就必须是多少。下载完把整个 /data/deps 目录用 U 盘、内网拷到目标机,目录里可能是几十个到上百个 rpm,不用管谁是谁,全带过去。
2.3 目标机器安装:rpm 依赖报错就再跑一遍
到目标机上,进到 rpm 包目录,直接一把梭。
cd /data/deps rpm -Uvh *.rpm-U是升级安装,包没装过就等同安装,装过会升级成仓库里新版本;-v显示过程,-h输出进度条。第一次执行中途大概率会停下来,提示缺某个依赖包,原因很简单:rpm 是严格按命令行里通配符展开顺序逐个装的,某些包的安装顺序会影响到后续包的依赖检查。
这时候不要慌,把报错里提到的包名记下来,这个包其实已经躺在 /data/deps 里了,只是还没轮上安装。直接再执行一次rpm -Uvh *.rpm,或者单独装提示缺失的那几个包,两三轮之后依赖树就捋顺了。不推荐--nodeps硬过,后面编译时缺头文件更痛苦。
装完后验证一下:
gcc --version g++ --version make --version ls /usr/src/kernels/$(uname -r)/include最后一行能列出目录内容,说明 kernel-devel 和当前内核匹配,编译环境这就齐了。
2.4 备选方案:手头有 CentOS 7 安装镜像就挂本地源
如果手头有 CentOS 7 的 Everything 安装 ISO,其实比 repotrack 更省事。ISO 本身就是完整的软件仓库,把它挂载成本地 yum 源,目标机就能直接 yum install。
mount -o loop /opt/CentOS-7-x86_64-Everything.iso /mnt/repo cat > /etc/yum.repos.d/local.repo <<'EOF' [local] name=local baseurl=file:///mnt/repo gpgcheck=0 enabled=1 EOF yum clean all yum install -y gcc gcc-c++ make kernel-devel这个方案的优点是不需要第二台联网机器,ISO 里自带完整依赖;缺点是你得找得到对应版本的镜像,而且服务器得能挂载光盘或 USB。两套方案选一个就行,实测下来 repotrack 方案对大多数机房场景更通用。
3. 拿下官方 R8125 源码包:解压、目录结构与编译入口
3.1 确认拿对包:R8125 和 R8168 别拿混
R8125 是一颗 2.5G 网卡芯片,官方驱动包和 R8168 是两套独立的驱动树,代码不能混用。下载时认准包名里带 r8125 字样的压缩包,文件名一般是r8125-x.x.x.tar.gz这种格式。
拿到 tar 包先别急着解压,先用tar -tf不落盘查看内容,确认包内有没有src/r8125.c这个核心文件。
tar -tf r8125-*.tar.gz | grep -E "src/Makefile|src/r8125.c|README"这一步能看出两件事:一是这个包到底是不是 r8125 的驱动源码,二是包结构是否完整。如果 grep 出来只有几个 README 文件、没有 src 目录,说明下载的很可能是个文档包或者拿错成了 r8168,赶紧换。
有些 ODM 主板上用的 RTL8125BG 等衍生型号,驱动都共用这一套 r8125 源码,不用额外区分。真正要区分的是网卡 PCI 设备 ID,在目标机器上用lspci -nn看,形如10ec:8125,记下这个 ID,后面验证驱动是否接管时要用。
3.2 在 Linux 下解压,别在 Windows 上先解压
离线场景经常遇到一个问题:压缩包是从内网 Windows 机器上下载的,顺手就在 Windows 上用解压软件解压完,再通过 FTP 传到 Linux。这样做十有八九会出问题——tar 包里的文件权限位全丢,autorun.sh没了可执行权限,甚至文本文件被转成 CRLF 换行,make 脚本读到一半报错。
正确做法是把原始 tar 包整包拷到 Linux 机器上,用 tar 自己解压。tar 包在拷贝过程中要选择二进制模式传输,不要用 ASCII 模式,否则一样会坏掉。
chmod 755 r8125-*.tar.gz tar xvf r8125-*.tar.gz cd r8125-*/ chmod +x autorun.shtar xvf解压后,源码包内的目录权限和生产环境需要的可执行位都能保留。如果手头确实只有 Windows 解压过的版本,那就用chmod +x补权限,再用dos2unix autorun.sh修换行符,这是补救做法,能少踩一个算一个。
3.3 先看目录结构和编译入口:autorun.sh 与 src/Makefile
解压后目录里最重要的两个东西:autorun.sh是官方给的自动安装脚本,src/是真正的内核模块源码。自动脚本做的事本质上是:先把内核里旧驱动卸载,进入 src 目录执行 make,再把编出来的 r8125.ko 拷贝到模块目录并加载。听起来一气呵成,但现场我不推荐直接跑 autorun.sh,原因很简单:只要 make 报一个错,你根本看不清楚它死在哪个环节。
我一般直接进 src 目录手动编,每个步骤都在自己眼皮底下过。
cd src ls -l Makefile r8125.cMakefile 是编译入口,r8125.c 是驱动主体,几千行的规模。Makefile里最关键的变量是 KSRC,它指向内核源码树路径。官方包默认值经常写得不对,这是后面编译报错的头号来源。先看两眼默认配置,心里有个底,然后进入下一章的修改环节。
4. 修改官方驱动错误:内核接口变了,编译报错这样定位和改
4.1 官方驱动在 CentOS 7 上编译的三类典型报错
先泼一盆冷水:官方驱动源码不等于解压就能编译过。它的目标内核版本往往比你的系统新,或者比你的系统旧,两边不对齐就会报错。在 CentOS 7 上编译,常见的报错大致三类,先对号入座再动手。
| 报错片段 | 原因 | 解决方向 |
|---|---|---|
Cannot find kernel headers/build: No such file or directory | Makefile 里 KSRC 变量指向的内核头文件目录不存在 | 显式指定 KSRC 为当前内核对应的 /usr/src/kernels 路径 |
error: assignment ... from incompatible pointer type | 驱动的回调函数原型和新内核结构体定义不匹配 | 定位到报错的赋值行,删除或改写对应回调 |
error: implicit declaration of function/redefinition of ... | 驱动用了旧内核 API,新内核已换写法或改名 | 把旧 API 换成新写法,比如 init_timer 换 timer_setup |
这里有个特别容易迷惑人的点:CentOS 7 的内核版本号显示3.10.0-1160.el7.x86_64,但它 backport 了大量新内核接口,你要是真按老内核去改,反而会改错方向。判断标准只有一个,编译器报什么就按什么改,你盯着 system 版本号猜不算数。
4.2 修改点一:Makefile 显式指定 KSRC,解决找不到内核头文件
这是出现频率最高的报错,现象是 make 一开始就停住,提示找不到内核构建目录。
uname -r ls -ld /usr/src/kernels/$(uname -r) ls -ld /lib/modules/$(uname -r)/build第一行确认当前内核版本,第二行看 /usr/src/kernels 下有没有对应的目录,第三行看 /lib/modules 下的 build 软链接有没有断。CentOS 7 装好 kernel-devel 后,这两个位置应该都能访问,且最终指向同一套内核源码树。
然后进入驱动源码目录,执行编译并显式传入 KSRC:
cd /root/r8125-*/src make clean make KSRC=/usr/src/kernels/$(uname -r)make KSRC=...的意思是覆盖 Makefile 里默认的 KSRC 变量,让它指向当前内核的头文件目录。如果你不想每次敲一长串,也可以先改 Makefile:找到KSRC ?=那一行,把路径改成/lib/modules/$(uname -r)/build,保存后直接make。两个路径是软链接关系,效果一样。
4.3 修改点二:适配新内核,处理结构体成员和接口签名
KSRC 修好之后,真正的重头戏来了。编译报错如果停在src/r8125.c里,定位方法很简单:报错信息会提示是哪一行、哪一个函数、什么类型的错误。先把所有可疑的旧接口一次找出来。
grep -n "init_timer\|ndo_udp_tunnel_add\|get_regs_len\|get_settings" src/r8125.c | head -30搜索出来的每一行,都可能是编译报错点。最经典的是init_timer。老内核里初始化定时器这样写:
/* 旧内核(4.x 及更早)写法 */ init_timer(&tp->timer); tp->timer.function = rtl8125_timer_callback; tp->timer.data = (unsigned long)tp; add_timer(&tp->timer);新内核里init_timer被删掉,换成了timer_setup,而且回调函数原型也不一样了。旧回调从data参数里取出私有指针,新回调要用from_timer宏从struct timer_list *反推容器结构体地址。
/* 新内核 5.x 写法 */ static void rtl8125_timer_callback(struct timer_list *t) { struct rtl8125_private *tp = from_timer(tp, t, timer); /* ... */ } timer_setup(&tp->timer, rtl8125_timer_callback, 0); add_timer(&tp->timer);from_timer(tp, t, timer)的意思是根据 timer_list 指针 t,计算出它所在结构体 rtl8125_private 的首地址,timer是结构体里 timer_list 成员的名字。这个宏是新内核的标准姿势,比当年用container_of手写干净得多。
再一类报错是net_device_ops结构体里有成员在新内核被删了或被改名了。报错长这样:error: 'struct net_device_ops' has no member named 'ndo_udp_tunnel_add'。处理办法是打开 src/r8125.c,找到 net_device_ops 赋值的位置,把报错的那一行注释掉或删掉。这类回调大多是可选的辅助功能,删掉不影响网卡基本收发。
4.4 改完后重新编译:命令、参数与产物确认
改完代码,重新编译。这次应该能一路编到底。
cd /root/r8125-*/src make clean make KSRC=/usr/src/kernels/$(uname -r) ls -l r8125.ko file r8125.komake clean把上一次半成品目标文件清掉,防止旧的 .o 混进新编译结果。file r8125.ko拿来确认产物格式,正常输出是ELF 64-bit LSB relocatable, x86-64。如果输出的是stripped或者奇怪的格式,说明编出来的不是内核模块,基本是 KSRC 指错了或者内核头文件不对,一行行接着查。
到这一步,r8125.ko 已经躺在 src 目录里。后面就是加载、验证和固化。
5. R8125 安装高频坑排查:从 modprobe 失败到重启掉网
5.1 手动加载前先做的三件事:rmmod、blacklist、depmod
编出 r8125.ko 之后,不要急着 insmod,先把环境捋干净。这三个动作按顺序执行,能避免大量玄学问题。
rmmod r8169 2>/dev/null echo "blacklist r8169" > /etc/modprobe.d/blacklist-r8169.conf depmod -a modprobe r8125 ip link第一行rmmod r8169:把内核里可能占着这块网卡的老驱动卸载。CentOS 7 的 3.10 内核里 r8169 对 RTL8125 的识别能力很弱,但如果你换过内核或者装过额外驱动,它可能已经抢占了设备。第二行写 blacklist 配置,防止 reboot 后 r8169 又把设备抢走。第三行depmod -a重建模块依赖关系,因为新 r8125.ko 还没被内核模块系统认识。第四行modprobe r8125让它按 modules.alias 找驱动并加载。最后ip link看网卡设备有没有出现。
如果加载失败,按顺序看这三条输出,能定位九成问题:
lspci -nnk | grep -A2 -i ethernet dmesg | grep -iE "r8125|r8169" ls -l /sys/bus/pci/devices/*/driver 2>/dev/nulllspci 看网卡设备 ID,dmesg 看内核日志里驱动的报错,最后一条看当前是哪个驱动接管了这个 PCI 设备。
5.2 坑一:modprobe 报 Exec format error
现象:insmod r8125.ko或modprobe r8125直接报Exec format error。
原因:r8125.ko 编译时用的内核头文件和你当前 boot 的内核版本不一致,或者编译器版本差太多。最常见的是在 A 机器上编好 .ko,拷贝到 B 机器去加载;只要两台机器的内核版本、编译工具链不匹配,这个错误必现。
解决:回到目标机器上,用目标机器的内核头文件重新编译。uname -r确认版本,ls /usr/src/kernels/$(uname -r)确认头文件存在,然后重新执行make KSRC=/usr/src/kernels/$(uname -r)。编完就在本机加载,不要跨机器拷贝 .ko。
5.3 坑二:lspci 看得到网卡,ip link 看不到
现象:lspci -nn里明明有 R8125 设备,但ip link输出里没有任何对应网卡。
原因:驱动模块没有成功加载,或者加载了但 pci 设备 ID 不匹配。有些老版本的官方驱动包 pci_device_id 表里没加新修订版网卡的 ID,probe 阶段直接放弃。
解决:先dmesg | tail -50看有没有 r8125 的报错日志。如果是no device found一类提示,基本就是驱动不认识这个 PCI 设备 ID,把lspci -nn输出的 ID 记下来,去换新一版官方驱动包。如果 dmesg 里根本没 r8125 的日志,说明模块没加载,回到 5.1 的顺序重新走。
5.4 坑三:协商速率只有 1000Mb/s
现象:网卡能通,但 ethtool 看到 Speed 是 1000Mb/s,不是期望的 2500Mb/s。
原因:不一定是驱动问题。2.5G 需要网线两端设备都支持 2500 协商,对端交换机口如果只支持千兆,或者网线质量不行,协商结果就是 1000。驱动层面一般不会限制这个速率。
解决:先换根网线、换交换机上支持 2.5G 的口再测。如果确定对端支持 2500,再查 ethtool 里的 advertised link modes。不建议上来就ethtool -s 网卡名 speed 2500 duplex full强制,这个命令在链路不可靠时会把网络直接打掉,还不如让它自动协商。
5.5 坑四:重启后网卡又消失或换成了老驱动
现象:刚装完一切正常,一 reboot,网卡要么没了,要么被 r8169 接管,要么系统根本起不来(少见)。
原因:r8125.ko 没有进入内核模块持久化目录,系统重启后没有加载它;或者内核里 r8169 比 r8125 先 probe,把设备绑定走了。
解决:把 r8125.ko 放进标准模块目录,刷新依赖,重建 initramfs。这一套下来才能保证重启后驱动依然生效,具体命令在下一章给出。注意 CentOS 7 必须用 dracut 重建 initramfs,用 mkinitrd 的老一套在这版本上已经不好使了。
5.6 坑五:make 时报找不到 build 目录或 stdio.h
现象:编译时提示/lib/modules/3.10.0-1160.el7.x86_64/build: No such file or directory,或者干脆报stdio.h: No such file or directory。
原因:build 软链接断了,或者 kernel-devel、glibc-devel 没装全。有人只装了 kernel-devel,漏了 glibc-devel,结果编译头文件缺一大片。
解决:回到第 2 章,用 repotrack 把 glibc-devel、kernel-headers 一并拉下来装上。ls -l /lib/modules/$(uname -r)/build看软链是否存在,如果指向的目录不存在,问题就出在 kernel-devel 版本不匹配上,别犹豫,重装对应版本。
6. 用 ethtool 和 dmesg 验证网卡,把驱动固化到开机
6.1 用三条命令确认驱动真正生效
加载完成后,别急着配 IP,先确认驱动真的接管了网卡。三条命令足够。
ethtool -i 网卡名 ethtool 网卡名 dmesg | grep r8125网卡名用ip link查,可能是 eth0,也可能是 enp3s0 这种带 PCI 位置的名字。ethtool -i看 driver 一栏是不是 r8125,version 是官方驱动的版本号;ethtool不带 -i 看 Speed 协商结果,Link detected 必须是 yes;dmesg里应该有 r8125 初始化链路和 link up 的日志。
还有一个确认方式:
ls -l /sys/bus/pci/devices/0000:03:00.0/driver输出指向的目录名如果是 r8125,说明这块网卡百分百由 r8125 驱动接管。如果指向 r8169,说明 5.1 那套顺序没走完。
6.2 固化安装:dracut 重建 initramfs 与网卡自启配置
验证通过后,把驱动固化进系统,做到重启不掉网。下面这套脚本我每次装完都会跑一遍:
DRV_DST=/lib/modules/$(uname -r)/updates/r8125.ko mkdir -p /lib/modules/$(uname -r)/updates cp /root/r8125-*/src/r8125.ko "$DRV_DST" depmod -a echo "blacklist r8169" > /etc/modprobe.d/blacklist-r8169.conf echo "r8125" > /etc/modules-load.d/r8125.conf dracut -f /boot/initramfs-$(uname -r).img $(uname -r)mkdir 先建目录,cp 把驱动拷贝到 updates 目录,这是外部模块的标准落位,depmod 会扫描并把它写进 modules.dep。blacklist r8169 确保老驱动不再抢设备,modules-load.d 让系统启动时自动加载 r8125,最后 dracut 把模块塞进 initramfs,这一步直接解决 reboot 后驱动消失的坑。
网卡自启还要配 ifcfg 文件,按实际网卡名建一个:
# /etc/sysconfig/network-scripts/ifcfg-enp3s0 TYPE=Ethernet BOOTPROTO=dhcp NAME=enp3s0 DEVICE=enp3s0 ONBOOT=yes如果这台机器走静态 IP,把 BOOTPROTO 改成 none,再加 IPADDR、NETMASK、GATEWAY、DNS1 几行就行。文件放在 /etc/sysconfig/network-scripts/ 下,NetworkManager 重启后会自动接管。
装完驱动的习惯动作,我永远是先备份 /boot 下的 initramfs 再动 dracut,新 initramfs 万一有问题还能用旧的救回来。R8125 这套流程跑过几次后其实不难,难的是第一次面对报错时不知道往哪个方向查。希望这篇能把离线装 R8125 的门槛降下来,希望帮到你。
本文还有配套的精品资源,点击获取