本文深入源码分析了Coffee文件删除文件cfs_remove技术细节,包括remove_by_pageCOFFEE_EXTENDED_WEAR_LEVELLING

1. cfs_remove

Coffee文件系统删除文件cfs_remove,删除成功返回0,否则返回-1cfs_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_headerflags中的O位(isolated,孤立)置1,更新file_header,将该文件关联的file_descflags设为COFFEE_FD_FREE和初始化file结构体,必要时(COFFEE_EXTENDED_WEAR_LEVELLING0gc_allowed1)进行垃圾回收。

值得一提的是,通过将文件头file_headerflags中的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_page参数分别是file->pageREMOVE_LOGCLOSE_FDSALLOW_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_GREEDYGC_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源代码

本文系Spark & Shine原创,转载需注明出处本文最近一次修改时间 2022-03-22 00:10

results matching ""

    No results matching ""