針對資料庫存儲存滿的情況,FlashDB具有良好的處理機制。一般flash存滿之後都需要整塊擦除才能進行下一次存儲,這樣會造成擦除時間過長占用時間片的問題。FlashDB資料通過動态擦除、動态存儲和狀态位切換的方式完美的解決了資料庫存滿之後需要整塊擦除的弊端。
1.sector(區塊)定義
(1)區塊大小
FlashDB的區塊大小為4K位元組,剛好是一個外部flash的最小區塊單元,這樣劃分便于擦除和寫入。
(2)區塊參數
區塊主要有校驗标志、狀态、區塊首位址、起始node時間、結束node時間、結束node的id、結束node的狀态、剩餘的長度等參數,以下會針對具體參數進行詳細闡述。
/* TSDB section information */
struct tsdb_sec_info {
bool check_ok; /**< sector header check is OK */
fdb_sector_store_status_t status; /**< sector store status @see fdb_sector_store_status_t */
uint32_t addr; /**< sector start address */
uint32_t magic; /**< magic word(`T`, `S`, `L`, `0`) */
fdb_time_t start_time; /**< the first start node's timestamp, 0xFFFFFFFF: unused */
fdb_time_t end_time; /**< the last end node's timestamp, 0xFFFFFFFF: unused */
uint32_t end_idx; /**< the last end node's index, 0xFFFFFFFF: unused */
fdb_tsl_status_t end_info_stat[2]; /**< the last end node's info status */
size_t remain; /**< remain size */
uint32_t empty_idx; /**< the next empty node index address */
uint32_t empty_data; /**< the next empty node's data end address */
};
(3)區塊狀态
FlashDB的區塊狀态用于标記該區塊目前的狀态,主要分為未使用、空、使用中、滿四個狀态
/* the flash sector store status */
enum fdb_sector_store_status {
FDB_SECTOR_STORE_UNUSED,
FDB_SECTOR_STORE_EMPTY,
FDB_SECTOR_STORE_USING,
FDB_SECTOR_STORE_FULL,
};
(3)區塊首位址
FlashDB的區塊首位址是用于标記該區塊在flash中的具體位置,同時也是通過這個首位址進行區塊復原。
2.復原處理流程
針對Sector不同狀态會進行不同的處理,是以會出現以下三種情況。
(1)初次存儲
當FlashDB資料庫都為空的時候所有的區塊都是EMPTY狀态,此時直接将該區塊标記為USING狀态,接着直接将本次資料和時間戳寫入資料庫。
(2)過程中存儲
當該區塊已經标記為USING狀态,同時下次寫入的數量也不會超過該區塊,直接将本次資料和時間戳寫入資料庫。
(3)滿狀态存儲
當該區塊已經标記為USING狀态,同時下次寫入的數量超過該區塊範圍。這時就會将區塊暫時标記為FULL狀态,直接将首位址偏移到下一個區塊,同時擦除下一個區塊,這是目前區塊就是一個空的狀态(将目前區塊标記為空)。
通過循環往複執行(1)(2)(3)方式的存儲就可以實作分區塊存儲,如果檢測到區塊為flash的最後一塊區域直接跳轉到第一個區塊。這樣就可以實作復原存儲的功能。以下代碼是區塊狀态切換的流程處理。
static fdb_err_t update_sec_status(fdb_tsdb_t db, tsdb_sec_info_t sector, fdb_blob_t blob, fdb_time_t cur_time)
{
fdb_err_t result = FDB_NO_ERR;
uint8_t status[FDB_STORE_STATUS_TABLE_SIZE];
if (sector->status == FDB_SECTOR_STORE_USING && sector->remain < LOG_IDX_DATA_SIZE + FDB_WG_ALIGN(blob->size)) {
uint8_t end_status[TSL_STATUS_TABLE_SIZE];
uint32_t end_index = sector->empty_idx - LOG_IDX_DATA_SIZE, new_sec_addr, cur_sec_addr = sector->addr;
/* save the end node index and timestamp */
if (sector->end_info_stat[0] == FDB_TSL_UNUSED) {
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false);
FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t), false);
FLASH_WRITE(db, cur_sec_addr + SECTOR_END0_IDX_OFFSET, &end_index, sizeof(end_index), false);
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END0_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true);
} else if (sector->end_info_stat[1] == FDB_TSL_UNUSED) {
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_PRE_WRITE, false);
FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_TIME_OFFSET, (uint32_t * )&db->last_time, sizeof(fdb_time_t), false);
FLASH_WRITE(db, cur_sec_addr + SECTOR_END1_IDX_OFFSET, &end_index, sizeof(end_index), false);
_FDB_WRITE_STATUS(db, cur_sec_addr + SECTOR_END1_STATUS_OFFSET, end_status, FDB_TSL_STATUS_NUM, FDB_TSL_WRITE, true);
}
/* change current sector to full */
_FDB_WRITE_STATUS(db, cur_sec_addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_FULL, true);
sector->status = FDB_SECTOR_STORE_FULL;
/* calculate next sector address */
if (sector->addr + db_sec_size(db) < db_max_size(db)) {
new_sec_addr = sector->addr + db_sec_size(db);
}
else if (db->rollover) {
new_sec_addr = 0;
} else {
/* not rollover */
return FDB_SAVED_FULL;
}
read_sector_info(db, new_sec_addr, &db->cur_sec, false);
if (sector->status != FDB_SECTOR_STORE_EMPTY) {
/* calculate the oldest sector address */
if (new_sec_addr + db_sec_size(db) < db_max_size(db)) {
db->oldest_addr = new_sec_addr + db_sec_size(db);
} else {
db->oldest_addr = 0;
}
format_sector(db, new_sec_addr);
read_sector_info(db, new_sec_addr, &db->cur_sec, false);
}
} else if (sector->status == FDB_SECTOR_STORE_FULL) {
/* database full */
return FDB_SAVED_FULL;
}
if (sector->status == FDB_SECTOR_STORE_EMPTY) {
/* change the sector to using */
sector->status = FDB_SECTOR_STORE_USING;
sector->start_time = cur_time;
_FDB_WRITE_STATUS(db, sector->addr, status, FDB_SECTOR_STORE_STATUS_NUM, FDB_SECTOR_STORE_USING, true);
/* save the start timestamp */
FLASH_WRITE(db, sector->addr + SECTOR_START_TIME_OFFSET, (uint32_t *)&cur_time, sizeof(fdb_time_t), true);
}
return result;
}
3.總結
以上僅僅是通過資料寫入時的狀态切換簡單的描述FlashDB復原功能,與此同時還會針對資料、位址、目前時間進行操作,在這裡就不去具體分析了。