基于embedded式Linux的设备通常需要更新应用程序和系统文件的机制。 例如,一个带有USB端口的(非联网的)实验室仪器可以从一个USB棒获得软件更新。
运行脚本将文件复制到设备的内部闪存中将是一件简单的事情。 但是,在更新过程中会有设备失去电源的危险,并且最终会出现问题。
应用程序文件的情况要容易一些,因为有足够的空间来复制应用程序目录,更新一个副本,并快速交换旧目录和新目录,从而最大限度地减less故障窗口。
由于内核和系统文件分布在整个文件系统中,所以对于内核和系统文件来说更是如此。
我们在文件系统中使用硬链接和软链接来识别关键文件。 我们使用散列文件和档案来validation文件的完整性。 我们已经考虑在内核中使用紧急ramfs来提供从更新后的文件系统启动失败的回退。
你对这个要求有什么办法?
我将采用与应用程序文件相同的方法:为关键文件做好准备,完成自己的分区,链接到它们,然后复制分区。 在你所有的init中,你应该首先检查链接是否显示全部到同一个分区,如果没有的话,重新设置它们(到具有某个文件的最新日期的文件的分区)。 如果你想更新只是复制到新的分区,如果一切正常(crcs ok)循环的文件,并设置每个从一个文件系统的链接到另一个。
这样你的关键文件应该始终处于正常状态。
场景:
将文件复制到新分区时更新失败
没有问题,因为链接仍显示在旧的工作。
链接时更新失败
没有问题,因为所有新文件都是有效的,并且已经被复制(否则重新链接步骤将不会启动),安装程序检查正确
如果你必须确保可靠性,你可以有两个闪存分区(甚至是芯片),一个是当前的工作配置,另一个是新的配置。 然后使用一个硬件看门狗,它将重置单元,并将活动的引导闪存分区切换到“最后一次正确的”配置。
至少有两个分区。 我建议4
靴
备用引导
程序数据备份
编程易失性数据
如果引导失败,使用grub fallback引导启动备用。
所以如果更新失败,备用工作。
永远不要更新引导装载程序。
如果数据分区已经过烘烤,请重新格式化并复制备份数据分区。
现在你不能失败,除非闪存盘死亡。 如果您正在使用COTS硬件,并且主磁盘是Compact Flash,那么您可以使用一个USB钥匙进行物理隔离备份。
恕我直言,任何更新不是原子可以打破系统或使一致性检查相当困难。 我同意必须避免更新引导加载程序,因为它不能关闭电源。 通常,制造商希望从固件xxx到版本yyy进行更新,而不会在内核和/或单个文件被更新的情况下进行更新。 更新单个文件可能成为服务的噩梦,因为很难理解客户硬件上运行的是什么。 也许你正在使用双拷贝的方法(应用程序是多余的)。 我认为这并没有什么帮助,因为系统的完整性是由链中的弱组件完成的。 如果根文件系统的更新失败,则重复应用程序并不重要。
如果您需要,双重复制方法可以保证更新不会停止服务。 但是它需要大量的资源,因为所有的组件都必须被复制。 就个人而言,我使用回退方法,如果主应用程序失败或上次更新不成功,则启动RAM中的小型rootfs。 如果出现任何问题,引导加载程序会自动启动此故障预置系统,请从USB笔更新系统(如果需要本地更新)。
我从来没有找到关于这些问题的OSS项目,根据我以前的经验,我最近开始了一个新的项目。 我有几个产品运行它,我的客户很满意。
也许你可以看看它。 您可以在github.com/sbabic/swupdate找到“swupdate”(项目名称)的github.com/sbabic/swupdate
。
斯特凡诺
我认为你在这里试图实现更新过程的原子性。 原子性对于嵌入式设备是至关重要的,原因之一就是功率损耗; 但也可能有其他硬件/网络问题。 我在更新的上下文中用于原子性的定义是:
对于嵌入式Linux,有几个软件组件可能需要更新,有不同的设计可供选择; 这里有一篇论文: https : //mender.io/user/pages/04.resources/_white-papers/Software%20Updates.pdf