2023930


虚拟文件系统

VFS作为内核的子系统

所有FS不但以来VFS共存,也依靠VFS协同工作

不同FS,甚至不同介质的FS可进行读写工作

13.1 通用文件系统接口

13.2 文件系统抽象层

通用文件系统模型:囊括了任何文件系统的常用功能集

可支持多种差异很大的FS,从DOS系统的FAT到windows的NTFS

一方面,系统调用时通用VFS接口,提供给用户空间的前端

另一方面,系统调用是具体文件系统的后端

userspace -> VFS -> FS -> 物理介质

13.3 Unix文件系统

非unix风格的FS,必须经过封装,得到符合unix概念的接口(文件,目录项,inode,挂载点)

13.4 VFS对象及其数据结构

VFS的主要对象:

  • 超级块,代表一个具体的FS
  • 索引节点对象,代表一个具体文件
  • 目录项对象,代表目录项,是路径的组成部分(目录或文件)
  • 文件对象

每个主要对象,包括一个操作对象,描述了内核对主要对象可以使用的方法

  • super_operations对象,对文件系统,如write_inode()和sync_fs()
  • inode_operations,create()和link()
  • dentry_operations, d_compare() and d_delete()
  • file_operations, read() and write()

操作对象作为一个结构体指针实现(类比IO_FILE)

除此之外:

  • 注册的文件系统用file_system_type结构体
  • 安装点用vfsmount结构体
  • fs_struct
  • file

13.5 超级块对象

通常为磁盘超级快;对于内存文件系统,会现场创建超级块保存在内存

super_block结构体定义在文件<linux/fs.h>

13.6 超级快操作

最重要的s_op,操作函数表,定义在文件<linux/fs.h>

文件系统需要执行超级快操作时,需要在超级快对象中,找需要的操作方法,如写自己的超级快:

sb->s_op->write_super(sb);

sb:super block指针,找op再找write

尽管函数来自超级快,调用时还是要把超级快作为参数传递

如果c++只需要

sb.write_super();

//创建初始化新的索引节点
struct inode* alloc_inode(struct super_block *sb)
//释放给定的索引节点
void destroy_inode(struct inode* inode)
//索引节点脏时调用,日志文件系统执行函数,更新日志
void dirty_inode(struct inode* inode)
//索引节点写入磁盘,wait表示写操作是否需要同步
void write_inode(struct inode *inode, int wait)
//最后一个引用消失,VFS调用删除inode
void drop_inode(struct inode *inode)
void delete_inode(struct inode *inode)
//卸载文件系统时释放超级快,调用者必须持有s_lock
void put_super(struct super_block *sb)
//用给定的超级快更新磁盘的超级快
void write_super(struct super_block *sb)
//文件系统的数据元与磁盘上的fs同步
int sync_fs(struct super_block *sb, int wait)
//禁止对文件系统改变,再用超级快更新磁盘超级快,LVM(逻辑卷标管理)使用
void write_super_lockfs(struct super_block *sb)
//重新安装文件系统
int remount_fs(struct super_block *sb,int *flag, char* data)
//释放inode+清空
void clear_inode(struct inode *inode)
//中断安装操作,网络文件系统如NFS使用
void umount_begin(struct super_block *sb)

除了dirty_inode都可以阻塞

如果操作函数指针为NULL,调用通用函数执行操作

13.7 索引节点对象

索引节点:内核操作文件或目录时需要的全部信息

Unix风格FS直接读取

其它需要提取inode相关信息

13.8 索引节点操作

调用方式

i->i_op->truncate(i)

这些函数可能VFS执行,也可能具体文件系统执行

//为dentry创建新的索引节点,mode设置初始模式
int create(struct inode*dir, struct dentry*dentry, int mode)
//目录中寻找索引节点
struct dentry loookup(struct inode *dir, struct dentry *dentry)
//link()调用,创建硬连接
int link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
//unlink()调用,删除dentry指定的inode
int unlink(struct inode *dir, struct dentry *dentry)
//symlink调用,创建软连接
int unlink(struct inode *dir, struct dentry *dentry, const char *symname)
//mknod调用,创建特殊文件。创建的文件放在dir,目录项dentry,关联的设备rdev
int mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
//readlink()调用,拷贝数据到特定的buffer
int readlink(struct dentry *dentry, char *buffer, int buflen)
//从符号链接查找索引节点,由dentry解析,结果存放在nd指向的nameidata结构体中
int follow_link(struct dentry *dentry, struct nameidata *nd)
//清除follow_link
int put_link(struct dentry *dentry, struct nameidata *nd)
//修改文件的大小
void truncate(struct inode* inode)
//检查inode所代表的文件是否允许特定访问模式
int permission(struct inode* inode, int mask)

13.9 目录项对象

每个dentry代表路径中的一个特定部分

常规的字符串比较,执行耗时,代码繁琐

目录项对象的引入使得过程更简单

13.9.1 目录项状态

三种状态:被使用,未被使用和负状态

被使用:对应一个有效的索引节点,有一个多个使用者

被使用不能被丢弃

未被使用:d_count为0

负状态:没有对应节点(NULL),用于确认路径不存在

13.9.2 目录项缓存

将元素逐个解析为目录项,浪费时间

将目录项对象缓存在dcache中

三个主要部分:

被使用的目录项链表

最近被使用的双向链表:包含未被使用的,负状态的

散列表,相应的散列函数

散列表由数组dentry_hashtable表示, 元素为指向相同键值的目录项对象链表的指针

实际的散列值由d_hash函数计算:内核给FS唯一的散列函数

13.10 目录项操作

//比较文件名是否相同,对于FAT不区分大小写
int d_compare(struct dentry *dentry, struct qstr *name1, struct qstr* name2)

13.11 文件对象

文件对象在进程观点上,代表打开文件

OS视角上目录项对象代表打开文件,inode和目录项对象是唯一的

记录文件是否脏是索引节点记录的

13.12 文件操作

对不感兴趣的操作可置为NULL,通用操作

//更新偏移量指针
loff_t lleek(struct file *file, loff_t offset, int origin)
//从offset读取count字节到buf,更新文件指针
ssize_t read(struct file *file, char *buf, size_t count, loff_t offset)
//从iocb描述的文件里,同步方式读取count字节到buf。
ssize_t aio_read(struct kiocb *iocb, char *buf, size_t count, loff_t offset)
//返回目录列表中的下一个目录
int readdir(struct file *file, struct poll_table_struct *poll_table)
//所有被缓存数据写回磁盘
int fsync(struct file *file, struct dentry *dentry, int datasync)
//打开或关闭异步IO的通告信号
int fasync(int fd, struct file*file, int on)
//从一个文件拷贝数据到另一个文件,全部在内核完成,避免了向用户不必要的拷贝
ssize_t sendfile(struct file*file, loff_t *offset,size_t size, read_actor_t actor, void *target)
//提供忠告锁
int flock(struct file *flip, int cmd, struct file_lock *fl)

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

file_system_type

描述各种FS类型

vfsmount

描述一个FS的实例

理清文件系统和其它安装点的关系,是最复杂的工作

vsmount中的各种链表,用于跟踪相关信息

标准安装标志列表

标志 描述
MNT_NOSUID 禁止exe设置setuid,setgid标志
MNT_MODEV 禁止访问该FS上的设备文件
MNT_NOEXEC 禁止执行FS上的exe

13.14 进程相关数据结构

每个进程都有一组自己打开的文件

files_struct

struct files_struct
{
    atomic_t count;
    ...
    struct file *fd_array[NR_OPEN_DEFAULT];
}

可容纳64个文件对象

若超过64,分配一个新数组

可以适当增大NR_OPEN_DEFAULT的预定义

count:可以让多个进程共享结构体,计数防止撤销

fs_struct

包括文件系统和进程的相关信息

mmt_namespace

单进程命名空间

每一个进程在系统中都看到为一个安装文件系统

默认情况,所有进程共享同样的命名空间

13.15 小结


文章作者: N1co5in3
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 N1co5in3 !
  目录