你的位置:首页 > 技术 > 嵌入式

LittleFS - 一个高度完整的嵌入式文件系统


拥有小巧灵活的文件系统对许多物联网设备至关重要。使用文件系统并将其与正确的存储技术(如外部闪存或SD卡)配对可能很困难。Mbed操作系统使文件系统的组合变得简单。Mbed OS 5.7既支持FAT文件系统,又引入了高度集成的嵌入式文件系统。这包括对没有自己的磨损平衡控制器的闪存芯片的磨损平衡支持。

LittleFS  - 一个高度完整的嵌入式文件系统

将数据存储在嵌入式设备上非常有用:无论是配置文件,传感器信息批量还是新的固件更新。您可以抓取一些非易失性存储器(如EEPROM或SD卡),并将这些数据写入随机闪存页面,但容易出错。没有关于闪存数据的概述,不保证您不覆盖其他数据使用的页面,并且一遍又一遍地写入同一闪存页面对于耐久性是不利的。所以,你需要一个文件系统(自1965年左右),为您管理这一切。

嵌入式系统和物联网设备的文件系统有一些额外的要求:

  1. 断电恢复能力 - 要求文件系统保持一致,并将数据刷新到底层存储。

  2. 平均磨损 - 通常情况下,存储支持每块数量有限的擦除,因此使用整个存储设备对于可靠性非常重要。

  3. 微小的占地面积 - 物联网设备受到ROM和RAM的限制。占地面积小可以节省资金。

市场上有许多商业和开源嵌入式文件系统,但都没有达到我们的设计代码大小,功能或可靠性要求,这对于成功部署物联网设备至关重要。我们正在发布高完整性嵌入式文件系统,这是一个为嵌入式系统设计的小型故障安全文件系统。它作为Mbed OS 5.7的一部分以早期版本形式提供,并作为非Mbed系统的独立C库提供。它是在从GitHub的允许Apache 2.0许可和可用许可这里。

高完整性的嵌入式文件系统与FAT文件系统

Mbed OS长久以来都支持由安全数字卡或NOR闪存支持的FAT文件系统。FAT文件系统于2010年首次引入,作为外部库,然后作为Mbed OS 5.5核心操作系统的一部分进行集成。由于FAT文件系统对从DOS 6到Mac OS 10.13等其他操作系统的广泛支持和兼容性,FAT文件系统仍然是一个重要的功能。在许多物联网使用案例中,需要具有电源丢失弹性,数据完整性和更长的存储器使用寿命。对于很多物联网设备,与传统的FAT文件系统相比,高完整性嵌入式文件系统是一个更好的选择。

RAM / ROM大小

littlefs1.png

fatfs与littlefs RAM + ROM大小

Littlebed是Mbed OS中高度集成的嵌入式文件系统,可以在有限的RAM和ROM下工作。它避免了递归,将动态内存限制为可配置的缓冲区,并且不需要将整个存储块存储在RAM中。通过专注于一小组多用途数据结构,这个高度集成的嵌入式文件系统使用比FAT少的13K ROM和少于4K的RAM。

失电恢复能力

littlefs3.gif

fatfs引导计数应用程序 littlefs引导计数应用程序

我们为可能有随机电源故障的系统设计了这个文件系统。它具有强大的copy-on-write保证,并且磁盘上的存储总是保持有效状态。在上面的gif中,您可以看到FAT文件系统损坏的速度以及该文件系统的弹性。有关更多详细信息,请参阅我们用于测试的程序。

磨损均衡

littlefs4.gif

fatfs vs littlefs平均分

大多数嵌入式设备使用的存储芯片支持每个扇区有限的一组擦除。如果您没有支持磨损平衡的存储控制器,则嵌入式设备的使用寿命可能会受到影响。嵌入式文件系统提供动态损耗均衡,以在整个闪存的整个区域内跨扇区传播数据。这可以防止应用程序频繁地写入相同的扇区。失败模式是文件系统使用和文件系统中存储的数据量的组合。平均算法并不完美,但具有扩展闪存存储容量的优点,增加了设备的使用寿命。

gif显示了高完整性嵌入式文件系统与FAT文件系统在静态数据3K和动态数据3K之间的比较。要查看不同配置的磨损级别行为,请参阅此交互式仿真。

如何开始使用高完整性嵌入式文件系统?
任何实现BlockDevice接口的设备都可以在Mbed OS中托管文件系统。您可以更改基于BlockDevice的存储驱动程序,而无需更改您的应用程序或库代码。

开始:

1. 选择一个BlockDevice为您的闪存实现API 的驱动程序: DataFlash,SD卡,SFDP SPI Flash或实现您自己的。
2. 初始化并挂载文件系统:

BlockDevice bd =  / *获取块设备* / ;

//第一个参数是挂载点,例如文件将在/ fs /下可用,第二个指向块设备的指针 
LittleFileSystem fs(“fs”,&bd);

使用POSIX文件系统调用来读取和写入文件(例如fread,fopen等等)。
你可以在这里找到一个示例程序。如果您的开发板上没有任何存储空间,您可以使用HeapBlockDevice来测试这个高度集成的嵌入式文件系统,HeapBlockDevice将数据存储在内存中(但不会持久存储)。

测试
由于文件系统是设备固件的重要组成部分,因此这个高度集成的嵌入式文件系统带有一套完整的单元和集成测试,Mbed OS测试服务器场每天运行该测试。要运行这些测试,您需要mbed CLI:

功能测试 - mbed test -n 'features-filesystem-littlefs-tests-filesystem-*'。
重新测试 - mbed test -n 'features-filesystem-littlefs-tests-filesystem_retarget-*'。
磨平测试 - mbed test -n 'features-filesystem-littlefs-tests-filesystem_recovery-wear_leveling'。
模拟功率弹性测试mbed test -n 'features-filesystem-littlefs-tests-filesystem_recovery-resilience'。
硬件功率弹性测试mbed test -n 'features-filesystem-littlefs-tests-filesystem_recovery-resilience_functional'。
另外,我们建立了浸泡测试来验证文件系统在真实的硬件上正确处理闪存耗尽失败。

-

Jan Jongboom是Arm的物联网开发人员,他看到太多损坏的SD卡。Chris Haster是Arm的一名软件工程师。


参考链接:LittleFS - 一个高度完整的嵌入式文件系统

arm官方项目地址:armmbed/mbed-littlefs

原作者项目地址:geky/littlefs

移植代码:

#include "lfs.h"

// variables used by the filesystem
lfs_t lfs;
lfs_file_t file;

// configuration of the filesystem is provided by this struct
const struct lfs_config cfg = {
    // block device operations
    .read  = user_provided_block_device_read,
    .prog  = user_provided_block_device_prog,
    .erase = user_provided_block_device_erase,
    .sync  = user_provided_block_device_sync,

    // block device configuration
    .read_size = 16,
    .prog_size = 16,
    .block_size = 4096,
    .block_count = 128,
    .lookahead = 128,
};

// entry point
int main(void) {
    // mount the filesystem
    int err = lfs_mount(&lfs, &cfg);

    // reformat if we can't mount the filesystem
    // this should only happen on the first boot
    if (err) {
        lfs_format(&lfs, &cfg);
        lfs_mount(&lfs, &cfg);
    }

    // read current count
    uint32_t boot_count = 0;
    lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
    lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));

    // update boot count
    boot_count += 1;
    lfs_file_rewind(&lfs, &file);
    lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));

    // remember the storage is not updated until the file is closed successfully
    lfs_file_close(&lfs, &file);

    // release any resources we were using
    lfs_unmount(&lfs);

    // print the boot count
    printf("boot_count: %d\n", boot_count);
}


  • 发表评论
  • 查看评论
【现已经有1条评论】

watrt  2018-12-13 09:21:46 回复该评论
#include "lfs.h"
// variables used by the filesystem
lfs_t lfs;
lfs_file_t file;
// configuration of the filesystem is provided by this struct
const struct lfs_config cfg = {
// block device operations
.read = user_provided_block_device_read,
.prog = user_provided_block_device_prog,
.erase = user_provided_block_device_erase,
.sync = user_provided_block_device_sync,
// block device configuration
.read_size = 16,
.prog_size = 16,
.block_size = 4096,
.block_count = 128,
.lookahead = 128,
};
// entry point
int main(void) {
// mount the filesystem
int err = lfs_mount(&lfs, &cfg);
// reformat if we can't mount the filesystem
// this should only happen on the first boot
if (err) {
lfs_format(&lfs, &cfg);
lfs_mount(&lfs, &cfg);
}
// read current count
uint32_t boot_count = 0;
lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
lfs_file_read(&lfs, &file, &boot_count, size