Linux中的文件系统
Linux文件系统的设计初衷
Linux操作系统中,有个很显著的特点就是:所有的IO对象都被抽象为文件,如果要进行IO操作,那么则通过调用Linux的虚拟文件系统提供的`open` 、`write `、`read`一系列接口,来到具体的IO块设备中使用设备驱动来实现。总而言之,就是Linux提供了一个内核层的软件,即虚拟文件系统,来提供所有的IO操作。
Linux虚拟文件系统支持的文件系统
磁盘文件系统
磁盘文件系统是用来管理本地磁盘的存储空间,或者其他可以起到磁盘作用的存储设备(U盘)。
Linux可以很好的支持对各种类型的文件系统的磁盘的访问,比如Linux使用的Ex4(第四拓展文件系统),Windows使用的NTFS以及 UNIX家族的各种操作系统
网络文件系统
网络文件系统是一种通过 VFS 把远程存储挂载到本地目录树的技术,本质是“文件系统驱动 + 网络协议”,它让应用像操作本地文件一样去读写远程数据。常见的RPC技术就是访问远程的网络文件系统的方式
特殊文件系统
特殊文件系统是 Linux 内核中不依赖磁盘的文件系统实现,它通过 VFS 提供统一的文件操作接口,但数据直接来自内存或内核数据结构,常用于系统信息、配置接口和临时存储。
常见的特殊文件系统介绍:
名称 | 挂载点 | 功能 |
---|---|---|
procfs | /proc |
提供进程、内核状态信息(/proc/cpuinfo 、/proc/meminfo ) |
sysfs | /sys |
反映内核对象(设备、驱动、总线等)的层次结构 |
tmpfs | 任意挂载点(常见 /run 、/dev/shm ) |
内存中的临时存储,可当 RAM Disk 用 |
devtmpfs | /dev |
自动创建设备节点 |
sockfs | 内核内部使用 | 为 socket 提供 VFS 接口 |
pipefs | 内核内部使用 | 管道/匿名管道实现 |
configfs | /sys/kernel/config |
允许用户空间创建和配置内核对象 |
虚拟文件系统中的数据结构
superblock
超级块是存放在磁盘上文件系统开头的一小块(通常在引导块之后),同时内存中也会有一份副本用于运行时访问。它是文件系统的“元信息(metadata)”,描述了整个文件系统的布局和关键参数。内核在挂载(mount)文件系统时,会读取超级块,将里面的参数加载到内存,用来指导后续的目录、inode、数据块的管理。
superblock 的主要内容:
文件系统基本信息
- 魔数(magic number):标识文件系统类型,防止挂载错误的分区。
- 文件系统大小:总共有多少块(blocks)。
- block 大小(一般 1KB、2KB、4KB)。
- inode 总数。
文件系统布局信息
- inode 表起始位置、数量。
- 数据块区域起始位置、数量。
- 位图(block bitmap / inode bitmap)位置。
一致性与日志相关信息
- log 区域(用于 journaling 文件系统,比如 ext3/ext4 的 journal)。
- 最近挂载时间、写入时间。
- 是否被 cleanly unmount。
运行时信息(仅内存态 superblock 保存)
- 挂载点引用计数。
- 指向 inode 缓存、dentry 缓存的指针。
- 同步/锁机制。
简单地说,superblock 就是文件系统的“元信息控制中心”,它告诉操作系统如何解释磁盘上的数据,确保文件系统能被正确挂载、管理和维护。
索引节点对象(inode)
inode是文件系统中用来描述一个文件的 元数据结构 。每一个文件(普通文件、目录、符号链接、设备文件等)都有一个 inode。inode 不存储文件名 ,而是存储文件的属性和指向数据块的指针。
inode 中包含的主要信息
- 文件属性(metadata)
- 文件类型(普通文件、目录、符号链接、字符设备、块设备…)
- 权限(rwx)
- 所属用户 ID (UID)、组 ID (GID)
- 文件大小(bytes)
- 时间戳(atime/mtime/ctime)
- 链接计数(多少个目录项指向该 inode)
- 数据块位置
- 直接块指针 (direct blocks):直接指向数据块。
- 间接块指针 (indirect blocks):指向一个块,这个块里再存放更多数据块地址。
- 二级/三级间接块指针 (large file 才需要)。
文件对象(file)
在 Linux 内核中,文件对象(file object) 就是 struct file
结构体。它并不是“磁盘上的实体”,而是 进程在打开文件时,内核为该打开实例创建的内存对象 。可以说,文件对象是站在进程的角度所设计出来的,它表示进程打开该文件的一次实例(动态的,运行时信息)
file中包含的主要信息
路径 & inode
f_path
:指向 dentry(目录项)和挂载点,用于路径解析f_inode
:快速访问 inode(文件的元数据)
操作方法
f_op
:指向一组函数指针(struct file_operations
),定义了 read/write/ioctl/mmap 等操作的具体实现- 不同文件系统会注册自己的操作集,比如 ext4、procfs、sockfs
运行时状态
f_flags
:打开时传入的 flag(如 O_APPEND、O_NONBLOCK)f_mode
:读写模式(FMODE_READ, FMODE_WRITE)f_pos
:文件读写指针(当前偏移量)
引用计数和同步
f_count
:多少个 fd 指向这个 file 对象(dup
、fork
时会增加)f_lock
:保护并发访问
缓存和私有数据
f_mapping
:指向页缓存,用于文件和内存的映射private_data
:给具体文件系统或设备驱动使用(例如 socket 就会存 socket 的内部结构体)
有时候很容易混淆inode和file,简而言之,inode
描述文件是什么,file
描述“我这次打开它怎么用”
目录项对象(dentry)
dentry(directory entry,目录项对象) 是 VFS 用来表示 路径名的一部分(目录项) 的内存对象。它的作用是把 文件名 和 inode 关联起来。需要注意的是:磁盘上存储目录项地方是目录文件的数据块,内存中对应的运行时抽象就是 dentry。
dentry中包含的主要信息
名字信息
d_name
:文件名(含长度和哈希值,便于快速查找)d_parent
:指向父目录的 dentry(形成目录树)
层级关系
d_subdirs
:子目录链表- 支持在内存里维护完整的目录树结构
关联文件系统对象
d_inode
:指向对应的 inoded_sb
:指向所属的超级块
缓存与引用
d_hash
:放入哈希表(dcache)中,用于快速查找路径d_count
:引用计数(多少个进程/内核对象正在用这个 dentry)d_flags
:标记 dentry 状态(是否有效、是否负向等)
目录项缓存(dentry cache ,简称 dcache)
dcache是 Linux VFS(虚拟文件系统)中非常重要的一部分。它的主要作用是: 加速路径名解析,避免重复的磁盘访问和 inode 查找 。
目录项缓存的作用
- 路径名到 inode 的快速映射
- dentry 保存了目录名(比如
docs
)与其对应 inode 的关系。 - 下次访问相同路径时可以直接从缓存获取 inode,而不用重新从磁盘读取。
- dentry 保存了目录名(比如
- 加速路径解析
- 路径解析是逐级查找的,dcache 缓存了父目录到子目录/文件的映射,避免重复查找。
- 例如多次访问
/home/user/docs/
,后续只需查缓存。
- 支持硬链接和别名
- dentry 允许同一个 inode 被多个不同的目录项引用,帮助 VFS 处理硬链接。
- 减少磁盘 I/O
- 频繁打开、关闭文件时,通常 inode 和目录项都已经在缓存中,不需要再次读磁盘。
- 配合 inode cache
- dentry 只缓存 “路径 → inode 的映射”,而 inode cache 保存 “inode 号 → inode 对象”。
- 两者结合起来,VFS 可以几乎不访问磁盘就完成大部分路径解析。
注意,这里dentry cache和inode cache都是保存在内存之中的
文件系统的路径名查找
这里以进程打开一个位于磁盘上的文件为例子 open(filename, flags, mode)
由于open要使用到系统的资源,因此需要使用SYS_OPEN, 陷入系统调用之后. 此时内核会去检查 dentrycache
查看有没有文件名对于的inode.如果找到了,那么根据该inode以及flags,mode,绑定对应inode中操作的函数指针,创建一个file类型,挂载在进程的file_struct中,同时,给该file对象分配一个fd,并且在fd_table中添加fd到file的映射,以根据文件描述符直接定位到制定的file. 如果没有找到,那么会依据目录名通过VFS中文件名解析的方式去查找,来返回对应文件名的inode,找到之后,在 dentrycache
中添加这对映射关系,然后重复上面更新到进程的步骤.最后将对应的文件描述符返回.