CentOS 8 安装 gcc,这话题看着简单,实际操作起来坑不少。尤其 CentOS 8 官方仓库停止维护之后,默认源都迁移到了 vault 地址,你要是直接跑一句yum install gcc -y,十有八九会撞上Failed to download metadata for repo 'AppStream'这种报错,然后一脸懵。
我帮同事和客户处理过太多这类问题了,从在线安装、离线装包到源码编译,基本都走了一遍。这篇把实测过的方法和踩过的坑整理出来,命令、参数、原理、排查思路都有,全部以 CentOS 8 为基准。不管你是刚接触 Linux 的新人,还是被内网环境折磨的老鸟,应该都能找到能直接抄的答案。
1. 安装前先搞明白:CentOS 8 的特殊处境与 GCC 版本选择
1.1 为什么 CentOS 8 装个 gcc 也会翻车
按照以前 CentOS 7 的习惯,一条yum install gcc -y就完事了。换到 CentOS 8 之后,情况完全不一样。CentOS 8 默认软件仓库里的 gcc 版本是 8.5.0,对应的是 RHEL 8 的 Toolset 策略。问题是 CentOS 8 的生命周期结束后,官方把默认的 BaseOS、AppStream 仓库整体迁移到了 vault.centos.org,如果你系统里配置的源还是老的 mirrorlist 地址,执行安装命令就会报错。
这不是小概率事件,手头只要是没改过源的 CentOS 8 机器,现在去执行安装大概率都会报错。所以第一步根本不是急着装 gcc,而是先检查源的状态。我习惯先跑一条命令试水:
dnf repolist如果输出里能看到仓库列表且没有报错,说明源是通的,可以继续。如果报错,就得先处理源的问题。
处理源的问题,最省事的办法是把/etc/yum.repos.d/下所有 repo 文件里的mirrorlist=注释掉,启用baseurl=并指向 vault 地址。改完之后别忘了执行:
dnf clean all dnf makecache这两个命令的作用是清空缓存并重建元数据,很多改完源之后依然报错的情况,都是因为没清缓存。
1.2 你需要的 GCC 版本到底该怎么选
在动手之前,要想清楚你到底需要哪个版本的 gcc。CentOS 8 官方源里的 8.5.0 支持 C90/C99/C11 以及 C++14 和大部分 C++17 特性,对绝大多数日常编译场景来说完全够用。编译普通 C 程序、写内核模块、做简单的 C++ 项目,直接装官方源版本就行,没必要追求新版本。
但如果你要编译比较新的第三方软件,比如依赖 C++20 特性的库,或者某些对编译器版本敏感的框架,8.5.0 就不够用了。这时候有两条路可以走:
- 安装 AppStream 仓库里的
gcc-toolset-10或gcc-toolset-11,这几个包是红帽提供的新版编译器工具链,安装和使用都相对省事。 - 源码编译安装更新的 GCC,比如 12.x、13.x,灵活度高,但后续的环境变量、库路径都要自己打理。
以我个人的经验,先判断项目对编译器的最低要求,再选安装方式。不要一上来就搞源码编译,那是最后的手段,后面我会详细拆。
2. 最省事的在线安装:dnf/yum 一条命令搞定
2.1 先更新系统索引再装
CentOS 8 默认的包管理器是 dnf,yum 实际上是指向 dnf 的软链接。实测两种写法都能用,但我建议直接用 dnf,输出更清晰,处理依赖关系也更规范。
安装之前先看一眼系统现状:
which gcc gcc --version如果which gcc没有输出,说明没装过,直接开始安装就行。如果有输出但gcc --version报异常,说明环境变量或者软链接有问题,先把旧的卸载掉再装,避免莫名其妙的问题。
源没问题的情况下,直接执行安装:
dnf install -y gcc gcc-c++这里有一个新手特别容易踩的坑:gcc-c++别漏了。很多人只装 gcc,结果编译 C++ 文件时提示g++: command not found,回头又跑去论坛提问。gcc 是 C 编译器,g++ 是 C++ 编译器,这俩在 CentOS 里是分开的两个包,必须一起装。
2.2 卸载旧版本再重装的具体操作
如果系统里已经装过老版本,或者你怀疑之前的装法有问题,想干净地重装一遍,可以先卸载再安装:
dnf remove -y gcc gcc-c++这里特别提醒一句:不要手动去卸载libgcc。libgcc 是系统运行时的基础组件,很多系统工具都依赖它,强行卸载可能连带移除一批关键软件包,甚至导致系统异常。你只需要卸载 gcc 和 gcc-c++ 这两个用户层面的包就够了,libgcc 会作为依赖被保留下来。
卸载完成后重新安装,并验证版本:
dnf install -y gcc gcc-c++ gcc --version g++ --version正常情况下会输出类似gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-18)的版本信息。看到这行输出,说明基础环境已经就绪。
2.3 安装开发工具组
很多时候光装 gcc 还不够,编译项目还需要 make、autoconf、automake、flex、bison 这些配套工具。CentOS 8 提供了一个叫Development Tools的包组,把常用编译工具都打包在一起了。
安装命令:
dnf groupinstall "Development Tools" -y输出会比较多,耐心等跑完就行。装完之后 gcc、g++、make、gdb 这些就都齐了。如果你刚开始接触 C/C++ 开发,我建议直接装这个包组,省得后面编译项目时缺这个缺那个,来回补包浪费时间。
补充一点,groupinstall装的是整个软件包组的默认成员,如果你只做嵌入式开发或者只编译纯 C 项目,可能用不到里面的所有工具,但多装一些基本不会有坏处,占用的磁盘空间也有限。
3. 离线环境怎么办:RPM 依赖包离线下载与安装
3.1 在有网的机器上拉取依赖
离线安装是栽跟头最多的场景。很多内网服务器无法访问外网,系统里又没装 gcc,这时候就得在一台有网的 CentOS 8 机器上把 gcc 和它的所有依赖包全部拉下来,再通过 U 盘或者其他方式拷贝到内网机器。
在 CentOS 8 上,dnf download 属于 dnf-plugins-core 插件,需要先确认是否已安装:
dnf install -y dnf-plugins-core然后创建目录并下载:
mkdir -p /tmp/gcc-rpms cd /tmp/gcc-rpms dnf install --downloadonly --downloaddir=/tmp/gcc-rpms gcc gcc-c++--downloadonly表示只下载不安装,--downloaddir指定保存目录。执行完成后,目录里会有一堆 rpm 文件,包括 cpp、glibc-devel、libgcc、libstdc++-devel、kernel-headers 等。这些就是 gcc 在 CentOS 8 上的完整依赖树。
有一个细节值得注意:下载操作必须在和目标机器相同版本、相同架构的 CentOS 8 上进行。比如目标机器是 x86_64 架构的 CentOS 8.5,那下载机器最好也是同样的版本和架构,否则容易出现体系结构不匹配的安装错误。
3.2 离线机器上安装
把/tmp/gcc-rpms整个目录拷贝到离线机器上,然后执行:
cd /tmp/gcc-rpms rpm -Uvh *.rpm这里用-U而不是-i是因为-U会自动处理已安装包的升级关系,避免同版本冲突。-v和-h是为了显示详细的安装进度。
如果安装过程中报依赖缺失,通常是因为下载的依赖树不完整。最简单的处理办法是回到有网机器上,重新用dnf --downloadonly拉一遍,确保目录里包含了所有依赖。
另一种更稳妥的离线方式是构建本地仓库。在离线机器上先安装 createrepo 工具:
dnf install -y createrepo createrepo /tmp/gcc-rpms然后在/etc/yum.repos.d/下新建一个local.repo文件:
[local-gcc] name=Local GCC Repository baseurl=file:///tmp/gcc-rpms enabled=1 gpgcheck=0配置完成后,再用dnf install gcc gcc-c++ -y安装。这种方式让 dnf 自动解析依赖,比手动 rpm 逐个装可靠得多,也更符合日常操作习惯。
3.3 用 ISO 或本地仓库
还有一种离线方案是使用 CentOS 8 的安装 ISO 镜像。把 ISO 挂载到本地,就能作为软件源使用,不需要外网连接。
mkdir -p /mnt/cdrom mount -o loop /path/to/CentOS-8.iso /mnt/cdrom然后在/etc/yum.repos.d/下分别建立 BaseOS 和 AppStream 两个仓库配置,baseurl分别指向/mnt/cdrom/BaseOS和/mnt/cdrom/AppStream。
这种方式适合内网有规整的镜像管理、但服务器不能直接连接镜像服务器的场景。不过有个局限:ISO 里的软件包版本是固定的,如果你的项目需要特定版本的 gcc,ISO 里不一定有。这时候还是方案 B(downloadonly 拉全套依赖)更灵活。
4. 源码编译安装:定制 GCC 版本的终极方案
4.1 下载源码与准备依赖
当系统仓库里的最大 GCC 版本也满足不了需求时,就只能走源码编译这条路了。完整流程是下载源码、配置、编译、安装,耗时少则二三十分钟,多则一两个小时,取决于机器性能。
以安装 GCC 12.3.0 为例。源码包可以从 GNU 官方镜像站下载,也可以找国内镜像站点。下载之后先解压:
tar -xf gcc-12.3.0.tar.xz cd gcc-12.3.0编译源码需要依赖三个基础库:gmp、mpfr、mpc。这三个库是 GCC 数学运算的基础,如果缺失,configure 阶段会直接报错。我习惯在编译之前先用 dnf 装好:
dnf install -y gmp-devel mpfr-devel libmpc-devel另外,系统里必须已经有一个可用的 gcc,哪怕版本老一点都行。这是因为 GCC 的源码编译需要一个已有的编译器来引导,这个流程叫 bootstrap。
4.2 配置、编译、安装
GCC 官方强烈不建议直接在源码目录里执行 configure 和 make,而是建议建一个独立的 build 目录。这样源码目录始终保持干净,编译出错时直接删掉 build 目录重来,不用重新解压源码。这个习惯我后来一直保留着,真的很省心。
mkdir build && cd build ../configure --prefix=/usr/local/gcc-12.3.0 --enable-languages=c,c++ --disable-multilib make -j$(nproc) make install几个关键参数说明一下:
--prefix指定安装位置。我习惯装到独立的/usr/local/gcc-12.3.0,而不是直接覆盖系统的默认路径,这样两个版本可以共存,需要哪个就把哪个的 bin 目录加入 PATH。--enable-languages=c,c++只编译 C 和 C++ 前端,能大幅缩短编译时间。如果你还需要 Fortran、Ada 等,再往里加。--disable-multilib关闭 32 位兼容库支持。如果你需要编译 32 位程序,就别加这个参数。
编译过程中的常见问题是内存不足。GCC 编译本身就是个吃内存大户,建议机器至少预留 2GB 可用内存。make -j$(nproc)会自动使用所有 CPU 核心,但如果内存不够,加大并行度反而更容易触发 OOM,这时候可以把并行的任务数降下来,比如make -j4。
4.3 为什么升级后还是旧版本(PATH 优先级问题)
源码安装完成之后,很多人会遇到一个经典问题:明明装好了新版本,执行gcc --version显示的却还是旧版本。这个问题的根源是 PATH 环境变量的优先级。
系统默认的 gcc 位于/usr/bin/gcc,你新装的 gcc 位于/usr/local/gcc-12.3.0/bin/gcc。哪个生效,取决于 PATH 环境变量里谁排在前面。排查命令:
echo $PATH which gcc如果which gcc的输出是/usr/bin/gcc,说明/usr/bin在 PATH 里排在前面。解决办法有两个方向:
临时生效,只对当前会话有效:
export PATH=/usr/local/gcc-12.3.0/bin:$PATH永久生效,写入 profile 文件:
echo 'export PATH=/usr/local/gcc-12.3.0/bin:$PATH' > /etc/profile.d/gcc.sh source /etc/profile.d/gcc.sh这里有个很容易被忽略的后续问题:库文件路径。源码编译安装的 GCC 自带新的libstdc++.so.6等运行库。你用新版 g++ 编译出来的程序,如果拿到别的机器上运行,很可能会报:
libstdc++.so.6: version GLIBCXX_3.4.30 not found这是因为目标机器系统的运行库版本太旧。解决办法是把新版本的 libstdc++.so.6 所在目录加入LD_LIBRARY_PATH环境变量,或者做软链接指向新版本库。这个坑我踩过不止一次,处理起来不复杂,但很费时间。
5. 常见问题与排查技巧实录
5.1 常见问题速查表
| 现象 | 原因 | 处理办法 |
|---|---|---|
| dnf install 报 Failed to download metadata | 源地址失效 | 改用 vault 源或替换可用镜像源 |
| 安装后 gcc 命令不存在 | 路径未加入 PATH 或安装不完整 | 检查 which gcc,确认安装包 |
| g++ 命令不存在 | 缺少 gcc-c++ 包 | dnf install -y gcc-c++ |
| 编译报 bits/c++config.h 不存在 | 缺少 libstdc++-devel | dnf install -y libstdc++-devel |
| 编译报 gmp.h 找不到 | 缺少 gmp-devel | dnf install -y gmp-devel |
| 升级后版本不变 | PATH 优先级不对 | 调整 PATH 或软链接 |
| 运行程序报 GLIBCXX not found | 运行库版本不匹配 | 处理 libstdc++.so.6 软链接 |
| rpm 离线安装报依赖缺失 | 下载的依赖树不完整 | 重新用 --downloadonly 拉取 |
| 源码 make 时报内存不足 | 编译内存不够 | 降低并行任务数或加内存 |
这张表基本把我这些年遇到的 gcc 相关问题都覆盖了。碰到问题先对着表查,查不到再深入分析日志,能省不少时间。
5.2 排查思路
遇到 gcc 相关报错,我的排查顺序是固定的:先看编译器本身的版本和路径,再看头文件路径,最后看库文件路径。
版本和路径这条线:which gcc、gcc --version、echo $PATH三个命令就能定位大部分环境变量问题。
头文件这条线:写一个最简单的 hello.c 编译试探:
echo 'int main(){return 0;}' > /tmp/test.c gcc /tmp/test.c -o /tmp/test && echo OK如果报xxx.h: No such file or directory,基本就是缺少对应的-devel包。比如编译 C++ 程序报bits/c++config.h不存在,就是libstdc++-devel没装。
库文件这条线:编译出来的程序运行时报loading shared libraries错误,用ldd命令查看程序依赖了哪些库、哪些库没找到,然后针对性地把缺失库路径加进/etc/ld.so.conf或LD_LIBRARY_PATH。
5.3 避坑技巧
第一,能在线装就别离线装,能离线装就别源码装。这是在无数案例中总结出来的优先级。源码编译看似功能最全,但它引入的环境变量问题、库路径问题、多版本共存问题是最多的,投入产出比很低。
第二,CentOS 8 生命周期结束以后,所有官方源都指向 vault。不改源的话,几乎什么包都装不了。推荐的做法是一开始就把源统一换成可用的镜像源,或者按需临时指定仓库。
第三,升级 gcc 时,千万别想着直接覆盖系统自带的/usr/bin/gcc。系统组件默认依赖特定版本的 libgcc 和运行库,强行替换可能导致系统级的动态库兼容问题。多个版本共存、按需切换,才是长期稳定的做法。
第四,遇到大型项目编译失败,先不要怀疑编译器本身。用 hello.c 测试编译器的基本工作状态,能快速区分是编译器问题还是项目自身的构建配置问题。这个习惯帮我排除了大量无效调试。
6. 从工具链视角看:装完 gcc 之后还缺什么
6.1 配套工具链的完整性检查
gcc 装上不代表万事大吉。实际开发中你还需要确认 make、ld、ar、as 这些工具是否可用。我见过不少人装完 gcc 后编译项目,卡在make: command not found,就是没装 make。
快速检查工具链完整性:
which make ld as ar nm objcopy objdump readelf gdb缺哪个就补哪个,多数在 Development Tools 包组里已经包含了。如果你的环境里没有安装这个包组,也可以用dnf install -y binutils make gdb单独补齐。
6.2 头文件与库编译环境
编译 C/C++ 程序还需要头文件和静态库,也就是-devel包。比如编译依赖 libuuid 的程序,除了要装 libuuid 本身,还要装 libuuid-devel 才能找到头文件和链接库。这个规律适用于几乎所有系统库。
判断是否缺头文件,最简单的办法是看编译报错信息里的文件路径。报错指向/usr/include/下的文件,说明是头文件缺失;报错链接阶段找不到-lxxx,说明是库文件缺失。这两个方向的处理方式不同,前者装xxx-devel,后者装xxx或xxx-devel(如果静态库也需要的话)。
我在实际项目里养成了习惯:每次编译新的第三方库之前,先dnf builddep或者查看官方文档里要求的依赖,一次性把所有-devel包装齐,再开始编译,能省掉大量来回试错的成本。
这个内容后续还想继续扩展,比如交叉编译、gcc 插件开发、不同工具链版本的管理切换,都是可以深挖的方向。不过对于多数人来说,先把 gcc 装好、跑通编译链路,就已经解决了最大的问题。