虚拟文件系统
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
单进程命名空间
每一个进程在系统中都看到为一个安装文件系统
默认情况,所有进程共享同样的命名空间