1. yaffs2文件系统
yaffs2文件系统是用于NAND FLASH的文件系统. 由Android中增加的. 在原来的Linux2.2.26系统中没有这个文件系统.
2. 应用层的文件操作
应用层的文件操作为fopen,fread, fwrite, 常用的文件操作函数. 下方操作我们以fread为例.
struct file的定义在/kernel/include/linux/fs.h, 这个结构要记住.
struct file{
…
const struct file_operations *f_op;
…
}
3. read的系统调用
SYSCALL_DEFINE3(read,unsigned int, fd, char __user *, buf, size_t, count)
{
struct file *file;
ssize_t ret = -EBADF;
int fput_needed;
file = fget_light(fd, &fput_needed);
if (file) {
loff_t pos = file_pos_read(file);
ret = vfs_read(file, buf, count,&pos);
file_pos_write(file, pos);
fput_light(file, fput_needed);
}
return ret;
}
在系统调用中使用了函数vfs_read.此函数的实现在/kernel/fs/read_write.c中.
在vfs_read中, 有以下调用语句:
struct file *file;
if (file->f_op->read)
ret = file->f_op->read(file,buf, count, pos);
else
ret = do_sync_read(file, buf, count,pos);
即, 如果file_operations类型的指针f_op有具体实现时, 使用具体实现; 否则直接使用do_sync_read.
4. yaffs2中的对file_operations实现
file_operations在/kernel/fs/yaffs2/yaffs_vfs.c下有相应实现.
static conststruct file_operations yaffs_file_operations = {
.read = do_sync_read,
.write = do_sync_write,
.aio_read = generic_file_aio_read,
.aio_write = generic_file_aio_write,
.mmap = generic_file_mmap,
.flush = yaffs_file_flush,
.fsync = yaffs_sync_object,
.splice_read = generic_file_splice_read,
.splice_write = generic_file_splice_write,
.llseek = generic_file_llseek,
};
在yaffs2中, 依然使用了do_sync_read. 但是其他如目录的管理等, 则是yaffs2的内容了.
在do_sync_read中, 有对yaffs2中的代码的调用, 即:
ret =filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos);
.aio_read =generic_file_aio_read, 说明依然不是在yaffs2中实现的.
5. mm中对generic_file_aio_read的实现
generic_file_aio_read的定义是在/kernel/mm/filemap.c中实现的.
其中有调用do_generic_file_read(filp,ppos, &desc, file_read_actor);
do_generic_file_read是一个内容比较长的函数, 其中有page同步的调用. 大意是, 如果该文件的页面已在缓存中, 则直接读取; 否则, 先从设备中将该页面读取到缓存,然后再读取.
当page_ok时, 执行读取操作.
注意页面缓存这文件读取过程中的作用.
6. 复杂函数do_generic_file_read
这个复杂函数的执行过程如下:
A. 根据address_space和索引去查找页面;
B. 如果没有找到页面, 则跳转到no_cached_page去进行页面缓存; 具体过程是, 先分配一个页面, 加入address_space链表, 然后error =mapping->a_ops->readpage(filp, page);然后执行PageUptodate.
C. 如果找到了, 执行PageUptodate检查;
D. 如果page_ok, 则调用actor函数指针, 将文件内容从页面缓存读取到用户缓冲区. 这里的actor函数指针的值由do_generic_file_read传入,实际上是file_read_actor.
intfile_read_actor(read_descriptor_t *desc, struct page *page,
unsigned long offset, unsigned longsize)
{
char *kaddr;
unsigned long left, count = desc->count;
if (size > count)
size = count;
/*
*Faults on the destination of a read are common, so do it before
*taking the kmap.
*/
if(!fault_in_pages_writeable(desc->arg.buf, size)) {
kaddr = kmap_atomic(page, KM_USER0);
left =__copy_to_user_inatomic(desc->arg.buf,
kaddr + offset, size);
kunmap_atomic(kaddr, KM_USER0);
if (left == 0)
goto success;
}
/* Do it the slow way */
kaddr = kmap(page);
left = __copy_to_user(desc->arg.buf, kaddr+ offset, size);
kunmap(page);
if (left) {
size -= left;
desc->error = -EFAULT;
}
success:
desc->count = count - size;
desc->written += size;
desc->arg.buf += size;
return size;
}
7.与nandflash驱动的结合
与驱动的结合部分在address_space,它将设备中的内容读取到缓存中. 它的定义在yaffs2中.
static structaddress_space_operations yaffs_file_address_operations = {
.readpage= yaffs_readpage,
.writepage= yaffs_writepage,
.write_begin= yaffs_write_begin,
.write_end= yaffs_write_end,
};
static intyaffs_readpage(struct file *f, struct page *pg)
{
int ret;
yaffs_trace(YAFFS_TRACE_OS,"yaffs_readpage");
ret = yaffs_readpage_unlock(f, pg);
yaffs_trace(YAFFS_TRACE_OS,"yaffs_readpage done");
return ret;
}
yaffs_readpage_unlock调用了yaffs_readpage_nolock,后者又调用了yaffs_file_rd.
yaffs_file_rd在/kernel/fs/yaffs2/yaffs_guts.c中实现.
yaffs_file_rd调用了yaffs_rd_data_obj, 后者调用了yaffs_rd_chunk_tags_nand.
yaffs_rd_chunk_tags_nand在/kernel/fs/yaffs2/yaffs_nand.c中实现.
其中有
if(dev->param.read_chunk_tags_fn)
result =
dev->param.read_chunk_tags_fn(dev, realigned_chunk, buffer,
tags);
param类型为struct yaffs_param. 在yaffs_vfs.c中yaffs_internal_read_super函数中被赋值.
param->read_chunk_tags_fn = nandmtd2_read_chunk_tags;
nandmtd2_read_chunk_tags被定义在yaffs_mtdif2.c中.
通过struct mtd_info *mtd = yaffs_dev_to_mtd(dev);, nandmtd2_read_chunk_tags调用了如下语句:
retval = mtd->read(mtd, addr,dev->param.total_bytes_per_chunk,
&dummy, data);
MTD(memory technology device内存技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。
所有组成MTD原始设备的Flash芯片必须是同类型(无论是interleave还是地址相连),在描述MTD原始设备数据结构中采用同一结构描述组成Flash芯片。每个MTD原始设备有一个mtd_info结构,其中的priv指针指向一个map_info结构,map_info结构中的fldrv_priv指向一个cfi_private结构,cfi_private结构的cfiq指针指向一个cfi_ident结构,chips指针指向一个flchip结构的数组。其中mtd_info、map_info和cfi_private结构用于描述MTD原始设备,因为组成MTD原始设备的NOR型Flash相同,cfi_ident结构用于描述Flash芯片信息;而flchip结构用于描述每个Flash芯片专有信息。