在打开一个文件的过程中,文件系统所要做的事情就是找到指定文件的inode,所以在这个过
程中会有磁盘元数据读操作。一旦文件所属的inode被找到,那么需要在内存中初始化一个描
述被打开文件的对象,这个对象就是file。所以,dentry,inode之类的信息在磁盘上是永久存
储的,file对象是在内存中是临时存在的,它会随着文件的创建而生成,随着文件的关闭而消
亡。
在Linux系统中文件类型是多种多样的,一个USB设备也是一个文件,一个普通的Word文档也
是一个文件,一个RAID设备也是一个文件。虽然他们在系统中都是文件,但是,他们的操作方
式是截然不同的。USB设备可能需要采用字符设备的方式和设备驱动交互;RAID设备可能需要
采用块设备的方式和设备驱动进行交互;普通Word文件需要通过cache机制进行性能优化。所
以,虽然都是文件,但是,文件表面下的这些设备是不相同的,需要采用的操作方法显然是截
然不同的。作为一个通用的文件系统,如何封装不同的底层设备是需要考虑的问题。在Linux
中,为了达到这个目的,推出了VFS概念。在VFS层次对用户接口进行了统一封装,并且实现了
通用的文件操作功能。例如打开一个文件和关闭一个文件的操作都是相同的。在VFS下面会有
针对不同需求的具体文件系统,例如针对Word文档可以采用EXT3文件系统进行操作,对于磁
盘设备可以采用bdev块设备文件系统进行操作。在打开一个文件,对文件对象file进行初始化
的时候,会将具体的文件系统操作方法关联到file->f_op和file->f_mapping对象。在后面的读写
过程中,我们将会看到针对不同的文件类型,会采用不同的f_op和f_mapping方法。
当一个文件被打开之后,用户态程序就可以得到一个文件对象,即文件句柄。一旦获取文件句
柄之后就可以对其进行读写了。用户态的读写函数write对应内核空间的sys_write例程。通过系
统调用可以陷入sys_write。Sys_write函数在VFS层做的工作及其有限,其会调用文件对象中指
定的操作函数file->f_op->write。对于不同的文件系统,file->f_op->write指向的操作函数是不同
的。对于EXT3文件系统而言,在文件inode初始化的时候会指定ext3_file_operaons操作方法
集。该方法集说明了EXT3文件系统的读写操作方法,说明如下:
const struct file_operations ext3_file_operations = {
.llseek= generic_file_llseek,
.read= do_sync_read,
.write= do_sync_write,
.aio_read= generic_file_aio_read,
.aio_write= ext3_file_write,
.ioctl= ext3_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl= ext3_compat_ioctl,
#endif
.mmap= generic_file_mmap,
.open= generic_file_open,
.release= ext3_release_file,
.fsync= ext3_sync_file,
.splice_read= generic_file_splice_read,
.splice_write= generic_file_splice_write,
};
如果文件设备是一个USB设备,并且采用的是字符设备的接口,那么在初始化文件inode的时候
会调用init_special_inode初始化这些特殊的设备文件。对于字符设备会采用默认的def_chr_fops
方法集,对于块设备会采用def_blk_fops方法集。不同的文件类型会调用各自的方法集。下面
章节会对EXT3文件写和块设备文件写进行详细阐述。由于字符设备类型比较简单,在此进行
简单说明。
Def_chr_fops方法集其实就定义了open方法,其它的方法都没有定义。其实字符设备的操作方
法都需要字符设备驱动程序自己定义,每个设备驱动程序都需要定义自己的write、read、
open和close方法,这些方法保存在字符设备对象中。当用户调用文件系统接口open函数打开
指定字符设备文件时,VFS会通过上述讲述的sys_open函数找到设备文件inode中保存的
def_chr_fops方法,并且执行该方法中的open函数(chrdev_open),chrdev_open函数完成的
一个重要功能就是将文件对象file中采用的方法替换成驱动程序设定的设备操作方法。完成这
个偷梁换柱的代码是:
filp->f_op = fops_get(p->ops)
相关文档
评论