FreeRTOS 中的 Reliance Edge:面向资源受限 MCU 的掉电安全事务文件系统使用与移植指南
【免费下载链接】FreeRTOS'Classic' FreeRTOS distribution. Started as Git clone of FreeRTOS SourceForge SVN repo. Submodules the kernel.项目地址: https://gitcode.com/GitHub_Trending/fr/FreeRTOS
本篇指南基于本仓库中 Reliance Edge 的官方说明文档,系统讲解这一面向微控制器的“掉电安全(power-fail safe)”事务文件系统的定位、双 API(POSIX 风格与 FSE 精简接口)、移植与配置流程、事务提交机制,以及如何在本仓库的 FreeRTOS 移植层上完成集成。读完后,你可以明确 Reliance Edge 与 FAT/日志文件系统的能力差异、它在 4~5 KB RAM 级别的硬件上如何运行,并知道从 README 与源码目录出发完成“移植 → 配置 → 初始化 → 挂载 → 读写 → 事务提交”的完整落地路径。
1. 什么是 Reliance Edge,为什么需要它
README 对 Reliance Edge 的定义是:一个小型、可移植、高可靠、掉电安全的文件系统,专为微控制器等资源受限的嵌入式系统设计,使用 C 语言编写,提供两种使用方式:
- POSIX 风格 API:熟悉且功能完整,适合常规文件管理需求;
- FSE(File Systems Essentials)API:极简接口,适合“固定数量、静态定义文件”的简单存储场景。
文档从三类对比中给出选择它的理由:
| 对比对象 | Reliance Edge 的优势 |
|---|---|
| 裸磁盘(raw disk)访问 | 免除人工跟踪“哪些扇区属于哪个对象”的负担,数据更新更可靠 |
| FAT 文件系统 | 不会把文件系统元数据留在不一致状态而损坏磁盘,不需要 fsck/CHKDSK 之类的修复工具 |
| 日志(journaling)文件系统 | 开销更小,存储介质磨损更少,设备寿命更长 |
其核心是独特的事务模型:不仅可以防止文件系统损坏,还能把一组修改以“全有或全无(all or nothing)”的原子方式提交。即使在一组相互关联的修改提交中途发生掉电,要么全部生效、要么全部回滚,应用层无需编写“从半成品更新中恢复”的代码。这一机制在源码中对应red_transact()/RedFseTransact()等 API(见第 5 节),提交点的底层实现位于 volume.c,其中注释明确说明:提交事务点前会保证相关数据真正落盘(flush),事务点才算完成。
2. 硬件要求与适用边界
README 的 “Hardware” 一节给出了明确的资源基线(典型配置):
| 资源 | 需求 |
|---|---|
| 处理器 | 典型为 32 位微控制器,也可适配其他目标 |
| RAM | 至少 4 KB ~ 5 KB |
| 代码空间(ROM/NOR Flash) | 11 KB ~ 18 KB |
| 栈 | 500 ~ 700 字节 |
文档同时划清了适用边界:Reliance Edge不面向运行 Linux 或 Windows Embedded Compact 等复杂操作系统的高端嵌入式系统,那类平台更适合其他文件系统。这与源码中的静态配置思路一致:整个驱动的行为(卷数、句柄数、任务数、API 开关)都由redconf.h/redconf.c编译期宏决定,不依赖动态内存分配框架。
关于版本与发行形态,redver.h 表明当前仓库中的代码为v2.0(Build 700)、RED_KIT == RED_KIT_GPL,即 GPLv2 开源发行版,磁盘布局版本RED_DISK_LAYOUT_VERSION为 1。release notes 记录了 v2.0(2017 年 1 月)相对 v1.x 的变化,包括新增 Linux 主机环境支持(商业套件中的redfuse用户态 FUSE 挂载工具)、修复删除文件后目录可能报告非零长度、以及修复元根块缺失/损坏导致挂载失败时驱动残留坏状态等缺陷。
3. 开源发行版的目录结构与移植基线
本仓库中 Reliance Edge 的完整源码位于 FreeRTOS-Plus/Source/Reliance-Edge 目录,从源码结构看,其组成如下:
| 目录 | 职责 |
|---|---|
| core/driver/ | 文件系统核心驱动:core.c、buffer.c(缓冲管理)、dir.c(目录)、inode.c/inodedata.c(inode)、imap.c/imapextern.c/imapinline.c(inode 映射)、volume.c(卷与事务提交)、format.c(格式化)、blockio.c(块 I/O 调度) |
| posix/ | POSIX 风格 API 实现(约 3100 行)与路径解析 path.c |
| fse/ | FSE 极简 API 实现 |
| os/freertos/ | FreeRTOS 移植层:services/下提供块设备、时钟、互斥量、断言、输出、任务、时间戳等 OS 服务的参考实现;include/下为redostypes.h、redosdeviations.h |
| util/ | 基础工具:CRC、位图、大小端、字符串、内存等 |
| tests/posix/ | POSIX 测试套件,含fsstress.c压力测试与 redposixcompat.h 兼容性封装 |
| include/ | 全部公开与内部头文件 |
| doc/ | 发布说明与编码风格约定 |
README 提到:完整的 Developer's Guide(含 API 参考、移植、构建、配置、测试的详细讨论)以及配置工具(Reliance Edge Configuration Utility)是单独分发的,需从官方渠道获取;README 中引用的projects/newproj工程模板、os/stub/空白移植函数集与 doc 目录中的 quick-start 指南同样属于完整套件的组成部分,未包含在本开源目录中——当前开源目录下doc/实际包含 release_notes.md、release_notes.txt 与 coding_style.txt。因此本文以开源目录内可查证的代码与文档为准展开。
4. 移植(Porting):实现 OS 服务函数
README 的 “Getting Reliance Edge Working” 一节指出:使用前必须移植并配置Reliance Edge。最低限度的移植就是填实一组函数,让文件系统能向存储介质发出命令;这些函数位于os/目录的某个子目录中。本开源发行版提供的是 FreeRTOS 移植,参考实现全部在 os/freertos/services/ 下:
| 服务文件 | 提供能力 |
|---|---|
| osbdev.c | 块设备 I/O:RedOsBDevOpen/Close/Read/Write/Flush |
| osclock.c | 获取时钟值 |
| osmutex.c | 互斥量(基于 FreeRTOS 信号量) |
| osassert.c | 断言回调 |
| osoutput.c | 调试字符输出 |
| ostask.c | 任务标识相关服务(per-task errno 等依赖它) |
| ostimestamp.c | 时间戳;自 v1.0 起不再要求configUSE_TIMERS == 1(见 release notes) |
其中块设备服务是移植的关键。osbdev.c 文件头注释列出了 5 种可直接选用的示例后端,通过BDEV_EXAMPLE_IMPLEMENTATION宏选择:
BDEV_F_DRIVER (0):复用为 FreeRTOS+FAT SL 编写的现有块设备驱动,只需定义gpfnRedOsBDevInit指向F_DRIVERINIT;缺点是 F_DRIVER 仅支持单扇区读写,Reliance Edge 会发起多扇区请求,逐扇区处理会明显拖慢文件系统;BDEV_FATFS (1):直接链接 FatFs 的diskio.h驱动即可使用;BDEV_ATMEL_SDMMC (2):基于修改版 Atmel Studio Framework(ASF)SD/MMC 驱动,支持真正的多扇区读写;BDEV_STM32_SDIO (3):通过 STM32Cube BSP/HAL 访问 microSD 卡,开箱支持 STM3240G-EVAL 与 STM32F746NG-Discovery 两块板(v1.0.2 引入,见 release notes);BDEV_RAM_DISK (4):RAM 盘。当前默认值(#define BDEV_EXAMPLE_IMPLEMENTATION BDEV_RAM_DISK,见 osbdev.c#L116),适合在存储驱动尚未就绪时先编译、跑通文件系统本身,但受目标板剩余 RAM 限制,通常只能建很小的盘。
移植时的两个源码级事实值得注意:
RedOsBDevOpen()首先校验bVolNum >= REDCONF_VOLUME_COUNT即返回-RED_EINVAL(osbdev.c#L156-L171),说明卷数上限在配置阶段就固定了;- release notes 记载 v1.0 起不再支持小于 256 字节的扇区:如使用 RAM 盘等小扇区介质,需要在块设备 OS 服务实现中自行模拟。
5. 配置(Configuring):redconf.h / redconf.c
README 指出配置分两步:
- 创建工程目录——以复制
projects/newproj模板起步(该模板随完整套件分发,不在本开源目录中); - 使用Reliance Edge Configuration Utility生成两个配置文件
redconf.h/redconf.c。
配置宏直接控制编译出来的 API 面。以 redposix.h 为例,整组 POSIX 函数声明被#if REDCONF_API_POSIX == 1包裹,且各函数还有细粒度开关:REDCONF_API_POSIX_READDIR(opendir/readdir 族)、REDCONF_API_POSIX_FORMAT(red_format)、REDCONF_API_POSIX_UNLINK/MKDIR/RMDIR/RENAME/LINK/FTRUNCATE,以及总开关REDCONF_READ_ONLY——只读构建下所有写操作 API 直接不编译。FSE 侧同理由REDCONF_API_FSE控制(见 redfse.h#L52)。这种“按宏裁剪”的方式正是 README 所说“highly configurable,可精确调优到应用所需”的落点。
release notes 还提示了配置文件的版本敏感性:v1.0.2 为每卷新增了“块设备读/写/flush 失败前重试次数”选项(在配置工具中启用 “Retry block device I/O on failure”),旧版redconf.c需用新工具重新保存;v1.1 为 discard/trim 接口新增字段,同样要求用 1.1 版配置工具更新。因此升级 Reliance Edge 版本后,务必用对应版本的配置工具重新生成 redconf 文件。
6. 使用 API 一:POSIX 风格接口
按 README “Using Reliance Edge”:使用 Reliance Edge 只需在应用中包含主头文件 redposix.h(或 redfse.h),编译并链接 Reliance Edge;驱动必须先初始化(red_init()或RedFseInit()),之后才能挂载卷、调用文件与目录函数。
redposix.h#L139-L203 声明了完整 API 面:
- 驱动生命周期:
red_init()/red_uninit(); - 卷管理:
red_mount()/red_umount(),可选red_format()(运行时格式化)、red_statvfs()(卷空间状态); - 事务控制:
red_transact()、red_settransmask()/red_gettransmask()(设置/查询哪些事件自动触发事务提交); - 文件操作:
red_open()/red_close()/red_read()/red_write()/red_fsync()/red_lseek()/red_fstat(),可选red_unlink()、red_ftruncate(); - 目录操作:
red_mkdir()/red_rmdir()/red_rename()/red_link()(硬链接),以及REDCONF_API_POSIX_READDIR使能时的red_opendir()/red_readdir()/red_rewinddir()/red_closedir()。
open 模式宏(redposix.h#L57-L76)与 POSIX 语义一致:RED_O_RDONLY 0x1、RED_O_WRONLY 0x2、RED_O_RDWR 0x4、RED_O_APPEND 0x8、RED_O_CREAT 0x10、RED_O_EXCL 0x20、RED_O_TRUNC 0x40;red_lseek()使用RED_SEEK_SET/CUR/END(0/1/2 传统值)。典型使用序列:
#include <redposix.h> int32_t iFildes; red_init(); /* 1. 初始化驱动 */ red_mount("/vol0"); /* 2. 挂载卷(卷名由 redconf 配置决定) */ iFildes = red_open("/vol0/data.bin", RED_O_WRONLY | RED_O_CREAT); if (iFildes >= 0) { red_write(iFildes, buf, ulLen); /* 3. 读写 */ red_fsync(iFildes); /* 4. 确保落盘 */ red_close(iFildes); } red_transact("/vol0"); /* 5. 提交事务点,保证原子性 */6.1 两个源码级的实现细节
per-task 的red_errno。redposix.h#L79-L104 说明:正常情况下每个使用文件系统的任务拥有独立的red_errno,任务 A 不会覆盖任务 B 要读的错误值;该宏可写作 lvalue(red_errno = 0;合法)。例外情形包括:任务槽满时退化为全局 errno、驱动未初始化时总是指向全局 errno、REDCONF_TASK_COUNT配置为 1 时恒用全局 errno。实现上,posix.c#L109-L118 在REDCONF_TASK_COUNT > 1时为每个任务维护TASKSLOT(任务 ID + 独立错误值),red_errnoptr()据此返回当前任务或全局的错误位置。
紧凑的文件描述符编码。posix.c#L49-L76 把int32_t描述符(符号位必须为 0,剩 31 位)拆为 11 位挂载代数 + 8 位卷号 + 12 位句柄索引,并编译期断言REDCONF_VOLUME_COUNT/REDCONF_HANDLE_COUNT不越界;同时FD_MIN设为 3,刻意避开 0/1/2 以杜绝与 STDIN/STDOUT/STDERR 混淆。这说明描述符本身就是“挂载代 + 卷 + 句柄”的位打包,挂载代用于识别过期描述符。
7. 使用 API 二:FSE 极简接口
redfse.h 文件头对 FSE 的定位非常清楚:面向“固定数量、静态定义文件”的简单用例;不支持动态创建/删除文件;文件不用名字而用固定文件号引用,没有目录、也没有文件句柄——文件不 open/close,偏移在每次调用时显式给出。
完整 API(redfse.h#L74-L110):RedFseInit/Uninit、RedFseMount/Unmount、可选RedFseFormat(v1.0 起新增的运行时格式化)、RedFseRead/RedFseWrite(显式ullFileOffset)、RedFseSizeGet、可选RedFseTruncate、RedFseTransact与RedFseTransMaskSet/Get。文件号从RED_FILENUM_FIRST_VALID (2U)起,头文件给出的典型用法是静态定义文件号:
#include <redfse.h> #define LOG_FILE (RED_FILENUM_FIRST_VALID) #define DATABASE_FILE (RED_FILENUM_FIRST_VALID + 1U) #define ICON1_FILE (RED_FILENUM_FIRST_VALID + 2U) RedFseInit(); RedFseMount(0); /* 按卷号(而非卷名)操作 */ RedFseWrite(0, DATABASE_FILE, 0, ulLen, pData); RedFseTransact(0); /* 提交事务点 */FSE 以卷号(uint8_t bVolNum)而非路径寻址,与 POSIX 接口以路径(如"/vol0/...")寻址形成对照,这也是“极简”的直接体现。初始化实现可参照 fse.c#L66-L85:RedFseInit()内部调用RedCoreInit()并置gfFseInited标志,重复调用幂等返回成功;RedFseUninit()则在仍有卷挂载时返回-RED_EBUSY(fse.c#L105-L120)。两者注释均明确非线程安全,不得从多个线程并发初始化/反初始化。若 FSE 过于受限,头文件建议改用功能更完整的 POSIX 风格 API——两套 API 正是 README 所说“familiar POSIX-like API … or an alternate minimalist API”的实体。
8. 事务机制:掉电安全的落地方式
README 把“unique transactional model”列为 Reliance Edge 的核心卖点:一组相互关联的修改并入单个原子事务,掉电时要么整体提交、要么整体不提交,应用无需编写半成品更新恢复代码。落到本仓库代码上:
- API 层:
red_transact()/RedFseTransact()显式提交事务点;red_settransmask()/RedFseTransMaskSet()配置“哪些事件(如文件创建、删除、元数据变更)会自动触发提交”,red_gettransmask()查询当前掩码; - 驱动层:volume.c 中的 “Commit a transaction point” 逻辑负责真正提交——注释表明提交会先保证底层数据真正写穿(flush),事务点才算完成;
- 缓冲层:buffer.c(约 1200 行)管理脏缓冲与写回策略,是“哪些写入必须在下一次提交前落盘”的执行者;
- 块 I/O 层:blockio.c 调度到 OS 块设备服务的读/写/flush 请求,与
RedOsBDevFlush()对应。
结合 release notes 中 v1.0.2 新增的“块设备 I/O 失败重试次数”卷级配置可以推断:重试机制服务于弱存储介质(如 SD 卡瞬态失败)下的可靠性,而非性能。
9. 测试与验证
本开源目录自带可查证的测试入口:tests/posix/fsstress.c 是 POSIX 压力测试(随机混合文件操作验证一致性),配套的 redposixcompat.h 提供 POSIX 名称到red_*的兼容层;tests/util/ 提供 atoi/printf/rand/math 等测试辅助实现。更完整的 API 测试与“OS-specific API test”(商业套件)见 README 对 Developer's Guide 的引用。README 还记载了一条与验证相关的历史:v2.0 修复了“挂载因元根块缺失/损坏而失败后驱动残留坏状态”的缺陷(release_notes.md),这类“失败路径”健壮性正是掉电安全文件系统的重点验证对象。
10. 许可与支持
- 许可:README 声明 Reliance Edge 为 GPLv2 开源项目(完整条款见 LICENSE.txt);因商业原因无法遵守 GPLv2 的组织可获取商业许可后再并入专有软件。商业版本额外包含 GPL 版不分发的测试与工具(如 discard/trim 接口、
redfuse、FUSE 验证测试等,见 release notes)。 - 贡献:欢迎贡献,但 Datalight 要求拥有并入 Reliance Edge 的全部代码版权;贡献大量代码需签署版权转让协议(详见 CREDITS.txt 及官方 CONTRIBUTING 说明);Bug 可通过官方渠道或邮件 RelianceEdgeSupport@datalight.com 反馈。
- 已知问题:release notes 记录 Win32 移植(主机工具与 Win32 测试工程所用)无法被 Visual Studio 2005 编译(VS2008 起正常),官方不再修复。
11. 小结:从文档到代码的映射
| README 主题 | 仓库中的落点 |
|---|---|
| 掉电安全 / 原子事务 | volume.c 事务提交、buffer.c、red_transact()族 API |
| POSIX-like API | redposix.h / posix.c |
| 极简 FSE API | redfse.h / fse.c |
| 移植(OS 服务函数) | os/freertos/services/ |
| 配置(redconf.h/.c) | 由外部配置工具生成;REDCONF_*宏在 redposix.h#L50 等处裁剪 API |
| 版本 / 发行说明 | redver.h(v2.0 Build 700,GPL kit)、release_notes.md |
| 许可 | LICENSE.txt(GPLv2) |
对于要在 FreeRTOS 工程中做配置存储、日志持久化或传感器数据库的开发者,本仓库的 Reliance Edge 源码给出了完整可参考的“移植层 + 双 API + 事务核心”实现:先以BDEV_RAM_DISK把流程跑通,再按 osbdev.c 的示例切换到 SD 卡等真实介质,配合配置工具生成redconf并用red_transact()保护关键更新,即可获得一个不依赖 fsck、掉电后仍自洽的文件系统。
【免费下载链接】FreeRTOS'Classic' FreeRTOS distribution. Started as Git clone of FreeRTOS SourceForge SVN repo. Submodules the kernel.项目地址: https://gitcode.com/GitHub_Trending/fr/FreeRTOS
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考