发布版 · 2026-07 · 最后更新:2026-07-29
我们把 QEMU/KVM 放进 manifest PR 门禁:独立基准中,TCG 的协议就绪耗时为 KVM 的 3.5~6.8 倍;一次代表性门禁总耗时 26 分 20 秒,其中 QEMU 阶段约 6 分钟,Robot Framework 46/46 通过。
1. 应用场景
固件编译成功,并不能证明 kernel、rootfs、DTB 与设备模型匹配,也不能证明 CLI、Web、Redfish、IPMI 已可访问。我们的做法是在代码合入前增加提交级启动冒烟:
Gitea manifest PR
→ Jenkins:统一镜像、install_sdk、编译 QEMU 产物
→ 精简载荷、SHA256、原子交付
→ ARM KVM Runner:QEMU hi1711、8 vCPU、多协议探测
→ Robot Framework、日志、报告和 PR 结论
QEMU 不替代单板。外设电气行为、上电时序、掉电恢复和长稳仍由实板验证;QEMU 负责把软件集成问题前移。
统一镜像只固定 OS、基础工具和打包能力,真正生效的 SDK、bmcgo/bingo 与 Conan 环境仍由本次 manifest 收敛:
bmcgo build -t install_sdk -b openUBMC -r artifactory -ucc
bmcgo build -b openUBMC -sc qemu -r artifactory
2. KVM 性能收益与 CI 开销
门禁的“6 分钟”还包含打包、传递、Runner 准备、Robot 和结果回传,不能当作 Guest 启动时间。社区 《基于 KVM 的 QEMU 性能测试》 给出的独立基准如下;最后一列是本文代表性生产门禁的首次观测值:
| 协议 | KVM 基准 | TCG 基准 | TCG / KVM | 本次门禁 |
|---|---|---|---|---|
| IPMI | 94.55 秒 | 404.71 秒 | 4.3x | 97.36 秒 |
| Redfish | 81.75 秒 | 410.59 秒 | 5.0x | 104.78 秒 |
| SSH | 60.53 秒 | 414.28 秒 | 6.8x | 84.83 秒 |
| Telnet | 16.25 秒 | 56.54 秒 | 3.5x | 37.13 秒 |
| Web | 80.63 秒 | 410.03 秒 | 5.1x | 84.85 秒 |
两组 KVM 数值都是真实数据,但不能互相替换:
- 性能基准在内核启动标记出现后设置 T0,5 个线程立即并行执行完整连接或接口检查;
- 门禁在
vemake包装脚本前设置 T0,固定等待 30 秒后才启动单循环探针,因此会计入准备时间,早期服务只能记录“首次观测”; - 两边对 Telnet、SSH、Web、Redfish、IPMI 的通过条件不同,固件样本和 Runner 负载也会带来波动。
所以,基准回答“硬件加速能省多少时间”,门禁数据回答“开发者实际要等多久”。不开 KVM 时,各协议达到可用状态的耗时为 KVM 的 3.5~6.8 倍。
一次代表性 Jenkins 构建的时间账如下:
| 阶段 | 耗时 | 占比 |
|---|---|---|
| 容器与 SDK 初始化 | 31.028 秒 | 1.96% |
| 编译与构建 | 1165.849 秒 | 73.75% |
| QEMU 冒烟 | 365.020 秒 | 23.09% |
| 收集与收尾 | 18.875 秒 | 1.19% |
| 合计 | 1580.772 秒(26:20.772) | 100% |
QEMU 阶段中,约 164 秒用于精简打包、传递和 Runner 准备;远端运行闭环 201 秒,其中 Robot 46 条用例执行 66.088 秒。近期正常样本约为 6:03~6:17,因此公开口径是“通常约 6 分钟”。
3. Robot 结果可以下钻
Jenkins 通过 RobotPublisher 发布 output.xml、报告、日志和趋势。代表性构建 46/46 通过:CLI 4 条、Web 5 条、Redfish 6 条、IPMI 31 条。
报告还能下钻到测试套和单条用例。下图是实际的 Get Thermal Info Via Redfish,包含描述、耗时、趋势和 PASS 状态。
4. 双终端复现完整数据面
下面是正式 CI 的可见缩影:终端 1 让 QEMU 始终占用前台并输出串口;终端 2 等待 Guest、登录并执行命令。以下 <...> 与 /absolute/path/... 均需替换为自己的环境。
4.1 从 Windows 进入统一构建镜像
ssh "<构建用户>@<构建宿主机>"
使用独立目录和容器名,不复用、不重启现有 CI 容器:
RUN_ID="community-qemu-$(date +%Y%m%d-%H%M%S)"
DEMO_ROOT="/absolute/path/community-qemu-demo/$RUN_ID"
BUILD_CTR="qemu-build-$RUN_ID"
BUILD_IMAGE="<registry>/openubmc-buildenv@sha256:<digest>"
mkdir -p "$DEMO_ROOT"/{source,tmp,opt,payload}
git clone "<manifest 仓库地址>" "$DEMO_ROOT/source/manifest"
docker run --rm -it --name "$BUILD_CTR" --network host \
--init --pids-limit=65535 --ulimit nproc=65535:65535 \
--privileged \
-v "$DEMO_ROOT/source:/home/workspace/source" \
-v "$DEMO_ROOT/tmp:/tmp" -v "$DEMO_ROOT/opt:/opt" \
-v "$DEMO_ROOT/payload:/home/workspace/payload" \
"$BUILD_IMAGE" bash
--privileged 与 host network 只用于专用可信构建节点,镜像必须固定 digest。
容器内构建并只打包 QEMU 所需内容:
cd /home/workspace/source/manifest
bmcgo build -t install_sdk -b openUBMC -r artifactory -ucc
bmcgo build -b openUBMC -sc qemu -r artifactory
test -f output/packet/inner/zImage_openUBMC
test -f output/packet/inner/openUBMC_qemu_default.cpio.gz
test -f temp/openUBMC_qemu_default/hi1711_9p8c.dtb
tar -C /home/workspace/source -cf - \
manifest/build/works/packet/qemu_shells \
manifest/output/packet/inner \
manifest/temp/openUBMC_qemu_default/hi1711_9p8c.dtb |
zstd -T0 -3 -o /home/workspace/payload/manifest.tar.zst
exit
4.2 校验并交付 ARM Runner
ARCHIVE="$DEMO_ROOT/payload/manifest.tar.zst"
ARM_TARGET="<ARM用户>@<ARM-KVM-Runner>"
REMOTE_TMP="/absolute/path/incoming/${RUN_ID}.uploading"
REMOTE_FINAL="/absolute/path/incoming/$RUN_ID"
(cd "$(dirname "$ARCHIVE")" &&
sha256sum manifest.tar.zst > manifest.tar.zst.sha256)
ssh "$ARM_TARGET" "test ! -e '$REMOTE_TMP' &&
test ! -e '$REMOTE_FINAL' && mkdir -p '$REMOTE_TMP'"
scp "$ARCHIVE" "$ARCHIVE.sha256" "$ARM_TARGET:$REMOTE_TMP/"
ssh "$ARM_TARGET" "cd '$REMOTE_TMP' &&
sha256sum -c manifest.tar.zst.sha256 &&
mv '$REMOTE_TMP' '$REMOTE_FINAL'"
这段是手工演示;生产 CI 还会为每次运行分配唯一 ID、动态端口和异常清理。
4.3 终端 1:QEMU 前台启动
ssh "<ARM用户>@<ARM-KVM-Runner>"
RUN_ID="<与构建端一致的运行 ID>"
INCOMING="/absolute/path/incoming/$RUN_ID"
DEMO_ROOT="/absolute/path/runs/$RUN_ID"
MANIFEST="$DEMO_ROOT/manifest"
CTR="qemu-demo-$RUN_ID"
CI_CTR="<现有 CI 基线容器名>"
RUN_IMAGE="sha256:<已核对的运行镜像 ID>"
mkdir -p "$DEMO_ROOT"
tar --zstd -xf "$INCOMING/manifest.tar.zst" -C "$DEMO_ROOT"
CPIO_STAGE="$DEMO_ROOT/cpio-stage"
QEMU_SETUP="$MANIFEST/temp/qemu_temp/qemu_start_temp"
mkdir -p "$CPIO_STAGE" "$MANIFEST/output/data" "$QEMU_SETUP"
gzip -dc "$MANIFEST/output/packet/inner/openUBMC_qemu_default.cpio.gz" |
(cd "$CPIO_STAGE" && cpio -idm --quiet)
cp -a "$CPIO_STAGE/data" "$MANIFEST/output/data/data"
cp -a "$CPIO_STAGE/mockdata" "$MANIFEST/output/data/mockdata"
cp -a "$CPIO_STAGE/opt/bmc/pram" "$MANIFEST/output/data/pram"
cp -a "$MANIFEST/temp/openUBMC_qemu_default/hi1711_9p8c.dtb" "$QEMU_SETUP/"
sudo install -m 0755 "<ARM64 QEMU 绝对路径>" \
"$QEMU_SETUP/qemu-system-aarch64-release"
test -c /dev/kvm
sudo podman inspect "$CI_CTR" \
--format 'CI_BEFORE={{.State.Status}} STARTED={{.State.StartedAt}}'
完整前台命令如下;没有 -d,qemu-system-aarch64 就是容器主进程:
sudo podman run --rm -it \
--name "$CTR" \
--network host \
--device /dev/kvm \
--security-opt label=disable \
--cpus=8 --memory=12g --pids-limit=512 \
-v "$DEMO_ROOT:/demo:rw" \
-w /demo/manifest \
"$RUN_IMAGE" \
./temp/qemu_temp/qemu_start_temp/qemu-system-aarch64-release \
-machine hi1711 \
-accel kvm \
-nographic \
-smp 8 -m 4G \
-kernel output/packet/inner/zImage_openUBMC \
-dtb temp/qemu_temp/qemu_start_temp/hi1711_9p8c.dtb \
-append 'root=/dev/ram0 ip=dhcp console=ttyS0 rdinit=/init memmap=1886M@0x87200000 ramdisk_size=30720 earlycon=serial-mm,0x08710000 printk.time=y ignore_loglevel ekbox=0x1f000$0x84be0000 kbox_mem=2432k@0x84900000' \
-initrd output/packet/inner/openUBMC_qemu_default.cpio.gz \
-netdev 'user,id=eth0,hostfwd=tcp:127.0.0.1:22101-:22,hostfwd=tcp:127.0.0.1:22102-:443,hostfwd=tcp:127.0.0.1:22103-:23,hostfwd=udp:127.0.0.1:22104-:623,hostfwd=udp:127.0.0.1:22105-:161' \
-fsdev 'local,security_model=passthrough,id=fsdev1,path=output/data/pram' \
-device 'virtio-9p-device,id=fs1,fsdev=fsdev1,mount_tag=pramshare' \
-fsdev 'local,security_model=passthrough,id=fsdev0,path=output/data/data' \
-device 'virtio-9p-device,id=fs0,fsdev=fsdev0,mount_tag=hostshare' \
-fsdev 'local,security_model=passthrough,id=fsdev2,path=output/data/mockdata' \
-device 'virtio-9p-device,id=fs2,fsdev=fsdev2,mount_tag=mockshare' \
-monitor 'telnet:127.0.0.1:22106,server,nowait'
4.4 终端 2:进入 Guest
ssh "<ARM用户>@<ARM-KVM-Runner>"
deadline=$((SECONDS + 240))
until ssh-keyscan -T 1 -p 22101 127.0.0.1 2>/dev/null |
grep -q 'ssh-ed25519'; do
(( SECONDS < deadline )) || exit 1
sleep 3
done
ssh -tt -p 22101 -o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null "<Guest用户>@127.0.0.1"
密码只在交互提示中输入。进入 Guest 后读取真实状态:
printf 'ARCH='; uname -m
printf 'MODEL='; tr -d '\0' </proc/device-tree/model; echo
printf 'VCPU='; grep -c '^processor' /proc/cpuinfo
printf 'UPTIME_SEC='; cut -d' ' -f1 /proc/uptime
systemctl --state=running --type=service --no-legend --no-pager | wc -l
实机结果为 aarch64、Hisilicon Hi1711 ASIC、8 vCPU 和 24 个 running service。上方仍是 QEMU 前台串口,下方才是 Guest shell。
结束时先退出 Guest,再通过本次私有 Monitor 端口关闭 QEMU:
exit
echo quit | nc -w 4 127.0.0.1 22106
sudo podman inspect "$CI_CTR" \
--format 'CI_AFTER={{.State.Status}} STARTED={{.State.StartedAt}}'
CI_AFTER 的 StartedAt 应与 CI_BEFORE 完全一致;否则不能声称本次演示没有影响原 CI。
5. 开销与工程结论
本次可视化复现的短时采样为:CPU 约 0.9~1 个宿主机逻辑核,内存约 1.33~1.55 GB,PIDs 15;Guest 配置为 8 vCPU/4 GiB,容器上限 8 CPU/12 GiB。它是单次即时采样,容量规划仍应看节点监控、队列和并发。
真正把演示变成 CI,还需要唯一运行 ID、独立目录与端口、/dev/kvm 显式透传、固定镜像 digest、SHA256 与原子交付、多协议分别探测、失败日志先收集后清理,以及 Robot 报告和 PR 状态回写。
最终得到的不是“后台跑了一个 QEMU”,而是一条可量化、可追溯的提交级质量门禁:
- 代表性门禁约 26 分钟,编译仍是主成本;
- QEMU 阶段通常约 6 分钟,占本次总时长约 23%;
- 独立基准中,TCG 的协议就绪耗时为 KVM 的 3.5~6.8 倍;
- 本次门禁在 37~105 秒间先后观测到协议就绪,Robot 46 条用例本身执行 66.088 秒;
- 双终端实操只是生产 CI 的可见缩影,单板验证仍不可替代。
6. 开发者实操:统一镜像构建到 ARM KVM Guest
前文站在生产 CI 视角说明门禁如何隔离运行、交付产物、执行 Robot 冒烟并回写
PR;本章站在开发者视角,给出从源码到 Guest 的完整个人标准操作流程。
以下地址、账号和路径均为占位符,使用时替换为自己的环境。密码只在交互提示中输入,
不要写入命令、脚本或截图。
两部分不是重复文档,也不能混用命令:
| 维度 | 第 4 章:CI 可见缩影 | 第 6 章:开发者个人 SOP |
|---|---|---|
| 目标 | 解释生产门禁的数据面和隔离机制 | 让开发者从零完成一次真实启动 |
| 镜像 | 固定 digest,保证门禁可复现 | 拉取 latest,同时记录实际镜像 ID |
| 构建 | 唯一运行 ID、CI 工作区和 Runner 交付 | 后台构建容器、固定个人工作区 |
| ARM 运行 | Runner 容器与 CI 基线保护 | ARM 主机原生 QEMU 前台运行 |
| 观察方式 | QEMU 与 Guest 双终端展示 | x86 构建、QEMU、Guest 三终端协作 |
| 输出 | Robot 报告和 PR 结论 | Guest 状态、人工验收和完整清理 |
6.1 阅读顺序与三个终端
建议使用三个 Windows Terminal 窗口:
| 终端 | 位置 | 用途 |
|---|---|---|
| 终端 1 | x86_64 构建机 | 拉 manifest、启动统一镜像、构建并传包 |
| 终端 2 | AArch64 KVM 主机 | 校验产物、准备 9p 目录、前台启动 QEMU |
| 终端 3 | AArch64 KVM 主机 | 等待并登录真实 Guest |
标准顺序如下,首次操作时不要跳步:
- 从 manifest main 的新工作区开始并记录 commit。
- 拉取
openubmc_buildenv:latest,后台启动本轮构建容器。 - 执行
install_sdk,成功后再采集生效环境。 - 构建 QEMU 单包,检查内容并生成 SHA-256。
- 将产物传到 ARM,校验后准备三个 9p 目录。
- 在终端 2 前台启动 Hi1711 QEMU/KVM。
- 从终端 3 登录真实 Guest 并完成运行态检查。
- 停止 QEMU,释放端口并清理两端本轮资源。
终端 1 登录构建机:
ssh <构建用户>@<x86构建机>
需要管理员权限时再进入 root Shell:
sudo -i
确认构建机和 Docker:
uname -m
docker version --format 'Docker {{.Server.Version}}'
如果构建机有多个网络出口,先确认专用 Docker 网络绑定到可以访问代码、镜像和
Conan 服务的物理网卡:
docker network inspect <构建网络> --format 'parent={{index .Options "parent"}}'
多出口环境不要直接改用 --network host,否则容器可能从错误路由访问内部服务。
6.2 通过 HTTPS 拉取 manifest main
进入工作目录:
cd <工作目录>
确认本轮目录不存在:
test ! -e manifest
通过 HTTPS 拉取最新 manifest:
git clone https://<Gitea地址>/<组织>/manifest.git
进入仓库:
cd manifest
确认分支和 commit:
git switch main
git pull --ff-only origin main
git status --short --branch
git rev-parse HEAD
每次都要记录实际 commit,不复用旧工作区或旧产物。
6.3 后台启动统一构建镜像
拉取最新统一镜像:
docker pull <镜像仓>/openubmc_buildenv:latest
记录本次实际镜像 ID:
docker image inspect <镜像仓>/openubmc_buildenv:latest --format '{{.Id}}'
后台启动本轮容器:
docker run -d --rm \
--name openubmc-qemu-build \
--init \
--pids-limit=65535 \
--ulimit nproc=65535:65535 \
--privileged \
--network <构建网络> \
-v <manifest绝对路径>:/home/workspace/source/manifest \
-v <SSH目录>:/root/.ssh:ro \
-w /home/workspace/source/manifest \
<镜像仓>/openubmc_buildenv:latest \
sleep infinity
检查容器:
docker ps --filter name=openubmc-qemu-build
检查默认路由:
docker exec openubmc-qemu-build ip -4 route show default
这里保留 -d、--rm 和 --init:容器在后台运行,停止后自动删除,init
负责回收构建过程中产生的子进程。
私有组件源码如果在 recipe 中仍使用 HTTPS,而构建凭据只提供 SSH,可只在本轮
容器中配置 URL 映射:
docker exec openubmc-qemu-build \
git config --global \
url."ssh://git@<Gitea地址>/".insteadOf \
"https://<Gitea地址>/"
该操作不修改 manifest 和组件仓代码。
如 SSH 服务会自动更新主机密钥,可只在本轮容器中关闭该行为并验证仓库可达:
docker exec openubmc-qemu-build \
git config --global core.sshCommand "ssh -o UpdateHostKeys=no"
docker exec openubmc-qemu-build \
git ls-remote ssh://git@<Gitea地址>/<组织>/manifest.git HEAD
6.4 初始化并核对生效环境
先执行 manifest 声明的环境初始化:
docker exec openubmc-qemu-build \
bmcgo build -t install_sdk -b openUBMC -r artifactory -ucc
首次执行可能下载较大的 SDK。只要进度或日志仍在变化,就不要并发启动第二个
install_sdk。
只有该命令成功后,才采集生效环境:
docker exec openubmc-qemu-build bmcgo --version
docker exec openubmc-qemu-build bingo --version
docker exec openubmc-qemu-build conan remote list
docker exec openubmc-qemu-build readlink -f /opt/hi1711sdk
统一镜像启动时自带的版本只是初始环境。这里显示的 bmcgo、bingo、Conan 和 SDK
必须与本次 manifest 一致,否则不要继续构建。
6.5 构建并校验 QEMU 单包
执行 QEMU 目标构建:
docker exec openubmc-qemu-build \
bmcgo build -b openUBMC -sc qemu -r artifactory
进入输出目录:
cd <manifest绝对路径>/output
检查产物:
ls -lh openUBMC_qemu_default.data.gz
检查包内容:
tar -tzf openUBMC_qemu_default.data.gz
至少应包含:
openUBMC_qemu_default/hi1711_9p8c.dtb
openUBMC_qemu_default/openUBMC_qemu_default.cpio.gz
openUBMC_qemu_default/zImage_openUBMC
生成校验文件:
sha256sum openUBMC_qemu_default.data.gz \
> openUBMC_qemu_default.data.gz.sha256
显示并留存本轮校验值:
cat openUBMC_qemu_default.data.gz.sha256
6.6 传输并准备 ARM 运行目录
终端 2 登录 ARM KVM 主机:
ssh <ARM用户>@<ARM-KVM主机>
确认架构和 KVM:
uname -m
test -c /dev/kvm && echo KVM_OK
确认 Hi1711 machine 和 KVM accelerator:
<Hi1711-QEMU绝对路径>/qemu-system-aarch64 -machine help | grep -w hi1711
<Hi1711-QEMU绝对路径>/qemu-system-aarch64 -accel help | grep -w kvm
确认本轮端口未被占用:
ss -H -lntup | grep -E ':2210[1-6]\b'
无输出后确认本轮目录不存在:
test ! -e <ARM工作目录>.uploading
test ! -e <ARM工作目录>
两项都通过后创建临时上传目录:
mkdir <ARM工作目录>.uploading
回到终端 1,传输包和校验文件:
scp -O \
openUBMC_qemu_default.data.gz \
openUBMC_qemu_default.data.gz.sha256 \
<ARM用户>@<ARM-KVM主机>:<ARM工作目录>.uploading/
如果目标未启用 SFTP subsystem,需要保留 -O 使用传统 SCP 协议。
回到终端 2,校验并原子改名:
cd <ARM工作目录>.uploading
sha256sum -c openUBMC_qemu_default.data.gz.sha256
必须显示 openUBMC_qemu_default.data.gz: OK,否则停止后续操作。
cd ..
mv <ARM工作目录>.uploading <ARM工作目录>
解压:
cd <ARM工作目录>
tar -xzf openUBMC_qemu_default.data.gz
cd openUBMC_qemu_default
mkdir -p share output/data
只提取 QEMU 需要的 9p 数据:
cd share
gzip -dc ../openUBMC_qemu_default.cpio.gz | \
cpio -idmu --quiet \
'data' 'data/*' \
'mockdata' 'mockdata/*' \
'opt/bmc/pram' 'opt/bmc/pram/*'
cd ..
cp -a share/data output/data/data
cp -a share/mockdata output/data/mockdata
cp -a share/opt/bmc/pram output/data/pram
检查 QEMU 最终需要的目录:
du -sh output/data/data output/data/mockdata output/data/pram
三个目录都必须存在,不能把 share 本身错误地作为 9p 根目录。
6.7 前台启动 QEMU/KVM
在终端 2 前台执行:
<Hi1711-QEMU绝对路径>/qemu-system-aarch64 \
-machine hi1711 \
-accel kvm \
-nographic \
-smp 8 \
-m 4G \
-kernel ./zImage_openUBMC \
-dtb ./hi1711_9p8c.dtb \
-initrd ./openUBMC_qemu_default.cpio.gz \
-append 'root=/dev/ram0 ip=dhcp console=ttyS0 rdinit=/init memmap=1886M@0x87200000 ramdisk_size=30720 earlycon=serial-mm,0x08710000 printk.time=y ignore_loglevel ekbox=0x1f000$0x84be0000 kbox_mem=2432k@0x84900000' \
-netdev 'user,id=eth0,hostfwd=tcp:127.0.0.1:22101-:22,hostfwd=tcp:127.0.0.1:22102-:443,hostfwd=tcp:127.0.0.1:22103-:23,hostfwd=udp:127.0.0.1:22104-:623,hostfwd=udp:127.0.0.1:22105-:161' \
-fsdev 'local,security_model=passthrough,id=fsdev1,path=./output/data/pram' \
-device 'virtio-9p-device,id=fs1,fsdev=fsdev1,mount_tag=pramshare' \
-fsdev 'local,security_model=passthrough,id=fsdev0,path=./output/data/data' \
-device 'virtio-9p-device,id=fs0,fsdev=fsdev0,mount_tag=hostshare' \
-fsdev 'local,security_model=passthrough,id=fsdev2,path=./output/data/mockdata' \
-device 'virtio-9p-device,id=fs2,fsdev=fsdev2,mount_tag=mockshare' \
-monitor 'telnet:127.0.0.1:22106,server,nowait'
终端 2 必须保留在前台持续显示串口日志,不使用 -d 或 nohup。端口开始监听
只表示 QEMU 已建立转发,不代表 Guest 服务已完成启动。
本次实测和生产门禁均使用 8 vCPU / 4 GiB。manifest 默认配置虽然是
4 vCPU / 4 GiB,但当前 Hi1711 设备树描述了 8 个 CPU;4 核启动会对
CPU4 到 CPU7 报 PSCI 启动失败。8 核不是为了堆资源提速,而是为了与设备树和
生产门禁保持一致。
6.8 登录并验收真实 Guest
终端 3 登录同一 ARM 主机:
ssh <ARM用户>@<ARM-KVM主机>
等待 Guest SSH 主机密钥:
ssh-keyscan -T 3 -p 22101 127.0.0.1 \
> <ARM工作目录>/guest_known_hosts
test -s <ARM工作目录>/guest_known_hosts && echo GUEST_SSH_READY
没有输出时等待一段时间后重试,不要只根据端口监听判断 Guest 已启动完成。
登录 Guest:
ssh -tt -p 22101 \
-o StrictHostKeyChecking=yes \
-o UserKnownHostsFile=<ARM工作目录>/guest_known_hosts \
<Guest用户>@127.0.0.1
进入 Guest 后检查:
uname -m
tr -d '\0' < /proc/device-tree/model
grep -c '^processor' /proc/cpuinfo
uname -r
systemctl --no-legend --type=service --state=running | wc -l
正常结果应能确认 aarch64、Hisilicon Hi1711 ASIC、8 个处理器以及实际
kernel 版本。Guest 是精简系统时可能没有 getconf,处理器数量以
/proc/cpuinfo 为准。
上图左侧是仍在运行的 QEMU 前台串口,右侧才是通过转发端口登录的真实 Guest
Shell。仅看到 QEMU 日志不能替代 Guest 登录验收。
6.9 正常停止与完整清理
先在终端 3 退出 Guest:
exit
回到终端 2,可在 QEMU 前台依次按 Ctrl+A 和小写 x 停止。若前台转义键
不可用,再从终端 3 连接本轮 monitor:
nc 127.0.0.1 22106
看到 (qemu) 后输入:
quit
QEMU 退出后检查端口:
ss -H -lntup | grep -E ':2210[1-6]\b'
确认无输出,再删除 ARM 本轮目录:
rm -rf <ARM工作目录>
回到终端 1,停止并自动删除构建容器:
docker stop openubmc-qemu-build
确认容器已由 --rm 自动删除:
docker ps -a --filter name=openubmc-qemu-build
确认工作区不再需要后删除:
rm -rf <manifest绝对路径>
不要执行 docker system prune,也不要停止其他 CI 容器或 QEMU 实例。
6.10 常见故障与处理
| 现象 | 常见原因 | 处理 |
|---|---|---|
| manifest 目录已存在 | 复用了旧工作区 | 先检查是否有未提交内容,再新建本轮目录;不要直接覆盖 |
| clone 遇到证书错误 | 私有 CA 未进入系统信任链 | 优先安装 CA;不要全局关闭 Git TLS 校验 |
| 容器访问不到依赖服务 | Docker 网络走错物理出口 | 回到 6.1、6.3,检查专用网络和容器默认路由 |
| 构建要求输入 Gitea HTTPS 账号 | recipe URL 与 SSH 凭据不匹配 | 检查只读 SSH 挂载和本轮 URL 映射,不在构建日志输入密码 |
| Conan 显示 anonymous 后停住 | Conan 凭据或 buildenv 初始化未生效 | 检查 install_sdk 完整日志和 manifest buildenv 配置 |
fork: Resource temporarily unavailable |
缺少 init 或进程额度不足 | 保留 --init、--pids-limit 和 nproc 参数 |
| QEMU 包缺少 DTB、cpio.gz 或 kernel | 构建未完成或取错输出目录 | 检查构建返回码和 output 目录,不继续传包 |
SCP 认证后提示 Connection closed |
ARM 未启用 SFTP subsystem | 使用 scp -O 强制传统 SCP 协议 |
| Guest 设备数据缺失 | 9p 目录层级错误 | 检查 output/data/{data,mockdata,pram} |
| 端口监听但 Guest SSH 不通 | QEMU 转发已建立,Guest 仍在启动 | 等待串口和 ssh-keyscan,不要只看端口 |
系统 QEMU 找不到 hi1711 |
使用了发行版通用 QEMU | 切换到包含 Hi1711 machine 的 QEMU 绝对路径 |
| 4 核启动出现 CPU4~CPU7 PSCI 错误 | vCPU 数少于当前设备树描述 | 使用与生产门禁一致的 8 vCPU / 4 GiB |
6.11 实测耗时与社区基准
本轮标准关键路径如下:
| 阶段 | 实测耗时 |
|---|---|
| HTTPS 拉取 manifest | 0.816 s |
| 拉取统一镜像 | 0.183 s,已有缓存 |
install_sdk |
77.868 s |
| QEMU 构建 | 212.025 s |
| SCP 传输 85 MiB | 30.189 s |
| ARM 解包与 9p 准备 | 1.373 s |
| 8 核 QEMU 启动到内核标记 | 11.911 s |
| 内核标记到五种协议全部就绪 | 95.440 s |
| 可相加关键路径 | 429.805 s,即 7:09.805 |
该关键路径不包含人工输入、截图、Guest 手工登录和清理。审计式完整实操还执行了
4/8 核 A/B、异常恢复和证据采集,总墙钟时间为 45:33;正常只执行一次标准流程,
建议预留 10 到 12 分钟,首次下载镜像或 SDK 时另计网络时间。
为了与社区
《基于 KVM 的 QEMU 性能测试》
对齐,本轮复用了同一套 QA BootTimeMonitor 五协议并行探测语义:
| 协议 | 社区 KVM 基准 | 本轮 4 vCPU | 本轮生产配置 8 vCPU |
|---|---|---|---|
| IPMI | 94.55 s |
93.17 s |
93.17 s |
| Redfish | 81.75 s |
94.58 s |
95.44 s |
| SSH | 60.53 s |
73.29 s |
73.29 s |
| Telnet | 16.25 s |
29.02 s |
29.02 s |
| Web | 80.63 s |
93.79 s |
94.42 s |
社区脚本原先等待的 " blocks" 串口标记在当前固件中没有出现,本轮改用首条
"Linux version" 内核日志作为 T0。协议检查语义一致,但固件样本、T0 文本和
Runner 负载并非完全相同,因此这里用于对齐数量级和趋势,不把十几秒差值直接解释
为性能回退。
4 核与 8 核的五协议就绪差异均小于 1 秒,说明当前服务启动主要是串行依赖,增加
vCPU 不能显著缩短启动时间。8 核配置的价值是消除 4 核时 CPU4 到 CPU7 的 PSCI
错误,并与当前设备树及生产 CI 保持一致。
社区基准记录的峰值 CPU 4.54 cores、平均 CPU 1.01 cores 是宿主机实际使用量,
不是 Guest vCPU 数。本轮 8 核运行态单点采样约为 0.53 个宿主机逻辑核,RSS 约
1.48 GiB,内存数量级与社区 KVM 基准一致。
个人复现的价值,是让开发者能够沿着与生产门禁一致的数据链独立定位构建、交付和
启动问题;它不替代正式 CI 的自动化结论,也不替代单板上的硬件行为和长稳验证。
















