ARTICLE DETAIL

资讯详情

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

麒麟V10服务器源码编译升级GCC 9.3完整指南

麒麟V10服务器源码编译升级GCC 9.3完整指南 1. 为什么值得折腾这一趟1.1 麒麟V10自带的GCC到底有多老很多刚上手麒麟V10服务器版的朋友第一件事就是急着跑编译任务部署新服务、装企业级中间件、交叉编译一套AI推理框架。结果编译到一半系统里自带的GCC直接甩出一句“unrecognized command line option”或者“internal compiler error”就罢工了。这个现象的原因很直接麒麟V10服务器版默认仓库里携带的GCC一般停留在老版本部分发行批次甚至是GCC 4.8.5或者7.x级别的老工具链。这个版本跑老代码没问题但一旦遇到需要C14/C17标准特性、OpenMP新接口或较新SDK编译出身的程序老GCC基本就无能为力了。GCC 9.3是2019年发布的9系列补丁版本它支持更完整的C17特性对C11、C99标准的支持也成熟得多现在很多主流开源组件比如新版MySQL、Redis模块、TensorFlow、llvm依赖层在编译时都直接要求GCC 8.3以上GCC 9.3在兼容性和稳定性上几乎都是最佳落点。从长期运维角度看服务器工具链不能随便跟到太新的版本。GCC 12、GCC 13虽然开源社区已经很成熟但在国产化操作系统环境里不一定有完整适配的依赖库贸然升级会引发一连串连锁问题。GCC 9.3作为保守且功能够用的版本是生产环境的合理折中。这篇文章你可以直接当操作手册来用尤其是那些被“依赖下载”和“环境变量配置”这两步折磨过的人后面我会把最容易踩坑的点全部拎出来讲清楚。1.2 二进制仓库和源码编译两条路线怎么选我在回答各种升级需求时第一句话永远是先不要急着源码编译查一下系统仓库有没有离线的GCC9.3包。麒麟V10服务器版有些时候能通过配置好的yum源直接安装更高版本的gcc或者借助scl软件集Software Collections方式实现多版本共存。如果你只是业务软件安装要求“gcc 9”能用yum解决就不要自己编译省时省力。不过现实情况往往是内网环境没有可用仓库源或者即使源里存在gcc相关包也缺少对应的gmp、mpfr、mpc依赖强行yum install后一运行就提示缺少动态库。当仓库路线走不通时源码编译就是最可控的手段。这里还可以提一句像银河麒麟V10经常会出现在金融、政务、能源这类内网场景外网下载受限你完全可以用一台能访问外网的机器拉好源码包和依赖包再通过内网传给目标服务器。前期多花十分钟下载依赖源码后面编译时会顺利非常多。1.3 升级到GCC9.3后能解决哪些真实业务问题先举两个我在实际部署中遇到的案例。第一个案例是某架构开发团队在内网服务器上编译一套基于CMake的C项目项目本身要求C17标准且使用了std::filesystem老GCC直接提示头文件缺失。第二个案例是编译某个国产数据库的分支版本时脚本检测到GCC版本过低后直接退出。这两个问题都卡在开发环境无法继续推进最终都是靠源码编译GCC9.3解决的。除了支持新标准GCC 9.3在编译性能上也有优化。9系列带来了改进的链接时优化和向量化支持即使跑老代码也有可能让最终可执行程序体积和运行效率都有一些提升。对用户来说最直接的体感是编译一个大型项目时警告信息更清晰了.so库的结构更干净依赖分析不再那么玄学。所以如果你眼下正好被某个编译报错拦住升级GCC9.3真不是乱折腾而是绕过上游依赖门槛的必要路径。2. 动手前先备好三样东西2.1 确认系统架构和基础工具运行任何编译操作前我先习惯性地检查三样东西操作系统架构、现有编译器版本、基础工具链是否完整。uname -a cat /etc/os-release gcc --version make --version前三行很好理解最后一行make --version是为了确认make是否存在、版本是否满足要求。很多人在内网服务器上一顿操作下载源码、解压、./configure都顺利结果卡在make不存在或者太老无法识别Makefile。如果在服务器上执行make --version报错先执行yum install -y make bzip2 gcc-g glibc-devel这里解释一下为什么需要这些包。make是构建核心bzip2解压工具用于打开部分源码包gcc-g提供系统默认的C编译器虽然后面我们会在新安装的GCC9.3上编译但系统自带的g常在configure阶段承担“bootstrap编译器”的角色glibc-devel提供标准C库的头文件和链接脚本没有它编译任何C语言程序都会提示缺少stdio.h。架构这里必须多说一句。麒麟V10服务器版不只跑在x86_64上很多信创环境都是鲲鹏aarch64或者飞腾arm64架构。GCC源码在ARM架构下同样能编译但依赖包的编译参数、configure选项可能要微调。最稳妥的做法是先去/proc/cpuinfo确认架构然后统一下载对应架构的依赖包源码编译不要图省事从x86机器上拷贝二进制包过来架构不匹配的库文件会让你排查到怀疑人生。这也是我看到“银河麒麟v10 国防版 飞腾(arm64架构) /离线部署qt”这类搜索时特别想强调的一点源码编译的优势是通用但要在configure和依赖阶段保持架构一致。2.2 依赖准备GMP、MPFR、MPC 一个都不能少GCC源码本身不包含数学运算的高级依赖它需要GMPGNU Multiple Precision Arithmetic Library、MPFR多精度浮点运算和MPC复数运算三个库支持。这三个库是GCC内部进行编译优化时的重要底层支撑升级版本前必须提前编译并安装到系统目录中。缺少它们的报错很典型configure: error: Building GCC requires GMP 4.2, MPFR 2.4.0 and MPC 0.8.0处理办法有三种一是通过系统包管理器安装比如yum install -y gmp-devel mpfr-devel libmpc-devel二是在GCC源码目录中通过contrib/download_prerequisites脚本自动下载三是手工从GNU镜像站手动下载源码编译。内网环境最常见的就是第三种手动下载时最好自己建立一个目录统一管理和记录版本。我个人倾向于第三种处理方式因为内网离线场景里yum源大概率没有配套的devel包。把三个依赖源码包放到/usr/local/src下依次编译安装cd /usr/local/src tar -xzf gmp-6.1.2.tar.gz cd gmp-6.1.2 ./configure --prefix/usr/local/gmp make -j4 make install cd /usr/local/src tar -xzf mpfr-4.0.2.tar.gz cd mpfr-4.0.2 ./configure --prefix/usr/local/mpfr --with-gmp/usr/local/gmp make -j4 make install cd /usr/local/src tar -xzf mpc-1.1.0.tar.gz cd mpc-1.1.0 ./configure --prefix/usr/local/mpc --with-gmp/usr/local/gmp --with-mpfr/usr/local/mpfr make -j4 make install注意一个细节MPFR和MPC配置时都要显式通过--with-gmp和--with-mpfr指明依赖路径。GCC源码编译时要通过LD_LIBRARY_PATH或LIBRARY_PATH告诉链接器去哪里找这些库如果没有install到默认的/usr/lib或/usr/lib64即使依赖编译成功GCC配置阶段一样会报找不到。2.3 给磁盘和内存留出安全余量GCC的编译可能是很多人第一次接触的“巨型编译任务”它的编译过程会生成大量中间文件稍不注意磁盘就满了。我建议在编译前检查一下df -h /usr/local free -h nproc/usr/local建议至少保留10GB可用空间如果你用默认的/usr/local作为安装目录这就是make install的落盘位置。编译过程中GCC源码目录本身也会膨胀到几个GB如果/usr/src和/usr/local不同分区两边都要留足空间。内存方面make -j4时任务并发多低配置机器2GB内存极容易触发OOM Killer导致编译进程直接被杀死建议内存低于4GB时只使用make -j2或者干脆先加2GB swap临时过渡一下。我曾在一台4GB内存的边缘服务器上编译GCC9.3直接上make -j8结果CPU满载跑了十分钟后进程消失查看dmesg发现就是OOM。把并发数降到-j2编译虽然慢大约半小时以上但全程稳定不崩。编译工具链这种事真的不能只看CPU核心数内存不够就是不够。3. 源码编译GCC9.3的核心步骤拆解3.1 下载源码包并确认版本来源进入GNU官方镜像站国内可以用清华开源镜像下载gcc-9.3.0.tar.gz。这里我建议优先选择.tar.xz或.tar.gz压缩格式它们体积小、下载速度快。下载完不急着解压先确认文件完整性wget https://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-9.3.0/gcc-9.3.0.tar.gz md5sum gcc-9.3.0.tar.gz官方会在下载页面提供MD5或SHA256校验值比对一下避免下载损坏。同样的校验步骤也适用于GMP、MPFR、MPC三个依赖包。曾有同事在线下载的源码包在解压时报错“gzip: invalid compressed data”就是下载传输过程出错但没校验文件完整性重新下载校验后才通过。省这一步看似省不了多少时间但一旦官方源码损坏后面所有流程全部白费。下载地址选择有一条经验国内能直连GNU官方服务器的网络一般都不稳定更建议直接用清华镜像、中科大镜像或阿里云镜像。热词里有人搜“银河麒麟v10镜像iso下载”说明大家普遍关心镜像可信度官方源虽然权威但内网环境下拉取速度可能非常感人。用镜像站下载后同样做一次md5校验确保一致性这样既快又稳。3.2 configure参数逐个说清楚别照抄命令解压进入源码目录后重头戏就是configure。这里我先给出一个在生产环境中实测比较稳妥的方案mkdir /usr/local/gcc-9.3.0 cd /usr/local/src/gcc-9.3.0 mkdir build cd build ../configure -prefix/usr/local/gcc-9.3.0 \ --enable-languagesc,c \ --enable-checkingrelease \ --disable-multilib \ --with-gmp/usr/local/gmp \ --with-mpfr/usr/local/mpfr \ --with-mpc/usr/local/mpc注意我不会直接在源码根目录执行./configure而是通过新建build目录进行源码外构建out-of-source build。好处是编译产生的中间文件全部落在build目录里不污染源码目录以后要重新编译或清理只需要删掉build目录不至于把下载好的源码包也弄脏了。各个参数都值得说清楚。--prefix指定GCC的安装目录我把它独立放到/usr/local/gcc-9.3.0而不是直接覆盖系统默认的/usr/bin/gcc这是一个非常重要的决定。老GCC保留在系统目录里新GCC安装在独立目录两个版本互不干扰等确认新的工具链稳定、编译产品验证无误后再决定是否切换默认版本或把它链接到/usr/bin下。这种方式在服务器运维中是最稳妥的操作。--enable-languagesc,c表示只编译C和C编译器。如果以后有Fortran语言需求可以加上fortran但现阶段不要贪全减少编译任务量就意味着减少编译失败概率。--enable-checkingrelease是优化编译器内部检查项release模式能减少运行时性能损耗。--disable-multilib这个参数经常被人忽略它的作用是关闭32位库的编译支持。在64位系统上如果你不关掉multilibGCC会尝试编译32位和64位两套库这会导致编译时间翻倍还会因为缺少32位glibc头文件而失败。--with-gmp、--with-mpfr、--with-mpc这三个参数对应前面编译的依赖路径告诉configure从哪里读取数学库。这里再次强调路径要写对如果编译安装依赖时使用的是默认./configure make make install即安装到/usr/local/lib那这里可以省略这三个参数让GCC去默认路径下找但如果依赖被安装到了独立目录就必须显式指定不然就会卡在“Building GCC requires GMP 4.2”这个错误上。3.3 make阶段的时长与并发数控制configure顺利通过后进入真正的编译环节make -j4这里的-j4不是拍脑袋写的我建议编译并发数不超过CPU线程数的一半如果内存低于8GB就不要超过-j2。GCC编译每个编译单元都很吃内存一个cc1plus进程可能占用1GB左右并发一多内存直接爆掉。我自己的经验是4核8GB的机器用-j4编译大约35-45分钟内存更小的机器用-j2可能需要60分钟以上。等待过程中不要直接干等可以观察编译输出里有没有error、internal compiler error等字样。一旦出现这种字样不要继续往下等CtrlC中断优先排查报错原因不然白白烧电。编译完成后执行make install这一步会把编译器、头文件和库文件安装到--prefix指定的目录。install过程通常比较快几分钟内结束。安装完成后查看二进制文件是否存在ls -l /usr/local/gcc-9.3.0/bin/gcc /usr/local/gcc-9.3.0/bin/gcc --version如果能输出gcc (GCC) 9.3.0说明编译器本体已经装好了。但注意此时如果你直接在任意目录输入gcc --version看到的一般还是老版本因为新GCC还没有进入PATH环境变量。这就是文章标题里“环境变量配置”要解决的问题很多人在这一步以为升级失败其实是环境变量没生效。3.4 bootstrap编译方式要不要用GCC源码支持bootstrap模式也就是用当前系统的老GCC去编译一套新的GCC再用这套新GCC重新编译一遍自身以保证新编译器的自举能力和稳定性。默认情况下GCC的make过程会自动启用bootstrap也就是编译三遍GCC。好的一面是编译出的工具链更可靠坏的一面是编译时间成倍增加。如果你只是需要尽快在服务器上用上GCC9.3可以考虑关掉bootstrap模式make -j4 bootstrap-leanbootstrap-lean会在编译结束后清理中间文件在一定程度上节省磁盘空间。但除非你是资深编译器开发者否则我建议保留默认的bootstrap流程毕竟GCC是工具链的核心组件稳定性永远优先于时间成本。我在生产环境上编译时基本都开着完整bootstrap前前后后编译两次得到的编译器在之后编译大型C项目时确实很少出现莫名其妙的内部错误。4. 环境变量配置最容易翻车的三个细节4.1 PATH要写到哪个文件里才真正生效GCC安装完成但是运行gcc --version依然显示老版本这是全部报错里最高频的一种。原因很简单系统默认的/usr/bin/gcc在PATH环境变量中的优先级更高shell在执行命令时先找到它。正确的做法不是在命令行临时export因为那是针对当前shell会话的一关终端就失效。在服务器上我们要让所有用户、所有新的登录会话都能直接使用新版本推荐在/etc/profile.d/下新建一个独立脚本vim /etc/profile.d/gcc93.sh内容如下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写入后赋予执行权限chmod x /etc/profile.d/gcc93.sh重新登录或执行source /etc/profile.d/gcc93.sh使其立即生效。猜测会有人问为什么不直接改/etc/profile其实/etc/profile.d下的脚本会被/etc/profile自动加载逻辑清晰、方便单独管理以后要升级或移除版本时直接删掉这个文件即可对系统其他配置毫无影响。但这里有一个隐藏很深的坑如果服务器上存在已登录的root会话或正在跑的定时任务它们并不会自动加载新的环境变量。生产服务器必须运维层面二次确认定时任务、守护进程的调用环境必要时在该任务的启动脚本里显式加上source /etc/profile.d/gcc93.sh或直接写全路径。踩过这个坑的人都明白在crontab里跑脚本时gcc --version显示老版本是常事不是编译器问题而是crontab默认环境变量极简/etc/profile.d里的设置根本不会被读取。4.2 LD_LIBRARY_PATH不配的话会怎样环境变量配置绝不只是PATH的问题。很多人配好PATH运行gcc --version显示9.3.0后满心欢喜转头去编译一个C程序链接阶段直接报错error while loading shared libraries: libstdc.so.6: cannot open shared object file: No such file or directory这个错误的根因是动态链接器在默认库路径/lib64、/usr/lib64里找不到新GCC自带的libstdc.so.6。老GCC自带的老版本libstdc.so.6可能确实存在但新编译的程序需要的是符号版本更高的新库文件所以我们必须把新库所在目录加进LD_LIBRARY_PATHexport LD_LIBRARY_PATH/usr/local/gcc-9.3.0/lib64:/usr/local/gcc-9.3.0/lib:$LD_LIBRARY_PATH这里要说明一个很多人容易忽略的细节GCC 9.3在64位系统上一般把库文件装在lib64而32位兼容库在lib目录。如果你的系统是纯64位LD_LIBRARY_PATH里至少要包含lib64如果编译的是32位程序则需要把lib也带上。把两个路径都写上能覆盖大多数情况不会出错。不过话说回来LD_LIBRARY_PATH只是一个运行时解决方案更一劳永逸的方法是把新库路径写入ldconfig配置echo /usr/local/gcc-9.3.0/lib64 /etc/ld.so.conf.d/gcc93.conf ldconfig这样系统动态链接器就会在默认搜索路径中带上新GCC的库目录不需要依赖环境变量。不过我个人的习惯是两个都配因为ldconfig是系统级生效的LD_LIBRARY_PATH则更灵活方便在个别情况下临时指定特定库。还有一个风险必须提醒不要在LD_LIBRARY_PATH中把/usr/local/gcc-9.3.0/lib64放在老库路径之外就完事因为有些老程序依赖的是老版本的libstdc.so.6强行让它加载新版库可能会导致ABI不兼容、程序启动崩溃。这也是为什么我不建议直接删除或覆盖系统自带的GCC而是要让新旧版本共存。4.3 LIBRARY_PATH和头文件路径也要同步处理都知道配置PATH和LD_LIBRARY_PATH但很多人忽略了LIBRARY_PATH和头文件路径。LIBRARY_PATH是链接时的库搜索路径LD_LIBRARY_PATH是运行时的库搜索路径两者是不同阶段需要的。编译C程序时链接器ld要找到libstdc.so如果它不在默认搜索路径上即使运行时能靠LD_LIBRARY_PATH找到链接阶段也会失败/usr/bin/ld: cannot find -lstdc解决方法是把新库目录加到LIBRARY_PATHexport LIBRARY_PATH/usr/local/gcc-9.3.0/lib64:/usr/local/gcc-9.3.0/lib:$LIBRARY_PATH还有头文件搜索路径。编译时如果提示找不到iostream、vector等标准头文件或者提示stdio.h: No such file or directory说明编译器在默认头文件搜索路径中没能找到新的标准库头文件。GCC 9.3安装完成后头文件位于$prefix/include/c/9.3.0大部分情况下编译器会自己找但如果你自定义了C_INCLUDE_PATH或CPLUS_INCLUDE_PATH反而会覆盖默认搜索路径。遇到头文件找不到的情况先检查有没有乱设这两个变量。为了系统化避免这类问题我在/etc/profile.d/gcc93.sh里还会加上export LIBRARY_PATH/usr/local/gcc-9.3.0/lib64:/usr/local/gcc-9.3.0/lib:$LIBRARY_PATH export C_INCLUDE_PATH/usr/local/gcc-9.3.0/include:$C_INCLUDE_PATH export CPLUS_INCLUDE_PATH/usr/local/gcc-9.3.0/include/c/9.3.0:$CPLUS_INCLUDE_PATH不过说实话这些路径一般都不用强制指定GCC编译时会自动查找$prefix/include和$prefix/lib/gcc下的路径。真正需要你关心的是之前有没有为了装某个软件而在/etc/profile里鱼龙混杂地设过各种环境变量里面可能残留了老的库路径导致新编译器编译时找到老库头文件然后ABI冲突。排查这种问题时执行echo $LIBRARY_PATH逐一确认即可。4.4 多版本切换的技巧和思路GCC升级从来不是非黑即白的事情生产环境要求的是稳定共存、按需切换。除了环境变量覆盖法还有两个比较优雅的方法一是软链接法二是update-alternatives。软链接法的思路是ln -sf /usr/local/gcc-9.3.0/bin/gcc /usr/local/bin/gcc ln -sf /usr/local/gcc-9.3.0/bin/g /usr/local/bin/g这样/usr/local/bin在PATH中优先级高于/usr/bin执行gcc时自然走新版本而系统内置的/usr/bin/gcc老版本仍然保留。update-alternatives则是Debian系系统常见的版本管理工具麒麟V10基于某些RHEL衍生体系有些版本支持alternatives命令alternatives --install /usr/bin/gcc gcc /usr/local/gcc-9.3.0/bin/gcc 93 alternatives --config gcc这种方式的好处是可以在多个GCC版本之间一键切换适合开发机上有不同项目需要不同编译器版本的场景。我自己的经验是服务器上这种切换需求不频繁使用环境变量或软链接就够了。软链接方案最简单最直白出问题时一眼就能看出gcc指向哪里不需要折腾alternatives的配置状态。5. 常见报错排查实录5.1 configure阶段报错速查表报错信息原因解决方法configure: error: Building GCC requires GMP 4.2, MPFR 2.4.0 and MPC 0.8.0依赖库未安装或路径错误重新检查GMP/MPFR/MPC安装路径configure参数中的--with-gmp、--with-mpfr、--with-mpc是否正确configure: error: cannot compute suffix of object files: cannot compile系统缺少基础编译工具链yum install -y gcc gcc-c glibc-devel makechecking whether the C compiler works... no老GCC或系统环境异常检查磁盘空间执行df -h确认有足够空间清理/tmp下残留文件configure: error: invalid feature name: releaseconfigure参数拼写错误检查--enable-checkingrelease还是--enable-checkingrelease确认无空格configure阶段是排错最友好的阶段错误信息一般都会明确告诉你缺什么。最让人无奈的是GMP等库已经装了但configure还是报找不到这基本是LD_LIBRARY_PATH的问题。在编译GMP时如果install到了/usr/local头文件和库文件都在系统默认搜索路径内如果install到了自定义目录记得configure GCC前先export相关路径export LD_LIBRARY_PATH/usr/local/gmp/lib:/usr/local/mpfr/lib:/usr/local/mpc/lib:$LD_LIBRARY_PATH export C_INCLUDE_PATH/usr/local/gmp/include:/usr/local/mpfr/include:/usr/local/mpc/include:$C_INCLUDE_PATH export CPLUS_INCLUDE_PATH/usr/local/gmp/include:/usr/local/mpfr/include:/usr/local/mpc/include:$CPLUS_INCLUDE_PATH5.2 make阶段报错和解决思路make阶段报错最常见的有三种我直接说结论。第一种是internal compiler error这多半是内存不足或编程过程中触发了老GCC的bug。解决办法降低并行度make -j1或make -j2同时检查free -h确认内存阈值。如果低内存机器持续编译失败临时加swap是一个有效手段dd if/dev/zero of/swapfile bs1M count4096 chmod 600 /swapfile mkswap /swapfile swapon /swapfile第二种是recipe for target all failed这种含糊错误不好直接定位。解决套路是往上翻日志找到第一个真正的error:所在行而不是盯着最后的几行输出。很多新手一看到“failed”就往最后看但真正的报错通常出现在日志中部甚至靠前后面跟着的只是连锁反应的雪崩。用make -j4 21 | tee build.log把日志落盘方便反复翻查。第三种是磁盘空间不足GCC编译过程的中间文件极大。用df -h确认当前分区使用率超过90%就清理一下或把GCC源码目录和build目录放到其他剩余空间更大的挂载点上比如/home、/data。记住configure阶段可能只需要几百MBmake到一半时可能会突然膨胀到几个GB所以提前留空间永远比中途迁移目录省心。5.3 安装和配置后环境不生效的排查常见场景是配好环境变量后重新登录gcc --version还是老版本。第一步先确认自己source的文件到底有没有被完成which gcc echo $PATH echo $LD_LIBRARY_PATH如果which gcc输出的是/usr/local/gcc-9.3.0/bin/gcc但gcc --version还是老版本说明/usr/local/gcc-9.3.0/bin/gcc这个文件本身执行时调用了动态库而动态库链接到了老版本上。先执行ldd /usr/local/gcc-9.3.0/bin/gcc输出里如果libstdc.so.6指向的是/usr/lib64/libstdc.so.6说明新GCC在运行时动态加载的还是系统老库。这时要确保LD_LIBRARY_PATH包含新的lib64目录同时注意环境变量中不要存在更早的目录把新库覆盖掉了。我之前遇到过一台机器上配置了某个应用的环境变量里面显式写了LD_LIBRARY_PATH/opt/oldlib:$LD_LIBRARY_PATH导致所有动态加载都优先去翻老库新GCC一直起不来。如果echo $PATH里没有出现/usr/local/gcc-9.3.0/bin说明/etc/profile.d/gcc93.sh没有生效或者文件权限不对。此时执行bash -x -l -c echo $PATH开启bash调试模式可以看到系统加载profile.d下脚本时有没有报错以及哪个脚本可能覆盖了PATH。5.4 升级后老程序链接新库导致崩溃的特殊情况这部分是很多人容易忽视的高级坑升级GCC后新编译的程序链接了新的libstdc.so.6如果同一台机器上还有一堆老程序它们在运行时也会因为LD_LIBRARY_PATH里的新库路径而被迫加载新库一旦ABI不兼容可能出现程序启动报错或段错误。排查方法是在老程序启动前强制指回老库路径或者用LD_PRELOAD指定特定版本库。但更彻底的方案是不要在生产机器上以环境变量方式全局修改LD_LIBRARY_PATH。改为在具体编译任务脚本里统一设置环境或者采用本地包含方式需要用到GCC9.3接口的项目在启动脚本中单独source对应配置不需要的项目保持系统默认。我之前在一台跑着老旧采集程序的服务器上踩过这个坑升级GCC9.3后新程序正常运行老采集程序却随机段错误。查了三天才定位到是动态库版本冲突。后来把LD_LIBRARY_PATH的全局设置去掉改成新服务启动脚本中单独设置老程序彻底恢复稳定。这是一个非常典型的教训全局环境变量是双刃剑越具体的作用域越安全。5.5 麒麟V10服务器版特殊注意点麒麟V10服务器版基于RHEL系体系但它在用户权限、安全策略上可能做了一些加固。比如在编译时如果碰到奇怪的权限拒绝先看SELinux是否开启getenforce如果输出Enforcing可以直接临时改成Permissive模式试试setenforce 0但生产环境不建议长期关闭SELinux只是临时验证是否为SELinux拦截。如果确实是SELinux导致的问题要用semanage fcontext和restorecon设置放行规则而不是一关了之。另外麒麟V10默认的/tmp目录可能有独立的挂载选项如noexec这会导致从/tmp下解压源码并执行./configure时报权限错误。遇到这种情况把GCC源码目录移动到/usr/local/src或者/home下再编译即可。这也是为什么在前面我特意强调统一在/usr/local/src下操作既能保证磁盘空间也避开/tmp的挂载限制。6. 依赖下载失败和离线部署的方案6.1 外网不通时的离线包准备思路麒麟V10服务器版大量部署在政务内网、企业专网环境外网不通是常态。这种情况下规划好“下载机内网传输”的方案就特别关键。先在能访问外网的机器上手动下载以下内容gcc-9.3.0.tar.gzgmp-6.1.2.tar.gzmpfr-4.0.2.tar.gzmpc-1.1.0.tar.gz下载后不要只传输源码包。我建议把编译依赖所需的gmp-devel、mpfr-devel、libmpc-devel的RPM包也一并下载下来备用因为有些程序在后续链接阶段会依赖系统的.so文件。使用yumdownloader工具可以一次性拉取RPM包yum install -y yum-utils yumdownloader --resolve --destdir/root/rpmpackages gmp-devel mpfr-devel libmpc-devel到了内网服务器上用rpm -ivh *.rpm或者rpm -Uvh *.rpm安装。如果提示依赖不满足就用yum localinstall *.rpm让它自动解决依赖。不过要提醒一句离线安装RPM最大的风险是依赖链断裂如果内网yum源不完整就可能出现装了a需要b、装了b需要c、c又要求a的情况。这时候源码包方式反而是更可控的路线因为依赖编译环境是相对纯净的。6.2 离线环境编译依赖库的次序和坑离线环境升级GCC整个编译安装的先后次序必须严格保持GMP - MPFR - MPC - GCC。这个顺序不是随便定的MPFR在configure时要检测GMP的存在MPC在编译时依赖GMP和MPFRGCC则依赖前面三个。如果跳过了某个依赖直接编译GCC只会在GCC的configure阶段得到一个醒目的错误提示然后再回头补装其实时间上并没有节省。在离线环境配置这些依赖时有一个很隐蔽的坑MPFR在configure时如果检测不到GMP不会直接报错而是使用内置的近似版本或自动降级。这样一来GCC虽能编译成功但部分优化能力会受损甚至在编译某些大型C项目时出现精度相关异常。所以我强烈建议在安装MPFR和MPC时都要显式指定依赖路径最好在configure之后查看一下config.log中的实际检测结果确认“GMP uses”真实路径。6.3 备份与回滚方案升级GCC9.3前最好保留系统自带的旧GCC可执行文件。由于我们采用自定义--prefix方式安装新GCC旧GCC自动保留在/usr/bin/gcc所以回滚方式很简单rm -f /etc/profile.d/gcc93.sh export PATH/usr/bin:$PATH gcc --version如果后续你在/usr/local/bin下创建了软链接一键删除软链接即可unlink /usr/local/bin/gcc unlink /usr/local/bin/g很多生产环境出问题时不在于升级过程而在于升级之后没有做回滚预案。把回滚步骤写在运维文档里遇到问题第一时间不是查原因而是先恢复服务再慢慢排查。这也是资深的运维和刚入行的同学最大的区别。7. 从GCC升级看整条工具链的组合拳7.1 GCC升级后配套工具最好一起更升级完GCC9.3只算完成了一半。如果你日常要用CMake编译项目那么CMake版本可能也需要升级因为老版本CMake可能无法识别GCC9.3的新特性或会产生错误警告。同理binutils包含ld、as等二进制工具也很重要。GCC编译出来的目标文件如果和系统里老版本的binutils不匹配可能出现链接时找不到某些段的问题或者生成的可执行文件无法被gdb正确识别。经验是用源码升级GCC后如果条件允许把binutils也升级到配套版本。好在GCC9.3对binutils的要求不算苛刻系统自带的binutils 2.27以上基本都能配合。只有在链接特别复杂的现代C17代码时才可能遇到ADDR28重定位错误这类问题届时再单独升级binutils也不迟。7.2 开发团队跨机器统一工具链的必要性服务器部署的场景里经常会出现开发机是Ubuntu 20.04自带的GCC 9而生产机是麒麟V10自带的老GCC。代码在开发机编译正常一上生产环境就各种报错根因往往是工具链版本的不一致。尤其是一些依赖模板元编程的C库不同GCC版本对模板展开的处理行为差异可以很大。所以我在运维规范里一直建议在麒麟V10服务器上以源码方式统一GCC版本至9.3.0开发、测试、生产环境全部使用同一个版本。这样至少能消除一部分“开发环境正常但生产环境编译失败”的问题。这个层面的价值甚至超越了GCC本身它让整个团队的交付链路变得可预测减少不必要的半夜紧急排查。文章开头没有提结尾也不打算总结什么高大上的道理。这篇指南是我在麒麟V10服务器上折腾过完整流程后沉淀下来的执行笔记希望帮你避开我在依赖、编译、环境变量这三个环节踩过的坑。生产环境里省时间最好的办法不是照抄某条命令而是理解每个步骤背后的原因这样无论遇到多离谱的报错你都能在五分钟内定位到问题方向。
返回列表