ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CentOS 7升级GCC 9.3完整指南:SCL与源码编译两种方案

CentOS 7升级GCC 9.3完整指南:SCL与源码编译两种方案 CentOS 7默认的gcc 4.8.5说实话已经有点跟不上时代了。尤其是你只要碰C14、C17的语法或者编译一些新版本的第三方库立刻就能感受到什么叫“缺胳膊少腿”。我也踩过这个坑编译一个需要C17特性的项目报错报得怀疑人生最后老老实实把gcc升到9.3才消停。这篇东西就是记录我自己在CentOS 7.9环境下安装gcc/g 9.3的完整过程。里面既有直接省事的SCL方式也有从源码编译的通用玩法还有一些我实际遇到过、网上很少写清楚的坑。不整虚的直接说怎么做以及为什么这么做。1. 为什么非要折腾高版本GCC1.1 CentOS 7自带编译器到底够不够用CentOS 7官方yum源里默认的gcc是4.8.5这个版本发布于2015年说实话它在当时算是不错的。但放到现在它有个很尴尬的问题——对C11标准支持不完整C14、C17基本是残废状态。你要是写std::make_unique、std::string_view、结构化绑定这类东西4.8.5直接甩脸子给你看。有人可能会说那我不用新特性不就行了问题是很多开源项目已经不给你选的机会了。你随便去GitHub拉一个稍微有点热度的C项目CMakeLists.txt里往往写着set(CMAKE_CXX_STANDARD 17)这时候gcc 4.8.5连configure那关都过不去。还有像Python 3.8以上版本从源码编译、Redis 7.x某些模块、新版Node.js的native插件都隐式地要求一个比较新的编译器。所以升级gcc不是追求时髦而是实际工作的刚需。1.2 为什么挑9.3这个版本而不是最新的你可能想问直接上gcc 12、gcc 13不是更香吗说句实在话在CentOS 7这个老系统上gcc 9.3是一个在“新功能”和“系统兼容性”之间平衡得特别好的版本。首先gcc 9系列对C17的支持已经非常完善日常开发完全够用。其次Red Hat官方提供的SCL软件源里就有现成的9.3版本这意味着它是经过Red Hat在RHEL 7上全面测试过的兼容性有保证。你要是自己搞gcc 12往往会碰到glibc版本太旧、binutils不配套这类连锁问题解决起来那叫一个酸爽。另外很多知名项目在CentOS 7上编译时给的官方指导就是gcc 9.x。比如一些深度学习框架的CPU版本构建文档、某些数据库中间件的编译要求都明确写着“GCC 9.3”。跟着官方推荐走踩坑概率最低。1.3 先搞清楚你到底要哪种安装方式在动手之前你要想清楚一个问题我是只在自己开发机上用还是要部署到生产环境或者是要给团队做一套标准化镜像这会直接影响安装方式的选择SCL软件集方式安装快、卸载干净、不影响系统自带的gcc 4.8.5。适合个人开发机或者不想破坏系统原有环境的场景。源码编译方式安装目录可控、版本任选、不依赖第三方源。适合内网隔离环境、需要定制化编译参数、或者后续要打包成镜像的场景。两种方式我在下文都会详细写。你要是图省事直接看第2章的SCL部分就行如果你更在意灵活性和可控性第3章的源码编译是你的菜。2. 最省事的方案用SCL软件集安装2.1 SCL是什么为什么它能解决版本冲突SCLSoftware Collections是Red Hat搞出来的一套并行安装机制。它的核心理念很简单同一个系统里可以同时存在多个版本的软件互不干扰。它不是像yum upgrade那样把旧版本覆盖掉而是把新版本装到一个独立的目录里通常在/opt/rh/下然后通过环境变量切换使用。这个设计太适合CentOS 7这种老系统了。系统的glibc、内核这些底层组件依赖gcc 4.8.5编译的库你要是直接把系统gcc换成高版本万一碰到ABI不兼容的问题那可不是开玩笑的。SCL方式则完全没有这个风险你要用9.3的时候执行一句scl enable devtoolset-9 bash就能切过去不用的时候它就是一堆静态文件躺在那里不碍事。2.2 完整实操步骤2.2.1 安装SCL源和devtoolset-9先确认系统能联网然后依次执行# 安装SCL仓库源 sudo yum install -y centos-release-scl # 安装devtoolset-9这里面包含gcc 9.3、g 9.3、gdb等一组开发工具 sudo yum install -y devtoolset-9这一步会拉取大约200MB左右的包具体看网络情况。安装期间如果有依赖冲突多半是epel源的版本问题建议先yum clean all yum makecache再重试。我遇到过一次比较诡异的情况在装centos-release-scl时报缺少centos-release包后来发现是系统的release文件被改过用yum reinstall centos-release恢复后再执行就正常了。安装完成后可以看一下装到哪了ls /opt/rh/devtoolset-9/正常会看到root/、enable这些目录和文件。enable这个脚本就是后面切换环境要用的。2.2.2 临时切换与永久生效需要临时使用gcc 9.3时执行scl enable devtoolset-9 bash这句命令会开启一个新的shell会话在这个会话里gcc --version就能看到9.3.1了。退出这个会话回到原来的终端gcc又变回4.8.5非常干净。如果你不想每次都手动切换希望登录系统就默认用新版本推荐把启用命令写入个人shell配置echo source /opt/rh/devtoolset-9/enable ~/.bashrc source ~/.bashrc这里有个小细节要注意source .../enable和scl enable ... bash有细微区别。前者是直接在当前shell里导入环境变量退出登录后再进来依然生效因为写进了.bashrc后者是启动一个子shell退出就失效。如果你只是想临时编译个东西用后者想长期使用把source那行写进.bashrc更合适。2.2.3 验证安装结果gcc --version g --version which gcc输出应该是gcc (GCC) 9.3.1 20200408 (Red Hat 9.3.1-2)which gcc指向的路径是/opt/rh/devtoolset-9/root/usr/bin/gcc而不是/usr/bin/gcc这就对了。它实际上是通过环境变量PATH优先级把新版本的gcc暴露出来的。2.2.4 额外需要的工具链很多时候光有gcc还不够编译一些项目还需要新版cmake、make等工具。devtoolset-9里其实也带了一整套举个例子scl enable devtoolset-9 -- cmake --version如果显示cmake版本还是系统的老版本说明devtoolset-9里没带或者你没启用成功。但好消息是SCL源里通常还有一个devtoolset-9-toolchain元包装上之后把常用的开发工具都补齐了sudo yum install -y devtoolset-9-toolchain这个包会额外安装gdb、binutils、make等工具的9.x版本编译调试环境一站到位。2.3 SCL方式的优缺点你要心里有数优点很明显快、隔离、干净不需要编译等待时间出问题了可以随时关掉。对绝大多数开发场景都够用了。缺点也有主要是两点软件版本固定SCL源里的版本是Red Hat维护的不会自己升级如果你想用gcc 9.4或9.5的小版本更新等不到。环境切换容易忘如果你把scl enable写进脚本但脚本里没有执行这句那编译时用的还是老gcc很容易出现“明明升了级编译却报旧版本错误”的诡异问题。这在热词里提到的“gcc升级后为啥还是旧版本”就是这类情境。先确认是否真的在devtoolset-9环境里再检查PATH顺序别一上来就怀疑安装有问题。3. 更灵活的方案从源码编译安装GCC 9.3如果你追求的是彻底掌控编译路径、不依赖第三方源比如内网机器、信创环境那就得走源码编译这条路。说实在的编译gcc并不难真正难的是“编译过程中五花八门的报错处理”。3.1 前期准备依赖包必须装齐源码编译gcc最怕的就是缺依赖而且有些依赖是编译到一半才报错的非常折磨人。为了避免反复试错我在实际动手前会一次性把下面这些包装齐sudo yum groupinstall -y Development Tools sudo yum install -y gmp-devel mpfr-devel libmpc-devel这三个库gmp、mpfr、libmpc是gcc编译时用来处理复杂数学运算和高精度计算的缺了任何一个到后面都会报cannot find -lgmp或者PPL library not found之类的错。另外flex和bison偶尔也会被检查到建议顺手装上sudo yum install -y flex bison如果你的系统非常精简连make、gcc这些基础工具都没有那先执行sudo yum install -y gcc gcc-c make先把4.8.5装起来因为编译9.3需要一个可用的编译器来“孵化”它自己。3.2 下载源码包并解压去gcc官网的镜像站下载源码包这个步骤要注意gcc官网的原始下载地址在国外国内直连速度极慢最好用国内镜像。# 下载到/usr/local/src 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 ./contrib/download_prerequisites./contrib/download_prerequisites这个脚本会自动下载gmp、mpfr、libmpc三个依赖库的源码到当前目录并且解压好省去手工下载的麻烦。执行这个脚本的时候需要联网如果遇到下载失败多执行几次或者手动从镜像站下载对应版本的压缩包放到源码目录下。3.3 配置编译参数与正式编译这里非常重要的一点是不要在源码目录里直接运行configure。gcc官方文档明确建议建立一个空的编译目录把所有编译中间产物放到编译目录里这样源码目录始终是干净的万一编译失败要重新来过直接删掉编译目录就行。mkdir -p /usr/local/build/gcc-9.3.0-build cd /usr/local/build/gcc-9.3.0-build /usr/local/src/gcc-9.3.0/configure \ --prefix/usr/local/gcc-9.3.0 \ --enable-languagesc,c \ --disable-multilib说说这几个参数什么意思--prefix指定安装目录。我习惯用/usr/local/gcc-9.3.0和系统目录分开后面管理起来清楚。--enable-languagesc,c只编译C和C编译器。如果你还需要fortran或objective-c就加上但一般用不到编译时间长。--disable-multilib在64位系统上禁用32位库的编译支持。如果不禁用configure阶段如果检测不到32位库支持会直接报错。说白了在纯64位环境下这个参数能避免很多不必要的麻烦。配置完成后开始编译。这里建议别直接用make而是指定并行编译的进程数能省大量时间make -j$(nproc)nproc会返回你机器CPU核心数如果核心数多编译速度会明显加快。但要注意如果你的机器内存不够大比如小于2GB-j开太高可能会导致内存溢出编译到一半被系统kill掉。内存小的机器建议make -j2或make -j4稳一点。以4核8G内存的机器为例编译9.3大概需要20到40分钟视机器性能而定。这个阶段可以去泡杯茶了。3.4 安装与环境变量配置编译完成且没有报错的情况下做安装sudo make install这会往/usr/local/gcc-9.3.0目录下安装完整的gcc、g、gfortran如果你选了和对应的头文件、库文件。安装完成后把可执行文件目录加入PATH并更新动态库搜索路径。这里有个细节值得留意直接改/etc/profile或~/.bashrc都行但动态库路径要额外处理好。# 修改用户级配置文件 echo export PATH/usr/local/gcc-9.3.0/bin:$PATH ~/.bashrc echo export LD_LIBRARY_PATH/usr/local/gcc-9.3.0/lib64:$LD_LIBRARY_PATH ~/.bashrc source ~/.bashrcLD_LIBRARY_PATH如果漏掉会碰到一个经典错误gcc能运行但链接时找不到libstdc.so.6或者程序编译成功但运行时提示version GLIBCXX_3.4.28 not found。这是因为新版本gcc依赖新的libstdc动态库而系统默认搜索路径里没有。为了更稳妥一点我建议你还把库文件加到系统的ldconfig配置里echo /usr/local/gcc-9.3.0/lib64 /etc/ld.so.conf.d/gcc-9.3.0.conf sudo ldconfig这样系统中所有需要libstdc的程序都能找到正确版本而不是只对当前用户生效。验证一下gcc --version # 输出 gcc (GCC) 9.3.0 echo int main(){return 0;} | g -x c - -o /tmp/test_gcc /tmp/test_gcc第二条命令如果正常执行没有任何输出说明g能正常编译链接运行整个工具链可用。3.5 源码编译模式的独家建议如果在编译过程中报错了我的建议是不要盲目重启整个编译流程而是先看错误日志。通常make会在最后几行显示出错的源文件和错误类型。常见的报错是缺系统头文件比如#include gnu/stubs-32.h找不到这说明multilib相关的32位头文件缺失直接用--disable-multilib就能绕过去。另外一个我踩过的坑是内存不足导致编译进程被系统kill掉。报错信息往往不是“段错误”而是g: fatal error: Killed signal terminated program cc1plus。这种情况不要慌把-j参数调低或者临时加一点swap分区就能继续。源码编译方式最香的一点是你可以在configure阶段定制各种特性。比如某些嵌入式编译场景需要--enable-targetsall来支持交叉编译某些需要特定架构优化的场景可以用--with-arch...来指定。这是SCL方式做不到的。4. 安装完成后的环境治理与验证4.1 如何确认自己用的到底是哪个gcc这是一个非常现实的问题尤其是当你同时装了系统自带gcc和SCL或源码编译的gcc后一不留神就会用错。我教你一个从小白到大神都该掌握的三连检查法which gcc # 看路径如果显示/usr/bin/gcc那是4.8.5如果显示/opt/rh/devtoolset-9/root/usr/bin/gcc或/usr/local/gcc-9.3.0/bin/gcc说明环境切换成功 gcc --version # 看版本号9.3.0或者9.3.1是正常的 echo $PATH # 确认相应路径是否在PATH前面。echo $PATH会按顺序列出目录排在前面的优先级更高如果你source了~/.bashrc但还是显示旧版本检查一下是不是有别的配置文件比如/etc/profile.d/下的脚本把PATH又改回去了。系统在启动shell时会依次读取多个配置文件后执行的覆盖先执行的。可以用type gcc这个命令看看gcc实际被解析到哪条路径。4.2 动态库层面的坑GLIBCXX版本不匹配编译器版本切换成功只是第一步真正让人头疼的是动态库不匹配。常见场景是你安装了新gcc用新g编译出的程序放到另一台只装了gcc 4.8.5的机器上运行报错/lib64/libstdc.so.6: version CXXABI_1.3.11 not found (required by ./a.out)这个报错的意思是你的程序在编译时链接到了新版本libstdc里的符号但运行环境的系统库版本太低没有这些符号。解决思路一般是三种用静态链接编译时加-static-libstdc -static-libgcc把C标准库和编译器运行时库静态编入程序运行环境就不依赖特定版本的libstdc.so.6了。把新版本的libstdc.so.6拷贝到程序所在目录设置LD_LIBRARY_PATH.来运行相当于绿色版依赖。使用SCL方式时确保运行环境下也装了对应的devtoolset并通过source enable来激活。我在生产环境发布程序时比较推荐第一种方式。虽然最终二进制文件会大不少可能多个几MB但换来的是“一次编译到处运行”的省心体验。编译命令g -static-libstdc -static-libgcc your_program.cpp -o your_program如果项目用了OpenMP-fopenmp还要加上-static-libgomp否则OpenMP运行时库还是动态依赖。4.3 与系统默认gcc共存的管理策略安装了9.3之后系统里会有两套甚至三套编译器同时存在。这是正常的不用非要“干掉”旧版本。原因有三系统里很多核心软件比如内核模块、部分yum插件在编译或升级时还依赖默认gcc删了容易出事。CentOS的yum源增量更新默认按原版本走如果替换掉系统默认gccyum更新时可能触发依赖冲突。新老版本共存你就可以在同一个系统里测试代码在不同编译器下的可移植性对库作者来说反而是优势。我自己的习惯是/usr/local/gcc-9.3.0作为主要工作用编译器通过PATH优先使用/usr/bin/gcc留着给系统工具链兜底。互不打扰各有各的用场。5. 弯路汇总这些坑我替你踩过了5.1 常见安装错误与处理办法我把实际操作中高频出现的问题整理成了一张表方便你直接翻阅错误现象根本原因解决办法configure: error: C preprocessor /lib/cpp fails sanity check系统没有安装g执行yum install -y gcc-c装好基础编译链cannot find -lgmp/cannot find -lmpfrgmp、mpfr依赖库缺失执行yum install -y gmp-devel mpfr-devel libmpc-develfatal error: gnu/stubs-32.h: No such file or directorymultilib头文件缺失configure时加--disable-multilib或者安装glibc-devel.i686g: fatal error: Killed signal terminated program cc1plus内存不足编译进程被OOM killer干掉调低-j参数或增加swap空间make[3]: *** [stage1-bubble] Error 2编译中断原因可能是网络超时下载依赖失败检查网络重新执行./contrib/download_prerequisites然后继续makeerror: GLIBCXX_3.4.28 not found动态库路径没配置好运行时找不到新版本libstdc配置LD_LIBRARY_PATH和ldconfig或者静态链接5.2 安装后编译代码仍然报错的排查思路如果安装、验证都没问题但你在编译某个项目时依旧报错我的排查路径是这样的首先确认报错的是编译器还是链接器。如果是编译阶段报错看它说找不到哪个头文件大概率是include路径没有指向新版本的/usr/local/gcc-9.3.0/include/c/9.3.0。需要手动设置CPLUS_INCLUDE_PATH参数让编译器优先找新头文件export CPLUS_INCLUDE_PATH/usr/local/gcc-9.3.0/include/c/9.3.0:$CPLUS_INCLUDE_PATH如果报错是在链接阶段说找不到某个libxxx.so那多半是库搜索路径没设对。先确认这个库是否存在于/usr/local/gcc-9.3.0/lib64或/usr/lib64再决定用LIBRARY_PATH环境变量还是-L参数来指定。如果链接阶段报的错是“版本不匹配”比如libstdc.so.6: version GLIBCXX_3.4.28 not found那就要判断是不是你自己的程序在运行时动态加载了系统目录下的老库。用ldd your_program看看所有依赖库都从哪加载的一目了然。5.3 一个隐藏较深的坑CentOS 7与新版GCC的ABI兼容这是个深水区但有必要说一下。gcc 5之后C标准库引入了新的ABIstd::string、std::list等容器内部布局发生了变化默认启用_GLIBCXX_USE_CXX11_ABI1。这意味着用gcc 9.3编译的C代码与用gcc 4.8.5编译的C代码在传递std::string这类对象时二进制层面默认为不兼容。最典型的症状是你用gcc 9.3编译了一个动态库项目里又链接了一个用gcc 4.8.5编译的第三方静态库编译时可能没问题但运行阶段出现意想不到的内存错误或者干脆报undefined reference to std::__cxx11::basic_string...。这就是ABI冲突。解决思路有两条如果跟你对接的第三方库源码可控建议全部用gcc 9.3重新编译一遍统一ABI版本。如果第三方库只有预编译的二进制那你可能要用-D_GLIBCXX_USE_CXX11_ABI0来编译强制使用旧ABI。但代价是放弃新标准库的所有新特性且编译出的二进制在不同gcc版本间跳来跳去维护成本高。我的经验是在新环境中编译任何C库之前先确认好整个依赖树的ABI版本策略避免混用。尤其是接手老项目时先用strings命令看看已有库文件里有没有GLIBCXX_3.4.28这类符号strings /path/to/libxxx.so | grep GLIBCXX看结果就知道这个库是在什么gcc版本下编译的心里大概有个数。6. 从一个实例看编译器环境是否配好理论说了一大堆最后我用一个简单的C17代码来验证整条链路是否真的通了。假设我们写一个用到了std::optional、std::variant这种C17特性的程序// test_modern.cpp #include iostream #include optional #include variant #include string int main() { std::optionalstd::string name CentOS GCC 9.3; std::variantint, std::string value 42; if (name.has_value()) { std::cout Name: *name std::endl; } std::visit([](auto v) { std::cout Value: v std::endl; }, value); return 0; }编译命令g -stdc17 test_modern.cpp -o test_modern ./test_modern如果一切正常你会看到Name: CentOS GCC 9.3 Value: 42如果这一步能顺畅跑通说明你的gcc 9.3环境已经完成了从编译、链接到动态库加载的完整闭环可以放心编译项目了。如果你想更严格地验证动态库没有加载错误可以用ldd test_modern看看输出里libstdc.so.6指向的是哪个路径。如果指向/usr/local/gcc-9.3.0/lib64/libstdc.so.6说明新环境完全生效。最后再分享一个小习惯我在源码编译gcc之后会写一个版本标记文件echo GCC 9.3.0 installed from source on $(date) by [你的名字] /usr/local/gcc-9.3.0/INSTALL_INFO.txt这个文件就像施工铭牌将来机器转手或者你忘了当时装了什么版本一看就明白省得再猜。你要是用SCL方式装好之后也可以把devtoolset-9-toolchain、libstdc-devel这些包的版本记录一下方便后续维护。CentOS 7这个系统年纪不小了但靠这些方式它依然能跟上新工具链的节奏。
返回列表