尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

CentOS 7 源码编译升级 GCC 9.3 完整指南

发布时间:2026/9/30 1:36:18

资讯中心
01
ARTICLE

CentOS 7 源码编译升级 GCC 9.3 完整指南

CentOS 7 源码编译升级 GCC 9.3 完整指南
先说一下我为什么会去折腾这个东西。之前在一台CentOS 7服务器上编译一个C项目用的是C17标准结果configure阶段直接报错提示gcc版本过旧、不支持某些特性。一查版本系统自带的gcc还是4.8.5。这台机器跑着线上服务不能轻易重装系统也不能乱动底层库所以只能在CentOS 7这个老环境里装一个高版本GCC。这篇文章就是基于这个背景整理出来的目标是让读者在CentOS 7上顺利装好gcc/g 9.3同时把那些容易踩的坑——比如明明升级了一敲gcc -v还是旧版本这类问题——一并讲清楚。文章适合两类人看一类是被迫在老系统上做新开发的运维或后端工程师另一类是刚接触Linux、想搞明白软件编译安装原理的入门读者。前者可以直接跳到操作步骤抄作业后者我建议从头看一遍因为里面涉及的依赖、路径、软链接这些概念在很多软件安装场景里都能复用。1. 版本选型为什么偏偏是GCC 9.3CentOS 7官方仓库自带的GCC版本是4.8.5这是个非常古老的分支。它的C11支持还算完整但C14基本是残缺的C17更是一点边都不沾。如果你只是编译一些老项目GCC 4.8.5凑合能用但如果要编译TensorFlow、新版Redis、Python 3.8以上的源码或者任何用到现代C特性的项目直接就卡死在编译器这一步。我选GCC 9.3而不是更新的版本主要基于三点考虑。第一9.x系列是GCC的一个长期稳定分支9.3属于该分支的修复版本相比9.1、9.2修了不少编译器的内部错误ICEInternal Compiler Error。这类内部错误在编译大型C项目时经常随机触发修复版能显著减少这种闹心问题。第二CentOS 7的系统库环境相对老旧。GCC 10以上版本虽然也能编译安装但它在生成目标代码时默认假设的某些运行环境行为和CentOS 7的binutils、glibc配合得并没有那么好。9.3的生成代码风格和CentOS 7自带的2.27版本glibc兼容性更稳编译出来的程序放到线上不容易因为动态库版本问题翻车。第三GCC 9.3完整支持了C17标准这对于绝大多数现代C项目来说是一道分水岭。C17之前的编译器在模板元编程、结构化绑定、if constexpr这些特性上要么不支持要么支持得很别扭9.3全面落地了这些能力日常开发完全够用。有人会问为什么不直接用SCL仓库里的devtoolset-9这个后面会专门对比先卖个关子。简单说SCL方式安装简单但它把GCC装在一个隔离的环境里和系统原生的编译链整合得不够彻底很多第三方项目在编译过程中找不到它的库路径处理起来反而麻烦。2. 安装前置条件磁盘、依赖库与工具链的一个都不能少在动手装GCC之前先确认三样东西磁盘空间、基础编译工具、必要的依赖库。这三样缺了哪个编译过程中都会以非常费解的方式报错而新手往往被这些报错带偏以为是GCC源码本身的问题。2.1 磁盘空间与内存评估GCC 9.3的源码压缩包大约110MB解压后约800MB编译过程产生的临时文件更多。加上gmp、mpfr、mpc这几个依赖库的编译产物以及最终安装到系统的文件建议预留至少5GB可用磁盘空间。内存方面GCC编译时有几个阶段极其吃内存尤其是C标准库的头文件解析阶段。如果是1GB内存的小机器建议先加swap否则随时可能遇到internal compiler error: Killed这类被OOM Killer杀掉进程的报错。可以用这个命令快速检查磁盘df -h /usr/local确保剩余空间不低于5GB。再看内存和swapfree -h如果swap是0而内存又小于2GB建议用fallocate快速加一个swap文件这个操作在生产环境里也能临时救急。2.2 基础编译工具有个容易被忽略的点编译GCC本身也需要一个能用的C编译器。CentOS 7系统自带的gcc 4.8.5虽然老但它本身是完好的可以用来编译GCC 9.3的C部分。此外还需要make、bison、flex这些构建工具。一次装齐yum install -y gcc gcc-c make bison flex如果你是全新最小化安装的CentOS 7这一步会把最基本的编译环境补全。注意这里的gcc是为了后面编译GCC的引导编译器装完之后它会继续扮演这个角色等新版本编译好以后再由新版本接管系统默认编译器的位置。2.3 依赖库GMP、MPFR、MPC这是整个安装过程中最容易出问题的部分。GCC在编译过程中依赖三个数学库GMP大数运算、MPFR浮点运算、MPC复数运算。CentOS 7的默认yum源里其实有这三个库的旧版本但版本太老GCC 9.3要求的版本高于系统自带的所以在configure阶段就会检查不通过。解决办法有两个。第一个是从EPEL仓库安装EPEL里通常有稍新一些的版本但也不保证满足GCC 9.x的要求。第二个是让GCC源码包自动下载并构建这些依赖。更靠谱的是直接在源码目录里执行cd gcc-9.3.0 ./contrib/download_prerequisites这个脚本会从GCC官网的镜像站点自动下载gmp-6.1.0、mpfr-3.1.4、mpc-1.0.3并解压到GCC源码目录下。GCC的configure脚本会自动识别这些目录在编译GCC的同时把它们一并编译链接为静态库。整个过程不需要额外干预出问题的概率最低。如果服务器在内网环境、无法直接访问外网你需要在能联网的机器上先下载这些依赖包并传到服务器放到gcc-9.3.0目录下然后手动解压保证解压后的目录名和脚本中预期的一致。这个离线模式的操作后续会单独说。3. 通过SCL仓库快速安装GCC 9的路径与边界说完源码编译先插一段SCL的安装方式因为它是很多教程的首选但用起来有坑。SCLSoftware Collections是Red Hat官方推出的软件版本覆盖方案。CentOS 7下可以通过centos-release-scl软件包启用这个仓库然后直接安装devtoolset-9。命令如下yum install -y centos-release-scl yum install -y devtoolset-9-gcc devtoolset-9-gcc-c安装完成后你不能直接用gcc命令因为SCL把新版本安装到了一个独立的目录/opt/rh/devtoolset-9/root/usr/bin/gcc。要使用它需要先启用环境变量scl enable devtoolset-9 bash这条命令会打开一个新的bash在这个bash里gcc -v就会显示9.3.1。注意这里有个细节SCL的环境变量只在当前bash会话里生效退出这个bash再进来gcc又变回4.8.5。有人通过往/etc/profile.d里写source命令来永久生效但实测下来这样会让部分依赖系统默认编译器的第三方脚本行为异常因为它们拿到的gcc是9.x而系统某些库却是按4.8.5的规范生成的偶尔会碰到ABI校验错误。另外SCL版本的gcc在编译C代码时默认的搜索路径包含了/opt/rh/devtoolset-9/root/usr/lib/gcc/...如果你在编译某些使用了autoconf的项目时configure脚本会去检查gcc是否能编译可执行程序这时候大概率能通过。但遇到CMake项目时可能出现CMAKE_CXX_COMPILER版本与预期不符这类问题需要显式指定编译器路径比较繁琐。所以我的结论是SCL方式适合只想临时编个程序、不想编译整个GCC源码的场景如果你需要长期维护一个现代C项目或者需要自定义GCC的编译选项老老实实源码编译才是正路。这也是本文标题选择9.3源码编译的原因一劳永逸把控制权握在自己手里。4. 源码编译GCC 9.3的完整拆卸式流程下面进入正题完整走一遍源码编译安装GCC 9.3的过程。我尽量把每个步骤背后的意图讲清楚而不是简单复制粘贴一堆命令。4.1 下载与解压GCC源码首先从GCC官网的镜像站下载9.3.0的源码包。考虑到国内网络环境建议挑一个离你近的镜像。清华源和阿里源都长期同步GCC的发布包速度稳定。cd /usr/local/src wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-9.3.0/gcc-9.3.0.tar.gz tar -zxvf gcc-9.3.0.tar.gz cd gcc-9.3.0解压之后先别急着configure。跑一下依赖脚本./contrib/download_prerequisites这个脚本执行完会在目录里看到gmp-6.1.0、mpfr-3.1.4、mpc-1.0.3这几个文件夹。到了这一步如果不放心可以检查一下这几个文件夹是否存在确认没问题再继续。4.2 创建独立的编译目录这是一个非常重要的经验千万不要在GCC源码目录里直接运行configure和make。GCC官方明确建议在源码目录之外单独建一个build目录这样源码树保持干净编译产物集中在一个地方方便之后彻底清理。mkdir /usr/local/src/gcc-9.3.0-build cd /usr/local/src/gcc-9.3.0-build这个源码目录和编译目录分离的做法也适用于很多其他软件比如binutils、glibc。编译过程中产生的临时文件、中间文件密密麻麻如果和源码混在一起出了问题很难排查。4.3 configure参数解析在build目录里执行configure参数要仔细斟酌。我用的命令如下../gcc-9.3.0/configure \ --prefix/usr/local/gcc-9.3.0 \ --enable-bootstrap \ --enable-languagesc,c \ --disable-multilib \ --enable-checkingrelease逐个说下这些参数的意义--prefix指定安装路径。这里我装了/usr/local/gcc-9.3.0单独建目录的好处是以后想卸载直接rm -rf这个目录就行干净利落。不建议直接覆盖到/usr因为那样会和系统的旧版gcc搅在一起以后想恢复原状就麻烦了。--enable-bootstrap开启引导编译。GCC会先用系统现有的编译器编译一遍然后用第一次编译出的GCC再编译一遍自己最后用第二次编译出的GCC编译第三遍三层编译后生成的编译器才是最稳定的。代价是编译时间大幅增加但为了可靠性这一步值得等。--enable-languagesc,c只需要C和C编译器。如果你还需要Fortran、Go、Ada等语言支持可能要在这里加上对应项但大部分人用不到加上了反而拖慢编译速度。--disable-multilib禁用多架构库。CentOS 7默认x86_64架构一般情况下不需要同时生成32位和64位库。这个参数能显著减少编译时间和链接负担。如果你的机器确实需要编译32位程序那就不能加这个参数。--enable-checkingrelease在release模式下只做最小限度的编译期检查这个参数的目的是降低编译器自身的运行开销对于生产环境是标准配置。如果configure阶段报了某个依赖库找不到通常就是前面download_prerequisites没执行成功或者下载后没有解压到正确位置。4.4 make编译与make install配置完成后开始编译make -j$(nproc)-j$(nproc)的意思是让make并发任务数和CPU核心数一致。如果你的机器是2核4线程nproc可能返回4这时候并发编译能快很多。但要注意GCC编译进程特别吃内存如果你在2GB内存的机器上直接开4个并发任务很容易把内存打满。这类机器建议保守一点make -j2编译时长按机器性能差异很大。4核8线程的机器编GCC 9.3大概需要40分钟到1小时如果是2核的入门云主机可能要1.5到2小时。这个过程会输出大量编译日志中间如果看到某个文件报错不要急着重新整体编译先定位具体错误原因。一个比较常见的错误是cannot compute suffix of object files: cannot compile这类错误通常是configure阶段检测编译器失败多半是依赖库缺失或者环境变量有问题。回头检查步骤2里的基础工具是否装全。编译完成后执行make install这条命令会把所有编译好的二进制和头文件拷贝到/usr/local/gcc-9.3.0目录下。执行完后验证一下关键文件是否到位/usr/local/gcc-9.3.0/bin/gcc --version如果能看到gcc (GCC) 9.3.0这样的输出说明安装本身已经成功。5. 环境变量与软链接彻底解决gcc还是旧版本的经典困惑很多人在这一步卡住明明装了新版GCC执行gcc -v却还是4.8.5。原因很简单系统命令查找时PATH变量里/usr/bin排在/usr/local/gcc-9.3.0/bin前面而系统默认的gcc在/usr/bin/gcc根本没跑到你新装的位置。5.1 调整PATH环境变量我建议为GCC 9.3创建单独的环境变量配置文件方便管理和回滚cat /etc/profile.d/gcc-9.3.0.sh EOF export PATH/usr/local/gcc-9.3.0/bin:$PATH export LD_LIBRARY_PATH/usr/local/gcc-9.3.0/lib64:/usr/local/gcc-9.3.0/lib:$LD_LIBRARY_PATH export CC/usr/local/gcc-9.3.0/bin/gcc export CXX/usr/local/gcc-9.3.0/bin/g EOF source /etc/profile.d/gcc-9.3.0.sh这里有三条变量值得解释PATH让shell找到新版gcc、g、gcov等可执行文件。LD_LIBRARY_PATH让运行时动态链接器能找到新版GCC的C标准库libstdc.so.6。这一步特别关键因为新版g编译出的C程序默认依赖新版libstdc如果这个库路径不在LD_LIBRARY_PATH里编译时能过运行时直接报version GLIBCXX_3.4.28 not found错误。CC和CXX很多configure脚本和Makefile会读取这两个变量来决定用哪个编译器显式指向新版可以避免它们又跑去用系统旧版。设置完环境变量后重新登录shell或者直接执行source然后验证which gcc gcc -v正常情况下which gcc会指向/usr/local/gcc-9.3.0/bin/gccgcc -v输出里也会显示9.3.0。5.2 软链接潜在的坑与建议有教程建议直接把/usr/bin/gcc软链接到新版本ln -sf /usr/local/gcc-9.3.0/bin/gcc /usr/bin/gcc这个方法见效快但风险很大。CentOS 7系统内部的很多组件的编译运行都依赖4.8.5尤其是一些内核模块、系统库的构建脚本它们会默认调用/usr/bin/gcc如果你把软链接改成9.3可能因为ABI差异导致编译失败甚至影响系统包管理器运行。我的建议是不要动/usr/bin下面的软链接让新版GCC通过PATH环境变量优先生效。这样系统工具需要旧版时仍然可以显式调用/usr/bin/gcc你的项目默认使用新版。两者井水不犯河水。5.3 动态库缓存的调整另一个必须注意的是动态库缓存。LD_LIBRARY_PATH方式是临时的如果你想让系统范围的程序都能找到新版libstdc.so可以把库路径写入ld.so.confecho /usr/local/gcc-9.3.0/lib64 /etc/ld.so.conf.d/gcc-9.3.0.conf ldconfig这样执行二进制时动态链接器会在默认路径之外额外搜索这个目录。但如果你又设置了LD_LIBRARY_PATH两者同时存在时LD_LIBRARY_PATH优先级更高。以我的实践经验两者都配好最稳妥。6. 编译实测一个C17程序的完整验证环境配置好以后写个测试程序验证一下g是否能正常编译、运行并且确认用的确实是不折不扣的C17特性。这里用一个简单的例子展示结构化绑定和if constexpr两个C17特性#include iostream #include tuple #include type_traits template typename T auto get_value() { if constexpr (std::is_integral_vT) { return 1; } else { return 0.5; } } int main() { auto [a, b] std::make_tuple(10, 0.5); std::cout a a , b b std::endl; std::cout int value get_valueint() std::endl; std::cout double value get_valuedouble() std::endl; return 0; }编译g -stdc17 test.cpp -o test ./test如果输出正常说明g 9.3编译C17程序没有问题。接着还要检查一个容易被忽略的东西——libstdc.so.6的版本strings /usr/local/gcc-9.3.0/lib64/libstdc.so.6 | grep GLIBCXX输出列表里应该包含GLIBCXX_3.4.28或更新的标识这是GCC 9.x新增的符号版本。那些报错GLIBCXX_3.4.28 not found的盆友基本都是因为运行程序时没有把新版libstdc的路径暴露给动态链接器要么加LD_LIBRARY_PATH要么走ldconfig二者必有其一。再验证一下编译出的二进制依赖的运行时库ldd test如果libstdc.so.6一栏指向的还是系统旧版本刚设置的环境变量未必被当前shell进程正确读取了。检查一下是否重新登录过、或者source过配置文件。7. 编译过程与使用过程中的典型报错及排查链路源码编译GCC的报错千奇百怪但我总结下来高频问题其实就那么几类。这里把完整的排查思路写出来比直接给答案更有价值。7.1 configure阶段报cannot find crt1.o这种报错的大意是链接器找不到C运行时启动文件。常见原因是configure时检测64位库路径失败。如果configure命令里加了--disable-multilib还是报这个错检查一下系统是否缺少glibc-develyum install -y glibc-devel glibc-static装完后重新configure。因为crt1.o这个文件通常由glibc-devel包提供路径在/usr/lib64/crt1.o缺了它任何C程序的编译链接都会失败。7.2 编译中途internal compiler errorICE有两种情况。一种是GCC自身的Bug换一个更新或更旧的版本试试另一种是内存不足导致进程被系统杀掉。区分方法很简单看编译输出末尾有没有Killed字样如果有基本可以断定是内存问题。处置办法是减少make并行数或者临时加swap。还有一种容易忽略的情况是磁盘写满。GCC编译时会产生海量中间文件如果/tmp或build目录所在分区的磁盘满了一定报错。用df -h检查一下把编译目录换到空间充足的分区即可。7.3 make install后头文件找不到装好之后写代码发现怎么也include不到新版本头文件。这是因为/usr/include被系统头文件目录占着新版GCC的头文件默认安装在/usr/local/gcc-9.3.0/include/c/9.3.0下面。一般你直接用g编程序时会自动搜索这个路径但如果某些IDE或第三方构建工具自己指定了头文件搜索路径就会覆盖GCC的默认行为导致找不到。解决办法是在构建系统里显式加上-I/usr/local/gcc-9.3.0/include/c/9.3.0同时也要加-L/usr/local/gcc-9.3.0/lib64这样链接器才知道去哪里找libstdc.so。7.4 运行时version GLIBCXX_X.X.X not found这个问题在前文提到过。它的本质是编译时用的libstdc新版和运行时加载的libstdc旧版不是同一个。运行中程序的动态库解析顺序默认是系统的/lib64和/usr/lib64而新版libstdc在/usr/local/gcc-9.3.0/lib64下面没被找到。修复链路按优先级排列echo /usr/local/gcc-9.3.0/lib64 /etc/ld.so.conf.d/gcc-9.3.0.conf ldconfig然后用ldd重新查看程序依赖确认libstdc.so.6是否指向新版。如果指向的还是旧版说明ldconfig没有生效检查路径是否写对。8. 离线环境下的安装补充方案前面提到过内网服务器无法直接访问外网的情况这里单独补一块。离线安装的核心是在能联网的机器上把所有需要的文件下载好然后一起传到目标机器。需要提前准备的包括GCC源码包、三个依赖库的源码包。download_prerequisites脚本会从网上下载离线环境不能直接执行需要手动准备。依赖库的下载地址如下gmp: https://gmplib.org/download/gmp/gmp-6.1.0.tar.bz2mpfr: https://www.mpfr.org/mpfr-3.1.4/mpfr-3.1.4.tar.bz2mpc: https://ftp.gnu.org/gnu/mpc/mpc-1.0.3.tar.gz把GCC源码包和这三个依赖包都放到同一台离线机器的/usr/local/src目录下解压GCC源码tar -zxvf gcc-9.3.0.tar.gz cd gcc-9.3.0 tar -jxvf gmp-6.1.0.tar.bz2 tar -jxvf mpfr-3.1.4.tar.bz2 tar -zxvf mpc-1.0.3.tar.gz mv gmp-6.1.0 gmp mv mpfr-3.1.4 mpfr mv mpc-1.0.3 mpc注意这里必须把解压后的目录重命名成gmp、mpfr、mpc这种不带版本号的名字因为GCC源码的configure脚本恰好以这些短目录名作为匹配条件。如果你不重命名可以创建一个符号链接效果一样ln -s gmp-6.1.0 gmp ln -s mpfr-3.1.4 mpfr ln -s mpc-1.0.3 mpc后面的configure和make就和在线环境完全一致了。另外离线机器上如果连基础工具链都没装那就更麻烦了需要从Red Hat的ISO安装镜像里制作本地yum源。这个内容可以单独写一篇这里只提一句把CentOS 7的DVD ISO挂载到/mnt目录然后配置一个以file:///mnt/为baseurl的yum源文件就能通过yum install把gcc、make等工具装齐。9. 切换到新版本GCC后的进一步配置与注意事项安装和验证到这里基本算结束了但实际开发中还有些细枝末节不注意会继续踩坑。9.1 让CMake项目使用新版GCCCMake默认会去/usr/bin里找编译器。如果你想让它使用新版需要在配置时显式指定cmake -DCMAKE_C_COMPILER/usr/local/gcc-9.3.0/bin/gcc \ -DCMAKE_CXX_COMPILER/usr/local/gcc-9.3.0/bin/g \ ..还有一种做法是在CMakeLists.txt里通过set指定编译器路径但这样做会让CMakeLists丧失可移植性同队其他人要是没装这个路径的编译器整个项目就挂了。所以我更推荐在配置阶段传参。9.2 新版GCC编译出的程序部署到其他机器如果你的程序需要拷贝到其他CentOS 7机器运行你会发现程序依赖了新版libstdc.so.6。目标机器上如果没有这个库就会报error while loading shared libraries: libstdc.so.6: cannot open shared object file。做法是把新版libstdc.so.6也一起拷贝到目标机器的/usr/local/gcc-9.3.0/lib64目录下并在目标机器上配置ld.so.conf.d或者在程序启动脚本里设置LD_LIBRARY_PATH。更稳妥的方案是编译时使用静态链接g -static-libstdc -static-libgcc test.cpp -o test这样libstdc和libgcc的内容会直接编译进可执行文件运行时完全不依赖目标机器上的动态链接库。代价是可执行文件体积变大但对于部署到多台服务器的情况这个代价完全值得。9.3 与Python等解释型语言扩展模块的兼容不少Python的C扩展模块在编译时会调用系统gcc。如果你把PATH环境变量全局改成了新版GCC扩展模块也会跟着用新版编译。这在大多数时候没有问题但偶尔会碰到个别扩展模块的setup.py写得不够健壮对新版GCC的编译警告处理得不好导致编译失败。我的习惯是在编译Python扩展模块时临时把PATH里的新版路径去掉用回旧版gcc编完了再恢复。这听起来原始但确实能避免很多莫名的兼容问题。10. 日常使用中的一点小习惯最后分享几个我在实际使用中养成的习惯不一定适合所有人但确实帮我省了不少事。第一我习惯在项目根目录下放一个env.sh里面写着编译这个项目要用的环境变量export PATH/usr/local/gcc-9.3.0/bin:$PATH export LD_LIBRARY_PATH/usr/local/gcc-9.3.0/lib64:$LD_LIBRARY_PATH这样换一台新机器拉完代码后先source env.sh再编译不需要把环境变量全局乱改。第二我把环境变量配置写进/etc/profile.d/之后做了备份。因为以后系统升级或者装了别的软件可能会覆盖这个文件。备份一下出问题能快速恢复。第三GCC版本升级之后旧的编译产物最好用make clean清理一遍再重新编译。我见过不少明明升级了编译器重新编译还是报旧错误的情况多半是Makefile增量编译导致没有真正触发重新编译。这次在CentOS 7上装GCC 9.3的完整过程就是这样。从版本选型、环境准备、源码编译、环境配置到典型坑位的排查一路走下来核心就一句话版本隔离、路径分明、环境变量管好。做到这三点不管以后是升级到10.x还是11.x或者换一台机器重新部署都能少走很多弯路。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。