虚拟文件系统
虚拟文件系统
虚拟文件系统(VFS)是内核子系统, 作为用户空间的文件与文件系统相关的接口.
一个写操作 ret = write(fd, buf, len)
- write函数系统调用
sys_write()
函数处理.sys_write
函数找到fd实际写入的是哪一个文件系统的文件- 然后通过write写入
VFS
介质中.- 最后
VFS
将写入介质的数据再写回所对应文件系统的文件.
一方面, 系统调用是通过VFS
接口, 提供给用户空间的前端; 另一方面, VFS
将具体文件系统的操作给了后端.
VFS
就是文件操作的中转站(介质), 表面看上去文件操作都是统一的, 其实文件是属于不同的文件系统, 只是通过VFS
“转”成统一的.
linux对ext文件系统族的支持是最好的,因为VFS抽象层的组织与ext文件系统类似,这样在处理Ext文件系统时可以提高性能,因为在Ext和VFS之间转换几乎不会损失时间. 同样linux的文件系统也是ext.
索引节点(inode)
索引节点 : 记录文件相关信息(元数据).包含了内核操作的文件或目录的全部信息.
索引节点只有文件名和文件索引号没有记录. 而是把两个信息放在目录中, 通过文件名在目录中找到相应的索引号, 后续文件访问就直接使用索引号来标记文件了.
那索引节点与VFS有什么关系呢?
因为有些文件系统是不具备inode结构的, 需要通过VFS将文件系统统一成一个结构, 所以文件系统中的文件也就都会统一具备了 inode结构.
为什么索引节点信息不包含文件名称?
索引节点会储存对于其自身唯一的信息。对于一个硬链接,一个索引节点可能会含有指向同一索引节点的两个不同的文件名. 并且在修改文件名时, 改变也就是仅仅是文件名, 索引号不会改变, 索引节点也不会改变.
在移动文件时索引节点会发生变化吗?
即使将文件从一处移动到另一处,索引节点号码还是会保持不变,但前提是在同一文件系统之下。如果在不同的文件系统之间移动,索引节点号码就会发生变化。
关于inode结构
struct inode {
/* 全局的散列表 */
struct hlist_node i_hash;
/* 根据inode的状态可能处理不同的链表中(inode_unused/inode_in_use/super_block->dirty) */
struct list_head i_list;
/* super_block->s_inodes链表的节点 */
struct list_head i_sb_list;
/* inode对应的dentry链表,可能多个dentry指向同一个文件 */
struct list_head i_dentry;
/* inode编号 */
unsigned long i_ino;
/* 访问该inode的进程数目 */
atomic_t i_count;
/* inode的硬链接数 */
unsigned int i_nlink;
uid_t i_uid;
gid_t i_gid;
/* inode表示设备文件时的设备号 */
dev_t i_rdev;
u64 i_version;
/* 文件的大小,以字节为单位 */
loff_t i_size;
/* 最后访问时间 */
struct timespec i_atime;
/* 最后修改inode数据的时间 */
struct timespec i_mtime;
/* 最后修改inode自身的时间 */
struct timespec i_ctime;
umode_t i_mode;
spinlock_t i_lock;
struct mutex i_mutex;
struct rw_semaphore i_alloc_sem;
/*inode 信号量*/
struct semaphore i_sem;
/* inode操作表 */
const struct inode_operations *i_op;
/* file操作 */
const struct file_operations *i_fop;
/* inode所属的super_block */
struct super_block *i_sb;
struct file_lock *i_flock;
/* inode的地址空间映射 */
struct address_space *i_mapping;
struct address_space i_data;
};
索引节点是保存在一个slab
缓存中的inode_cache
中, 提供inode的快速分配和释放的.
目录项
在Linux操作系统中,目录就是目录文件。
一个目录文件包含了一组目录项,目录项是放在data block中的.一个目录项主要包括了文件名和索引节点号,索引节点号是指向索引节点表中对应的索引节点的。
目录项对象没有对应的磁盘数据结构, 所以他并非是保存在磁盘上, 而是VFS
根据字符串形式的路径名来创建他们, 目录项具体是存放在dcache中. 当你对文件的访问的时侯, VFS
会先在dcache
中查找, 没有找到的时侯就解析路径, 然后将该目录项加入到dcache
中, 以便之后能快速的访问. 同样, 目录项中包含的索引节点也加入到了icache
缓存中
目录项结构
struct dentry {
atomic_t d_count;
unsigned int d_flags;
spinlock_t d_lock;
/* 该dentry是否是一个装载点 */
int d_mounted;
/* 文件所属的inode */
struct inode *d_inode;
/* 全局的dentry散列表 */
struct hlist_node d_hash;
/* 父目录的dentry */
struct dentry *d_parent;
/* 文件的名称,例如对/tmp/a.sh,文件名即为a.sh */
struct qstr d_name;
/* 脏的dentry链表的节点 */
struct list_head d_lru;
union {
struct list_head d_child;
struct rcu_head d_rcu;
} d_u;
/* 该dentry子目录中的dentry的节点链表 */
struct list_head d_subdirs;
/* 硬链接使用几个不同名称表示同一个文件时,用于连接各个dentry */
struct list_head d_alias;
unsigned long d_time;
const struct dentry_operations *d_op;
/* 所属的super_block */
struct super_block *d_sb;
void *d_fsdata;
/* 如果文件名由少量字符组成,在保存在这里,加速访问 */
unsigned char d_iname[DNAME_INLINE_LEN_MIN];
};
目录项也通过散列来快速的进行相关目录项的解析.
超级块
超级块代表一个已经安装的文件系统,用于存储文件系统的控制信息,例如文件系统类型、大小、所有inode对象、脏的inode链表等.
超级块结构
struct super_block {
/* 全局链表元素 */
struct list_head s_list;
/* 底层文件系统所在的设备 */
dev_t s_dev;
/* 文件系统中每一块的长度 */
unsigned long s_blocksize;
/* 文件系统中每一块的长度(以2为底的对数) */
unsigned char s_blocksize_bits;
/* 是否需要向磁盘回写 */
unsigned char s_dirt;
unsigned long long s_maxbytes;
/*脏数据链表*/
struct list_head s_dirty;
/*写回链表*/
struct list_head s_io;
/* 文件系统类型 */
struct file_system_type *s_type;
/* 超级块操作方法 */
const struct super_operations *s_op;
struct dquot_operations *dq_op;
struct quotactl_ops *s_qcop;
const struct export_operations *s_export_op;
unsigned long s_flags;
unsigned long s_magic;
/* 全局根目录的dentry */
struct dentry *s_root;
struct rw_semaphore s_umount;
struct mutex s_lock;
};
超级块是通过alloc_super()函数进行初始化的.
总结
目录项, 索引节点, 超级块的关系(google图片中找到的)