本文深入源码分析了Coffee文件删除文件cfs_remove
技术细节,包括remove_by_page
、COFFEE_EXTENDED_WEAR_LEVELLING
。
1. cfs_remove
Coffee文件系统删除文件cfs_remove
,删除成功返回0
,否则返回-1
。cfs_remove
首先找到name
对应的文件file指针(find_file
函数),而后调用remove_by_page
删除文件,源码如下:
int cfs_remove(const char *name)
{
struct file *file;
file = find_file(name);
if(file == NULL)
{
return - 1;
}
return remove_by_page(file->page, REMOVE_LOG, CLOSE_FDS, ALLOW_GC);
}
find_file
函数用于找到文件名name
对应的file
指针。若name
对应的文件file
还驻留在内存(即还在coffee_files[COFFEE_MAX_OPEN_FILES]
数组里),并且对应的物理文件是有效的,则直接返回file
指针,否则扫描整个FLASH,将name
对应文件file
(在FLASH中但没缓存)缓存到内存(这一步得确保coffee_files
数组有可用的项,否则返回空NULL
,参见load_file
函数)。详情见博文《打开文件cfs_open》二。这个有点纳闷了,删除文件,事先得把file_header
缓存。
2. remove_by_page
remove_by_page
,删除微日志文件(如果有的话),将文件头file_header
的flags
中的O
位(isolated,孤立)置1,更新file_header
,将该文件关联的file_desc
的flags
设为COFFEE_FD_FREE
和初始化file
结构体,必要时(COFFEE_EXTENDED_WEAR_LEVELLING
为0
且gc_allowed
为1
)进行垃圾回收。
值得一提的是,通过将文件头file_header
的flags
中的O
位置1,虽表示文件已删除,但此时,文件占用的空间还不能被使用(还没擦写),只有垃圾回收处理后才能使用,而垃圾回收只有当存储空间不足时才执行。源码如下:
//return remove_by_page(file->page, REMOVE_LOG, CLOSE_FDS, ALLOW_GC);
static int remove_by_page(coffee_page_t page, int remove_log, int close_fds, int gc_allowed) //2.1
{
struct file_header hdr;
int i;
read_header(&hdr, page);//read_header用于读取物理文件的元数据(即file_header)
if (!HDR_ACTIVE(hdr)) //若A为1,O为0,I为0,则HDR_ACTIVE返回真
{
return - 1;
}
/***删除微日志文件***/
if (remove_log && HDR_MODIFIED(hdr))
{
if (remove_by_page(hdr.log_page, !REMOVE_LOG, !CLOSE_FDS, !ALLOW_GC) < 0)
{
return - 1;
}
}
hdr.flags |= HDR_FLAG_OBSOLETE; //标志为失效页
write_header(&hdr, page); //更新物理的file_header
*gc_wait = 0;
/***将待删除文件相关联的file_desc中flags设为COFFEE_FD_FREE(一个文件可能被多次打开,如多线程)***/
if (close_fds)
{
for (i = 0; i < COFFEE_FD_SET_SIZE; i++)
{
if (coffee_fd_set[i].file != NULL && coffee_fd_set[i].file->page == page)
{
coffee_fd_set[i].flags = COFFEE_FD_FREE;
}
}
}
/***将待删除文件相关联的file初始化,变为可用***/
for (i = 0; i < COFFEE_MAX_OPEN_FILES; i++)
{
if (coffee_files[i].page == page)
{
coffee_files[i].page = INVALID_PAGE;
coffee_files[i].references = 0;
coffee_files[i].max_pages = 0;
}
}
/***没有配置COFFEE_EXTENDED_WEAR_LEVELLING且gc_allowed为1,则进行垃圾回收,见2.2***/
#if !COFFEE_EXTENDED_WEAR_LEVELLING
if (gc_allowed)
{
collect_garbage(GC_RELUCTANT);
}
#endif
return 0; //邮件成功返回0
}
2.1 remove_by_page参数
删除文件cfs_remove
传给remove_by_pag
e参数分别是file->page
、REMOVE_LOG
、CLOSE_FDS
、ALLOW_GC
,如下:
//return remove_by_page(file->page, REMOVE_LOG, CLOSE_FDS, ALLOW_GC);
#define REMOVE_LOG 1 //删除微日志文件
#define CLOSE_FDS 1 //关闭FD,事实上是将file_desc的flags设为COFFEE_FD_FREE
#define ALLOW_GC 1 //允许垃圾回收,得确保COFFEE_EXTENDED_WEAR_LEVELLING为0,才会进行垃圾回收
2.2 COFFEE_EXTENDED_WEAR_LEVELLING
Coffee文件系统默认是配置了COFFEE_EXTENDED_WEAR_LEVELLING
,源码如下。在本例,文件删除并没有立即进行垃圾回收,而是待到没有空间可用的时候再回收(可理解成批处理),显然这样做有一个明显的缺点,垃圾回收会占用比较长的时间。
#ifndef COFFEE_EXTENDED_WEAR_LEVELLING
#define COFFEE_EXTENDED_WEAR_LEVELLING 1
#endif
系统提供了两种垃圾回收机制:GC_GREEDY
和GC_RELUCTANT
,前者垃圾回收过程中,擦除尽可能多的区(即贪心回收),后者擦除一个区后就停止,删除文件采用的是后一种。两种回收机制源代码如下:
/* "Greedy" garbage collection erases as many sectors as possible. */
#define GC_GREEDY 0
/* "Reluctant" garbage collection stops after erasing one sector. */
#define GC_RELUCTANT 1
参考资料:
[1] Tsiftes Nicolas,Dunkels Adam,He Zhitao.Enabling large-scale storage in sensor networks with the coffee file system[J].International Conference on Information Processing in Sensor Networks.2009,349-360
[2]
[3] Contiki源代码