《Linux内核设计与实现》读书笔记—虚拟文件系统

VFS

  • VFS作为内核子系统,位用户空间提供文件和文件系统相关的接口,如下图所示:

  • VFS使得用户可以直接使用open(),read(),write()这样的系统调用而无需考虑具体文件系统和实际的物理介质。
  • 之所以可以这样使用这种通用接口,是因为内核在底层文件系统接口上建立了一个抽象层,该抽象层提供了一个通用文件系统模型,该模型囊括了任何文件系统的常用功能集和行为。
  • 以用户空间的代码 ret =write(fd,buf,len) 为例,该系统调用的实际行为如下所示:

UNIX文件系统

  • Unix使用了四种和文件系统相关的传统抽象概念:文件、目录项、索引节点和挂在点。
  • 从本质上讲,文件系统是特殊的数据分层存储结构,它包含文件、目录和相关的控制信息。
  • 文件系统的通用操作包含创建、删除和安装等;文件的通用操作包括读,写,创建,删除等。
  • Unix文件系统将文件的相关信息和文件本身这两个概念加以区分,文件相关信息(例如控制权限,大小,拥有者,创建者)被存储在一个单独的数据结构中,被称为inode。
  • inode和文件系统的控制信息一起被存储在超级块中,也被称为文件系统数据元。

VFS对象及其数据结构

  • VFS中有四个主要的对象类型,它们分别是:超级块对象,代表一个具体的已安装文件系统;索引节点对象,代表一个具体文件;目录项对象,代表一个目录项;文件对象,代表由进程打开的文件。
  • 超级块对象的结构体为super_block,其操作对象为super_operations,包括内核针对特定文件系统所能调用的方法,比如write_inode()和sync_fs()等方法。
  • 索引节点对象的结构体为inode,其操作对象inode_operations,包括内核针对特定文件所能调用的方法,比如create()和link()等方法。
  • 目录项对象的结构体为dentry,其操作对象dentry_operations,包括内核针对特定目录所能调用的方法,比如d_delete()和d_compare()等方法。
  • 文件对象的结构体为file,其操作对象为file_operations,包括进程针对已打开的文件所能调用的方法,比如read()和write()等方法。

超级块对象

  • 超级块对象的结构体为super_block,超级块对象通过alloc_super()函数创建并初始化。在文件系统安装时,文件系统会调用该函数以便从磁盘读取文件系统超级块,并且将其信息填充到内存中的超级块对象中。
  • super_block中最重要的成员变量为struct super_operations* s_op,其包含了超级块的操作函数表。
  • struct inode* alloc_inode(struct super_block* sb)在给定的超级块下创建和初始化一个新的索引节点对象
  • void destroy_inode(struct inode* inode)用于释放给定的索引节点
  • void dirty_inode(struct inode* inode)在索引节点被修改时会调用此函数。日志文件系统执行该函数进行日志更新。
  • void write_inode(struct inode* inode,int wait)用于将给定的索引节点写入磁盘,wait参数指明写操作是否需要同步。
  • void drop_inode(struct inode* inode)在最后一个指向索引节点的引用被释放后,VFS会调用该函数。
  • void delete_inode(struct inode* inode)用于从磁盘上删除给定的索引节点。
  • void put_super(struct super_block* sb)用给定的超级块更新磁盘上的超级块。VFS通过该函数对内存中的超级块和磁盘中的超级块进行同步,调用者必须一直持有s_lock锁。
  • int sync_fs(struct super_block *sb,int wait)使文件系统的数据元与磁盘上的文件系统同步,wait参数指定操作是否同步。
  • void write_super_lockfs(struct super_block* sb)首先禁止对文件系统做改变,再使用给定的超级块更新磁盘上的超级块。
  • void unlockfs(struct super_block *sb)对文件系统解除锁定,它是write_super_lockfs的逆操作。
  • int statfs(struct super_block* sb, struct statfs* statfs)VFS通过调用该函数获取文件系统状态,指定文件系统相关的统计信息将放置在statfs中。
  • int remount_fs(struct super_block* sb,int* flags,char*data)当指定新的安装选项重新安装文件系统时,VFS会调用该函数,调用者必须一直持有s_lock锁。、
  • void clear_inode(struct inode* inode) VFS调用该函数释放索引节点,并清空包含相关数据的所有页面。
  • void umount_begin(struct super_block* sb) VFS调用该函数卸载超级块。

索引节点对象

  • 索引节点对象的结构体为inode,其包含了内核在操作文件或目录时需要的全部信息。
  • 一个索引节点代表文件系统中的一个文件,它可以是普通文件,目录文件,设备文件或者管道文件这样的特殊文件。
  • inode中最重要的成员变量为struct inode_operations* i_op,其包含了索引节点的操作函数表。
  • int create(struct inode* dir,struct dentry* denrty,int mode) 该函数被系统调用create()和open()来调用,从而为dentry对象创建一个新的索引节点,在创建时使用mode指定初始模式。
  • struct dentry* lookup(struct inode* dir,struct dentry* dentry) 该函数在特定目录中寻找索引节点,该索引节点需要对应于dentry中给出的文件名。
  • int link(struct dentry* old_dentry,struct inode* dir,struct dentry* dentry) 该函数被系统调用link()调用,用来创建硬链接。硬链接名称由dentry参数指定,连接对象是dir目录中old_dentry目录项所代表的文件。
  • int unlink(struct inode* dir,struct dentry* dentry) 该函数被系统调用unlink()调用,从目录dir中删除由目录项dentry指定的索引节点对象。
  • int symlink(struct inode* dir,struct dentry* dentry,const char* symname) 该函数被系统调用symlik()调用,创建符号链接。该符号连接名称由symname指定,连接对象是dir目录中的dentry目录项。
  • int mkdir(struct inode* dir,struct dentry* dentry,int mode) 该函数被系统调用mkdir()调用,创建一个新目录。创建时使用mode指定初始模式。
  • int rmdir(struct inode* dir,struct dentry* dentry) 删除dir目录中的dentry目录项代表的文件。
  • int mknod(struct inode* dir,struct dentry* dentry,int mode,dev_t rdev) 该函数被系统调用mknod()调用,创建特殊文件(设备文件,命名管道或套接字)。要创建的文件放在dir目录中,目录项为dentry,关联的设备为rdev,初始权限由mode指定。
  • int rename(struct inode* old_dir,struct dentry* old_dentry,struct inode* new_dir,struct dentry* new_dentry) VFS调用该函数来移动文件,文件源路径再old_dir目录中,源文件由old_dentry目录项指定,目标路径在new_dir目录中,目标文件由new_dentry指定。
  • int readlink(struct dentry* dentry, char* buffer,int buflen) 该函数被系统调用readlink()调用,拷贝数据到特定的缓冲buffer中。拷贝的数据来自dentry指定的符号连接,拷贝大小最大可达buflen字节。
  • int follow_link(struct dentry* dentry,struct nameidata* nd)该函数由VFS调用,从一个符号连接查找它指向的索引节点。由dentry指向的连接被解析,其结果存放在由nd指向的nameidata结构体中。
  • int put_link(struct dentry* dentry,struct nameidata* nd)在follow_link()调用之后,该函数由VFS调用进行清楚工作。
  • void truncate(struct inode* inode)该函数由VFS调用,修改文件的大小。在调用前,索引节点的i_size项必须设置为预期的大小。
  • int permission(struct inode* inode,int mask) 该函数用来检查给定的inode所代表的文件是否允许特定的访问模式。
  • int setattr(struct dentry* dentry,struct iattr* attr)该函数被nofity_change调用,在修改索引节点后,通知发生了“改变事件”。
  • int getattr(struct vfsmount* mnt,struct dentry* dentry,struct kstat* stat) 在通知索引节点需要从磁盘中更新时,VFS会调用该函数。
  • int setxattr(struct dentry* dentry,const char* name,const void* value,size_t size, int flags) 该函数由VFS调用,给dentry指定的文件设置扩展属性。属性名为name,值为value。
  • ssize_t getxattr(struct dentry* dentry,const char* name,const void* value,size_t size) 该函数由VFS调用,向value中拷贝给定文件的扩展属性name对应的数值。
  • ssize_t listxattr(struct dentry* dentry,char* list,size_t size) 该函数将特定文件的所有属性列表拷贝到一个缓冲列表中。
  • int removexattr(struct dentry* dentry,const char* name) 该函数从给定文件中删除指定的属性。

目录项对象

  • VFS把目录当作文件对待,所以/bin/vi中,bin和vi都属于文件,bin是目录文件,vi是普通文件,在文件系统中都有索引节点表示。
  • 但是VFS经常需要执行目录相关的操作,比如路径名查找等,为了方便查找操作,VFS引入了目录项的概念。以上述路径为例,/,bin,vi都属于目录项对象。
  • 目录项对象的结构为dentry,与super_block和inode不同,目录项对象不需要保存在磁盘中,而是VFS根据目录名现场创建它,因此目录项对象没有修改标志相关的成员变量。
  • 目录项对象的有效状态为三种:被使用,未被使用和负状态。
  • 一个被使用的目录项对应一个有效的索引节点(其成员变量d_inode指向的索引节点),且该对象存在一个或多个使用者(其成员变量d_count > 0)。
  • 一个未被使用的目录项对应一个有效的索引节点(其成员变量d_inode指向的索引节点),且该对象不存在使用者(其成员变量d_count == 0)。
  • 一个负状态的目录项没有索引节点(其成员变量d_inode == NULL)。
  • 内核使用目录项缓存减少解析路径名生成目录项的开销,目录缓存包括三个重要的部分:“被使用的”目录项链表,“最近被使用”的目录项双向链表和散列表。
  • 目录项对象dentry的成员变量struct dentry_operations* d_op,指明了VFS操作目录项的所有方法。
  • int d_revalidate(struct dentry* dentry,struct nameidata* nid) 该函数判断目录对象是否有效。
  • int d_hash(struct dentry* dentry,struct qstr* name) 该函数为目录项生成散列函值。
  • int d_cmpare(struct dentry* dentry,struct qstr* name1,struct qstr* name2) VFS调用该函数来比较name1和name2这两个文件名。
  • int d_delete(struct dentry* dentry) 当目录项对象的d_count为0时,调用该函数。使用该函数需要加dcache_lock锁和目录项的d_lock。
  • void d_release(struct dentry* dentry) 当目录项对象将要被释放时,调用该函数。默认情况下它什么也不做。
  • void d_iput(struct dentry* dentry,struct inode* inode) 当目录项对象丢失了其相关的索引节点时,VFS调用该函数。默认情况下VFS会调用iput()函数释放索引节点。

文件对象

  • 文件对象表示某个进程已打开的文件,是已打开的文件在内存中的表示。文件对象由open()系统调用创建,由close()系统调用撤销。
  • 多个进程可以同时打开和操作同一个文件,所以同一个文件可能存在多个对应的文件对象。但是一个文件对应的目录项对象和索引节点是唯一的
  • 文件对象类似于目录下对象,实际上并没有对应的磁盘数据,文件对象实在打开文件时现场创建的,因此文件对象的结构体中没有代表其对象是否修改的标志。
  • 文件对象的结构体为file,成员变量struct file_operation* f_op,包含了文件相关的文件操作表。
  • loff_t lleek(struct file* file,loff_t offset,int origin) 该函数用于更新偏移量指针,由系统调用lleek()调用它。
  • ssize_t read(struct file* file,char* buf,size_t count,loff_t * offset) 该函数从给定文件的offset偏移处读取count字节的数据到buf中,同时更新文件指针,由系统调用read()调用。
  • ssize_t aio_read(struct file* file,char* buf,size_t count,loff_t * offset) 该函数从iocb描述的文件里,以同步方式读取count字节的数据到buf中。由系统调用aio_read()调用它。
  • ssize_t write(struct file* file,const char* buf,size_t count,loff_t * offset) 该函数从给定的buf中读取count字节的数据,写入给定文件的offset偏移出,同时更新文件指针。由系统调用write()调用它。
  • ssize_t aio_write(struct file* file,const char* buf,size_t count,loff_t * offset) 该函数以同步方式从给定的buf中取出count字节的数据,写入由iocb描述的文件中,由系统调用aio_write()调用它。
  • int readdir(struct file* file,void* dirent, filldir_t filldir) 该函数返回目录列表中的下一个目录,由系统调用readdir()调用它。
  • unsigned int poll(struct file* file,struct poll_table_struct* poll_table) 该函数睡眠等待给定文件活动,由系统调用poll()调用它。
  • int ioctl(struct inode* inode,struct file* file,unsigned int cmd,unsigned long arg) 该函数用来给设备发送命令参数对。当文件是一个被打开的设备节点时,可以通过它进行设置操作。由系统调用ioctl调用它,调用者必须持有BKL。
  • int unlocked_ioctl(struct file* file,unsigned int cmd,unsigned long arg) 该函数实现与Ioctl()类似的功能,只不过不需要调用者持有BKL。
  • int compat_ioctl(struct file* file,unsigned int cmd,unsigned long arg) 该函数是ioctl()的可移植变种,该函数被设计成即使在64位的体系结构上,对32位也是安全的,它可以进行必要的字大小转换。compat_ioctl()也不需要调用者持有BKL。
  • int mmap(struct file* file,struct vm_area_struct * vma) 该函数将给定的文件映射到指定的地址空间上,由系统调用mmap()调用它。
  • int open(struct inode* inode,struct file* file) 该函数创建一个新的文件对象,并将它和相应的索引节点对象关联起来,由系统调用open()调用它。
  • int flush(struct file* file) 当已打开文件的引用计数减少时,该函数被VFS调用。它的作用根据具体文件系统而定。
  • int release(struct inode* inode,struct file* file) 当文件的最后一个引用被注销时,该函数会被VFS调用。它的作用根据具体的文件系统而定。
  • int fsync(struct file* file,struct dentry* dentry,int datasync) 将给定文件的所有缓存数据写回磁盘,由系统调用fsync()调用它。
  • int aio_fsync(struct kiocb* iocb,int datasync) 将iocb描述的文件的所有缓存写回磁盘,由系统调用aio_fsync()调用它。
  • int fasync(int fd,struct file* file,int on) 该函数用于打开或关闭异步I/O的通告信号。
  • int lock(struct file* file,int cmd,struct file_lock* lock)该函数用于给指定文件上锁。
  • ssize_t readv(struct file* file,const struct iovec *vector,unsigned long count,loff_t* offset) 该函数从给定文件中读取数据,并将其写入由vector描述的count个缓冲中去,同时增加文件的偏移量。由系统调用readv()调用它。
  • ssize_t writev(struct file* file,const struct iovec *vector,unsigned long count,loff_t* offset) 该函数酱油vector描述的count个缓冲中的数据写入到由file指定的文件中去,同时减小文件的偏移量。由系统调用writev()调用它。
  • ssize_t sendfile(struct file* file,loff_t* offset,size_t size,read_actor_t actor,void* target) 该函数用于从一个文件拷贝数据到另一个文件中,他执行的拷贝操作完全在内核中完成,避免了向用户空间进行不必要的拷贝。有系统调用sendfile()调用它。
  • ssize_t sendpage(struct file* file,struct page* page,int offset,size_t size,loff_t* pos,int more) 该函数用来从一个文件向另一个文件发送数据。
  • unsigned long get_unmapped_area(struct file* file,unsigned long addr,unsigned long len,unsigned long offset,unsigned long flags) 该函数用于获取未使用的地址空间来映射给定的文件。

和文件系统相关的数据结构

  • file_system_type用来描述各种特定文件系统类型,比如ext3,ext4或UDF。
  • vfsmount用来描述一个安装文件系统的实例。
  • 对于每种文件系统类型,不管有多少实例安装到了对应的挂载点,还是根本就没有挂载到系统中,都只有一个file_system_type结构。而vfsmount用来代表每个挂载到系统的文件系统实例。

和进程相关的数据结构

  • 系统中的每一个进程都有自己的一组打开的文件,像跟文件系统、当前工作目录、挂载点等。有3个数据结构将VFS层和系统的进程紧密联系在一起,它们分别是:file_struct、fs_struct和namespace结构体。
  • file_struct结构体由进程描述符中的files目录项指向,保存该进程打开的文件对应的文件对象和文件描述符
  • fs_struct结构体由进程描述符中的fs域指向,它包含文件系统和进程相关的信息,包括当前工作目录,根目录,当前正在执行的文件等。
  • namespace结构体由进程描述符中的mmt_namespace域指向,它使得每一个进程在系统中都看到唯一的安装文件系统,为一个根目录和唯一的文件系统层次结构。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值