简介:面向Ubuntu系统开发与Realtek网卡驱动维护场景,这套离线安装包集中收录了gcc、make、build-essential等基础编译工具,以及r8125网卡驱动源码与依赖,适合在没有互联网或内网受限的环境中快速搭建编译环境并安装千兆网卡驱动。压缩包共42个文件,大小30.01MB,主体为23个deb安装包,涵盖gcc-7、g++、libc6-dev、linux-libc-dev等常用开发库;另含r8125驱动源码中的6个c文件、8个h头文件及Makefile、autorun.sh等脚本,readme与makefile_linux24x等辅助文件也一并保留,能支撑从源码编译、内核模块更新到驱动加载的完整流程。资源内deb包已按依赖关系整理,已有3767人学习下载,对运维人员、嵌入式开发者以及常需处理Realtek网卡兼容问题的用户,可直接补齐依赖,省去逐一下载和联网搜索的麻烦,大幅提升离线部署与驱动排障效率,同时整个过程无需额外联网即可完成。
1. 一台没有外网的服务器,为什么连网卡驱动都装不上
做内网服务器维护的人,大概率遇到过这个死循环:机器没有网络,要装网卡驱动才能联网,可驱动是源码包,编译它又必须先有 gcc、make、build-essential 和一堆依赖。尤其是带 r8125 芯片的 2.5G 网卡,系统自带内核里通常没有驱动,只能自己编。结果就是卡在第一块拼图上:工具链没落地,一切都白搭。这篇文章就是要讲清楚,如何在一台完全没有互联网的机器上,从零把 gcc、make、build-essential 连同全部依赖离线装好,并把 r8125 驱动编译加载成功。整个过程适合机房运维、内网开发的场景,照着复现即可,不需要你有外网权限。
2. 离线安装的根本矛盾:gcc、make、build-essential 三者要怎么先落地
离线安装的核心难点不在单个包,而在依赖的链条。gcc 本身不是一个孤立的二进制,它要调用 cpp、binutils、libc-dev 等一系列组件;make 倒是相对独立,但 build-essential 作为元包又把这些全部包装在一起。当你没有网络时,包管理器失去了自动解析依赖的能力,所有依赖都必须预先准备好,这就是离线安装的第一课:不是拷几个 deb 文件过去就行,而是要复制一整套依赖闭包。
2.1 为什么不能只拷一个 gcc 二进制
很多人第一次做离线安装时,会尝试从有网的机器上直接拷 /usr/bin/gcc 过去,结果通常是三个错误轮着来:找不到动态库、找不到头文件、cpp 子进程启动失败。原因很好理解,gcc 在编译 .c 文件时,实际是调用 cc1(在 /usr/lib/gcc 目录下)去完成真正的编译,同时还要找 /usr/include 下的系统头文件,还要调用 as(binutils 提供)做汇编、ld 做链接。任何一个环节缺失,都会报错。
所以离线安装的正确对象不是可执行文件,而是 deb 包。deb 包在安装时会把文件放到正确目录,同时触发 postinst 脚本建立 alternatives 链接、刷新 ldconfig 缓存。这些副作用是裸拷贝完全模拟不了的。
2.2 在有网机器上收集离线包的三种常见做法
先明确一个前提:你需要一台与目标机器系统版本一致的有网机器,才能保证依赖库版本兼容。版本不一致是离线安装翻车的最大源头,后面第五节会单独讲。
常见做法有三种。第一种最朴素,去 packages.ubuntu.com 这类网页手动下载 .deb,但这只适合一两个包;遇到 build-essential 这种带几十个依赖的元包,手动方式根本数不清依赖关系。第二种是使用 apt download 命令批量下载,让 apt 自己解析依赖,这是目前我推荐的主流方式。第三种是在有网机器上完整安装一遍相同系统,然后把 /var/cache/apt/archives 目录里缓存的所有 deb 拷走,这种做法适合目标机器和这台机器配置完全一样的场景,但缓存目录里通常混着旧版本和无关包,不推荐新手使用。
2.3 用 apt download 批量拉取依赖的原理与坑
apt download 的用法看似简单,但它的行为有一个关键限制:它只下载你在命令行里指定的包,不会下载该包的依赖。这意味着你必须自己先递归地把依赖树弄清楚,再逐个下载。
我一般会用一个循环脚本,先把目标元包的 Depends 字段解析出来,再对每个依赖递归解析。这里有一个最容易被忽略的坑:build-essential 的依赖列表里包含 libc6-dev,而 libc6-dev 的依赖又包含 libc6,版本还有下限要求。你需要保证下载到的 libc6 版本不低于目标系统里已安装版本,否则安装时会报 libc6 版本不满足。解决方法是把整个依赖树的下载做成一个循环,而且每轮下载后都重新解析一遍目录里新出现 deb 的依赖,直到不再产生新包为止。
3. 制作离线安装包的完整流程:从有网机器到内网机器
整个流程可以分为四个阶段:准备有网机器、下载离线包、搬运到内网、离线安装。很多人一上来就编译 r8125,结果缺编译器;也有人拷了 gcc 却发现缺少共享库。下面这套流程是经过多次内网环境验证的,按步骤操作即可。
3.1 在有网机器上获取 gcc、make、build-essential 及依赖的脚本
以下脚本假设有网机器和目标机器系统版本一致(例如同为 Ubuntu 22.04)。它会递归下载所有依赖,并把结果存放在当前目录下的 debs 文件夹中:
mkdir -p debs cd debs # 第一步:下载 build-essential、gcc、make 本体 apt download build-essential gcc make 2>/dev/null # 第二步:循环解析依赖,直至没有新包产生 # 使用 awk 提取 Depends 字段中的包名,再交给 apt download while true; do # 扫描当前目录所有 .deb 的依赖信息 NEW_DEPS=$(dpkg-deb -f *.deb Depends 2>/dev/null \ | tr ',' '\n' \ | sed 's/(.*//g; s/|.*//g; s/[ <>]//g' \ | sort -u) # 过滤已下载的包 MISSING="" for pkg in $NEW_DEPS; do if ! ls ${pkg}_*.deb >/dev/null 2>&1; then MISSING="$MISSING $pkg" fi done # 如果没有缺失,跳出循环 if [ -z "$MISSING" ]; then break fi # 下载缺失依赖 apt download $MISSING 2>/dev/null || true done echo "下载完成,当前目录共有 $(ls *.deb | wc -l) 个 deb 包"这段脚本的核心逻辑是循环收敛:每一轮扫描当前目录里所有 deb 的 Depends 字段,找出还没有下载的依赖,继续补下。当某轮没有新依赖产生时,说明依赖闭包已经完整。注意tr ',' '\n'是为了处理多依赖并列的情况,sed部分把版本号(括号内容)和候选包(| 分隔的备用包)全部清理掉,只留纯包名。
参数说明:dpkg-deb -f *.deb Depends是批量提取所有 deb 的 Depends 控制字段;tr和sed做文本清洗;apt download后面直接接包名数组。这个脚本的优点是全自动,缺点是如果源里某个包名称有特殊字符(如 + 号),需要额外处理,但 build-essential 这条链上的包都比较规矩。
3.2 离线安装与验证命令
把 debs 目录打包(tar czf debs.tar.gz debs)传到内网机器后,就进入安装阶段。装的时候不要用dpkg -i *.deb一口气装完,因为多个 deb 之间存在依赖顺序,dpkg 虽然会尽力处理,但在缺少某个中间包时可能报错。我用的是分两步策略:
# 第一步:安装所有 deb,允许报错 dpkg -i *.deb 2>&1 | tail -20 # 第二步:修复可能的依赖缺口(此时依赖树已全部在目录里,会直接成功) apt-get -f install -y 2>&1 | tail -10第一步的dpkg -i *.deb会把所有包按命令行顺序依次安装,由于依赖包大多在前面,大部分情况一次成功。apt-get -f install的用途是修复破损的依赖关系——它会把没有装全的依赖补齐,而因为 deb 文件都在本地,它不需要网络就能完成。注意这里的 apt-get 推荐行为在离线环境可能会因为无法访问源而报索引错误,但-f install分支通常能绕过。
安装完成的验证命令很简单:
gcc --version make --version dpkg -l | grep build-essential三条命令分别确认 gcc、make 可用,build-essential 元包状态为 ii(已安装)。如果dpkg -l输出中 build-essential 前面是un,说明元包本身没有安装,只是依赖被满足了,这种情况不影响编译,但后续要补装。
3.3 下载 r8125 驱动源码并准备编译环境的关键点
工具链到位后,还需要两个关键资源:r8125 驱动源码和对应内核的头文件。驱动源码从官方仓库下载,注意选择与目标内核版本兼容的 release,不要盲目下最新版。内核头文件则必须严格匹配:
# 查看目标机器的内核版本 uname -r # 在有网机器上下载对应版本的内核头文件 apt download linux-headers-$(uname -r) linux-headers-$(uname -r)-generic这里有一个非常隐蔽的坑:uname -r在部分 Ubuntu 系统上输出版本号不带 -generic 后缀,而实际头文件包名是带后缀的。下载前建议先在有网机器上执行apt-cache search linux-headers-$(uname -r)确认完整包名。内核头文件装不上,驱动编译会直接失败,这是 r8125 离线安装里最常见的拦路虎。
4. 编译 r8125 驱动:Makefile、内核头文件与 DKMS 参数
工具链和依赖都齐了,真正编译 r8125 时依然有几个参数和前置条件必须确认。这一章把编译过程拆开讲清楚,尤其是 Makefile 里的选项和 DKMS 注册逻辑。
4.1 内核头文件为什么是隐藏的刚需
驱动本质上是内核模块,不是普通的用户态程序。编译内核模块必须能访问到内核源码树或至少是头文件及构建脚本。Ubuntu/Debian 下,这个路径是/lib/modules/$(uname -r)/build,它来自linux-headers-$(uname -r)包。
r8125 的 Makefile 会通过uname -r自动探测内核构建目录,如果缺失,报错通常比较直白,比如*** Unable to find the kernel source tree或Kernel headers not found。很多人这时会误以为是 gcc 版本问题,实际上是内核头文件没装。另一个隐藏点:如果目标机器内核是 HWE 版本(比如 5.15.0-XX-generic 带 hwe 后缀),头文件包名也带 hwe,下载时按 3.3 节的方式确认即可。
4.2 r8125 编译的命令序列与可选参数
拿到源码后建议先解压进入目录,观察一下文件结构。典型的 r8125 源码包里有autorun.sh、Makefile、src/子目录。我不推荐直接执行 autorun.sh,因为它会内部封装编译、安装、modprobe 全套动作,任何一步失败都不容易定位。手动分步执行更可控:
# 进入源码目录后,先清理历史编译产物 make clean # 指定内核目录编译,-j 参数用 nproc 并行加速 make -j$(nproc) KSRC=/lib/modules/$(uname -r)/build # 安装模块到当前内核目录 make install参数说明:KSRC指定内核源码树路径,r8125 的 Makefile 支持这个变量,默认值虽然会自动探测,但离线环境下偶尔探测失败,显式指定最稳。-j$(nproc)的 nproc 返回 CPU 核心数,例如 8 核机器会执行make -j8,这能把编译时间从几分钟压到几十秒。
安装完成后,立刻执行modprobe r8125。如果返回错误(比如 Module not found),检查/lib/modules/$(uname -r)/extra/下是否有 r8125.ko 文件。这是make install的默认安装路径。
4.3 用 DKMS 注册驱动的两个必调参数
如果不想每次内核升级都重新编译一次驱动,DKMS 是唯一正解。注册流程:
# 第一步:把源码软链接到 DKMS 约定路径 # 注意版本号要和驱动源码对应,例如 9.010.01 ln -s /path/to/r8125-src /usr/src/r8125-9.010.01 # 第二步:添加 DKMS 记录 dkms add -m r8125 -v 9.010.01 # 第三步:编译并安装 dkms build -m r8125 -v 9.010.01 dkms install -m r8125 -v 9.010.01这里两个必调参数就是-m(模块名)和-v(版本号),它们共同决定/usr/src下的目录名必须是r8125-版本号的格式。dkms add读这个目录下的 dkms.conf 配置文件,如果目录名对不上,会报 Source tree not found。
注册完 DKMS 后,即使将来内核升级,DKMS 也会自动为新内核构建模块。但有一个前提:新内核的头文件也得先装好,否则 DKMS build 仍然会失败。所以内网环境下,升级内核前务必先把对应 linux-headers 下载好。
5. 离线装驱动的避坑记录:我从“缺依赖”到“驱动加载失败”的排查路径
离线环境下踩过的坑比在线环境多得多,因为每次报错都意味着要用有限信息做判断。这一章写五条最典型的踩坑记录,按现象、原因、解决展开。
5.1 装了 build-essential 仍然报错 cc: command not found
现象:dpkg 安装全部成功,gcc --version正常,但 make 编译时第一行就报cc: command not found。
原因:很多开源模块的 Makefile 写死了用 cc 而不是 gcc。在 Debian/Ubuntu 上,cc 是指向 gcc 的 alternatives 链接,而 alternatives 的注册动作发生在 gcc 包的 postinst 脚本里。离线安装时如果 dpkg 的触发器没有正常执行,这个软链接可能缺失。
解决:手动补一条命令即可:
ln -s /usr/bin/gcc /usr/bin/cc如果/usr/bin/cc已存在但指向错误版本,先rm /usr/bin/cc再建链。这条经验救过我好几次,编译中断在第一步的情况基本都是它。
5.2 make 报错但日志里只有一行,真正的错误在 config.log
现象:执行 make 立即失败,终端输出只有一行类似error: unknown type name ‘bool’,看起来像代码问题,但源码本身没问题。
原因:r8125 这类驱动在编译前会做内核特性检测,检测脚本会写一个config.log文件,里面记录了完整的失败原因。终端显示的那个错误只是最终表现,本质可能是编译器找不到某个头文件或某个内核宏未定义。
解决:编译失败时,先看config.log和src/Makefile里是否有更详细输出,而不是去翻源码。通常路径是源码目录/config.log。另外,加上make V=1重新编译,能看到完整的编译命令行,确认是否真的用了目标内核的 include 路径。这个问题在线环境不常见,因为在线时工具链版本和内核头文件往往自动对齐,离线后错位就暴露了。
5.3 编译通过但网卡不识别:r8125 与内核自带驱动冲突
现象:驱动编译安装成功,modprobe r8125也没有报错,但ip link里就是看不到新网卡。
原因:Realtek 的 2.5G 网卡在较新内核里会被另一个候选驱动绑定。大多数内核默认加载的是 r8169 驱动,它声称兼容 RTL8125 芯片,但实际兼容性存在边界。因为 r8169 先于 r8125 绑定设备,导致 r8125 的 probe 函数根本不会被调用。
解决:先把内核自带的 r8169 拉黑:
# 临时卸载 rmmod r8169 2>/dev/null # 永久禁用 echo "blacklist r8169" > /etc/modprobe.d/blacklist-r8169.conf # 重新加载目标驱动 modprobe r8125这里我一般会直接走永久禁用路径,原因很简单:内网环境不会频繁热插拔网卡,禁用 r8169 换来稳定的 r8125,性价比很高。如果担心影响其他 Realtek 网卡,可以先检查lspci确认目标网卡的设备 ID 只对应一种驱动。
5.4 重启后驱动丢失:没有注册 DKMS 或模块名冲突
现象:编译安装后网卡工作正常,重启后网卡消失,modprobe r8125提示找不到模块。
原因:make install只是把 .ko 文件拷贝到当前内核的模块目录,没有写入 module 加载配置,也没有注册 DKMS。重启后如果内核没变但模块目录被清空(某些系统更新触发),驱动就丢了;如果内核升级了,老模块路径更直接失效。
解决:一个动作是注册 DKMS,按 4.3 节执行即可;另一个动作是让 modprobe 在开机时自动加载:
echo "r8125" >> /etc/modules depmod -a reboot/etc/modules文件里每行一个模块名,系统启动时会按顺序加载。depmod -a用于重建模块依赖关系,让 modprobe 能正确解析 r8125.ko。两条配合使用,才能保证重启后网卡自动恢复。
5.5 依赖包版本和系统版本不一致导致安装报错
现象:在有网机器下载的 deb 包,拷到目标机器后dpkg -i报版本依赖不满足,例如libc6-dev: Depends: libc6 (= 2.35-0ubuntu3)但系统已有 2.31 版本。
原因:最常见情况是有网机器的源版本比目标机器新。比如有网机器是 22.10 的源,而目标机器是 22.04,下载的 libc6-dev 依赖新版本 libc6,不满足目标机器环境。这属于源版本错配,不属于网络问题。
解决:回到有网机器,把 /etc/apt/sources.list 改成目标机器对应的发行版源(例如把 jammy 换成目标机的版本代号),然后apt update再执行 3.1 的下载脚本。如果目标机器的系统版本较旧,最好直接在目标机器上cat /etc/os-release确认版本代号。这个坑最容易让人误判为下载遗漏,实际上重新对齐源版本后问题自动消失。
6. 把离线安装变成一条命令:脚本化与驱动自检
到了这里,完整流程已经通了。为了让这套方案在内网环境可复制,我习惯把整个流程收拢成一个脚本,同时加入自检逻辑,避免每次手动敲命令还要盯着输出。最后再补充一组验证命令,确认驱动和网卡真实工作。
6.1 整合安装脚本的骨架
这是我管理离线包时常用的脚本结构,核心价值在于:任何一步失败都会明确告知是依赖问题、编译问题还是加载问题。
#!/bin/bash # install_offline.sh - 离线安装 r8125 驱动 # 用法: ./install_offline.sh [deb目录] [驱动源码目录] DEB_DIR=${1:-/opt/offline-debs} SRC_DIR=${2:-/opt/r8125-src} set -e # 阶段1: 安装编译工具链 cd "$DEB_DIR" dpkg -i *.deb 2>/dev/null || true apt-get -f install -y 2>/dev/null || true # 阶段2: 确认工具链可用 if ! command -v gcc >/dev/null 2>&1; then echo "错误: gcc 不可用,请检查 deb 包完整性" exit 1 fi # 阶段3: 编译并安装驱动 cd "$SRC_DIR" make clean || true make -j$(nproc) KSRC=/lib/modules/$(uname -r)/build make install # 阶段4: 模块加载 modprobe r8125 # 阶段5: 验证模块状态 if lsmod | grep -q r8125; then echo "r8125 模块已加载" else echo "警告: r8125 模块未加载,检查 dmesg 输出" dmesg | tail -20 exit 1 fi脚本里的set -e让任何命令失败立即退出,避免错误状态继续蔓延。阶段1 的dpkg -i故意用|| true容忍单包失败,交给随后的apt-get -f install补仓。阶段2 的command -v gcc是快速门禁,gcc 不可用直接中断,省下后面无意义的编译时间。阶段5 的lsmod | grep判断模块是否常驻,这是最简单有效的自检方式。
6.2 网卡自检命令与加载验证
脚本成功执行只是第一步,我还会用以下三条命令验证网卡的物理状态、链路状态和实际速率:
# 查看网卡设备是否被识别 lspci | grep -i ethernet # 查看网卡接口名称和链路状态 ip link show | grep -A2 "state UP" # 查看协商速率(需要 ethtool 工具) ethtool enp3s0 | grep Speedlspci确认 PCI 设备存在,ip link确认接口状态是 UP,ethtool的 Speed 字段确认链路协商到了 2500Mb/s。如果 Speed 显示 100Mb/s,大概率是网线或对端设备问题,跟驱动无关。
最后想分享一个习惯:内网环境最怕的是版本回溯无据可查,所以每次离线安装我都会把当时的 deb 列表、内核版本、gcc 版本、驱动源码版本写进一个 VERSION.txt,和包一起存档。这个习惯也是之前翻车换来的——有次两台机器装完驱动,一台正常一台完全没反应,最后逐条对比才发现是内核小版本不同导致头文件路径不一致。血泪经验换来的结论是:离线安装这件事,版本记录和离线包本身一样重要。希望帮到你。
本文还有配套的精品资源,点击获取