聲明:本文檔來源於EasyFlash的倉庫,原作者armink。因github上圖片顯示緩慢,我轉載到了CSDN。
文檔鏈接:EasyFlash V4.0 ENV 功能設計與實現
EasyFlash V4.0 ENV 功能設計與實現
1、爲什麼要開發 V4.0
EasyFlash 是我個人開發的第二款開源軟件,自 2015 年初正式開源出來,至今(2019.02)已經經歷了 4 年多時間。期間有很多其他行業的嵌入式開發者與我取得聯繫,得知他們已經將 EasyFlash 應用於自己的產品上,我心裏也倍感欣慰,可見 EasyFlash 的成熟性已經得到了很多行業的認可。
1.1 功能簡潔,但性能差強人意
大家普遍的感覺是 EasyFlash 功能簡潔,可以很容易的應用於產品上。但隨着技術的演進,大家對於 KV 需求的多樣化,對於 MCU 資源(主要是 RAM)、Flash 存儲資源、Flash 壽命等性能指標越來越高,舊版本的 EasyFlash 在這些方面還是有提升的空間。比如:
1.2 舊版本的痛點
- 每個存儲在 Flash 上的 ENV 都會在 RAM 中緩存一份,這樣做雖然能夠簡化實現,但確實會佔用很多 RAM 資源;
- ENV 的值類型只支持字符串,如果想要保存其他類型的值(比如:數組、結構體)就比較麻煩了,雖然我後來爲此又專門開發了 struct2json 開源軟件,但還是不夠便捷;
- 每次保存 ENV 都需要重新擦寫整個 Flash 扇區,那麼位於扇區尾部未使用的區域始終無法得到利用,降低了 Flash 的使用效率,也就降低了 Flash 的使用壽命
1.3 從 0 開始的 NG 版本
也就是從 2017 年初開始,我便開始準備 EasyFlash 的性能優化工作,結合大家的需求,不斷的整理、迭代設計文檔,也與一些社區愛好者做過非常深入的交流。最終確定下來,如果單純的在原有基礎上進行完善,那麼會有太多的功能實現受到限制,所以乾脆重新開發全新一代 ENV 功能組件,這個版本被命名爲 NG(Next Generation) 版本。
NG 版本差不多在 2017 年底就已經設計完畢,但一直沒時間去開發。後來在親人的支持下,終於利用 2019 年豬年春節的假期,在岳父母家完成了 V4.0 NG 版本的開發(在此感謝岳父母、愛人的支持)。
2、V4.0 的特色有哪些
- 更小的資源佔用,內存佔用 幾乎爲 0 ;
- ENV 的值類型支持 任意類型 、任意長度,相當於直接 memcpy 變量至 flash ;
- ENV 操作效率比以前的模式高,充分利用剩餘空閒區域,擦除次數及操作時間顯著降低;
- 原生支持 磨損平衡、掉電保護功能 (V4.0 之前需要佔用額外的 Flash 扇區);
- ENV 支持 增量升級 ,固件升級後 ENV 也支持升級;
- 支持大數據存儲模式,長度無限制,數據可在多個 Flash 扇區上順序存儲。像腳本程序、音頻等佔用 Flash 超過 1 個扇區的資源也都可以存入 ENV;
- 支持 數據加密 ,提升存儲的安全性,物聯網時代的必備功能;
- 支持 數據壓縮 ,減低 Flash 佔用;
3、如何實現
3.1 算法
假定 ENV 分區裏有 4 個扇區,以下將按照操作 ENV 的方式,逐一舉例講解不同操作下,對應的 Flash 狀態及數據變化。
3.1.1 ENV 操作過程1(常規模式)
3.1.1.1 首次使用
首次使用時,EasyFlash 會檢查各個扇區的 header,如果不符合規定的格式將執行全部格式化操作,格式化後,每個扇區的頂部將被存入 header ,負責記錄當前扇區的狀態、魔數等信息。格式化的初始化狀態爲空狀態。
3.1.1.2 添加 KV1、KV2、KV3
在執行添加操作前,會先檢索合適地址來存放即將添加的新 KV,這裏檢索策略主要是:
- 確定當前選擇的扇區剩餘容量充足
- 優選選擇正在使用狀態的扇區,最後使用空狀態扇區
- 檢查新 KV 是否有同名的 KV 存在,存在還需要額外執行刪除舊值的動作
通過上圖可以看出, KV1、KV2 及 KV3 已經被放入 sector1 ,添加後,扇區狀態也被修改爲正在使用
3.1.1.3 修改 KV2 KV3,刪除 KV1,添加 KV4
修改 ENV 時,舊的 ENV 將被刪除,扇區的狀態也將被修改爲髒狀態,然後再執行新增 ENV 的操作。
-
執行修改 KV2 時,已經存在的 KV2 舊值被修改爲已刪除,sector1 狀態被修改爲髒狀態,此後將 KV2 新值放入 sector1,發現 sector1 已經沒有空間了,sector1 的狀態還會被修改爲已滿狀態;
-
執行修改 KV3 時,已經存在的 KV3 舊值被修改爲已刪除,sector1 狀態已經爲髒狀態,無需再做修改。經過查找發現 KV3 的新值只能放到 sector2,放到 sector2 後將其修改爲正在使用狀態;
-
執行刪除 KV1 時,找到 KV1 的位置,將其修改爲已刪除狀態,sector1 狀態已經爲髒狀態,無需再做修改;
-
執行添加 KV4 時,經過查找在 sector2 找到合適的存儲位置,將其添加後,sector2 狀態已經爲正在使用狀態,無需再做修改。
3.1.1.4 添加 KV5 KV6,觸發 GC
- 執行添加 KV5 操作,由於 KV5 體積較大,sector2 放不下,所以只能放在一個新扇區 sector3 上,添加後,修改 sector3 狀態爲正在使用
- 執行添加 KV6 操作,KV6 也只能放在 sector3 下,將其放入 sector 3 後,發現 sector3 空間已滿,所以將其修改已滿狀態。執行完成後,發現整個 ENV 的 4 個扇區只有 1 個狀態爲空的扇區了,這個扇區如果再繼續使用就沒法再執行 GC 操作了,所以此時觸發了 GC 請求;
- 執行 GC 請求,EasyFlash 會找到所有被標記爲已滿並且爲髒狀態的扇區,並將其內部的 ENV 搬運至其他位置。就這樣 sector1 上的 KV2 被搬運至了 sector2,騰空 sector1 後,又對其執行了格式化操作,這樣整個 ENV 分區裏又多了一個空狀態的扇區。
3.1.2 ENV 操作過程2(開啓大數據存儲模式)
馬上就來……
3.2 數據結構
結合上面的算法不難發現,其實所有的操作都圍繞着 扇區狀態 及 ENV狀態 ,這些狀態將被存放在扇區及 ENV 頭部,並且保證在不擦除扇區數據的前提下進行單向修改,在程序代碼實現上稱這些狀態及其他一些數據信息爲 元數據。
除了常規功能外,還有一項重要指標是 EasyFlash 非常看重的,那就是掉電保護能力,相當於在任何操作出現掉電異常,整個 EasyFlash 的容錯能力是否過硬,是否可以進行掉電恢復。像 準備寫入、準備刪除這些中間狀態就是爲了掉電保護功能而設計。
出於後期擴展性的考慮這裏也預留了一些保留屬性,還有一些提前規劃好的狀態及屬性後面將用過多扇區存儲、加密、壓縮功能的實現。
設計完成後,整個 ENV 的數據結構如下圖,該圖最終也可轉換爲對應的結構體。