文件系统
文件系统
文件系统主要负责管理和组织计算机存储设备上的文件和目录,其功能包括以下几个方面:
- 存储管理 :将文件数据存储到物理存储介质中,并且管理空间分配,以确保每个文件都有足够的空间存储,并避免文件之间发生冲突
- 文件管理 :文件的创建、删除、移动、重命名、压缩、加密、共享等等
- 目录管理 :目录的创建、删除、移动、重命名等等
- 文件访问控制 :管理不同用户或进程对文件的访问权限,以确保用户只能访问其被授权访问的文件,以保证文件的安全性和保密性
文件系统结构
Linux 文件系统会为每个文件分配两个数据结构:索引节点(index node)和目录项(directory entry),主要用来记录文件的元信息和目录层次结构
- 索引节点(inode):用来记录文件的元信息,比如 inode 编号、文件大小、访问权限、创建时间、修改时间、数据在磁盘的位置等
- 索引节点是文件的唯一标识,它们之间一一对应,也同样都会被存储在硬盘中,所以索引节点同样占用磁盘空间
- 目录项(dentry):用来记录文件的名字、索引节点指针以及与其他目录项的层级关联关系,多个目录项关联起来形成目录结构
- 与索引节点不同的是,目录项是由内核维护的一个数据结构,不存放于磁盘而是缓存在内存
由于索引节点唯一标识一个文件,而目录项记录着文件的名字,所以目录项和索引节点的关系是多对一,比如硬链接的实现就是多个目录项中的索引节点指向同一个文件
目录项是内核缓存在内存的数据结构,而目录是持久化存储在磁盘的文件
内核会把已经读过的目录用目录项缓存在内存以提高效率,同时目录项也可以表示文件
磁盘读写的最小单位是扇区,扇区的大小只有 512B,文件系统把多个扇区组成了一个逻辑块(数据块) 作为每次读写的最小单位以提高读写效率
Linux 中的逻辑块大小为 4KB,也就是一次性读写 8 个扇区
磁盘进行格式化的时候,会被分成三个存储区域
- 超级块:存储文件系统的详细信息,比如块个数、块大小、空闲块等
- 当文件系统挂载时进入内存
- 索引节点区:存储索引节点
- 当文件被访问时进入内存
- 数据块区:存储文件或目录数据
虚拟文件系统
操作系统在用户层与文件系统层引入了中间层以屏蔽不同文件系统的差异,提供统一接口,这个中间层就称为虚拟文件系统(Virtual File System,VFS)
根据存储位置的不同,可以把文件系统分为三类:
- 磁盘的文件系统:直接把数据存储在磁盘中,比如 Ext 2/3/4、XFS 等
- 内存的文件系统:文件系统的数据不存储在硬盘的而占用内存空间,
/proc和/sys文件系统都属于这一类,读写这类文件,实际上是读写内核中相关的数据 - 网络的文件系统:用来访问其他计算机主机数据的文件系统,比如 NFS、SMB 等等
文件使用
文件系统的基本操作单位是数据块
当用户进程从文件读取 1 个字节大小的数据时,文件系统需要获取字节所在的数据块,再返回数据块对应的用户进程所需的数据部分
当用户进程把 1 个字节大小的数据写进文件时,文件系统则找到需要写入数据的数据块的位置,然后修改数据块中对应的部分,最后再把数据块写回磁盘
操作系统通过为每个进程维护一个打开文件表来跟踪进程打开的所有文件
打开文件表的每一项都是一个文件描述符,其中包含打开文件的状态和信息:
- 文件指针:系统跟踪上次读写位置作为当前文件的位置指针,这种指针对打开文件的某个进程来说是唯一的
- 文件打开计数器:记录当前打开该文件的进程数,确保在文件被使用时,不会被意外地关闭或删除
- 文件磁盘位置:绝大多数文件操作都要求系统修改文件数据,该信息保存在内存中,以免每个操作都从磁盘中读取
- 访问权限:每个进程打开文件都需要有一个访问模式(创建、只读、读写、添加等),该信息保存在进程的打开文件表中,以便操作系统能允许或拒绝之后的 I/O 请求
文件存储
连续空间存放
文件存放在磁盘连续的物理空间中,文件头(inode)里需要指定起始块的位置和长度
优点:读写效率高
缺点:容易产生磁盘碎片,文件长度不易扩展
非连续空间存放
- 链表存放
- 隐式链表:文件头包含第一数据块和末尾数据块的位置,并且每个数据块里面留出一个指针空间,用来存放下一个数据块的位置
- 无法直接访问数据块,只能通过指针顺序访问文件
- 指针占用空间
- 稳定性差,某个指针损坏影响后面所有数据块
- 显式链接:将用于链接文件各数据块的指针,显式地存放在内存中的一张文件分配表(File Allocation Table,FAT)中,每个表项中存放链接指针,指向下一个数据块号,以一个不属于有效磁盘编号的特殊标记(如 -1 )结束
- 对于大磁盘,文件分配表占用过大内存空间
- 优点:文件的长度可以动态扩展,没有磁盘碎片
- 隐式链表:文件头包含第一数据块和末尾数据块的位置,并且每个数据块里面留出一个指针空间,用来存放下一个数据块的位置
- 索引存放:为每个文件创建一个索引数据块以存放指向文件数据块的指针列表,文件头里包含指向索引数据块的指针
- 优点:文件的增减方便,没有磁盘碎片,支持随机读写
- 缺点:存储索引带来开销
- 链式索引:在索引数据块留出一个存放下一个索引数据块的指针
- 多级索引:通过一个索引块来存放多个索引数据块
Unix 实现
早期 Unix 文件系统采用组合方式存放文件,根据文件的大小采用不同的存放方式:
- 如果存放文件所需的数据块小于 10 块,则采用直接查找的方式
- 如果存放文件所需的数据块超过 10 块,则采用一级间接索引方式
- 如果前面两种方式都不够存放大文件,则采用二级间接索引方式
- 如果二级间接索引也不够存放大文件,这采用三级间接索引方式
该方法对于小文件可使用直接查找的方式减少索引数据块的开销,但对于大文件采取的多级索引方式需要大量的 IO 查询,效率较低
目录存储
普通文件块里保存文件数据,目录文件块里保存目录里面一项一项的文件信息
目录文件块中最简单的保存格式是列表,将目录下的每项文件信息(如文件名、文件 inode、文件类型等)在列表中顺序存储,每一项代表该目录下的文件的文件名和对应的 inode,通过 inode 可以找到真正的文件
Linux 系统的 ext 文件系统采用哈希表来保存目录的内容,对文件名进行哈希计算并存储,查找时通过哈希值可实现迅速查找
可通过在内存中缓存当前使用的文件目录以降低磁盘操作次数,提高效率
软链接&硬链接
- 硬链接 - 多个目录项中的索引节点指向同一个文件
- 不可跨文件系统 - 每个文件系统有自己的独立 inode(索引节点)表,仅维护该系统内的 inode
- 只有删除文件的所有硬链接以及源文件时,系统才会彻底删除该文件
- 使用
ln命令创建
- 软链接 - 重新创建一个新文件,内容是另外一个文件的路径
- 可以跨文件系统
- 链接文件和目标文件独立,目标文件被删除时链接文件仍独立存在,但失效
- 使用
ln -s命令创建
空闲空间管理
Linux 采用位图法管理数据空闲块和 inode 空闲块,利用二进制的一位来表示磁盘中一个盘块的使用情况,磁盘上所有的盘块都有一个二进制位与之对应
文件 IO
常见的文件 IO 分类包括:
- 缓冲与非缓冲 I/O
- 直接与非直接 I/O
- 阻塞与非阻塞 I/O VS 同步与异步 I/O
缓冲/非缓冲 IO
缓冲 I/O 利用标准库的缓存实现文件的加速访问,而标准库通过系统调用访问文件
非缓冲 I/O,直接通过系统调用访问文件,不经过标准库缓存
缓冲 IO 可以减少系统调用的次数,进而减少 CPU 上下文切换的开销
直接/非直接 IO
直接 IO 直接经过文件系统访问磁盘,不会发生内核缓存和用户程序之间数据复制
非直接 IO 读操作时,数据从内核缓存中拷贝给用户程序,写操作时,数据从用户程序拷贝给内核缓存(PageCache),由内核决定写入数据到磁盘的时机
在使用文件操作类的系统调用函数时指定 O_DIRECT 标志则表示使用直接 IO,默认使用非直接 IO
非直接 IO 写入时机:
- 调用
write后内核缓存的数据太多时 - 用户主动调用
sync时 - 内存用尽无法再分配页面时
- 内核缓存的数据的缓存时间超过指定时间时
Page Cache
程序运行时具有局部性,PageCache 用于缓存最近被访问的数据,当空间不足时淘汰最久未被访问的缓存
PageCache 还包括预读功能,会先预读与被访问数据物理相邻的数据
对于大文件传输 PageCache 不适用,应使用异步 IO+直接 IO 来传输大文件
对于小文件传输,可使用 PageCache 实现 零拷贝技术
阻塞/非阻塞 IO&同步/异步 IO
当用户执行 read 时
阻塞 IO 等待内核数据准备完成和数据从内核态拷贝到用户态两个过程后返回数据
非阻塞 IO 在数据未准备好的情况下立即返回,同时应用系统不断轮询内核直到数据准备完成,再等待内核将数据拷贝到应用程序缓冲区后返回数据
基于非阻塞 IO 的 IO 多路复用可以在一个线程内同时处理多个 socket 的 IO 请求
访问管道或 socket 时设置 O_NONBLOCK 标志使用非阻塞 IO ,默认使用阻塞 IO
阻塞 IO 和非阻塞 IO 都为同步 IO,都需要等待内核将数据由内核空间拷贝至用户空间
异步 IO 中内核自动将数据从内核空间拷贝到应用程序空间,之后再通知应用程序处理数据
举个你去饭堂吃饭的例子,你好比用户程序,饭堂好比操作系统
阻塞 IO 好比,你去饭堂吃饭,但是饭堂的菜还没做好,然后你就一直在那里等啊等,等了好长一段时间终于等到饭堂阿姨把菜端了出来(数据准备的过程),但是你还得继续等阿姨把菜(内核空间)打到你的饭盒里(用户空间),经历完这两个过程,你才可以离开
非阻塞/O 好比,你去了饭堂,问阿姨菜做好了没有,阿姨告诉你没,你就离开了,过几十分钟,你又来饭堂问阿姨,阿姨说做好了,于是阿姨帮你把菜打到你的饭盒里,这个过程你是得等待的
基于非阻塞的/O 多路复用好比,你去饭堂吃饭,发现有一排窗口,饭堂阿姨告诉你这些窗口都还没做好菜,等做好了再通知你,于是等阿等(se1ct 调用中),过了一会阿姨通知你菜做好了,但是不知道哪个窗口的菜做好了,你自己看吧。于是你只能一个一个窗口去确认,后面发现5号窗口菜做好了,于是你让5号窗口的阿姨帮你打菜到饭盒里,这个打菜的过程你是要等待的,虽然时间不长。打完菜后,你自然就可以离开了
异步 IO 好比,你让饭堂阿姨将菜做好并把菜打到饭盒里后,把饭盒送到你面前,整个过程你都不需要任何等待
DMA
过去 IO 过程中由 CPU 将数据从设备拷贝至 PageCache,再由 PageCache 拷贝至用户缓冲区
DMA 技术下在进行 I/O 设备和内存的数据传输的时候,数据拷贝工作全部由 DMA 控制器执行,CPU 仅指定数据和传输位置

