文献标识码: A
文章编号: 0258-7998(2015)03-0024-04
0 引言
尽管Linux作为嵌入式操作系统有着诸多的优势,但是由于嵌入式操作系统自身的特点,如嵌入式应用范围的多样性和复杂性,导致嵌入式产品维护难度大的问题更加突出。嵌入式系统在实际环境中投入运行后,掉电等意外的灾难、用户错误或恶意地对数据进行修改和删除、一部分无法在开发中充分测试的错误等都会导致功能失效,严重的可能导致系统瘫痪。如果采用人工更新方式,由于安装位置等因素有时会不方便。因此,嵌入式系统的自动备份与恢复机制是恢复数据最容易和最有效的保证方法[1],并逐渐成为嵌入式系统实际应用中的一个重要问题。
目前,普遍使用双机热备份[2]、容灾备份[3]等技术做为实现嵌入式系统高可靠性的设计方案,由于采用独立的两套系统,因此增加了一定的设计难度和成本[4]。本文针对当前嵌入式系统备份技术存在的一些问题,通过分析嵌入式U-Boot及Linux内核,将嵌入式系统镜像在一套设备上备份为多份,每次仅有一个系统运行。当运行的操作系统出现故障时,就会触发系统备份恢复机制,使用下一个可用的备份分区覆盖掉出现故障的系统分区,然后启动下一个可用的系统。整个系统不需要备用设备,大大地节约了成本和功耗,提高了系统的稳定性和可靠性。
1 系统总体方案设计
1.1 系统总体设计
嵌入式Linux系统中的备份与恢复总体实现方案如图1所示。
该系统由x-loader(SPL)、U-Boot、U-Boot Env、Judge-
Area、Kernel(uImage)和Rootfs(ubi.img)六部分组成。mtd是同一NAND Flash上划分出的不同分区,并且uImage和ubi.img在设备上备份了多份,系统各组成部分特性及作用介绍如下:
(1)x-loader是一级引导程序[5],U-Boot是二级引导程序[6],U-Boot Env存储内核启动参数。
(2)Judge-Area存储系统备份及恢复参数。在U-Boot和Linux下可以共享访问。
(3)Kernel是Linux内核[7],Rootfs采用开源的无排序区块图像文件系统UBIFS(Unsorted Block Image File System),特别适用于嵌入式系统[8]。
A、B、C均为系统镜像区,存放内核和文件系统。U-Boot通过判断Judge-Area中的参数,启动其中一个系统镜像区,并将启动过程中的故障情况反馈给Judge-Area。当系统运行出现故障或者崩溃时,使用watchdog机制来使其硬件复位,通过U-Boot下的自动备份恢复机制,用其他可运行的备份来覆盖出现故障的系统镜像区,确保能启动一个可用系统。
目前运行Linux系统的NAND Flash配置都比较大,而裁剪后的内核和精简的UBI文件系统实际占用不到20 MB容量,或者更少[9]。因此,这为实现Linux系统的多重备份提供了可能。
1.2 备份及恢复机制
下面以三重备份与恢复为例来说明系统的备份及恢复流程,整个系统镜像分3个地方保存在NAND Flash,具体的过程如下:
(1)系统上电或复位U-Boot启动运行,假设A区相关的r_active_1(引导标志)和b_success_1(内核启动成功标志)为“yes”,BC区域均为“no”。在系统镜像未成功启动以前的过程可以认为是启动失败的,所以先设置A区r_active_1、b_success_1为“no”,rec_kernel_1(内核恢复标志)、rec_fs_1(文件系统恢复标志)为“yes”。还要设置B区r_active_2、b_success_2为“yes”。
(2)然后U-Boot加载A区域的系统,若Linux系统运行正常,则在Linux下还原所有标志值为初始值。若引导失败,则若干时间后触发watchdog复位系统,此时U-Boot再次运行,判断b_success_1为“no”,表示上次引导失败,并且rec_kernel_1、rec_fs_1为“yes”,表示需要将B区的内核及文件系统覆盖到A区,覆盖成功后清除恢复标志和重置b_success_1,再通过watchdog复位系统。
(3)U-Boot判断r_active_2、b_success_2为“yes”,故选择启动B区域的系统。在系统启动成功以前,设置B区r_active_2、b_success_2为“no”,rec_kernel_2、rec_fs_2为“yes”,还要设置C区r_active_3为“yes”。
(4)加载B区域的系统,若Linux系统能正常运行,则在Linux下还原所有标志为初始值。如果B区也失败了,复位系统,在U-Boot下用C区覆盖B区域。覆盖成功后清除恢复标志和重置b_success_2,再通过watchdog复位系统。同理,再从C区域的系统启动,当C区域也出现问题后,则重新从A区域启动,循环依次选择。
2 系统实现
2.1 Judge-Area基础设计
Judge-Area是在NAND Flash上划分出的单独区域,内部存储系统备份及恢复的环境参数,该区类似U-Boot Env区域。
为了能在U-Boot下查看和修改这些参数,将本区域的所有参数定义在一个judge_tab数组中,然后导入到哈希表judge_htab中。
struct hsearch_data judge_htab;
himport_r(&judge_htab, (char *)judge_tab, \
sizeof(judge_tab), ′\0′, 0)
为方便调试和实现手动进行备份恢复操作,添加U-Boot下串口打印命令printjudge,修改命令setjudge,保存命令savejudge。打印命令printjudge的实现为:
/* print a single name */
hsearch_r(e, FIND, &ep, &judge_htab);
/* print whole list */
len=hexport_r(&judge_htab, ′\n′, &res, 0);
同理,修改命令setjudge也是对judge_htab的简单操作,由于这些修改只是对内存上的数值进行操作,所以修改后的值没有保存在NAND Flash中。保存命令savejudge便是对这些变量进行存储,存储的过程如下:使用hexport_r将数据导出到新的哈希表judge_htab_new,然后调用nand_erase_opts擦除Judge-Area区域,最后使用nand_write将数据写入到该区域。为提高读写数据的可靠性,开启硬件坏块检测、ECC校验。
2.2 U-Boot下备份及恢复设计
U-Boot下备份及恢复机制设计如图2所示。
函数judge_get()实际上调用hsearch_r函数查询哈希表judge_htab,从Judge-Area读取下一个系统镜像的地址addr_x及大小size_x。
函数img_recover()的功能是调用img_read、img_write恢复下一个系统镜像到当前系统镜像区。
Judge-Area参数重置函数judge_reset()主要工作是调用setjudge分别设置b_success_x为"yes",设置rec_fs_x、rec_kernel_x为"no"。
U-Boot Env参数重置函数uEnv_reset()的工作是调用setenv设置nand_src_addr、nand_img_siz、nand_root对应为"n_kaddr_x"、"n_ksize_x"、"ubi0:rootfs rw ubi.mtd=y,2048"。其中,setenv为U-Boot自带的函数[10],y(y=2*x+6)为文件系统所在分区。
参数预处理函数judge_init()主要工作是调用setjudge分别设置r_active_x、b_success_x为"no",设置rec_kernel_x、rec_fs_x、r_active_y、b_success_y为"yes"。其中,y=cyc_add(x),函数cyc_add()为周期相加函数,即当x<MAX_MTD_SYSTEM(系统镜像区数量)时,x++,否则x=1。
2.3 Linux下备份及恢复设计
在系统镜像启动之前,U-Boot已经修改了Judge-Area中的参数并保存到NAND Flash,那么,Linux下就是对这些参数进行后期处理。本文采用开机自启动脚本方式实现参数的处理[11],针对实际的应用环境,可以将脚本插入到“不信任”位置。一旦系统或程序崩溃,脚本就会接收到由应用程序、内核等传来的命令。然后决定是进行参数还原,还是复位系统。Linux下备份及恢复机制主要就是通过该脚本实现,如图3所示。
本文使用MTD+UBIFS的方式管理Flash,跳过FTL/NFTL(Flash转换层/NAND Flash转换层),大大提高了管理能力[12]。借用mtd-utils工具包中针对NAND操作的工具[13],可以将Judge-Area(mtd6)区中的内容保存到judge.txt文件中。然后调用Linux下的sed命令修改标志位,最后将修改后的文件写回到Judge-Area区。整个操作由anti_judge_init脚本完成,主要工作如下:
mtd_debug read /dev/mtd6 0 $filesize judge.txt
/* 修改参数old_flag为new_flag */
sed -e "s/$old_flag/$new_flag/g" judge.txt
mtd_debug erase /dev/mtd6 0 $filesize
mtd_debug read /dev/mtd6 0 $filesize judge.txt
为保证NAND分区一致性,需要修改内核NAND partition信息。内核分区信息存放在mtd_partition结构体中,该文件一般位于arm/arm/plat目录下。另外,制作UBIFS文件系统也要和内核分区保持一致。制作UBIFS镜像文件,需要使用mkfs.ubifs工具,该工具也是mtd-utils工具包中的内容[14]。
3 系统测试
本系统在多款开发板上测试通过,以Tiny210开发板上的三重备份与恢复系统为例,测试方法是重新编译包含自动备份与恢复机制的u-boot.img、uImage和ubi.img。ubi.img中加入了视频监控程序,在该程序中预留运行一段时间摄像头就会打开失败的BUG。因此,视频监控程序就是本系统“不信任”位置,如果程序运行失败,就执行Linux下的自动备份与恢复机制。Tiny210开发板集成了512 MB SLC NAND Flash,将NAND Flash被分成12个区域,见表1。
进入SD卡上的U-Boot,运行updatesys将生成的u-boot.img、uImage、ubiubi.img烧写到指定区域。上电,系统启动成功,运行视频监控程序一段时间后,系统自动重启,在串口上查看到的备份与恢复信息如图4所示。
从打印的信息可以看出,A区的系统镜像被B区覆盖,接着运行另一个分区的系统镜像。
4 结论
从测试结果看,本文设计的备份与恢复机制可以保证系统能在特殊的环境下稳定地工作,整个过程都是系统自动完成,维护方便。目前,NAND Flash的成本越来越低,容量越来越大[15],这种备份与恢复方法无疑是降低成本、保证系统稳定性方便有效的方法。该方案可以应用在一些对功能稳定性要求高和维护不方便的系统测试等场合。
参考文献
[1] 索红军.嵌入式系统中热备份双机切换技术研究[J].微计算机信息,2008,24(8):32-34.
[2] 马锦荣.一种嵌入式系统bootrom自动备份及切换技术[J].单片机与嵌入式系统应用,2011,11(12):74-75.
[3] 谢长生,韩德志,李怀阳.容灾备份的等级和技术[J].中国计算机用户,2003,19(18):30-31.
[4] 郭荣佐,黄君.嵌入式实时控制系统硬件可靠性及应用研究[J].电子技术应用,2012(5):11-14.
[5] 蔡利平,任家富,童锐,等.基于ARM的Nand Flash启动分析与移植[J].计算机工程与设计,2012(3):931-935.
[6] 高文辉,师奕兵,张伟.基于S3C2440的U-Boot双启动实现[J].测控技术,2012(2):87-91.
[7] 胡勇其,侯紫峰.嵌入式Linux下NAND存储系统的设计与实现[J].计算机工程,2006(4):61-63,81.
[8] 韦斯,丁志刚,张伟宏.LINUX下UBI子系统的研究与应用[J].计算机应用与软件,2010(10):68-71.
[9] 贾源泉,肖侬,赖明澈,等.基于NAND FLASH的多路并行存储系统中坏块策略的研究[J].计算机研究与发展,2012(z1):68-72.
[10] 武贝贝.面向NAND闪存的SQLite数据恢复技术研究与应用[D].浙江:杭州电子科技大学,2012.
[11] 陈鹏,王树志,董孝峰,等.一种嵌入式操作系统休眠唤醒后程序自动运行的方法[J].电子技术应用,2012,38(2):11-13.
[12] 张少波,徐广辉,田小锋,等.基于Nand FLASH高可靠自恢复实时文件系统[J].计算机工程与科学,2012(6):169-173.
[13] 高丽,张欢庆.嵌入式Linux中NAND Flash设备驱动研究[J].电脑开发与应用,2014(5):11-16.
[14] 鞠高明,俞建新.UBIFS闪存文件系统分析与研究[J].电脑知识与技术,2014(4):749-754.
[15] 鲁慧荣.2012年闪存技术与市场变化趋势[J].集成电路应用,2013(1):4-6.