星標公眾號,讓嵌入式知識 “投喂” 不停歇!
大家好,我是雜燴君。
本次介紹一個極致輕量化的嵌入式存儲方案 ——UTFS(μTFS,micro TAR File System)。
UTFS項目地址:
https://github.com/clisystems/utfs
MIT 開源協議

UTFS 是一款類 TAR 歸檔、面向扁平字節尋址存儲的極簡存儲層,不是完整帶目錄、日志、磨損均衡的通用文件系統。
其定位是手寫固化結構體的標準化替代方案。
UTFS的特點:
體積極小:僅utfs.c + utfs.h兩個源碼文件,純 C99 實現;
移植門檻極低:只需要實現sys_read、sys_write兩個底層讀寫函數;
零動態內存:全程無malloc/free,全部靜態內存,不占用堆;
向前兼容:后續新增參數文件,不會破壞原有存儲布局,升級不丟舊數據;
極低存儲開銷:每個文件僅 24 字節固定頭部,數據無填充、無對齊浪費;
UTFS 不是 FatFS / LittleFS。它不做目錄樹、不做 journaling、也不做磨損均衡:

嵌入式開發中,參數持久化常見的坑:
很多產品的真實需求其實很樸素:
這時候如果硬上完整文件系統,目錄、權限、長文件名這些給人看的能力,往往用不上,代碼和 RAM 卻先漲一截。
反過來,繼續手搓一個巨型 struct 釘在 0x0000,短期最快,長期最容易在加字段那天翻車。

左邊那種布局,每個字段的地址都是產品契約的一部分;
右邊 UTFS 的思路是:
每個文自帶 24 字節頭,按名字匹配,數據背靠背排布。以后加一個 calib,舊文件還是按名字找,而不是全員挪偏移。
cd Examples/gcc_linux
make
./main.bin -v


整體使用邏輯:
utfs_set + utfs_register:把本地 RAM 結構體和文件名綁定注冊;
utfs_load:上電從 EEPROM/Flash 讀取所有注冊數據到 RAM;
utfs_save:參數修改后,一次性全量寫入持久化介質。

名字可以記成 micro TAR File System(μTFS / uTFS)。靈感來自 TAR:Header + Data 重復拼接。
桌面 TAR 頭是 512 字節一塊,嵌入式扛不住,也用不上用戶/用戶組/權限那一套。
UTFS 把頭壓到 24 字節,后面緊跟數據,文件之間沒有 padding。

UTFS 的使用節奏和傳統 open/read/write/close不太一樣。它更像:
一次性登記關心的文件,整表加載,整表保存。

典型寫法可以:
utfs_init(false);
utfs_set(&sysfile, "system", &sysdata, sizeof(sysdata));
utfs_register(&sysfile, UTFS_NOFLAGS, UTFS_NOOPT);
utfs_set(&appfile, "appdata", &appdata, sizeof(appdata));
utfs_register(&appfile, UTFS_NOFLAGS, UTFS_NOOPT);
utfs_load();
if (utfs_file_signature(&appfile) != 0xABCD) {
memset(&appdata, 0, sizeof(appdata));
utfs_file_signature_set(&appfile, 0xABCD);
appdata.test_value = 1234; // 首啟默認值
}
/* 業務里照常改 RAM 里的結構體……需要落盤時: */
utfs_save();
登記要持久化的東西,整表裝進 RAM,改完再整表交回去。
中間業務代碼不用圍著文件系統 API轉,繼續寫結構體字段就行。
移植縫也刻意留得很窄:庫自己不做 I/O,實現兩個函數就行:
uint32_t sys_write(uint32_t address, void *ptr, uint32_t length);
uint32_t sys_read (uint32_t address, void *ptr, uint32_t length);
EEPROM、片內 Flash、外部 SPI/I2C Flash,只要能按地址讀寫字節,就能接。
UTFS 適合的場景:
歡迎大家在評論區聊聊,交流一下你的看法~