首页 > 基础资料 博客日记
【深入浅出imx8企业级开发实战 | 04】嵌入式Linux设备掉电数据容错研究
2024-02-26 19:00:05基础资料围观371次
这是机器未来的第58篇文章
原文首发地址:https://robotsfutures.blog.csdn.net/article/details/126924015
《深入浅出i.MX8企业级开发实战》快速导航
【01】imx8qxp yocto工程构建指南
【02】Yocto工程repo源码gitee加速配置方法
【03】imx8qxp一键独立编译指南
【04】嵌入式Linux设备掉电数据容错研究
写在开始:
- 博客简介:专注AIoT领域,追逐未来时代的脉搏,记录路途中的技术成长!
- 博主社区:AIoT机器智能, 欢迎加入!
- 专栏简介:imx8qxp小白从拿到板子到完成项目的过程记录
- 面向人群:嵌入式工程师
1. 概述
本文针对嵌入式设备掉电应用数据丢失的问题,研究了掉电数据容错的一些措施。
2. 存储介质
如果是自有平台上开发的话,存储介质很好确认,如果是基于第三方平台开发应用,例如AG35 Open版这样的使用场景,如何判断存储介质的类型呢?
- 查看分区文件系统
/usrdata # df -T
Filesystem Type 1K-blocks Used Available Use% Mounted on
/dev/root squashfs 41600 41600 0 100% /
tmpfs tmpfs 76008 28 75980 0% /run
tmpfs tmpfs 76008 1292 74716 2% /var/volatile
tmpfs tmpfs 64 4 60 6% /dev
tmpfs tmpfs 76008 1292 74716 2% /var/lib
/dev/ubiblock1_0 squashfs 30720 30720 0 100% /firmware
/dev/ubi2_0 ubifs 7912 3564 4348 45% /systemrw
/dev/ubi2_0 ubifs 7912 3564 4348 45% /data
/dev/ubi2_0 ubifs 7912 3564 4348 45% /etc
/dev/ubiblock3_0 squashfs 128 128 0 100% /oemapp
/dev/ubi4_0 ubifs 139112 1592 137520 1% /usrdata
/dev/ubi5_0 ubifs 9516 40 9476 0% /persist
这里需要验证/usrdata所在分区在什么存储介质上,通过df -T可以看到其文件系统为ubifs。ubifs应用在裸flash上,因此可以判断存储flash为nandflash。
3. ubifs的writeback支持
ubifs **write-back 支持:**回写(写入到page cache即认为写入完成),同JFFS2的write-through(透写:立即写入内存)相比可以显著的提高文件系统的吞吐量。(**译者注:**write-back即回写,写入到page cache,就认为数据写入完成,而write-through即透写,只有将数据写入存储设备才认为写入成功;write-back相对于write-through,无需磁盘IO,因此具有更好的系统IO,但是数据一致性是得不到保证的)
write-back支持,需要应用程序注意及时同步重要的文件。否则掉电会导致这些文件的损坏和消失,掉电对于嵌入式系统而言是很常见的。
4. Linux文件系统中的缓存
缓存是用来减少高速设备访问低速设备所需平均时间的组件,文件读写涉及到计算机内存和磁盘,内存操作速度远远大于磁盘,如果每次调用read,write都去直接操作磁盘,一方面速度会被限制,一方面也会降低磁盘使用寿命,因此不管是对磁盘的读操作还是写操作,操作系统都会将数据缓存起来。
4.1 Page Cache
页缓存(Page Cache)是位于内存和文件之间的缓冲区,它实际上也是一块内存区域,所有的文件IO(包括网络文件)都是直接和页缓存交互,操作系统通过一系列的数据结构,比如inode, address_space, struct page,实现将一个文件映射到页的级别,这些具体数据结构及之间的关系我们暂且不讨论,只需知道页缓存的存在以及它在文件IO中扮演着重要角色,很大一部分程度上,文件读写的优化就是对页缓存使用的优化
4.2 Dirty Page
页缓存对应文件中的一块区域,如果页缓存和对应的文件区域内容不一致,则该页缓存叫做脏页(Dirty Page)。对页缓存进行修改或者新建页缓存,只要没有刷磁盘,都会产生脏页。
4.3 Linux系统中查看Page Cache和Dirty Page
LInux中有两种方式查看Page Cache
- free
/usrdata/QuecOpen # free
total used free shared buffers cached
Mem: 152656 148696 3960 1352 41992 28580
-/+ buffers/cache: 78124 74532
Swap: 0 0 0
- cat /proc/meminfo
/usrdata/QuecOpen # cat /proc/meminfo
MemTotal: 152656 kB
MemFree: 3188 kB
MemAvailable: 77660 kB
Buffers: 41992 kB
Cached: 28584 kB
SwapCached: 0 kB
Active: 66176 kB
Inactive: 34832 kB
Active(anon): 31644 kB
Inactive(anon): 136 kB
Active(file): 34532 kB
Inactive(file): 34696 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 0 kB
SwapFree: 0 kB
Dirty: 0 kB
Writeback: 0 kB
AnonPages: 30468 kB
Mapped: 10196 kB
Shmem: 1356 kB
Slab: 25820 kB
SReclaimable: 11076 kB
SUnreclaim: 14744 kB
KernelStack: 2936 kB
PageTables: 2116 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 76328 kB
Committed_AS: 1660112 kB
VmallocTotal: 781312 kB
VmallocUsed: 20520 kB
VmallocChunk: 729084 kB
关注Cached和Dirty即可。
4.4 Dirty Page脏页WriteBack参数
Linux有几个内核参数可以用来调整write-back
/usrdata/QuecOpen # sysctl -a 2>/dev/null | grep dirty
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 20
vm.dirty_writeback_centisecs = 500
-
vm.dirty_background_ratio
内存可以填充脏页的百分比(dirty数据与全部内存的最大百分比),当脏页总大小达到这个比例后,系统后台进程就会开始将脏页刷磁盘(vm.dirty_background_bytes类似,只不过是通过字节数来设置)
-
vm.dirty_ratio
绝对的脏数据限制,内存里的脏数据百分比不能超过这个值。如果脏数据超过这个数量,新的IO请求将会被阻挡,直到脏数据被写进磁盘
-
vm.dirty_writeback_centisecs
指定多长时间做一次脏数据写回操作,单位为百分之一秒,linux周期性write-back线程写出dirty数据的周期,这个机制可以确保所有的脏数据在某个时间点都可以写入介质
-
vm.dirty_expire_centisecs
指定脏数据能存活的时间,单位为百分之一秒,比如这里设置为30秒,在操作系统进行写回操作时,如果脏数据在内存中超过30秒时,就会被写回磁盘.
这些参数可以通过 sudo sysctl -w vm.dirty_background_ratio=5 这样的命令来修改,需要root权限,也可以在root用户下执行如下命令修改
echo 5 > /proc/sys/vm/dirty_background_ratio
4.5 文件读写流程
在有了页缓存和脏页的概念后,我们再来看文件的读写流程
4.5.1 读文件
- 1.用户发起read操作
- 2.操作系统查找页缓存
- a.若未命中,则产生缺页异常,然后创建页缓存,并从磁盘读取相应页填充页缓存
- b.若命中,则直接从页缓存返回要读取的内容
- 3.用户read调用完成
4.5.2 写文件
- 1.用户发起write操作
- 2.操作系统查找页缓存
- a.若未命中,则产生缺页异常,然后创建页缓存,将用户传入的内容写入页缓存
- b.若命中,则直接将用户传入的内容写入页缓存
- 3.用户write调用完成
- 4.页被修改后成为脏页,操作系统有两种机制将脏页写回磁盘
- 5.用户手动调用fsync()
- 6.由pdflush进程定时将脏页写回磁盘
5. 写入缓存测试
- 查看WriteBack回写周期
/usrdata/QuecOpen # sysctl -a 2>/dev/null | grep dirty
vm.dirty_background_bytes = 0
vm.dirty_background_ratio = 10
vm.dirty_bytes = 0
vm.dirty_expire_centisecs = 3000
vm.dirty_ratio = 20
vm.dirty_writeback_centisecs = 500
- 修改文件后立即掉电
/usrdata/QuecOpen # cat /dev/null > ag35.config
# 此时在3s内断电
/usrdata/QuecOpen # client_loop: send disconnect: Broken pipe
zhoushimin@zsm:~$ ./login_fouter.sh
spawn ssh root@198.18.1.1
root@198.18.1.1's password:
root@frouter:~# mkdir -p /home/root/.android/;echo 0x2c7c > /home/root/.android/adb_usb.ini;export PATH=$PATH:/mnt/tmp/adb
root@frouter:~#
root@frouter:~# # adb shell
root@frouter:~# ls
app init.sh ko
root@frouter:~# adb shell
* daemon not running. starting it now on port 5037 *
* daemon started successfully *
/ # ls
WEBSERVER cache firmware mnt run sys tmp
bin data home oemapp sbin system usr
boot dev lib persist sdcard systemrw usrdata
build.prop etc media proc share target var
/ # cd /usrdata/QuecOpen/
/usrdata/QuecOpen # ls
ag35.config app.config ecm_call factory ota_result.dat
ag35_app.md5 app_monitor.sh ecm_call-0.csv log_index.dat upgrade
/usrdata/QuecOpen # ls -la
total 1592
drwxrwxr-x 3 root root 864 Oct 9 05:40 .
drwxr-xr-x 3 root root 376 Sep 1 09:19 ..
-rw-r--r-- 1 root root 10 Oct 9 05:40 ag35.config # 发现文件内容未被清空
-rw-r--r-- 1 root root 0 Oct 9 05:40 ag35_app.md5
执行写入为空后,快速断电,还是很容易复现文件信息未生效的现象。
6. 优化方法总结
6.1 系统级设计
在嵌入式系统中为了保证系统的可靠性,至少要保证内核、rootfs数据的可靠性,保证系统可以运行起来,可以根据数据的类型分为系统和数据,为了考虑数据掉电容错,可以将系统分区设计为只读分区和可读写分区,例如squashfs+overlayfs/ext4。
详情参见:https://blog.csdn.net/toradexsh/article/details/109737842
6.2 调整Linux内核WriteBack参数
详见4.4 Dirty Page脏页WriteBack参数
6.3 sync方式
6.3.1 修改挂载方式为同步挂载
如果你想切换到同步模式,在mount文件系统是使用 -o 同步选项。然而,要注意文件系统性能将会下降。此外,要记住UBIFS mount为同步模式仍然不如JFFS2提供更多的保证
6.3.2 重要数据写入后执行fsync操作
- 一定要时刻记住运行fsync在你修改重要数据后;当然,没有必要sync临时的文件;要先考虑文件数据的重要性,不要执行没必要的fsync, 因为fsync操作会降低性能
- 如果你想更精确些,那么就使用fdatasync, 仅仅修改的数据被flushed, 而inode meta-data变化不会被flush(比如mtime或者permissions)
6.3.3 文件打开时,配置O_SYNC标志
你也可以在open()调用时是一年个O_SYNC标志;这使得这个文件所得修改的data(不包括meta-data)都会在write()操作返回前写入media;通常来说,最好使用fsync(), 因为O_SYNC使得每个写都是同步的,而fsync允许多个累积的写
对于一些频次低,数据小,重要的文件建议采用这种方案。
6.3.4 修改文件inodes为同步模式
可以使一定数目inodes为同步模式,通过设置inode的sync标志,一旦应用程序对这个文件执行了写操作,使系统立刻把修改的结果写到磁盘
在shell中执行chattr +S
chattr +S /usrdata/QuecOpen/ag35.config
在C程序中,则可以在ioctl命令使用FS_IOC_SETFLAGS配置同步 标志FS_SYNC_FL
int attr;
fd = open("pathname", ...);
ioctl(fd, FS_IOC_GETFLAGS, &attr); /* Place current flags
in aqattraq */
attr |= FS_SYNC_FL;
ioctl(fd, FS_IOC_SETFLAGS, &attr); /* Update flags for inode
referred to by aqfdaq */
注意, mkfs.ubifs工具会检查原始的FS树,如果文件在原始文件树是同步的,那么在UBIFS image也会是同步的
要强调的是,上面的方法对于任何文件系统都是可行的,包括JFFS2
fsync()可能包括目录 - 它同步目录inode的meta-data。 “sync” flag也可以用在目录上,使得目录inode变成同步的。但是"sync" flag是可继承的, 意味这这个目录下的所有新节点都有这个标志。 这个目录的新文件和新子目录也就变成同步的,子目录的child也是如此。 这个功能是非常有用的,如果想创建一个需要整个目录树都同步的目录。
fdatasync()调用对 UBIFS的目录是不起作用的, 因为UBIFS对目录项的操作都是同步的,当然不是所有文件系统都如此。类似的, “dirsync” inode 标志对UBIFS没有作用
以上提到的功能都是作用于文件描述符,而不是文件stream(FILE *)。 同步一个stream,你应该通过libc的fileno()取得文件描述符,首先使用fflush()来flush stream ,然后调用fsync或者fdatasync. 你也可以使用其他的同步方法,但是记得在同步文件前要先flush stream. fflush() 和sync(), fsync,fdatasync的区别在于前者仅仅同步libc-level的buffer,而后者则是kernel-level buffers。
6.4 其它实现机制
6.4.1 硬件缓冲
增加超级电容或电池,掉电时做退出处理。这种成本相对较低,但是仍然需要考虑超级电容或电池的使用寿命问题,是否在产品的生命周期之内。
6.4.2 软件缓冲
如果设备作为从设备挂载在主机上,主机在断电前,先通知从机即将断电,做一个断电通知,让从机正常做退出处理。
6.4.3 Copy-on-Write
COW(copy-on-write 的简称),是一种计算机设计领域的优化策略,其核心思想是:如果有多个调用者(callers)同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本(private copy)给该调用者,而其他调用者所见到的最初的资源仍然保持不变。这过程对其他的调用者都是透明的(transparently)。此作法主要的优点是如果调用者没有修改该资源,就不会有副本(private copy)被创建,因此多个调用者只是读取操作时可以共享同一份资源(摘自维基百科**)。**
6.4.4 Copy
对于重要文件,每次操作前,先备份副本并同步后再操作,如果正式版本出错,还可以从备份版本恢复。
参考文献:
- https://blog.csdn.net/weixin_31972679/article/details/111956256
- https://blog.csdn.net/shichaog/article/details/45932339
- https://www.cnblogs.com/youngerchina/p/5624559.html
- http://www.linux-mtd.infradead.org/doc/ubifs.html
- https://blog.csdn.net/rjszcb/article/details/118057620
- https://www.cnblogs.com/byronsh/p/norflash_nanflash_name_explaination.html
- https://blog.csdn.net/weixin_42653531/article/details/90745042
- https://www.cnblogs.com/zengkefu/p/5683590.html
- https://blog.csdn.net/weixin_42444974/article/details/116872740
— 博主热门专栏推荐—
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!
标签: