linux系统编程——文件IO——文件系统和描述符
1. 从磁盘说起
1.1 读写磁盘数据
众所周知,磁盘读写是通过 盘面,磁道,扇区 3D寻址,用磁极 NS 表示 01 ,且通过接口指令(如SATA)完成共工作。当然这些都不重要,对用户层系统编程而言,只需要记住 磁盘读写是以 数据块为单位,且读写速度慢。
1.2 读写磁盘上文件
文件由两部分组成:
- 文件元数据 : 描述文件自身的数据
在磁盘的位置
创建时间,修改时间
大小
读写执行权限
所有者,所属组
- 文件里的数据
数据有多有少,但磁盘空间最小单位为一个 数据块,所以 1字节文件也要占用一个数据块,大文件占用连续数据块
1.2.1 磁盘利用率
由于文件至少使用 一个块的空间,所以 若 磁盘最小块 越小,能存放文件数量越多,但寻址时间增加。
1.2.2 逻辑块
不同厂家的磁盘,最小数据块 可能不同,为了方便 OS 统一操作,虚拟文件系统提供了统一大小的逻辑块,逻辑块大小为实际数据块大小的倍数。
2. 从文件到逻辑块
到此我们有了浩如烟海的逻辑块,也知道如何读写逻辑块,但是我们操作的单位是文件,那么如何通过 一个文件 找到 相关的逻辑块呢?
linux用 inode 解决 从 文件到逻辑块的 映射。
2.1 inode
inode就是linux对文件元数据的称呼。
inode在磁盘格式化时生成,在文件系统挂载时,磁盘的inode加载到内存中,创建文件时找到空闲的inode,录入文件信息,删除文件时 将对应inode改为空闲状态。内存中的inode会适时写入磁盘 以保持同步。
注意inode没有文件名信息
2.2 super block
inode的数量也太多了,于是linux使用名为 super block的结构维护 inode的使用情况。
实际上 super block 记录整个文件系统的信息:单个inode, block的大小,inode,block使用情况等。
super block 和 inode表 一样,在文件系统格式化 完成构造,当文件系统挂载时,载入内存,并常驻内存。
2.3 如何创建 super block 和 inode
磁盘在挂载前,需要分区和格式化。
分区可以视为初始格式化,分区会在磁盘首部构造一个 MBR 结构,用于记录整个磁盘信息和 各个分区信息,同时给主分区首部构造一个 boot 扇区。
格式化在某个分区安装文件系统,不同分区可以安装不同文件系统。格式化就是 构造 super block 和 inode 表,data block表。
3. 从路径名到inode
可以说 inode 就是一个文件,拿到inode就能读写文件数据,但是我们只有pathname,怎么通过 pathname 得到 inode呢?
3.1 目录和目录项
文件名 和 inode编号 是 key-value 关系,所以可以将 一个目录下的 所有文件 的 key-value(文件名和 inode 编号) 存放在一张表中,这张表作为数据存放在磁盘 的 data block,那么这些 data block 也就有一个 相关 的 inode ,或者说是文件,这个文件就是目录。而key-value对就是目录项。
3.2 从目录到pathname
我们有了目录这张表,就能找到该目录下所有文件的inode,但是如何获得目录的数据呢(也就是获得目录的inode)。
目录也是文件,所以他的inode被记录在其父目录的 data block中,所以需要找到其父目录的 inode,如此往复,如何是个头。
于是我们规定 最顶层目录,也叫根目录的inode为固定值 2。
所以以pathname为线索,从根目录开始就可以找到对应文件的inode
注意上面的pathname指绝对路径。
3.3 从相对路径到绝对路径
我们知道获得文件inode实际是使用绝对路径,但是我们更喜欢相对路径,怎么办呢?
首先进程知道自己的当前目录(目录的inode号),而每个目录都有两个特殊目录项, . 和 .. ,表示当前目录和上级目录的的 文件名和inode对,所以可以回溯到上级目录,如此往复,根目录的 . 和 .. 相关,都是2,所以当.和..的inode相等,就说明到顶层了,同时也 得到了pwd,并接上 相对路径,就得到目标文件的绝对路径。
4. 挂载
磁盘经过分区格式化后,还要挂载才能访问,挂载时需要指定挂载点,作为挂载点的目录将被屏蔽。这是为什么呢?
4.1 为什么能访问挂载的磁盘
VFS有一个 VFSMOUNT hash table,里面装 vfsmount 对象。
vfsmount 对象描述一个文件系统的所有挂载信息
父文件系统的挂载点:vfsmount->mnt_mountpoint = /mnt
子文件系统的根目录: vfsmount->mnt_root = superblock->s_root
上面的 superblock 是 该磁盘挂载到虚拟文件系统时,从磁盘载入的super block信息,而构造的superblock对象。
我们知道通过superblock对象可以访问磁盘的inode(也就是文件)。
注意:根文件系统同样也是挂载的,挂载到虚拟文件系统的 根目录
4.2 为什么挂载点原有文件不能访问
挂载成功后,挂载点的目录项 dentry标记为 DCACHE_MOUNTED。
用户输入pathname后,递归查找目录项,走到 挂载点目录项,发现 被标记为 DCACHE_MOUNTED,所以不使用其inode,而计算目录hash code,去 VFSMOUNT hash table查找对应的 vfsmount对象。
根据vfsmount->mnt_root找到子文件系统的根目录,继续在子文件系统查找目录项。
5. 虚拟文件系统
5.1 各种各样的文件系统
通常我们会同时使用多种文件系统,如磁盘文件系统ext,网络文件系统nfs等。
不同文件系统有不同的特性,这很好,但是文件系统读写方法差异很大,如何解决呢?
5.2 统一接口
虚拟文件系统采用面向对象思想解决 上述问题,通过回调函数满足不同文件系统有不同操作接口的需求,而对上层统一接口。
6. 进程读写文件
由上面可以知道,给内核一个pathname,内核能返回 对应的 inode,但我们知道进程不是直接使用inode,而是用一个 名为 文件描述符的东西,文件描述符和inode怎么关联呢?
6.1 file_struct
task_struct下有个 file_struct,这个结构记录此进程所有打开的文件。
struct file_struct {
...
struct file *fd_array[NR_OPEN_DEFAULT];
};
file_struct 下有个 fd_array的 指针数组,明显 文件描述符就是 这个数组的索引,所以 struct file就表示 打开的文件
6.2 struct file
struct file {
union {
struct llist_node fu_llist;
struct rcu_head fu_rcuhead;
} f_u;
struct inode *f_inode;
const struct file_operations *f_op;
spinlock_t f_lock;
atomic_long_t f_count;
fmode_t f_mode;
struct mutex f_pos_lock;
loff_t f_pos;
struct fown_struct f_owner;
struct address_space *f_mapping;
...
}
f_u 说明所有 打开的文件会构成一个 内核级表,这也为进程间传递文件描述符埋下基础
f_inode:磁盘的文件,多个进程可以打开同一个文件,所以多个struct file的 f_indoe 可以指向相同inode
f_op : 文件的读写等方法
f_count : 应该和 close()相关,表示被引用数,当引用数为0就真正释放本struct file
f_mode : 打开的模式
f_pos : 文件当前操作位置
f_mapping : 这和文件映射相关
7. 链接
- 硬链接: 多个 目录项 的inode相同,文件名不同,则为硬连接。当删除硬链文件,也就是删除目录项,当inode没有任何目录引用,则会被转为空闲
只能对存在文件创建硬链接,不能对目录创建硬链接,创建硬链接不能跨越文件系统分区
- 软链接:软链接本身为文件,data block的内容为 目标文件的 路径,可以用相对路径,也可以用绝对路径,删除软链接不会影响目标文件本身
可以对不存在的文件或目录创建软链接,可以对目录创建软链接,可以跨文件系统创建软连接
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?