1.flash磨损平衡算法的重要性
Flash磨损平衡算法是一种用于管理和均衡闪存存储器中数据分布的算法。由于闪存存储器的特性,比如有限的擦写次数和块擦除的要求,长时间不进行数据的平衡处理可能会导致某些块的擦写次数明显超过其他块,造成闪存的磨损不平衡,进而降低闪存寿命和性能。
2. 基于双向循环链表的flash磨损平衡算法的实现原理
3.初始化
bool wearBlanceListInit(WearBlanceListQueue *queue, int cnt, int size, WearBlanceFun write, WearBlanceFun read, unsigned int address);
wearBlanceListInit实现了链表队列的初始化,使用Flash和读取状态来更新链表数据。在初始化时从Flash中读取数据,并将数据存储在链表中。这样,链表在初始化后就可以恢复到上一次运行时的状态。需要保证这个函数只能被调用一次
4.flash数据的读取
bool wearBlanceReadData(WearBlanceListQueue *queue, unsigned int index, unsigned char *data)
wearBlanceReadData通过遍历链表队列中的节点,查找并读取与给定索引匹配的数据块。如果找到匹配的索引,则从指定地址读取数据并返回读取操作的结果。如果未找到匹配的索引,则返回false
,表示未能成功读取数据块。
5.flash数据块的写入(flash磨损平衡算法的具体实现)
bool wearBlanceWriteData(WearBlanceListQueue *queue, unsigned int index, unsigned char *data)
-
首先,函数接受三个参数:
wearBlanceListQueue
类型的指针queue
,表示链表队列;unsigned int
类型的参数index
,表示要写入的数据块索引;unsigned char
类型的指针data
,表示要写入的数据。 -
如果
index
的值超过了链表队列中存在的块数(queue->blockCnt
),则返回false
,表示索引无效。 -
接下来,定义了一个指针
currentlister
和一个整型变量offset
,用于查找当前块是否有对应的链表维护和记录块的偏移地址。 -
使用
list_for_each_entry_reverse
宏遍历链表队列中的每个节点(WearBlanceList
类型)。 -
对于每个节点,检查其索引(
currentlister->index
)是否与目标索引(index
)匹配。 -
如果匹配成功,表示找到了对应的数据块。写入数据前需要先擦除块内的内容(调用
queue->write
函数擦除Flash中的这个块),并记录块的偏移地址为offset。
-
如果未找到匹配的索引,说明当前块没有对应的链表维护。
-
在接下来的循环中,通过循环遍历来查找下一个未被使用的块的偏移地址。遍历从
offset
+i + 1
*queue->oneBlockSize
开始,遍历结束时将得到一个新的offset
。 -
对于每个节点,检查其偏移地址(
lister->offsetAddress
)是否与新的offset
匹配,如果匹配则表示该地址已经被使用。继续查找下一个未被使用的块的偏移地址。 -
然后根据
isNewBlock
的值来判断是更新已存在的链表节点还是新增一个链表节点。 -
如果
isNewBlock
为false
,表示已存在对应的链表节点,则更新该节点的偏移地址为新的offset
。 -
如果
isNewBlock
为true
,表示没有找到对应的链表节点,则需要创建一个新的节点,分配内存并初始化相关成员变量。然后使用list_add
函数将新节点添加到链表队列的队首。 -
最后,调用
queue->write
函数将数据写入Flash中的指定offset
位置。
通过遍历链表队列,查找并更新或新增节点来实现Flash磨损平衡。对于已存在的链表节点,会先擦除对应的Flash块并更新偏移地址,以便写入新的数据。对于不存在的链表节点,则会新增一个节点并将数据写入对应的Flash块中。通过这种方式,可以均衡地利用Flash存储器中的块,减缓磨损的不平衡情况。
6.具体代码
#include "wearbalancealg.h"
#include <string.h>/* 只在初始化的时候调用一次 */
static void updateList(WearBlanceListQueue *queue)
{unsigned char *data = (unsigned char *)malloc(queue->oneBlockSize * sizeof(unsigned char));if (queue->read != NULL){for(int i = 0; i < queue->blockCnt; i++){/* 从flash中读数据 */bool success = queue->read(queue->address + queue->oneBlockSize*i, data, queue->oneBlockSize);if(success){/* 数据前2位为标记有效位 则新建链表进行关联 */if(data[0] == 0x55 && data[1] == 0xaa){WearBlanceList *newList = (WearBlanceList *)malloc(sizeof(WearBlanceList));memset(newList, 0, sizeof(WearBlanceList));newList->index = data[2]; //第三位表示第几个块newList->offsetAddress = i * queue->oneBlockSize;list_add(&newList, &queue->listHead); //向 list_head 的队首追加}}}}free(data);
}bool wearBlanceListInit(WearBlanceListQueue *queue, int cnt, int size, WearBlanceFun write, WearBlanceFun read, unsigned int address)
{/* 初始化链表头 */memset(&queue->listHead, 0, sizeof(struct list_head));INIT_LIST_HEAD(&queue->listHead);/* 记录链表节其他数据 */queue->blockCnt = cnt;queue->oneBlockSize = size;queue->write = write;queue->read = read;queue->address = address;updateList(queue);return true;
}bool wearBlanceReadData(WearBlanceListQueue *queue, unsigned int index, unsigned char *data)
{if(index >= queue->blockCnt){return false;}/* 查一下当前块是否有效 */WearBlanceList *lister = NULL;list_for_each_entry_reverse(lister, WearBlanceList, &queue->listHead, node){if(lister->index == index){return queue->read(queue->address + lister->offsetAddress, data, queue->oneBlockSize);}}return false;
}bool wearBlanceWriteData(WearBlanceListQueue *queue, unsigned int index, unsigned char *data)
{if(index >= queue->blockCnt){return false;}/* 查一下当前块是否有对应的链表在维护 */WearBlanceList *currentlister = NULL;unsigned int offset = 0;bool isNewBlock = true;list_for_each_entry_reverse(currentlister, WearBlanceList, &queue->listHead, node){if(currentlister->index == index){/* 将这一块擦除 块内的标记也在此时被清除 */queue->write(currentlister->offsetAddress, NULL, queue->oneBlockSize);offset = currentlister->offsetAddress;isNewBlock = false;break;}}/* 找到下一个需要擦写的offset地址 */bool addressisUsed = true;WearBlanceList *lister = NULL;for(int i = 0; i < queue->blockCnt; i++){if(addressisUsed){addressisUsed = false;/* 找到下一个有效的地址 */offset = (offset + (i + 1) * queue->oneBlockSize) % (queue->blockCnt * queue->oneBlockSize);list_for_each_entry_reverse(lister, WearBlanceList, &queue->listHead, node){if(lister->offsetAddress == offset){addressisUsed = true; break;}}}else{break;}}/* 当前链表正在使用,只需要改变链表对应的offset地址即可 */if(isNewBlock == false){/* 上面循环结束将得到一个新的offset */currentlister->offsetAddress = offset;}else /* 没有找到对应的链表则需要新增一个 */{WearBlanceList *newList = (WearBlanceList *)malloc(sizeof(WearBlanceList));memset(newList, 0, sizeof(WearBlanceList));newList->index = index; newList->offsetAddress = offset;list_add(&newList, &queue->listHead); //向 list_head 的队首追加}queue->write(offset, data, queue->oneBlockSize);return true;
}bool wearBlanceDisableOneBlock(WearBlanceListQueue *queue, unsigned int index)
{if(index >= queue->blockCnt){return false;}/* 查一下当前块是否有对应的链表在维护 */WearBlanceList *currentlister = NULL;unsigned int offset = 0;bool isNewBlock = true;list_for_each_entry_reverse(currentlister, WearBlanceList, &queue->listHead, node){if(currentlister->index == index){/* 将这一块擦除 块内的标记也在此时被清除 */queue->write(currentlister->offsetAddress, NULL, queue->oneBlockSize);list_del(¤tlister->node);free(currentlister);return true;}}return false;
}
#ifndef _WEAR_BLANCE_ALG_H_
#define _WEAR_BLANCE_ALG_H_#include "list.h"typedef struct _WearBlanceList
{ unsigned int index; struct list_head node; unsigned int offsetAddress;
} WearBlanceList;typedef struct _WearBlanceListQueue
{unsigned int blockCnt; //块的数量决定链表的数量unsigned int oneBlockSize; //一个块的大小unsigned int address; //选中flash的基地址bool (*read)(int offset, void *data, int size);bool (*write)(int offset, void *data, int size);struct list_head listHead; //队列头
} WearBlanceListQueue;typedef bool (*WearBlanceFun)(int offset, void *data, int size);bool wearBlanceListInit(WearBlanceListQueue *queue, int cnt, int size, WearBlanceFun write, WearBlanceFun read, unsigned int address);bool wearBlanceReadData(WearBlanceListQueue *queue, unsigned int index, unsigned char *data);bool wearBlanceWriteData(WearBlanceListQueue *queue, unsigned int index, unsigned char *data);bool wearBlanceDisableOneBlock(WearBlanceListQueue *queue, unsigned int index);#endif
7.总结
优势:
-
简单有效:使用链表队列来管理Flash中的数据块,通过遍历链表来查找、更新和新增节点,在Flash中实现磨损平衡。这种实现方式相对简单直观,并且能够有效地管理数据块的使用,减少Flash磨损不平衡的问题。
-
灵活性:链表队列的形式可以灵活地添加、删除和移动节点,从而管理Flash中数据块的布局。这样可以根据程序的需求进行灵活的数据管理操作,提供了一定的灵活性和可扩展性。
-
最大化使用寿命:通过使用链表队列和磨损平衡策略,可以尽可能地均衡使用Flash中的数据块,延长Flash的使用寿命。
弊端:
-
存储开销:每个节点都需要占用一定的存储空间来记录块的索引、偏移地址等信息,如果链表较长或者数据块数量较大时,可能会带来一定的存储开销。
-
时间复杂度:由于需要遍历链表来查找节点,可以导致时间复杂度较高,特别是当链表较长时。这可能会对性能产生一定的影响。
注意:
链表定义的块大小需要按照实际flash块大小(或者倍数)进行分配。