C++信创适配踩坑记

1. 引言

事情是这样的:前段时间我用 C++ 开发了一个功能模块,原本跑在主流 Linux 发行版上稳如老狗。项目临近上线,突然接到通知——得适配到信创环境,而且要在周末完成部署并现场演示。结果周一一大早,领导一进门就问:“这个适配工作,今天能做完吗?”

好家伙,我直接好家伙,周末截止的任务,周一问就我能不能当天搞定?现在层层加码一加就五天了?只能勉强答应了。不过好在领导也就是问问,实际上信创适配一共花了2~3天。其实大部分信创也就是 Linux 分发版的一种,如果理想的情况下,一天就适配完成大概率是可行的。这里就记录一下笔者在 C++ 信创适配过程中踩过的那些坑。

2. Docker方案

信创(信息技术应用创新)体系中的操作系统几乎全部基于 Linux 内核,属于 Linux 发行版的国产化分支。当前主流产品如下:

操作系统 开发单位 基础来源 主要架构 典型应用场景
银河麒麟(KylinOS / Ubuntu Kylin) 麒麟软件(中国电子旗下) Ubuntu / FreeBSD(早期) x86_64, ARM64(飞腾、鲲鹏) 政府、军工、金融
中标麒麟(NeoKylin) 麒麟软件(已与银河麒麟合并) CentOS / Red Hat x86_64, ARM64 企业服务器、关键基础设施
统信 UOS(UnionTech OS) 统信软件 Debian / Deepin x86_64, ARM64, LoongArch(龙芯) 党政办公、教育、通用桌面
中科方德 中科院软件所 Fedora / Red Hat x86_64, ARM64 金融、医疗、教育
普华操作系统 中国电科 Debian / Ubuntu x86_64 工业控制、嵌入式
麒麟信安操作系统 麒麟信安 CentOS / 自研加固 x86_64, ARM64 安全要求极高的专网环境
华为 openEuler 华为(开源社区) 自研(兼容 RHEL 生态) x86_64, ARM64, RISC-V 云计算、服务器、信创云底座

📌 注意

  • 龙芯(LoongArch) 是独立指令集,与其他架构不兼容。
  • 飞腾、鲲鹏、海光、兆芯 等 CPU 中:
    • 飞腾/鲲鹏 → ARM64
    • 海光/兆芯 → x86_64(兼容 Intel/AMD)

Docker 具有 环境隔离 + 自包含 的特性,一个 Docker 镜像自带:

  • 完整的用户空间(glibc、库、工具链)
  • 应用及其依赖
  • 运行时环境(Python、Java、Node.js 等)

因此信创适配最好的方案就是使用 Docker。但是使用 Docker 也没那么简单,因为信创系统不像其他 Linux 发行版那样随处可见,提前准备的 Docker 镜像不一定能在不同信创系统上运行。大概来说,至少需要需要 满足两个条件

条件 说明
1. CPU 架构一致 如都是 linux/arm64(飞腾/鲲鹏)或 linux/amd64(海光/兆芯)
2. 不依赖宿主机特有服务 如 systemd、特定硬件驱动、内核模块等

举例来说,如果提前构建了一个 arm64v8/ubuntu:20.04 Docker镜像,里面跑一个 C++ 程序,那么理论上这个镜像可以在如下系统运行:

  • 银河麒麟 V10(ARM64)
  • 统信 UOS(ARM64)
  • openEuler(ARM64)
  • 甚至树莓派(ARM64)

但这些只是理论上的,实际的情况可能很复杂,比如说:

  1. 依赖特定国产中间件或安全模块

    • 如调用 国密算法硬件加速卡可信计算模块(TPCM)
    • 这类功能需要容器挂载设备或加载内核驱动 → 需在目标机器上测试
  2. 使用了发行版特有包管理器或路径

    • 比如硬编码 /etc/kylin-release 或调用 uos-update
    • 解决方案:用标准 Linux 路径,避免探测发行版
  3. 龙芯(LoongArch)平台

    • 指令集完全不同,必须单独构建 linux/loong64 镜像
    • 目前 Docker Hub 官方镜像对 LoongArch 支持有限,需自行构建 base 镜像
  4. 安全合规要求

    • 某些信创项目要求所有软件必须通过认证(如等保、密评)
    • 即使技术上能跑,也可能需要提供适配证明检测报告

3. 运行时

3.1 gcc/g++7

虽然 Docker 方案好用,但是没有提前准备也是没辙,因此这里笔者还是在本地环境中部署。本地部署第一步当然是部署运行时环境,虽然 C++ 程序不用部署额外的本地运行时环境,但是谁能想到,这个信创系统没有预装 g++ :

[root@cg58 ~]# g++ --version
-bash: g++:未找到命令
[root@cg58 ~]# gcc --version
gcc (GCC) 7.3.0

尝试使用 apt 指令没有成功,那就先看看具体是哪个操作系统:

[root@cg58 ~]# cat /etc/os-release
NAME="kylin_arm"
VERSION="V10 (Halberd)"
ID="kylin"
VERSION_ID="V10"
PRETTY_NAME="kylin_arm"
ANSI_COLOR="0;31"

可以明确判断,使用的是 银河麒麟高级服务器操作系统 V10(ARM64 架构),代号 “Halberd”,通常底层兼容 CentOS / RHEL(尤其是早期版本),但部分软件源和包管理可能经过定制或限制。尝试使用 yum 进行安装:

sudo yum install gcc-c++ -y

也提示找不到安装包。不过好在可以联网,那么可以添加一个兼容的公网 ARM 源,因为银河麒麟 V10(ARM)与 openEuler 20.03 LTS 高度兼容,且 openEuler 提供完整的 ARM64 软件包,包括新版 GCC。

运行以下命令创建新 repo 文件:

cat <<EOF | sudo tee /etc/yum.repos.d/openeuler-arm.repo
[openEuler-os]
name=openEuler OS for aarch64
baseurl=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/OS/aarch64/
enabled=1
gpgcheck=0

[openEuler-EPOL]
name=openEuler EPOL (Extra Packages)
baseurl=https://repo.openeuler.org/openEuler-20.03-LTS-SP3/EPOL/main/aarch64/
enabled=1
gpgcheck=0
EOF

然后清理并重建 yum 缓存:

sudo yum clean all
sudo yum makecache

安装 g++:

sudo yum install gcc-c++ -y

检查是否成功:

[root@cg58 ~]# g++ --version
g++ (GCC) 7.3.0
[root@cg58 ~]# gcc --version
gcc (GCC) 7.3.0

3.2 gcc/g++9

g++ 7.3.0 总算安装上了。不过在后续部署的过程中发现了一个问题,那就是 GCC 7 对于C++17的支持是不全的。比如笔者这里构建std::filesystem相关的代码的时候就不能编译成功。但是上述源中是找不到 gcc/g++9 的,也就是使用 yum 安装的方法行不通。那么怎么办呢?解决思路很简单,就是自己编译 gcc/g++9 编译器。

编译编译器,这看起来有点天方夜谭。但其实像一些比较强大的编程语言,都有 自举 的能力。什么叫做自举呢?通俗来说,就像造汽车需要扳手这样工具,可问题是扳手本身也是别的工具来造,工具又需要工具的工具来造……那么最初没有工具的时候,是怎么开始的?“自举”就是解决这个问题的思路:先用简单的方法做出一个“能用但粗糙”的版本,再用这个版本去造更好的自己。回到编程语言上来,如果一种编程语言可以用它自己来编写自己的编译器,就叫“能自举”。而 C++ 就是这种能自举的编程语言,换句话说,完全可以使用 gcc/g++7 来编译构建 gcc/g++9。

具体的构建脚本如下所示:

# 1. 确保基础编译工具已安装(你已经有了)
sudo yum install -y gcc gcc-c++ make bison flex glibc-devel zlib-devel

# 2. 下载 GCC 9.4.0
cd /tmp
wget https://ftp.gnu.org/gnu/gcc/gcc-9.4.0/gcc-9.4.0.tar.gz
tar -xf gcc-9.4.0.tar.gz
cd gcc-9.4.0

# 3. 关键一步:自动下载 GMP/MPFR/MPC
./contrib/download_prerequisites

# 4. 配置(跳过文档,避免 texinfo 问题)
mkdir build && cd build
../configure \
    --prefix=/usr/local/gcc-9.4.0 \
    --enable-languages=c,c++ \
    --disable-multilib \
    --with-arch=armv8-a \
    --with-tune=cortex-a72 \      
    --build=aarch64-linux-gnu \
    --host=aarch64-linux-gnu \
    --target=aarch64-linux-gnu \
    --without-info \
    --without-man \
    --disable-bootstrap

# 5. 编译(耐心等待,ARM64 较慢)
make -j$(nproc)

# 6. 安装
sudo make install-strip

因为涉及到从远端下载文件,所以最好还是一步一步执行。如果获取 gcc-9.4.0.tar.gz 很慢,可以使用代理,或者找一台网速比较好的机器下载再上传。

./contrib/download_prerequisites是用来自动下载依赖库 GMP、MPFR 和 MPC的,对应的版本和地址是:

文件 官方 URL
gmp-6.1.0.tar.bz2 https://ftp.gnu.org/gnu/gmp/gmp-6.1.0.tar.bz2
mpfr-3.1.4.tar.bz2 https://ftp.gnu.org/gnu/mpfr/mpfr-3.1.4.tar.bz2
mpc-1.0.3.tar.gz https://ftp.gnu.org/gnu/mpc/mpc-1.0.3.tar.gz

也可以从其他能上网的机器(如 Windows 笔记本)下载以上 3 个文件,然后传到信创机器上。注意 GMP、MPFR 和 MPC 必须解压在 GCC 源码根目录下,并且创建同名软链接(或直接重命名目录),最终目录结构应类似:

/tmp/gcc-9.4.0/
├── gmp            → symlink to gmp-6.1.0/
├── mpfr           → symlink to mpfr-3.1.4/
├── mpc            → symlink to mpc-1.0.3/
├── gmp-6.1.0/     (解压后的源码)
├── mpfr-3.1.4/    (解压后的源码)
├── mpc-1.0.3/     (解压后的源码)
├── configure
└── contrib/

编译选项--disable-bootstrap表示避免三阶段自举编译,这样可以大幅缩短编译时间,但也可能导致最终的 GCC 9 包含因 GCC 7 特定行为导致潜在问题。一般快速验证可以加上这个编译选项,正式发布可以取消。

3.2 切换

那么现在系统中有 GCC 7 和 GCC 9 两个版本了,最好配置 update-alternatives 实现切换。

添加系统默认 GCC 7:

sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7.3.0 70 \
                         --slave /usr/bin/g++ g++ /usr/bin/g++-7.3.0

添加刚编译的 GCC 9:

sudo update-alternatives --install /usr/bin/gcc gcc /usr/local/gcc-9.4.0/bin/gcc 90 \
                         --slave /usr/bin/g++ g++ /usr/local/gcc-9.4.0/bin/g++

使用如下指令验证是否注册成功:

update-alternatives --list gcc

应输出:

/usr/bin/gcc-7.3.0
/usr/local/gcc-9.4.0/bin/gcc

交互式切换版本可输入如下指令:

sudo update-alternatives --config gcc

会看到类似:

There are 2 programs which provide 'gcc'.

  Selection    Command
-----------------------------------------------
*+ 1           /usr/local/gcc-9.4.0/bin/gcc
   2           /usr/bin/gcc-7.3.0

Enter to keep the current selection[+], or type selection number:

输入 2 切回 GCC 7,输入 1 切回 GCC 9。

可再次输入指令当前生效版本:

gcc --version
g++ --version

4. 库和程序

不得不说,在信创系统上找一个靠谱的源来下载工具和库包还挺麻烦的,尤其是 arm64 架构的。比如说我想安装 7z 压缩工具,系统官方源没有,openEuler 源也没有,甚至 7z 官方也不提供 arm64 安装包。没有别的办法,自己从源码进行构建吧。其实在 Linux 上构建程序还是挺简单的:

cd /root/work/program/

# 下载源码(17.04 是目前广泛使用的稳定版本)
wget https://github.com/p7zip-project/p7zip/archive/refs/tags/v17.04.tar.gz

# 解压
tar -xzf v17.04.tar.gz
cd p7zip-17.04

# 使用 7z makefile(功能最全)
make 7z

# 安装到 /usr/local/bin 等标准路径
sudo make install

至于一些常用的 C/C++ 依赖库包,可以使用的是这个 BuildCppDependency 工具,通过 Linux Shell 脚本离线安装。如果工具中没有自己想要的库,那么就比较考验自己的 C++ 程序的构建水平了。可以参考笔者的系列文章——《CMake构建学习笔记-目录》;另外 BuildCppDependency 工具中也有一些现成的脚本可供修改参考。

最后需要注意的是,因为 GCC 9 编译器是自己安装的,但是在构建的时候可能并不会优先找到 GCC 9 的环境中去,需要先设置 LD_LIBRARY_PATH:

export LD_LIBRARY_PATH=/opt/gcc-9.4.0/lib64:$LD_LIBRARY_PATH

另外,构建系统(比如CMake)可能也不会主动去找 g++9 编译器,导致编译失败。因此可以给构建系统显式指定 g++9 编译器的路径,以保证使用了 C++ 新特性的代码能够编译通过。

posted @ 2025-11-20 09:11  charlee44  阅读(46)  评论(0)    收藏  举报