文章目录
- vfs
- 1 super_block
- 2 dentry
- 2.1 dentry树
- 2.2 dentry的cache
- 2.3 挂载
- 3 inode
- 4 文件file
- 5 vfs各结构体的关系
vfs
Linux内核通过虚拟文件系统(Virtual File System,VFS)管理文件系统
VFS为所有的文件系统提供了统一的接口,对每个具体文件系统的访问要通过VFS定义的接口来实现
VFS本身只存在于内存中,它需要将硬盘上的文件系统抽象到内存中,这个工作是通过几个重要的结构实现的:
- dentry
- inode
- super_block
- file
1 super_block
超级块(super_block)代表了整个文件系统本身。通常,超级块是对应文件系统自身的控制块结构,其内容需要读取文件系统在硬盘上的超级块结构获得,所以超级块是具体文件系统超级块的内存抽象。
超级块简略结构如下:
struct super_block {
unsigned long s_blocksize; /* 文件系统的块大小 */
unsigned char s_blocksize_bits;
……/*省略超级块的链表、设备号等代码*/
unsigned long long s_maxbytes; /* Max file size */
struct file_system_type *s_type;
struct super_operations *s_op; /* 提供了一些重要的超级块操作函数 */
unsigned long s_magic; /* 每个文件系统都有一个magic */
struct dentry *s_root; /* 指向文件系统根dentry的指针 */
struct list_head s_inodes; /* 包含文件系统内所有的 inodes */
struct list_head s_dirty; /* dirty inodes */
struct block_device *s_bdev; /* 指向文件系统存在的块设备指针 */
void *s_fs_info; /* Filesystem private info */
};
参考
include/linux/fs.h
每个文件系统都有一个超级块结构,每个超级块都要链接到一个超级块链表。
文件系统内的每个文件在打开时都需要在内存分配一个inode结构,这些inode结构都要链接到超级块。
顺着super_blocks链表可以遍历整个操作系统打开过的文件的inode结构。
2 dentry
对于一个通常的文件系统来说,文件和目录一般按树状结构保存,目录项(dentry)就是反映了文件系统的这种树状关系。
2.1 dentry树
在VFS里,目录本身也是一个文件,只是有点特殊。每个文件都有一个dentry,这个dentry链接到上级目录的dentry,这样层层链接形成了目录树。
dentry简略结构如下:
struct dentry {
……/省略dentry锁、标志等代码/
struct inode *d_inode; /* 指向一个inode结构。这个inode和dentry共同描述了一个普通文件或者目录文件 */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* 链接到dentry cache的hash链表 */
struct dentry *d_parent; /* parent directory */
struct qstr d_name; /* 文件或目录的名字 */
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child; /* child of parent list */
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* 子项的链表头,所有子项d_child都要链接到这个链表 */
struct dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
int d_mounted; /* 指示dentry是否是一个挂载点,如果是则不为0 */
};
参考
include/linux/fs.h
2.2 dentry的cache
所有的dentry都指向一个dentry_hashtable
,dentry_hashtable
是个数组,它的数组成员是hash链表数据结构。
这里所说的dentry,指的是在内存中的dentry。
如果某个文件已经被打开过,内存中就应该有该文件的dentry结构,并且该dentry被链接到dentry_hashtable
数组的某个hash链表头。
后续再访问该文件的时候,就可以直接从hash链表里面找到,避免了再次读硬盘。这是dentry的cache概念。
2.3 挂载
通过dentry的d_mounted
成员不为0可以判断: 该目录不是一个普通的目录,而是一个文件系统的挂载点。
在一开始,根文件系统和要挂载的源文件系统分别有两个dentry树。
当文件系统被挂载的时候,它的vfsmount
结构就被链接到内核的一个全局链表—mount_hashtable
数组链表。
每个文件系统都有这样一个vfsmount结构
当发现mnt目录是个特殊的目录时,从mount_hashtable
数组找到hash链表头,再遍历整个hash链表,就能找到txt文件所在文件系统的vfsmount
,然后mnt目录的dentry就被替换,置换为新文件系统的根目录。
3 inode
inode代表一个文件。inode保存了文件的大小、创建时间、文件的块大小等参数,以及对文件的读写函数、文件的读写缓存等信息。
具体文件系统的inode(静态节点)存储在硬盘中,在使用时需要调入内存,填写vfs的inode(动态节点)
一个真实的文件可以有多个dentry,因为指向文件的路径可以有多个(考虑文件的链接),而inode只有一个。
inode简略结构如下:
struct inode {
struct list_head i_list; /* 用于描述inode当前状态的链表*/
struct list_head i_sb_list; /* 用于链接到超级块中的inode链表 */
struct list_head i_dentry; /* 该文件的所有dentry需要链接到i_dentry */
unsigned long i_ino; /* inode号 */
atomic_t i_count; /* inode引用计数 */
loff_t i_size; /* 字节为单位的文件长度 */
unsigned int i_blkbits; /* 文件块的位数 */
struct inode_operations *i_op;
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct address_space *i_mapping; /* 用于缓存文件的内容,便于快速读写 */
struct block_device *i_bdev; /* 指向文件系统所绑定的块设备 */
……/*省略锁等代码*/
};
参考
include/linux/fs.h
当创建一个新的inode的时候,成员i_list要链接到inode_in_use这个链表,表示inode处于使用状态,同时成员i_sb_list也要链接到文件系统超级块的s_inodes链表头。
内核提供了一个hash链表数组inode_hashtable
,所有的inode结构都要链接到数组里面的某个hash链表
inode结构的i_mode成员用不同的值代表不同的文件类型:
imode | 类型 |
---|---|
S_IFBLK | 块设备 |
S_IFCHR | 字符设备 |
S_IFDIR | 目录 |
S_IFSOCK | socket |
S_IFIFO | FIFO |
4 文件file
文件对象的作用是描述进程和文件交互的关系。
这里需要指出的是,硬盘上并不存在一个文件结构。进程打开一个硬盘上的文件时,内核就动态创建一个文件对象,返回一个文件描述符,并保存文件指针到文件描述符表里面的数组。
同一个文件,在不同的进程中有不同的文件对象.
file简略结构如下:
struct file {
struct dentry *f_dentry; /* 指向文件对应的dentry结构 */
struct vfsmount *f_vfsmnt; /* 指向文件所属于的文件系统的vfsmount对象 */
const struct file_operations *f_op;
atomic_long_t f_count; /* 使用此结构体的进程数量 */
……/*省略部分代码*/
loff_t f_pos; /* 表示进程对文件操作的位置。如读取前10字节,f_pos就指示第11字节位置 */
struct fown_struct f_owner;
unsigned int f_uid, f_gid;
struct file_ra_state f_ra; /* 用于文件预读的设置 */
struct address_space *f_mapping; /* 指向一个address_space结构。这个结构封装了文件的读写缓存页面 */
};
参考
include/linux/fs.h
files_struct
:用于记录进程中文件描述符的使用情况,是进程的私有数据
/*
* Open file table structure
*/
struct files_struct {
/*
* read mostly part
*/
atomic_t count; /* 共享该表的进程数量 */
bool resize_in_progress; /* 指示是否正在进行扩容操作 */
wait_queue_head_t resize_wait; /* 等待队列头,扩容时阻塞等待的进程 */
struct fdtable __rcu *fdt; /* 指向一个文件描述符表的指针 */
struct fdtable fdtab; /* 文件描述符表 */
/*
* written part on a separate cache line in SMP
*/
spinlock_t file_lock ____cacheline_aligned_in_smp; /* 保护如下所有的域 */
unsigned int next_fd;
unsigned long close_on_exec_init[1]; /* exec()关闭文件描述符的初始值 */
unsigned long open_fds_init[1];
unsigned long full_fds_bits_init[1];
struct file __rcu * fd_array[NR_OPEN_DEFAULT]; /* 文件描述符数组,存储每个打开的文件对应的file结构体指针 */
};
参考
include/linux/fdtable.h
fs_struct
表示文件系统信息的结构体:
struct fs_struct {
int users; /* 表示有多少用户正在使用该fs_struct */
spinlock_t lock;
seqcount_t seq; /* 一个序列号计数器,用于维护fs_struct的序列化访问 */
int umask; /* 代表用户文件屏蔽掩码,用于设置新创建文件的默认权限 */
int in_exec; /* 指示当前进程是否正在执行 */
struct path root, pwd; /* 根目录 当前目录 */
} __randomize_layout;
参考
include/linux/fd_struct.h