文件系统重要概念
虚拟文件系统VFS


Linux环境中,用户使用GLIBC(POSIX标准、GUN C运行时库)作为应用程序的运行时库,然后通过操作系统,将其转换为系统调用SCI(system-call interface),SCI是操作系统内核定义的系统调用接口,这层抽象允许用户程序的I/O操作转换为内核的接口调用。
每个文件系统是独立的,有自己的组织方法,操作方法。那么对于用户来说,不可能所有的文件系统都了解,那么怎么做到让用户透明的去处理文件呢?例如:我想写文件,那就直接read就OK,不管你是什么文件系统,具体怎么去读!这里就需要引入虚拟文件系统。
所以虚拟文件系统就是:对于一个system,可以存在多个“实际的文件系统”,例如:ext2,ext3,fat32,ntfs等,例如我现在有多个分区,对于每一个分区是一种“实际文件系统”,那么每个“实际的文件系统”的操作和数据结构肯定不一样,那么,用户怎么能透明使用它们呢?
这个时候就需要VFS作为中间一层!用户就只需要和VFS打交道。
VFS是一种软件机制,只存在于内存中,每次系统初始化期间Linux都会先在内存中构造一棵VFS的目录树。
VFS主要的作用是对上层应用屏蔽底层不同的调用方法,提供一套统一的调用接口,二是便于对不同的文件系统进行组织管理。
VFS提供了一个抽象层,将POSIX API接口与不同存储设备的具体接口实现进行了分离,使得底层的文件系统类型、设备类型对上层应用程序透明。
例如read,write,那么映射到VFS中就是sys_read,sys_write,那么VFS可以根据你操作的是哪个“实际文件系统”(哪个分区)来进行不同的实际的操作!这个技术也是很熟悉的“钩子结构”技术来处理的。
其实就是VFS中提供一个抽象的struct结构体,然后对于每一个具体的文件系统要把自己的字段和函数填充进去,这样就解决了异构问题(内核很多子系统都大量使用了这种机制)。
核心数据结构
inode
index node索引结构,用于存储元数据,以及指向实际数据的指针
type Inode struct {
Ino InodeID
Size uint64
Mtime time.Time
Ctime time.Time
Mode os.FileMode
Uid uint32 // userID
Gid uint32 // groupID
// 链接数,记录指向该inode的文件名总数
// *data,指向文件数据block的位置
}
注意
- 文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector)。每个扇区储存512字节(相当于0.5KB)。
操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block),block是文件存取的最小单位。"块"的大小,一般是4KB,即连续八个 sector组成一个 block。 - inode有两种,一种是VFS的inode,一种是具体文件系统的inode。前者在内存中,后者在持久化在磁盘中。所以每次其实是将磁盘中的inode调进填充内存中的inode
- 对象存储里的fs客户端,其中只有size ctime 文件名是s3后端的,其它的属性都是模拟出来的,并没有持久化,fs服务重启就没了
- 用户通过文件名打开文件,实际上在系统内部,这个过程分成三步:首先,系统找到这个文件名对应的inode号码;其次,通过inode号码,获取inode信息;最后,根据inode信息,找到文件数据所在的磁盘block,读出数据。
- inode结构不存储文件名字,它只存储节点号;dentry保存文件名和与inode指针
dentry
目录项(directory entry),它里面除了name,inode信息[注意是inode指针],还记录了parent,children的dentry指针,所以dentry是一个 双向指针
type Dentry struct {
Name string // 文件名或者目录名
Parent *Dentry // 向前的dentry指针
children map[string]*Dentry // 向后的dentry指针(当前的子目录项)
Inode *Inode // 自身inode结构
ttl time.Time // 缓存过期时间
}
- 每个 inode 至少有一个 dentry 与之对应(也有可能有多个,比如硬链接)
- 目录项是描述文件的逻辑属性,只存在于内存中,磁盘上并没有实际对应的结构,它是为了提高查找性能而设计出来的缓存结构。
- 不管是文件夹还是普通文件,都有dentry结构,所有的目录项一起构成树形目录结构。
- 顺序逐级查找。例如:/home/xxx/yyy.txt,那么/根目录、home、xxx、yyy.txt都是一个目录项(对应的name是"home"、"xxx"、"yyy.txt"),假设/和yyy.txt属于同一个文件系统,如果要open文件yyy.txt,那VFS首先需要读取根目录的dentry结构,需要顺着children指针一层一层搜索对应的每个目录项的inode,才能找到最终的文件。
- 当然,实际文件系统中,不可能这样链式查找,效率太低了,而是增加一层缓存dcache加速,比如将dentry存在hashmap中,通过dentry地址+name一起计算hash,然后将hash值存在dentry结构中。
文件对象(file)
file结构描述的是进程已经打开文件的属性,包括打开模式、当前偏移、预读等。因为一个文件可以被多个进程打开,所以一个文件可以存在多个文件对象。
linux中,用fd文件描述符(file descriptor)来表示一个打开的文件,它其实就是在files_struct中file数组的下标。还有一个指针f_dentry,指向该文件的dentry数据结构。
files_struct
对于一个进程来说,可能同时在处理多个文件对象,所以需要另一个结构来管理所有的files,即:用户打开文件表--->files_struct

关联图
进程->task_struct->files_struct->file->dentry->inode->Data Area

硬链接/软链接
- 硬链接:多个文件指向同一个inode结构,它们互为硬链接, inode链接数=所有硬链接数之和。(执行ls -l第二列的值就是硬链接)
- 软链接:文件A和文件B的inode号码虽然不一样,但是文件A的内容是文件B的路径。读取文件A时,系统会自动将访问者导向文件B(如果目标文件不存在,打开就会报错)。这时,文件A就称为文件B的"软链接"(soft link)或者"符号链接"(symbolic link)。readlink可以查看软链的指向。
- 创建目录时,默认会生成两个目录项:"."和".."。前者是当前目录的"硬链接",后者是父目录的"硬链接"。所以,任何一个目录的"硬链接"总数,总是等于2加上它的子目录总数(含隐藏目录);每创建一个子目录,父目录的硬链接都会增加1(因为子目录的..都是指向父目录);但如果创建文件,父目录的硬链接数不会增加。目录下执行ls -a可以列出"."和"..",以及所有隐藏项。
- 不能给目录创建硬链接,只能创建软链接
- linux是通过link数量来控制文件删除的,只有当文件的link数为0时,才能被删除
![]()
inode的特殊作用
由于inode号码与文件名分离,这种机制导致了一些Unix/Linux系统特有的现象。
1、有时,文件名包含特殊字符,无法正常删除。这时,直接删除inode节点,就能起到删除文件的作用。
2、移动文件或重命名文件,只是改变文件名,不影响inode号码。
3、可以通过设置硬链接的方式,防止重要文件被删


浙公网安备 33010602011771号