简介面向CentOS/RHEL 6 x86_64等离线环境下的Linux运维与开发者这份gcc_rpm.tar.gz资源包解决了无网络或内网隔离时GCC编译器及其依赖无法在线安装的痛点。包内共含24个文件以22个RPM安装包为主覆盖gcc、gcc-c、cpp以及mpfr、ppl、glibc-devel、kernel-headers等编译所需的核心依赖同时附带1个自动安装脚本和1份说明文档整体包大小约24.11MB。已有3988人学习下载适合需要在内网或受限网络中快速部署完整GCC编译环境的使用者。依赖项齐全无需再逐一查找和匹配软件包借助脚本可按序自动完成RPM安装降低手动处理依赖关系的复杂度。说明文档对适用系统、包体结构和安装顺序作了梳理能有效帮助离线部署者减少踩坑让C/C编译环境在短时间内就绪。 手头拿到一个gcc_rpm.tar.gz我第一反应就是这八成是哪个离线环境或者老系统上要升级 gcc 的活儿。以前我在 CentOS 7 上折腾过不少次默认的 gcc 4.8.5 放在新项目面前是真的憋屈——装 CUDA、编译新内核模块、跑需要 C14/17 特性的代码全都卡在版本这一关。这个压缩包把 rpm 包和源码 tar.gz 混在一起其实意图很直接不管你想省事直接装二进制包还是想自己编译做深度定制两条路都给你备好了。这篇文章就把这个包的完整用法讲透重点覆盖 CentOS 7 上手动升级 gcc 的实操流程以及升级后版本没变、找不到 rpm 命令、CUDA 验证失败这类高频坑适合正在老系统上为 gcc 版本发愁的朋友直接抄作业。1. 拿到 gcc_rpm.tar.gz 之后先想清楚这是什么场景1.1 一个包里同时有 rpm 和 tar.gz意图很明显一个压缩包里同时出现.rpm和.tar.gz后缀通常意味着打包的人给了你两套安装方案。rpm 那边是预编译好的二进制包装起来快适合系统里缺少开发工具、又不想等编译的场景tar.gz 这边要么是 gcc 的源码包要么是把某个已安装的 gcc 目录直接打包适合需要定制编译参数、或者目标机器根本没装 rpm 工具的情况。我先说一下怎么查看这个包里到底有些什么。拿到文件第一件事不是急着解压而是先看清单tar -tzf gcc_rpm.tar.gz | head -50这个命令会列出压缩包内的所有文件路径。如果看到一串gcc-*.rpm的文件那这包走的是 rpm 安装路线如果看到gcc-8.3.0.tar.gz或者gcc-9.4.0/这种源码目录结构那就是源码编译路线。我见过不少同事拿到包就直接tar -xzf结果解出来一堆 rpm 又不知道下一步干嘛纯属浪费感情。还有一种情况需要注意包里面可能还带着libstdc、gcc-c、gmp、mpfr、mpc这些关联包。因为 gcc 升级不是换个二进制就完事C 运行库和数学库的版本是配套的缺一个后面编译都会报错。所以看到这些文件不要觉得多余它们往往是让你少踩依赖坑的关键。1.2 解压前先摸清系统底细我习惯在任何操作之前先确认当前系统的版本和现有 gcc 的状态。这一步花不了两分钟但能省掉后面一大半的排查时间cat /etc/redhat-release gcc --version rpm --versioncat /etc/redhat-release能看到是 CentOS 7.x 还是其他发行版这直接决定包管理工具是 yum 还是 dnf。gcc --version记录当前版本号方便升级后对比。rpm --version则是确认系统里到底有没有 rpm 工具——别笑这个检查很有必要。有些精简安装的容器、或者从其他发行版迁移过来的环境rpm命令可能压根不存在。热词里那条没找到 rpm 命令就是这么来的。如果确认系统没有 rpm 命令那 rpm 路线就直接废了老老实实走 tar.gz 源码编译。这也是为什么我强调拿到包先看内容、再看系统两边的信息对齐了你才能决定走哪条路。2. 版本选择与升级路线为什么老系统升级 gcc 这么头大2.1 CentOS 7 自带的 gcc 4.8.5 到底差在哪CentOS 7 默认的 gcc 是 4.8.5这个版本有年头了对 C11 的支持只是勉强能用C14 和 C17 的特性基本是残缺的。我最近一次被它卡住是在编译一个需要 C17 特性的项目configure 阶段就直接报错提示 gcc 版本过低。如果你是要装 CUDA 10 以上的版本NVIDIA 官方要求 gcc 至少是 6.x 或者更高否则安装器在验证阶段就过不了日志里写的就是那句经典的failed to verify gcc version。另外老版本 gcc 编出来的 C 代码在 ABI 上跟新版本也有差异。gcc 5.1 开始C11 ABI 成了默认值用 4.8.5 编出来的 C 动态库跟新 gcc 编出来的程序混用很可能在运行时报GLIBCXX_3.4.x not found一类的错误。这也是为什么很多项目要求整个工具链统一升级而不是只换一个编译器命令。所以升级 gcc 说到底不是赶时髦是现实需求逼的要么是软件安装器的版本校验不通过要么是源码编译特性不支持要么是老 ABI 跟新运行库打架。2.2 yum、rpm、源码编译三条路线怎么取舍CentOS 7 上升级 gcc 有三条主流路线我按推荐程度说一下。第一条是用 SCL 软件集合命令很简单yum install centos-release-scl yum install devtoolset-8-gcc devtoolset-8-gcc-c装完之后用scl enable devtoolset-8 bash进入新环境gcc 版本就变了。这条路的优势是跟系统自带的 gcc 4.8.5 共存不污染系统目录缺点是 SCL 源在某些网络环境下访问很慢而且scl enable只在当前 shell 生效写脚本和做服务的时候容易忘记开启。第二条就是用离线 rpm 包安装也就是gcc_rpm.tar.gz里那批 rpm 的用法。这种方式适合没有外网、或者内网有私有 yum 源的场景装完之后通过软链或 alternatives 切换默认版本。第三条是源码编译适用于对编译器有特殊要求、或者连 rpm 都没有的机器。可控性最强但耗时也最长全量编译一次 gcc 根据机器配置可能从半小时到两个小时不等make -j$(nproc)是必须的。三条路线没有绝对好坏。我的建议是能用 yum 装就先用 yum毕竟依赖解析人家替你做了不能联网就 rpmrpm 都装不了再做源码编译。热词里那个centos7安装新版本gcc的搜索大概率就是在这三条路线之间徘徊。3. 走 rpm 路线最快上手但坑也不少的安装方式3.1 解包、查询、安装的完整操作流如果包内确认是 rpm 文件先解压到工作目录mkdir ~/gcc-rpm tar -xzf gcc_rpm.tar.gz -C ~/gcc-rpm cd ~/gcc-rpm ls -lh *.rpm解压之后我习惯先做一次模糊查询看看系统上已经装了哪些 gcc 相关的包避免重复安装或者版本冲突rpm -qa | grep gcc这个命令就是热词里说的rpm 模糊查询。rpm -qa列出所有已安装的包grep 再过滤出跟 gcc 相关的。如果输出里已经有老版本 gcc那安装的时候要用升级模式而不是安装模式。安装单个 rpm 包用rpm -ivh升级用rpm -Uvhrpm -Uvh gcc-8.3.0-1.el7.x86_64.rpm rpm -Uvh gcc-c-8.3.0-1.el7.x86_64.rpm rpm -Uvh libstdc-8.3.0-1.el7.x86_64.rpm如果报依赖缺失比如libmpfr.so.4()(64bit) is needed就用rpm -ivh --nodeps先装上试试但要注意跳过依赖检查之后编译时可能因为缺头文件而失败。所以更靠谱的做法是把包里的 gmp、mpfr、mpc 相关 rpm 一并装上顺序是先装依赖库再装 gcc 本体。安装完成后直接用rpm -qa | grep gcc再查一次能确认包确实装上了。但请注意包装上了不等于gcc --version就会变成新版本这就引出了下一个坑。3.2 升级后还是旧版本软链和 PATH 在捣鬼热词里gcc升级后为啥还是旧版本这个问题我至少被问过五次几乎每次都是下面三个原因之一。第一个原因是/usr/bin/gcc这个软链接还指向老版本。rpm 包虽然装了新 gcc 的文件但系统默认命令的软链不一定跟着切。用ls -l /usr/bin/gcc看一眼如果指向的还是gcc-4.8.5就需要手动更新ls -l /usr/bin/gcc*第二个原因是 PATH 环境变量的顺序。如果新 gcc 装在/usr/local/bin而系统在解析命令时先找到了/usr/bin/gcc那gcc --version显示的肯定是老版本。可以用which gcc看实际执行的是哪个路径。第三个原因比较隐蔽当前 shell 的 hash 缓存。bash 会把执行过的命令路径缓存起来即使你换了软链新开的 shell 没问题但当前这个 shell 可能还在用旧路径。执行一下hash -r清掉缓存就好。我个人的习惯是升级后新开一个终端窗口再执行gcc --version这样才能排除掉大部分由 shell 环境引起的假象。如果确认是软链问题可以用 alternatives 来管理alternatives --install /usr/bin/gcc gcc /usr/local/bin/gcc 80 alternatives --config gcc这样以后切换版本就方便多了不用每次手改软链。4. 走 tar.gz 源码路线可控性最强但要有点耐心4.1 先解决 GMP、MPFR、MPC 这三个依赖源码编译 gcc 是个体力活但也是三条路线里最可控的。我选这条路线一般是因为要打一个带自定义参数的 gcc或者目标机器连 rpm 都没有。gcc 源码包本身可以从内核镜像站或者 gcc 官网下载像gcc-8.3.0.tar.gz这种命名很常见。编译之前必须解决三个依赖库GMP、MPFR、MPC。这三个库负责高精度运算和浮点数解析gcc 源码自己不带完整代码需要额外准备。最省事的方式是解压 gcc 源码后直接执行它自带的脚本tar -xzf gcc-8.3.0.tar.gz cd gcc-8.3.0 ./contrib/download_prerequisites这个脚本会自动下载对应版本的 gmp、mpfr、mpc 源码并在 gcc 源码目录下生成链接。前提是机器能访问外网。如果没有外网就得手动把这三个包的源码放到 gcc 源码目录下再手动解压成gmp、mpfr、mpc目录gcc 的 configure 脚本会自己发现它们。我第一次手动搞的时候卡在 mpfr 和 mpc 的版本匹配上后来学乖了直接看download_prerequisites脚本里写的版本号照着一字不差地下载。4.2 configure 参数、make 编译与安装依赖就绪后强烈建议在源码目录外单独建一个 build 目录不要把编译产物跟源码混在一起mkdir build cd build ../configure --prefix/usr/local/gcc-8.3.0 \ --disable-multilib \ --enable-languagesc,c--disable-multilib这个参数值得单独讲讲。如果你的系统没有安装 32 位库默认开启 multilib 会导致编译报错提示找不到gnu/stubs-32.h。老系统上这种情况很常见直接禁掉最省心。--prefix指定安装目录我建议用/usr/local/gcc-8.3.0这种带版本号的路径而不是默认的/usr/local原因后面讲共存的时候会提到。接下来是编译。这里要注意make这个阶段对磁盘 IO 和内存都有要求千万别用单线程硬扛make -j$(nproc)我见过有人在 2 核的机器上直接make等了三个小时还没编完后面加-j4之后速度明显改善。编译完成后make installgcc 就装到了/usr/local/gcc-8.3.0下面。/usr/local/gcc-8.3.0/bin/gcc就是新编译器本体。4.3 编译产物和系统老版本如何共存源码编译安装的 gcc 默认不会覆盖系统自带的/usr/bin/gcc两者可以共存这也是我坚持用带版本号 prefix 的原因。切换方式有两种一是把新路径加进 PATH放在最前面export PATH/usr/local/gcc-8.3.0/bin:$PATH二是建立软链指向系统目录。我更喜欢前者因为不影响系统其他工具对老 gcc 的依赖。这里有个容易忽略的点光换编译器命令不够C 标准库也得跟着换。新 gcc 编出来的 C 程序默认链接/usr/local/gcc-8.3.0/lib64下的 libstdc.so如果不让动态链接器找到这个路径运行时会报error while loading shared libraries: libstdc.so.6: cannot open shared object file解决办法是添加一个 ld 配置echo /usr/local/gcc-8.3.0/lib64 /etc/ld.so.conf.d/gcc-8.3.0.conf ldconfig另外提醒一下热词里有人问用gcc编出的so差异会大吗。同样一份源码不同 gcc 版本编出来的 .soC 接口层面通常兼容但 C 层面差异会很明显因为类布局、异常处理、标准库实现都不同。如果你的 .so 是给第三方调用最好用与对方相同或相近的 gcc 版本编译否则可能踩到 ABI 兼容的暗坑。5. 排坑实录把踩过的几个经典问题一次说清5.1 常见问题速查表我把这些年升级 gcc 过程中碰到的问题整理成一张速查表按现象-原因-解法的格式来写方便遇到问题直接对号入座。现象常见原因解决操作升级后 gcc --version 还是旧版本软链未切 / PATH 顺序 / hash 缓存ls -l /usr/bin/gcc、hash -r、新开终端提示没有 rpm 命令精简环境未装 rpm 工具用 yum 装 rpm 或改走源码编译编译时找不到 gmp/mpfr/mpc依赖库未准备或版本不匹配执行./contrib/download_prerequisitesconfigure 报找不到 stubs-32.hmultilib 开启但缺 32 位库加--disable-multilib运行程序找不到 libstdc.so.6动态库路径未配置写 /etc/ld.so.conf.d 后ldconfigCUDA 安装器验证 gcc 版本失败gcc 版本过老或不匹配升级到 CUDA 要求版本或查看 /var/log/cuda-installer.log编译耗时过长未启用并行编译用make -j$(nproc)新 gcc 编的程序在旧系统跑不了glibc 或 libstdc 版本不兼容换低版本 gcc 编译或同步升级运行库这里特别说一下 CUDA 那个问题。安装 NVIDIA 驱动或者 CUDA 工具包时如果日志里出现failed to verify gcc version先去看/var/log/cuda-installer.log里面会写明要求的最低 gcc 版本。比如 CUDA 10.2 要求 gcc 版本不高于 8CUDA 11 以上对 gcc 版本有下限和上限。不要盲目把 gcc 升到最高版本有时候太高反而过不了验证。我见过有人装 CUDA 前把 gcc 升到了 11结果安装器直接拒绝因为超出了它测过的编译器范围。5.2 几个容易被忽视的边界场景WSL、conda 打包和 configure 差异热词里还有几个搜索方向我在这里统一补充。关于 WSL 里装 gcc 开发环境其实比 CentOS 物理机简单得多。WSL 的 Ubuntu 发行版直接sudo apt install build-essential就行这个包会把 gcc、g、make 一起装上。如果你用的是 WSL 里的 CentOS 镜像那就按前面讲的 yum 或者 rpm 方式处理。有个细节是 WSL 里升级 gcc 之后记得看一下/usr/local/bin和/usr/bin的优先级WSL 默认 shell 的 PATH 处理跟普通 CentOS 略有差别经常出现升级后依然调用旧版本的问题。关于 conda 环境里用 tar.gz 创建环境这个跟 gcc 本身关系不大但既然有人搜我就顺带说一句。如果你拿到一个打包好的 conda 环境 tar.gz解压后用 conda 的 prefix 机制注册即可或者直接修改环境变量的 PATH 指向解压目录。但要注意conda 打的包通常是绑定路径的直接换机器可能链接失效需要在目标机器上重新处理库路径。这类环境包里如果编译工具链是旧 gcc那你在这个环境里编译项目同样会遇到老版本问题别以为用 conda 环境就绕开了编译器版本。还有人说gcc 源码的 configure 不同这个指的是不同的发行版在打包 gcc 时会加不同的 configure 参数比如是否默认启用 PIE、默认架构优化级别、是否带 LTO 支持等。所以你会发现从 CentOS 源装的 gcc 和从源码自己编的 gcc版本号都是 8.3.0但编出来的程序行为可能会有细微差别。这也是为什么严肃的二进制发布项目会尽量用与目标环境一致的编译器来构建。我自己的经验是升级 gcc 之前一定先写清楚需求是给哪个软件用、那个软件要求什么版本范围、是要长期切换还是临时用一下。需求一旦模糊后面每一步都可能白干。比如为了一个只需要临时编译的项目把系统默认 gcc 换成全新版本结果把依赖旧 gcc 的服务搞挂了这种代价完全没必要。最后再分享一个小习惯每次升级完 gcc我都会顺手写一个版本切换脚本把 PATH、LD_LIBRARY_PATH、软链切换命令都放进去放到/etc/profile.d/下。这样以后来回切换版本只需要一行命令不用每次重新回忆路径和参数。折腾过几回 gcc 升级之后你会发现真正让人头大的不是编译本身而是版本切换时那些记不住、查不到的环境变量坑。本文还有配套的精品资源点击获取