摘要:
在Coffee,没有单独的文件创建函数。在name对应文件不存在的情况下,为cfs_open指定flags(取000或010或011或100或110或111)可创建新文件,本文深入源码并用多组示意图分析Coffee创建文件技术细节。包括find_file、page_count、reserve、find_contiguous_pages、write_header。
一、概述
在Linux,用以下两种方式创建一个新文件(两种方式等价):
- int creat(const char *pathname, mode_t mode); //不足之处是它以只写的方式打开创建的文件
-
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》,以下选取与文件创建相关代码进行分析,如下:
- fdp->file = find_file(name); //见2.1
-
if (fdp->file == NULL) //物理上没有name对应的文件file,则试图创建之
-
{
-
if ((flags &(CFS_READ | CFS_WRITE)) == CFS_READ)//检查flags是否符合创建文件要求,见2.2
-
{
-
return -1;
-
}
-
-
fdp->file = reserve(name, page_count(COFFEE_DYN_SIZE), 1, 0); //见三
-
if (fdp->file == NULL)
-
{
-
return - 1;
-
}
-
-
fdp->file->end = 0; //新创建的空文件,文件末尾自然是0
-
}
-
-
/*此处略去与文件创建无关的4行代码*/
-
-
fdp->flags |= flags;
-
fdp->offset = flags &CFS_APPEND ? fdp->file->end: 0; //如果flags没有设置CFS_APPEND,则文件偏移量设为0
-
fdp->file->references++; //文件引用次数加1
-
-
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,才有可能创建新文件,源代码如下:
- if ((flags &(CFS_READ | CFS_WRITE)) == CFS_READ) //若flags仅仅是CFS_READ则返回-1,即不创建新文件
-
{
-
return - 1;
-
}
那么,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,源代码如下:
- //page_count(COFFEE_DYN_SIZE)
-
#define COFFEE_DYN_SIZE (COFFEE_PAGE_SIZE*1) //在cfs_coffee_arch.h可以配置
-
-
static coffee_page_t page_count(cfs_offset_t size)
-
{
-
return (size + sizeof(struct file_header) + COFFEE_PAGE_SIZE - 1) /COFFEE_PAGE_SIZE;
-
}
三、创建文件主体函数reserve
reserve是创建新文件的主体函数,首先进行参数验证,接着查看物理FLASH是否有连续pages空闲页,若有,则返回该文件即将占有页的第一页。否则,调用Coffee垃圾回收,再次查看物理FLASH是否有连续pages空闲页,如果还没有就返回NULL。
值得注意的是,即便文件创建成功也可能返回-1(当coffee_files[COFFEE_MAX_OPEN_FILES]数组没有可用项时,新创建的文件并没有缓存到数组coffee_files)。源代码如下:
- //fdp->file = reserve(name, page_count(COFFEE_DYN_SIZE), 1, 0);
-
static struct file *reserve(const char *name, coffee_page_t pages,int allow_duplicates,unsigned flags)
-
{
-
struct file_header hdr;
-
coffee_page_t page;
-
struct file *file;
-
-
if (!allow_duplicates && find_file(name) != NULL) //参数验证,在本例中,!allow_duplicates为0,条件为假
-
{
-
return NULL;
-
}
-
-
page = find_contiguous_pages(pages); //查看物理FLASH是否有连续pages空闲页,若有,则返回该文件即将占有页的第一页。见3.1
-
if (page == INVALID_PAGE) //没有可用的页,尝试着调用垃圾回收
-
{
-
if (*gc_wait)
-
{
-
return NULL;
-
}
-
collect_garbage(GC_GREEDY); //垃圾回收
-
page = find_contiguous_pages(pages); //再次查看物理FLASH是否有连续pages空闲页,见3.1
-
if (page == INVALID_PAGE) //如果垃圾回收后还没找到可用的页,就返回NULL
-
{
-
*gc_wait = 1;
-
return NULL;
-
}
-
}
-
-
memset(&hdr, 0, sizeof(hdr)); //将hdr成员变量初始化为0
-
memcpy(hdr.name, name, sizeof(hdr.name) - 1); //给file_header的成员变量name赋值
-
hdr.max_pages = pages; //实参pages,在本例等于2
-
hdr.flags = HDR_FLAG_ALLOCATED | flags; //file_header的flags的A位置1,表示文件正在使用
-
write_header(&hdr, page); //将file_header写入物理FLASH,见3.2
-
-
PRINTF("Coffee: Reserved %u pages starting from %u for file %s\n", pages, page, name);
-
-
file = load_file(page, &hdr); //如果coffee_files[COFFEE_MAX_OPEN_FILES]数组没有可用项,就返回NULL了(但此时文件已创建完毕)
-
if (file != NULL)
-
{
-
file->end = 0;
-
}
-
-
return file;
-
}
3.1 find_contiguous_pages
find_contiguous_pages从next_free(全局变量,指向下一个空闲页)指向的页面开始查找,若找到start+amount<=next_file(),则返回start,否则返回INVALID_PAGE(说明没有足够空闲页甚至没有空闲页供使用)。
图3 find_contiguous_pages函数示意图
find_contiguous_pages函数源代码如下:
- static coffee_page_t find_contiguous_pages(coffee_page_t amount)
-
{
-
coffee_page_t page, start;
-
struct file_header hdr;
-
-
start = INVALID_PAGE; //初始化start,#define INVALID_PAGE ((coffee_page_t)-1)
-
-
for (page = *next_free; page < COFFEE_PAGE_COUNT;) //从next_free指向的页面开始查找
-
{
-
read_header(&hdr, page);
-
if (HDR_FREE(hdr)) //判断file_header的flags中A(Allocated)位,若A位为0,则HDR_FREE(hdr)返回真,即页面是空闲的
-
{
-
if (start == INVALID_PAGE)
-
{
-
start = page;
-
if (start + amount >= COFFEE_PAGE_COUNT) //如果剩下的页面数不足以分配,则跳出循环,直接返回INVALID_PAGE
-
{
-
break;
-
}
-
}
-
-
/***若找到start+amount<=next_file(),则返回start***/
-
page = next_file(page, &hdr); //返回下一个文件占用Coffee页的第一页(从page页开始)
-
if (start + amount <= page)
-
{
-
if (start == *next_free)
-
{
-
*next_free = start + amount; //找到空闲页,分配成功。更新next_free
-
}
-
return start;
-
}
-
}
-
else //如果文件正在使用(即A位为1),跳到下一个文件
-
{
-
start = INVALID_PAGE;
-
page = next_file(page, &hdr);
-
}
-
}
-
return INVALID_PAGE;
-
}
3.2 write_header
write_header将file_header写入物理FLASH,源代码如下:
- static void write_header(struct file_header *hdr, coffee_page_t page)
-
{
-
hdr->flags |= HDR_FLAG_VALID; //将file_header的flags中的V位置1,即标记文件头是完整的
-
COFFEE_WRITE(hdr, sizeof(*hdr), page *COFFEE_PAGE_SIZE);
-
}
-
-
#define HDR_FLAG_VALID 0x1
-
#define COFFEE_WRITE(buf, size, offset) stm32_flash_write(COFFEE_START + offset, buf, size)
本文图1~3源文件如下:
find_contiguous_pages函数示意图.rar
page_count函数示意图.rar
阅读(3811) | 评论(0) | 转发(1) |