Chinaunix首页 | 论坛 | 博客
  • 博客访问: 26349
  • 博文数量: 47
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2015-07-31 12:10
文章分类
文章存档

2015年(47)

我的朋友

分类: 嵌入式

2015-08-01 09:21:44

摘要:

    在Coffee,没有单独的文件创建函数。在name对应文件不存在的情况下,为cfs_open指定flags(取000或010或011或100或110或111)可创建新文件,本文深入源码并用多组示意图分析Coffee创建文件技术细节。包括find_file、page_count、reserve、find_contiguous_pages、write_header。


一、概述

在Linux,用以下两种方式创建一个新文件(两种方式等价):

  1. int creat(const char *pathname, mode_t mode);   //不足之处是它以只写的方式打开创建的文件
  2. int open(const char *pathname, O_WRONLY|O_CREAT|O_TRUNC, mode_t mode);  //mode指定访问权限

    但在Coffee,没有单独的文件创建函数。虽有cfs_open函数,但flags只能是CFS_READ、CFS_WRITE、CFS_APPEND,没有类似O_CREAT,最开始以为Coffee少了创建文件相关函数(因为Contki源码包的测试例子,有些问题),最近花了不少时间分析Coffee打开文件函数cfs_open,才搞明白。

    打开一个物理上不存在的文件,Coffee会试图创建新的文件。之所以说"试图",是因为创建并不一定会成功,得满足以下条件,才会创建成功:

(1) cfs_open函数的flags不能是(CFS_READ|CFS_APPEND)或者CFS_READ,见2.2

(2) 物理FLASH有连续pages空闲页或者经过垃圾回收后有连续的pages空闲页

    值得注意的是,即便文件创建成功也可能返回-1(当coffee_files[COFFEE_MAX_OPEN_FILES]数组没有可用项时,新创建的文件并没有缓存到数组coffee_files)。


二、源码分析

    Coffee打开文件cfs_open函数分析见博文《Contiki学习笔记:Coffee文件系统打开文件cfs_open》,以下选取与文件创建相关代码进行分析,如下:

  1. fdp->file = find_file(name); //见2.1
  2. if (fdp->file == NULL) //物理上没有name对应的文件file,则试图创建之
  3. {
  4.   if ((flags &(CFS_READ | CFS_WRITE)) == CFS_READ)//检查flags是否符合创建文件要求,见2.2
  5.   {
  6.     return -1;
  7.   }

  8.   fdp->file = reserve(name, page_count(COFFEE_DYN_SIZE), 1, 0); //见三
  9.   if (fdp->file == NULL)
  10.   {
  11.     return - 1;
  12.   }

  13.   fdp->file->end = 0; //新创建的空文件,文件末尾自然是0
  14. }

  15. /*此处略去与文件创建无关的4行代码*/

  16. fdp->flags |= flags;
  17. fdp->offset = flags &CFS_APPEND ? fdp->file->end: 0; //如果flags没有设置CFS_APPEND,则文件偏移量设为0
  18. fdp->file->references++; //文件引用次数加1

  19. return fd;

2.1 find_file

    执行find_file函数,若name对应的文件file还驻留在内存(即若file还在coffee_files[COFFEE_MAX_OPEN_FILES]数组里),并且对应的物理文件是有效的,则直接返回file,否则扫描整个FLASH,将name对应文件file(在FLASH中但没缓存)缓存到内存(这一步得确保coffee_files数组有可用的项,否则返回空NULL,参见load_file函数)。关于find_file详细分析见《Contiki学习笔记:Coffee文件系统打开文件cfs_open》。显然,这里要处理的情况是后者,即物理上没有name对应的文件file,则试图创建之。

2.2 cfs_open函数flags判断

首先检查cfs_open参数flags,flags至少有一项是CFS_WRITE,才有可能创建新文件,源代码如下:

  1. if ((flags &(CFS_READ | CFS_WRITE)) == CFS_READ) //若flags仅仅是CFS_READ则返回-1,即不创建新文件
  2. {
  3.   return - 1;
  4. }

    那么,if条件在什么时候会返回真呢,如下图所示,当flags中没有CFS_WRITE且有CFS_READ时(CFS_APPEND可有可无),即flags取值为001、101,条件为真。也就是说,

图1 cfs_open函数flags判断示意图

    当flags含有CFS_WRITE或者没有CFS_READA或者两者皆有之时才有可能创建新文件(CFS_APPEND可有可无),即flags取000、010、011、100、110、111。(这样的设计让我很费解)

2.3 page_count

    page_count函数用于算出给定的size需要多少Coffee页,即size加上file_header大小,并且按Coffee页(Coffee页大小与FLASH物理页大小不一定等同,见cfs_coffee_arch.h文件)对齐,示意图如下:

图2 page_count函数示意图

在本例中,page_count返回2,即2页Coffee_Page,源代码如下:

  1. //page_count(COFFEE_DYN_SIZE)
  2. #define COFFEE_DYN_SIZE (COFFEE_PAGE_SIZE*1) //在cfs_coffee_arch.h可以配置

  3. static coffee_page_t page_count(cfs_offset_t size)
  4. {
  5.   return (size + sizeof(struct file_header) + COFFEE_PAGE_SIZE - 1) /COFFEE_PAGE_SIZE;
  6. }


三、创建文件主体函数reserve

    reserve是创建新文件的主体函数,首先进行参数验证,接着查看物理FLASH是否有连续pages空闲页,若有,则返回该文件即将占有页的第一页。否则,调用Coffee垃圾回收,再次查看物理FLASH是否有连续pages空闲页,如果还没有就返回NULL。

    值得注意的是,即便文件创建成功也可能返回-1(当coffee_files[COFFEE_MAX_OPEN_FILES]数组没有可用项时,新创建的文件并没有缓存到数组coffee_files)。源代码如下:

  1. //fdp->file = reserve(name, page_count(COFFEE_DYN_SIZE), 1, 0);
  2. static struct file *reserve(const char *name, coffee_page_t pages,int allow_duplicates,unsigned flags)
  3. {
  4.   struct file_header hdr;
  5.   coffee_page_t page;
  6.   struct file *file;

  7.   if (!allow_duplicates && find_file(name) != NULL) //参数验证,在本例中,!allow_duplicates为0,条件为假
  8.   {
  9.     return NULL;
  10.   }

  11.   page = find_contiguous_pages(pages); //查看物理FLASH是否有连续pages空闲页,若有,则返回该文件即将占有页的第一页。见3.1
  12.   if (page == INVALID_PAGE) //没有可用的页,尝试着调用垃圾回收
  13.   {
  14.     if (*gc_wait)
  15.     {
  16.       return NULL;
  17.     }
  18.     collect_garbage(GC_GREEDY); //垃圾回收
  19.     page = find_contiguous_pages(pages); //再次查看物理FLASH是否有连续pages空闲页,见3.1
  20.     if (page == INVALID_PAGE) //如果垃圾回收后还没找到可用的页,就返回NULL
  21.     {
  22.       *gc_wait = 1;
  23.       return NULL;
  24.     }
  25.   }

  26.   memset(&hdr, 0, sizeof(hdr));                 //将hdr成员变量初始化为0
  27.   memcpy(hdr.name, name, sizeof(hdr.name) - 1); //给file_header的成员变量name赋值
  28.   hdr.max_pages = pages;                        //实参pages,在本例等于2
  29.   hdr.flags = HDR_FLAG_ALLOCATED | flags;       //file_header的flags的A位置1,表示文件正在使用
  30.   write_header(&hdr, page);                     //将file_header写入物理FLASH,见3.2

  31.   PRINTF("Coffee: Reserved %u pages starting from %u for file %s\n", pages, page, name);

  32.   file = load_file(page, &hdr); //如果coffee_files[COFFEE_MAX_OPEN_FILES]数组没有可用项,就返回NULL了(但此时文件已创建完毕)
  33.   if (file != NULL)
  34.   {
  35.     file->end = 0;
  36.   }

  37.   return file;
  38. }

3.1 find_contiguous_pages

    find_contiguous_pages从next_free(全局变量,指向下一个空闲页)指向的页面开始查找,若找到start+amount<=next_file(),则返回start,否则返回INVALID_PAGE(说明没有足够空闲页甚至没有空闲页供使用)。

图3 find_contiguous_pages函数示意图

find_contiguous_pages函数源代码如下:

  1. static coffee_page_t find_contiguous_pages(coffee_page_t amount)
  2. {
  3.   coffee_page_t page, start;
  4.   struct file_header hdr;

  5.   start = INVALID_PAGE; //初始化start,#define INVALID_PAGE ((coffee_page_t)-1)

  6.   for (page = *next_free; page < COFFEE_PAGE_COUNT;) //从next_free指向的页面开始查找
  7.   {
  8.     read_header(&hdr, page);
  9.     if (HDR_FREE(hdr)) //判断file_header的flags中A(Allocated)位,若A位为0,则HDR_FREE(hdr)返回真,即页面是空闲的
  10.     {
  11.       if (start == INVALID_PAGE)
  12.       {
  13.         start = page;
  14.         if (start + amount >= COFFEE_PAGE_COUNT) //如果剩下的页面数不足以分配,则跳出循环,直接返回INVALID_PAGE
  15.         {
  16.           break;
  17.         }
  18.       }

  19.       /***若找到start+amount<=next_file(),则返回start***/
  20.       page = next_file(page, &hdr); //返回下一个文件占用Coffee页的第一页(从page页开始)
  21.       if (start + amount <= page)
  22.       {
  23.         if (start == *next_free)
  24.         {
  25.           *next_free = start + amount; //找到空闲页,分配成功。更新next_free
  26.         }
  27.         return start;
  28.       }
  29.     }
  30.     else //如果文件正在使用(即A位为1),跳到下一个文件
  31.     {
  32.       start = INVALID_PAGE;
  33.       page = next_file(page, &hdr);
  34.     }
  35.   }
  36.   return INVALID_PAGE;
  37. }

3.2 write_header

write_header将file_header写入物理FLASH,源代码如下:

  1. static void write_header(struct file_header *hdr, coffee_page_t page)
  2. {
  3.   hdr->flags |= HDR_FLAG_VALID; //将file_header的flags中的V位置1,即标记文件头是完整的
  4.   COFFEE_WRITE(hdr, sizeof(*hdr), page *COFFEE_PAGE_SIZE);
  5. }

  6. #define HDR_FLAG_VALID 0x1
  7. #define COFFEE_WRITE(buf, size, offset) stm32_flash_write(COFFEE_START + offset, buf, size)


本文图1~3源文件如下:

 find_contiguous_pages函数示意图.rar  

 page_count函数示意图.rar   

阅读(155) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~