为什么? SWUpdate?
现场更新不可避免。 SWUpdate在更新固件、根文件系统和应用层方面,"NAS "提供了一个强大的模块化解决方案,所有这些都具有内置的回滚功能。
它开源、文档齐全,可与 A/B 分区布局无缝集成。
结构概述
SWUpdate由几个关键组件组成:
- 更新守护进程 (swupdate) - 在设备上运行并应用更新
- 更新处理程序 - 定义要更新的内容(rootfs、文件、脚本等)
- 客户端接口 - 网络接口、REST API 或本地 CLI
- sw-description文件 - 定义更新包的结构和逻辑
更新流程示例
创建一个更新包 (.swu) ,其中包含
- 新的根文件系统映像
- 新的 sw-description文件
- 可选脚本(用于自定义或验证)
2.swupdate将更新写入定义的设备。
更新后脚本确认成功,或在必要时触发回滚。
示例
在本例中,我们将一个正式的 Raspberry Pi OSTrixie 映像分割成两个文件:
- 2025-10-01-raspios-trixie-arm64.boot.vfat
- 2025-10-01-raspios-trixie-arm64.root.ext4
文件中引用这些文件 sw-description文件中引用,以创建 .swu 软件包:
software =
{
version = "0.1.0";
description = "Firmware update for XXXXX Project";
hardware-compatibility: [ "1.0", "1.2", "1.3"];
images: (
{
filename = "2025-10-01-raspios-trixie-arm64.boot.vfat";
device = "/dev/mmcblk0p1";
compressed = "zlib";
installed-directly = true;
},
{
filename = "2025-10-01-raspios-trixie-arm64.root.ext4";
device = "/dev/mmcblk0p2";
compressed = "zlib";
installed-directly = true;
}
);
scripts: (
{
type: "lua",
filename: "repair-disk-uuid.lua"
}
),
}下面的 Lua 脚本会调整 cmdline.txt和 fstab中的分区 UUID:
#!/usr/bin/lua
-- helper: run shell command and capture output
function run(cmd)
local f = io.popen(cmd)
local out = f:read("*a")
f:close()
return (out:gsub("%s+$", ""))
end
-- detect PARTUUIDs
local root_part = "/dev/mmcblk0p2"
local boot_part = "/dev/mmcblk0p1"
local root_uuid = run("blkid -s PARTUUID -o value " .. root_part)
local boot_uuid = run("blkid -s PARTUUID -o value " .. boot_part)
print("Rootfs PARTUUID: " .. root_uuid)
print("Boot PARTUUID: " .. boot_uuid)
-- mount points
os.execute("mkdir -p /mnt/root /mnt/boot")
os.execute("mount " .. root_part .. " /mnt/root")
os.execute("mount " .. boot_part .. " /mnt/boot")
-- update cmdline.txt
local cmdline_path = "/mnt/boot/cmdline.txt"
local file = io.open(cmdline_path, "r")
local text = file:read("*a")
file:close()
text = text:gsub("root=PARTUUID=[^ ]+", "root=PARTUUID=" .. root_uuid)
file = io.open(cmdline_path, "w")
file:write(text)
file:close()
-- update /etc/fstab
local fstab_path = "/mnt/root/etc/fstab"
local fstab = io.open(fstab_path, "r")
local content = fstab:read("*a")
fstab:close()
-- replace root line
content = content:gsub("PARTUUID=[^%s]+%s+/%s", "PARTUUID=" .. root_uuid .. " /")
-- replace boot line (/boot or /boot/firmware)
content = content:gsub("PARTUUID=[^%s]+%s+/boot", "PARTUUID=" .. boot_uuid .. " /boot")
fstab = io.open(fstab_path, "w")
fstab:write(content)
fstab:close()
os.execute("sync")
os.execute("umount /mnt/boot")
os.execute("umount /mnt/root")
print("All PARTUUIDs updated successfully.")使用 swugenerator来捆绑更新:(https://github.com/sbabic/swugenerator).
应用更新
测试生成的 .swu 文件
将 CM5 启动到应急系统中。
创建挂载点,例如
sudo mkdir -p /mnt/update挂载包含更新文件的 NFS 共享:
sudo mount -t nfs :/path/to/share /mnt/update- 应用更新:
sudo swupdate -i /mnt/update/update.swu整合的可能性
从本地用户界面或后台应用程序接口触发更新
- 对更新进行签名和验证,增强安全性
- 使用 SWUpdate’s网络界面进行测试或调试
- 与systemd 服务相结合,自动恢复和回滚
为何适合此堆栈
与 rpi-image-gen和 rpi-sb-provisioner, SWUpdate使画面更加完整:
- 构建 → (图像创建 rpi-image-gen(创建映像)
- 部署 → (设备配置 rpi-sb-provisioner(设备调配)
- 维护 → (OTA 更新和生命周期管理 SWUpdate(OTA 更新和生命周期管理)
最终形成一个灵活、开放、可维护的嵌入式 Linux平台--一个能与您的产品共同发展的平台,而不会产生 Yocto.
资料来源
- rpi-image-gen:https://github.com/raspberrypi/rpi-image-gen
- rpi-sb-provisioner:https://github.com/raspberrypi/rpi-sb-provisioner
- SWUpdate:https://github.com/sbabic/swupdate
- swugenerator:https://github.com/sbabic/swugenerator