QB杂货铺
底层劳动人民的不解忧杂货铺

【swupdate文档 一】嵌入式系统的软件管理

嵌入式系统的软件管理

嵌入式系统变得越来越复杂, 它们的软件也反映了这种复杂性的增加。
为了支持新的特性和修复,很有必要让嵌入式系统上的软件
能够以绝对可靠的方式更新。
在基于linux的系统上,我们可以在大多数情况下找到以下元素:

  • 引导装载程序
  • 内核和设备树
  • 根文件系统
  • 其他在后续挂载的文件系统
  • 用户资料,以裸数据格式存在或者保存在文件系统中
  • 特定用途的软件. 如,用于下载到相连接的微控制器的固件等

一般来说,在大多数情况下是需要更新
内核和根文件系统,保存用户数据-但实际情况各不相同。

仅在少数情况下,还需要更新引导加载程序,
事实上,更新引导加载程序总是很危险的, 因为更新中的失败会破坏设备。
在某些情况下,从损坏状态中恢复是可能的,
但这通常无法由最终用户完成,即设备需要返厂维修。

关于软件更新有很多不同的概念。我将解释其中的一些概念,
然后解释为什么我实施了这个项目。

通过引导加载程序完成更新

引导加载程序所做的工作远不止启动内核那么简单。
它们有自己的shell,且可以使用处理器的外围设备
进行管理,在大多数情况下是通过串行通讯。 它们通常是可执行脚本的,这使得
实现某种软件更新机制成为了可能。

然而,我发现这种方法有一些缺点,
这让我另行寻找基于运行在Linux上的应用程序的解决方案。

引导加载程序对外围设备的使用有局限性

并不是所有内核中支持的设备都可以在引导加载程序使用。
向内核添加设备支持是有意义的,因为这可以让外围设备对主应用程序可用,
但将驱动程序移植到引导加载程序中,就并不总是有意义的了。

引导加载程序的驱动程序不会被更新

引导加载程序的驱动程序大多是从Linux内核移植过来的,
但是由于经过调整的原因,它们以后不会被修复或与内核同步,
而bug修复则会定期在Linux内核中进行。

一些外围设备可能以不可靠的方式工作,
并且修复问题可能并不容易。引导加载程序中的驱动程序
或多或少是内核中相应驱动程序的复刻(fork)。

例如,用于NAND设备的UBI/UBIFS在内核中包含
了许多修复程序,这些修复程序并没有移植回引导加载程序。

USB协议栈也可以找到相同的情况。支持新外围设备或协议的工作,
在内核中进行得更好,而不是在引导加载程序中。

简化版的文件系统

支持的文件系统的数量是有限的。
将文件系统支持移植到引导加载程序需要付出很大的努力。

网络支持有限

网络协议栈是有限的,通常通过一个更新只能通过 UDP但不能通过TCP完成。

与操作人员交互

很难将接口暴露给操作员, 比如浏览器中的GUI或显示器上的GUI。

比起在引导加载程序中,复杂的逻辑可以在应用程序内部更容易实现。
扩展引导加载程序是复杂的,因为所有的服务和库都不可用。

引导加载程序更新的优点

然而,这种方法也有一些优点:

-更新软件通常更简单。
-占用空间更小:即使是一个仅用于软件管理的独立应用程序
也需要自己的内核和根文件系统。即使它们的大小能够被裁剪,
将更新软件不需要的部分去掉,它们的大小也是不可忽略的。

通过包管理器更新

所有的Linux发行版都使用包管理器做更新, 为什么这不适用于嵌入式系统?

我不能说它不能被使用,但是使用这种方法有一个重要的缺点。
嵌入式系统是使用特定的软件进行过良好测试的。
使用包管理器可能会让人觉得奇怪,因为软件本身不再是 原子的
而是分裂成一系列包。
我们怎样才能保证一个能基于库版本x.y正常工作的应用程序,
同样也能基于同一个库的不同版本工作呢?如何才能成功地做好测试?

对于制造商来说,通常更好的说法是发布了一个新的软件版本
(经过测试工程师的良好测试),并且可以更新新的软件(或固件)。
对测试人员来说,在包中进行拆分可能会产生噩梦和巨大的工作量。

简单地替换单个文件可以加快开发速度,
但是对于客户站点来说,这是一个软件版本的噩梦。
如果客户报告了一个bug,那么在之前已经向客户发送过
一些文件的补丁时,软件怎么可能还算是“2.5版本”呢?

原子更新通常是嵌入式系统的必备特性。

应用程序进行软件升级的策略

应用程序也可以用于升级系统,而不是使用引导加载程序。
应用程序可以使用操作系统提供的所有服务。
建议的解决方案是一个独立的软件,
它遵循客户规则,执行检查以确定软件是否可安装,
然后将软件安装到所需的存储上。

应用程序可以检测所提供的新软件是否适合硬件,
也可以检查软件是否由经过验证的权威机构发布。
支持的特性范围可以从小型系统扩展到复杂系统,
包括安装前和安装后脚本等等。

根据系统的资源,可以使用不同的策略。 下面我将列出其中一些。

双备份系统 - 支持回退

如果存储空间足够保存整个软件的两个副本,
那么即使软件更新被中断或断电,也可以保证始终有一个可用的副本。

每个副本必须包含内核、根文件系统和每个可以更新的组件。
需要一种机制来识别正在运行的版本。

SWUpdate应该集成到应用程序软件中, 当需要更新时,应用程序软件将触发它。
SWUpdate的职责是更新备用副本, 不修改正在运行的软件副本。

与引导加载程序的协作通常是必要的,
因为引导加载程序必须决定应该启动哪个副本。
同样,必须能够在两个副本之间进行切换。

重新启动后,引导加载程序决定应该运行哪个副本。

double_copy_layout

请参阅有关引导加载程序的章节,
了解可以实现哪些机制来确保更新后目标不会被破坏。

最明显的缺点是所需的空间量。 每个副本的可用空间小于存储空间的一半。
然而,即使在断电的情况下,更新也总是安全的。

这个项目支持这个策略。
作为该项目一部分的应用程序应该安装在根文件系统中,
并根据需要启动或触发。不需要额外的内核,
因为这两个副本保证总是可以升级不运行的副本。

SWUpdate将设置bootloader变量以通知新映像已成功安装。

单系统 - 以独立镜像形式运行

软件升级应用程序由内核(可裁剪掉不必要的驱动等)
和一个小的根文件系统以及应用程序及其库组成。
整个大小远远小于系统软件的一个副本。
根据设置,这个独立根文件系统的大小从 2.5MB 到 8MB 不等。
如果说大小对于小型系统非常重要,
那么对于具有大量存储或大容量NAND的系统, 其大小则可以忽略不计。

系统可以进入 "升级" 模式,只需向引导加载程序发出必须启动升级软件的信号。
具体方法可能有所不同,例如设置引导加载程序环境或使用和外部GPIO。

引导加载程序启动“SWUpdate”,
引导SWUpdate内核并将initrd映像作为根文件系统。
因为它在RAM中运行,所以可以升级整个存储。
与双拷贝策略不同,系统必须重新启动以将其自身置于更新模式。

这个方案比起使用两个副本,占用的存储空间更少,
但是它不能保证在不再次更新软件的情况下进行回退。
不过,至少它可以保证,当主应用不存在或损坏时,
以及当升级过程由于某种原因而中断时,系统自动进入升级模式。

single_copy_layout

事实上,可以将升级过程视为事务,
只有成功升级后,新软件才设置为“可引导”。
考虑到这些因素,使用此策略进行升级是安全的: 如果旧软件损坏或无法运行,
始终确保系统启动并准备好获得新软件。

使用U-Boot作为引导加载程序, SWUpdate能够管理U-Boot的环境设置变量,
以指示事务的开始和结束,以及包含有效的软件的存储区域。
针对GRUB环境块修改和EFI引导保护的类似特性也已被引入。

SWUpdate主要以如下配置的方式使用。
Yocto生成包含SWUpdate应用程序的initrd映像,
该映像在挂载根文件系统之后自动启动。

swupdate_single

有些事情出错了 ?

许多事情都可能出错,必须保证系统能够再次运行,
并且可能能够重新加载新的软件来修复损坏的映像。
SWUpdate与引导加载程序一起工作,以识别失败的可能原因。
目前支持U-Boot、GRUB和EFI引导保护。

我们至少可以列出一些常见的原因:

-安装过程中镜像损坏。
: SWUpdate能够识别它,并且更新过程会被中止。
旧的软件被保存下来,没有任何东西被真正复制到目标的存储中

  • 存储(flash)中损坏的镜像
  • 远程更新由于通信问题而中断
  • 意外掉电

SWUpdate的工作流程是事务性的。引导加载程序的环境变量“recovery_status”
被设置为向引导加载程序发出更新状态的信号。
当然,还可以添加更多变量,用于微调和报告错误原因。
recovery_status可以取值为“progress”,“failed”,或者它也可以被取消设置。

当SWUpdate启动时,它将recovery_status设置为“progress”。
更新成功完成后,变量将被删除。如果更新以错误结束,
recovery_status的值为“failed”。

当更新被中断时,不管什么原因,引导加载程序都能识别到,
因为recovery_status变量处于“progress”或“failed”状态。
然后,引导加载程序可以再次启动SWUpdate,以再次
加载软件(单副本情况)或运行应用程序的旧副本(双副本情况)。

意外掉电

如果发生断电,必须保证系统能够再次工作 —— 重新
启动SWUpdate或恢复软件的旧副本。

一般情况下,行为可以根据所选择的场景进行划分:

  • 单拷贝:SWUpdate被中断,更新事务没有以成功结束。
    引导加载程序能够再次启动SWUpdate,从而有可能再次更新软件。

  • 双拷贝:SWUpdate没有在备份系统和当前系统之间做切换。
    当前版本的软件,并没有被更新触及到,会再次启动。

为了完全安全,SWUpdate和引导加载程序需要交换一些信息。
引导加载程序必须检测更新是否由于断电而中断,
并重新启动SWUpdate,直到更新成功。

SWUpdate支持U-Boot、GRUB和EFI Boot Guard引导加载程序。 U-Boot和EFI Boot
Guard有用于保证掉电安全的环境变量,
SWUpdate能够读取和更改这些变量,以此与引导加载程序通信。
对于GRUB,则使用固定的1024字节环境变量块文件。
SWUpdate在开始更新系统时设置一个变量作为标志,
并在完成之后重置同一变量。引导加载程序可以读取此标志,
以检查在上次关机之前是否正在运行更新。

SoftwareUpdateU-Boot

升级SWUpdate本身会如何?

SWUpdate被认为用于整个开发过程,代替定制过程以在开发过程中更新软件。
在投产前,SWUpdate被针对这个项目进行过很好的测试。

如果SWUpdate本身应该被更新,那么当存储中只有一个SWUpdate副本时,
更新就不是安全的。只有当SWUpdate拥有两个副本时,才能保证安全更新。

如果SWUpdate是升级映像的一部分,则有一些方法可以避免这个问题:

  • 有两份SWUpdate
  • 承担风险,但准备一个在引导加载程序中可使用的救援程序。

升级引导加载程序会如何?

更新引导加载程序在大多数情况下无法做到的。
在大多数SOC上,不存在多个引导加载程序的副本,
当引导加载程序被破坏时,板子就无法引导启动了。

一些soc允许拥有多个引导加载程序副本。
但同样,没有通用的解决方案,因为它是 非常 特定于硬件的。

根据我的经验,大多数产品不允许更新引导加载程序。
当产品准备好量产时,还必须要更新引导加载程序,这种情况是非常少见的。

以上结论不适用于更新U-Boot环境变量,这是一种常见的情况。
U-Boot提供整个环境变量的两个副本,从SWUpdate中更新环境是
掉电安全的。其他引导加载程序则不一定具有此功能。

注:

本文地址 https://www.cnblogs.com/zqb-all/p/10090280.html

译自 swupdate 文档 https://sbabic.github.io/swupdate/overview.html

有更新会在github上发布 https://zqb-all.github.io/swupdate/overview.html

posted @ 2018-12-09 10:56 zqb-all 阅读(...) 评论(...) 编辑 收藏