本文重在讲述file
和file_header
的联系与区别,首先分别分析了file
与file_header
结构体,而后介绍两者的联系,最后分析几组易混的成员变量,包括page
与log_page
、file
与file_header
的flags
、deprecated_eof_hint
与end
、record_count
与log_records
。
1. file
结构体file
源码如下:
struct file
{
cfs_offset_t end;
coffee_page_t page;
coffee_page_t max_pages;
int16_t record_count;
uint8_t references;
uint8_t flags;
};
end
指向存放文件的最后一个字节的偏移量,当打开一个文件时,Coffee用蛮力法找到文件末尾(file_header
是不存储文件末尾的位置,因为文件长度经常改变)。详情见博文《打开文件cfs_open》。
page
指向物理文件的第一页,即存放文件元数据file_header
的页。通常的用法如下:
//用法1:static struct file *find_file(const char *name)
read_header(&hdr, coffee_files[i].page); //读取文件元数据file_header到hdr
//用法2:static int remove_by_page(coffee_page_t page, int remove_log, int close_fds, int gc_allowed)
coffee_files[i].page = INVALID_PAGE;//将file->page标识为INVALID_PAGE,表示该缓存file项可分配给其他文件用
#define INVALID_PAGE ((coffee_page_t)-1) //注1
max_pages
max_pages
含义跟file_header->max_pages
相同,即为该文件保留的页面数,加载文件load_file
函数将file_headermax_pages
赋给file
的max_pages
。
record_count
record_count
表示实际的微日志记录数量,不同于file_header
的log_records
。
references
Contiki提供类多线程编程环境,会有这样的情况,多个线程同时打开一个文件,需要记录引用次数。打开文件cfs_open
引用次数++
,关闭文件cfs_close
引用次数--
。
flags
flags
两种取值:0
和COFFEE_FILE_MODIFIED
,即标识该文件是否含有微日志文件。详情见博文《flags标志位》。
注:
这里用了个小技巧,使得程度更具移植性。因为在cfs-coffee.arch.h
,coffee_page_t
有可能是8位,也有可能是16位,源码如下:
#if COFFEE_PAGES <= 127
#define coffee_page_t u8_t
#elif COFFEE_PAGES <= 0x7FFF
#define coffee_page_t u16_t
#endif
把INVALID_PAGE
定义为coffee_page_t-1
,如果coffee_page_t
是8位,那么INVALID_PAGE
为0xFF
,如果coffee_page_t
是16位,那么INVALID_PAGE
为0xFFFF
,可以自适应coffee_page_t
类型的变化,更具移植性。关于这个技巧可参见博文《具有可移植的无穷大定义》。
2. file_header
结构体file_header
源码如下:
struct file_header
{
coffee_page_t log_page;
uint16_t log_records;
uint16_t log_record_size;
coffee_page_t max_pages;
uint8_t deprecated_eof_hint;
uint8_t flags;
char name[COFFEE_NAME_LENGTH];
};
log_page
不同于file->page
,log_page
指向微日志的第一页(如果配置了微日志)。
log_records
表示日志可以容纳的记录数量(log records denotes the number of records that the log can hold)
log_record_size
log_record_size
表示微日志文件大小,如果为0,则设置成默认值(见cfs-coffee-arch.h
文件配置#define COFFEE_LOG_SIZE 128
)。
max_pages
max_pages
指为文件保留着页面数(The max pages field specifies the amount of pages that have been reserved for the file)
deprecated_eof_hint
因为文件头不能存储文件长度(缘于文件长度经常变化着),所以用deprecated_eof_hint
指向文件的最后一个字节。文件关闭时,如果文件长度增加则需更新deprecated_eof_hint
。事实上,纵观源码,deprecated_eof_hint
都没用到,而是用file->end
存储文件末尾的位置(打开文件时,用蛮力法扫描得到该文件的末尾位置)。
flags
flags
反映了文件当前状态(The flag field tells us the current state),用了6个位,分别是ALOMIV
,详情请见博文《flags标志位》。
name
文件名,其中文件长度COFFEE_NAME_LENGTH
可配置,在contiki/cpu/平台(如arm/stm32f103)/cfs-coffee-arch.h
。
3. file与file_header联系
3.1 联系
file_header
存放物理文件的元数据(即描述该物理文件),而file
可以理解成物理文件元数据的内存表示,但又不是完全缓存,因为file
与file_header
成员变量差别甚大。为了提高性能,Contiki具体是这样做的:所有打开的文件都通过文件描述符fd
(非负整数,类似于Linux)引用,将file_desc
组织成一个数组file_desc coffee_fd_set[COFFEE_FD_SET_SIZE]
,每个fd
对应于一个file_desc
(以数组下标的形式),每个file_desc
对应于一个file
。通过数组file_desc coffee_fd_set[COFFEE_FD_SET_SIZE]
下标(即文件描述符fd
)就可以访问file
,而file
存储物理文件的一些信息,这样也就可以访问物理文件了。
3.2 page与log_page
file_page
是指向物理文件的第一页,即存放文件元数据file_header
的页。而file_header->log_pages
指向微日志的第一页(如果配置了微日志)。
3.3 file与file_header的flags
file_header->flags
记录的是整个物理文件的相关信息(即元数据),而file->flags只用来标识文件是否有微日志存在。详情请见博文《flags标志位》。
3.4 deprecated_eof_hint与end
file_header->deprecated_eof_hint
与file->end
都存储文件的末尾的位置,但事实上,纵观源码,file_header->deprecated_eof_hint
是没有用到的(这点与官方论文有出入),也就是说物理上没有存储文件的末尾位置,而是每次打开文件时,通过蛮力扫描,得到文件末尾位置,存入file->end
。
3.5 record_count与log_records
这两个关系,我也不是搞得很清楚。给我感觉就是,file_header->log_records
是指文件创建时的指定的微日志记录数量,默认的情况会是COFFEE_LOG_SIZE/log_record_size
(log_record_size
默认情况会是COFFEE_PAGE_SIZE
,见adjust_log_config
函数)。而file->record_count
是实际上的微日志记录数量。