适用范围:系统更新反复失败,日志报错
#051(grub-mkconfig 退出码 1)。
本报告记录该问题的现象、根因、定位方法与解决方案,供遇到同类问题的维护人员参考。
1. 问题现象
系统更新在「部署阶段」失败,日志出现以下记录(本次共出现 4 次,时间分别为 08-19 17:13、08-27 17:47 / 18:58 / 19:17):
[ERROR]: Bootloader write config: /usr/sbin/grub-mkconfig: 子进程已退出,代码 1 (1)
DeployUpdatFinished success = False , error_code = #051 ,
error_desc = Bootloader write config: /usr/sbin/grub-mkconfig: 子进程已退出,代码 1
系统更新异常,您的电脑即将在5 秒后重启。
特征:
- 错误码固定为 #051(更新器将 OSTree 退出码映射为
#05+1)。 - 均在 bootloader 写入阶段失败,随后强制重启,更新不生效。
- 与维护/正常模式无关,多次重试结果一致。
2. 根因
Kylin 定制的 OSTree GRUB 生成器(ostree admin instutil grub2-generate)在多个部署共享同一内核版本时,生成的「历史版本」子菜单递归嵌套,且闭合括号数量计算错误,导致 grub-mkconfig 自检失败(退出码 1)。
触发条件(本次全部满足):
- 系统存在 ≥2 个 OSTree 部署;
- 其中两个部署引用同一内核版本(本次为 6.6.0-76-generic,同时被当前部署与待部署的新版本使用);
- 更新时部署新版本,BLS 条目由 2 条扩展为 3 条。
证据:
- 失败产物
/boot/loader.1/grub.cfg.new括号统计{=41、}=38,缺 3 个。 grub-script-check在文件末尾(289 行)报语法错误,属「块未闭合至 EOF」的典型特征。- 「历史版本」submenu 被递归嵌套 3 层(第 178/200/222 行)。
- 在 END 标记前补 3 个
}后grub-script-check通过(EXIT=0)。
更新器本身无缺陷,仅透传 OSTree 的失败;缺陷位于 GRUB 生成器。
3. 定位方法
| 步骤 | 操作 | 结论 |
|---|---|---|
| 1 | 查看日志 DeployUpdatFinished success = False 的错误码 |
均为 #051,停留在 bootloader 写入 |
| 2 | grub-script-check /boot/loader.1/grub.cfg.new |
289 行语法错误,EXIT=1 |
| 3 | 统计括号并逐行追踪 15_ostree 段 | 41/38 不平衡,嵌套 3 层,深度未归零 |
| 4 | 补 3 个 } 后再校验 |
通过,确认根因即括号闭合 |
| 5 | 查 /etc/grub.d/15_ostree |
末行 exec ostree admin instutil grub2-generate,确认生成方 |
| 6 | 对比 loader.0/entries(2 条,成功)与 loader.1/entries(3 条,失败) |
确认触发条件为 BLS 扩展至 3 条 |
| 7 | 临时切换 /boot/loader 至 loader.1 后 grub-mkconfig |
复现同样的失败(38/36 不平衡),切回后成功 |
排除的错误假设:清理 /usr/lib/modules 残留模块目录无效(生成器依据 BLS 条目而非模块目录);$menuentry_id_option 参数错误假设不成立(修复括号后原样保留仍通过校验)。
4. 解决方案
核心思路:缩减部署数量,使下次部署时 BLS 条目保持 2 条(成功场景),绕开生成器缺陷。
注意:该操作会删除系统回退(rollback)保险。操作前请备份重要数据。
如何确定要解除的部署:执行 sudo ostree admin status,输出中带 * 的为当前运行的部署(绝对不能删);带 (rollback) 标记或 Pinned: yes 的即为回退部署(本次要删的对象)。索引从 0 开始计数(第一行=0,第二行=1)。可交叉验证当前运行部署:cat /proc/cmdline 中 ostree/ostree-<哈希> 与带 * 行的哈希一致。
# 1) 查看部署列表,确定要删除的 rollback 部署(索引从 0 开始)
sudo ostree admin status
# 2) 解除 Pinned 标记(pinned 部署须先 unpin 才能删除)
sudo ostree admin pin --unpin 1
# 3) 删除该部署
sudo ostree admin undeploy 1
# 4) 清理残留 checkout
sudo ostree admin cleanup
# 5) 验证:仅剩 1 个当前部署
sudo ostree admin status
# 6) 预验证 grub 生成恢复正常
sudo grub-mkconfig -o /tmp/grub.cfg.verify ; echo "EXIT=$?" # 预期 EXIT=0
完成后在正常模式下重新触发更新:
sudo /opt/update-module/current/usr/bin/kylin-update-trigger
验证结果:重新部署成功(日志 Deploy finished with result: True),grub-mkconfig 未再报 #051;重启后 ostree admin status 显示新版本 2205d981 生效。
5. 解决方案的副作用
本方案存在一项可能的副作用(非必然发生):
数据迁移报 #0308(依赖冲突)。升级到「数据迁移」阶段时,若用户自定义软件包与新系统存在依赖冲突,日志可能出现:
DeployUpdatFinished success = False , error_code = #0308 ,
error_desc = 数据迁移安装失败,请检查环境配置或联系管理员
原因:数据迁移通过 apt 将用户自定义安装的软件包重装进新系统时,用户侧 Qt5 开发包(qtbase5-dev 等 0.33 版)强依赖 libqt5dbus5t64 = 0.33,而新系统内 Qt5 库已升级至 0.39,依赖解析失败。
说明:
- 该副作用仅在用户自定义软件与新系统存在依赖冲突时发生;若无冲突,数据迁移正常完成,升级全程无报错。
- 本次实例中因 Qt5 版本冲突触发,属于上述可能场景之一。
影响评估:
- 即使触发,系统核心更新已成功(新部署生效),#0308 不影响系统切换。
- 仅用户自定义软件(如 wps-office、linuxqq 等)未迁移至新系统,数据文件不受影响,可手动重装补齐。
结论:该副作用为可能场景,且即便发生也在可接受范围内,不影响更新目标。
6. 研发修复建议
- 修复
ostree admin instutil grub2-generate在多部署共享内核时「历史版本」子菜单的嵌套与括号闭合计算缺陷;「历史版本」子菜单应平铺而非递归嵌套。 - 生成器输出后自行校验括号平衡(深度归零)或调用
grub-script-check,不合格即报错回滚,避免写入 /boot。 - 环境变量缺失时(如直接运行
grub2-generate)应优雅报错,而非 C 断言 abort。 - 数据迁移重装用户包时,应容忍/跳过与新系统版本冲突的旧包,避免整体失败。
- 复现路径:存在 ≥2 个共享内核的部署时执行
sudo grub-mkconfig -o /tmp/x。
7. 相关文件与日志
以下为排查中涉及的系统内客观存在的路径;排查时生成的证据副本(失败产物、修复后文件、演示脚本)保存在本机分析目录,他人可依据下表路径自行定位。
| 项目 | 路径 |
|---|---|
| 升级日志 | /var/log/kylin-system-updater/kylin-system-updater.log.1(#051:194/1140/1525/1842 行;#0308:8812/8834 行) |
| 失败产物 | 失败时生成于 /boot/loader.1/grub.cfg.new(括号不平衡,可用 grub-script-check 校验) |
| 生成脚本 | /etc/grub.d/15_ostree(末行 exec ostree admin instutil grub2-generate) |
| 错误码映射 | backend/SystemUpdater/backend/BackendOstreeNext.py(185-205 行) |