1. VFS介绍
“一切皆文件”,文件系统是linux系统的基础,Linux内核通过虚拟文件系统(Virtual File System, VFS)管理文件系统 ,VFS为所有的文件系统提供了统一的接口,对每个具体文件系统的访问要通过VFS定义的接口来实现,VFS既是向下的接口(所有文件系统都必须实现该接口),同时也是向上的接口(用户进程通过系统调用最终能够访问文件系统功能)。
文件系统官方的定义为操作系统中负责管理和存储文件信息的软件机构,文件本身和文件的内容可以分开理解,就像容器和它存放的液体一样。
操作文件本身:比如磁盘中的普通文件,如果需要操作它(删除或者更改属性等),因为程序是在内存中,不会直接操作磁盘,而文件又在磁盘中,所以需要一个抽象作为它的代表,这个代表会把操作最终传递至文件。
同样地,如果需要操作文件的内容(read/write),首先应该找到它或者它的代表,获取操作内容的方法和数据,然后对它的内容进行读写等。
以上提供了几个概念:文件系统、文件本身、文件的代表和文件内容,它们在内核中都有对应的数据结构,弄清楚了这几个概念,文件系统的整个框架就了然于心了。
概念 | 数据结构 |
文件系统 | super_block |
文件本身 | inode |
文件的代表 | dentry |
文件的内容 | file |
2. 数据结构
2.1 超级块 super_block
super_block结构体代表了整个文件系统本身,超级块的内容需要读取具体文件系统在硬盘上的超级块结构获得,所以超级块是具体文件系统超级块的内存抽象。
结构体定义源码路径:include/linux/fs.h
字段 | 类型 | 描述 |
s_list | list_head | 将super_block链接到super_blocks变量指向的链表中 |
s_blocksize | unsigned long | 系统中文件的最小块大小 |
s_maxbytes | loff_t | 文件系统中最大文件的尺寸 |
s_type | file_system_type* | 提供文件系统的mount、kell_sb等回调函数 |
s_magic | unsigned long | 魔术数字,每个文件系统都有的 |
s_op | super_operations* | 提供alloc_inode、destroy_inode等回调函数 |
s_root | dentry* | 指向文件系统根dentry的指针 |
s_inodes | list_head | 文件系统的inode组成的的链表头 |
s_instances | hlist_node | 将它链接到同一种文件系统组成的链表 |
s_mounts | list_head | 挂载它的mount对象组成的链表头 |
s_bdev | struct block_device* | 指向文件系统存在的块设备指针 |
2.2 dentry 目录项
文件和目录一般按树状结构保存,在VFS里,目录本身也是一个文件,每个文件都有一个dentry(可能不止一个),这个dentry链接到上级目录的dentry。根目录有一个dentry结构,而根目录里的文件和目录都链接到这个根dentry,二级目录里的文件和目录,同样通过dentry链接到二级目录。这样一层层链接,就形成了一颗dentry树。从树顶可以遍历整个文件系统的所有目录和文件。
结构体定义源码路径:include/linux/dcache.h
字段 | 类型 | 描述 |
d_parent; | struct dentry * | 指向父dentry结构 |
d_name | struct qstr | 名字和哈希值信息 |
d_inode | struct inode * | 指向一个inode结构。这个inode和dentry共同描述了一个普通文件或者目录文件。 |
d_op | struct dentry_operations | dentry相关操作 |
d_hash | struct hlist_bl_node | 将dentry链接到hash链表中 |
d_subdirs | struct list_head | d_subdirs是子项(子项可能是目录,也可能是文件)的链表头,所有的子项都要链接到这个链表 |
d_child | struct list_head | d_child是dentry自身的链表头,需要链接到父dentry的d_subdirs成员,当移动文件的时候,需要把一个dentry结构从旧的父dentry的链表上脱离,然后链接到新的父dentry的d_subdirs成员 |
du.d_alias | struct hlist_node | 将dentry链接到文件的硬链接链表中 |
2.3 inode 索引节点
inode代表一个文件。inode保存了文件的大小、创建时间、文件的块大小等参数,以及对文件的读写函数、文件的读写缓存等信息。一个真实的文件可以有多个dentry,因为指向文件的路径可以有多个(考虑文件的链接),而inode只有一个。
结构体定义源码路径:include/linux/fs.h
字段 | 类型 | 描述 |
i_mode | umode_t | 文件的类型 |
i_uid; i_gid | kuid_t | 文件的属性 |
i_op | inode_operations* | inode操作函数,create/link/unlink/mkdir |
i_sb | super_block* | 指向inode所属的super_block |
i_ino | unsigned long | 成员i_ino是inode的号 |
i_count | atomic_t | i_count是inode的引用计数 |
i_size | loff_t | 字节为单位的文件长度 |
i_atime i_mtime i_ctime | timespec | access/mdify/change time |
i_fop | file_operations* | 文件的读写函数和异步io函数都在这个结构中提供 |
i_hash | hlist_node | 将inode链接到hash链表中 |
i_sb_list | list_head | 将inode链接到super_block链表中 |
i_dentry | hlist_head | 文件的硬链接的dentry组成的链表头 |
i_nlink | unsigned int | 文件的链接数目 |
i_link | char* | 链接的目标文件路径 |
i_mapping | address_space* | 这个结构目的是缓存文件的内容,对文件的读写操作首先要在i_mapping包含的缓存里寻找文件的内容。如果有缓存,对文件的读就可以直接从缓存中获得,而不用再去物理硬盘读取,从而大大加速了文件的读操作。写操作也要首先访问缓存,写入到文件的缓存。然后等待合适的机会,再从缓存写入硬盘 |
i_bdev | block_device* | 指向块设备的指针。这个块设备就是文件所在的文件系统所绑定的块设备。 |
2.4 file 文件
文件对象的作用是描述进程和文件交互的关系,file结构体对应文件的内容,并不是说它包含了文件的内容,而是说它记录了文件的访问方法和访问状态。
struct file {
union {
struct llist_node fu_llist; /*单链表成员*/
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path; /*文件路径信息*/
struct inode *f_inode; /*指向内核文件inode实例*/
const struct file_operations *f_op;
/*文件操作结构指针,通常在打开文件时设为inode->i_fop*/
spinlock_t f_lock;
atomic_long_t f_count;
unsigned int f_flags; /*open()系统调用传递的flags标记参数*/
fmode_t f_mode; /*标记进程以何种模式打开文件*/
struct mutex f_pos_lock;
loff_t f_pos; /*文件当前读写位置,相对于文件开头处的字节偏移量*/
struct fown_struct f_owner;
const struct cred *f_cred;
struct file_ra_state f_ra;
u64 f_version;
#ifdef CONFIG_SECURITY
void *f_security;
#endif
void *private_data; /*文件私有数据指针,例如设备文件指向驱动程序定义的数据结构*/
#ifdef CONFIG_EPOLL
struct list_head f_ep_links;
struct list_head f_tfile_llink;
#endif
struct address_space *f_mapping; /*文件地址空间指针,用于具有外部存储介质的文件*/
} __attribute__((aligned(4)));
2.5 file_system_type
在使用file_system_type前需要先调用register_filesystem,将其注册到系统中,后者将它链接到file_systems变量指向的单向链表中,使用时将它的名字作为参数调用get_fs_type即可获取。
所有可用的file_system_type组成一个链表,同一种文件系统(super_block)链接到file_system_type的fs_supers字段表示的链表中。