todo
- 为什么文件系统不默认提供每个目录的空间使用量呢?
- 多文件系统嵌套, 还是需要用户自己去df挂载的位置里去找所在目录在哪个分区里, 然后分区大小多少, 为什么不能直接显示出当前目录使用量及这个文件系统根的容量呢?
- subvolume btrfs子文件系统支持
- 说到这个,df默认为啥没有什么超时机制呢?卡在显示所有上? 可能df只提供基本查询, 自己有超时需求自己处理?
- 文件系统配额, 如果不开启的话, 怎么知道这个文件系统究竟有多少空间可以用呢?
- 文件系统写满时, 到底是哪层做的错误返回?
- 分配器提供的吧?
- 所以对于分布式块设备上建的文件系统, 得是支持扩容的文件系统?
- 文件系统中的原子事务如何实现? 幂等性如何保障
- extent技术
- 隐藏的预留容量, 超级块
与块设备相比
问题原因 * 如何找到你想要的信息 * 如何保证一个用户不会读取另一个用户的数据 * 如何得知哪些块是空闲的, 等等
文件是上面这些问题的抽象解决.
文件系统概念[^3]
- 主要为存储设备提供一致的访问和管理方式.
- 数据以文件的形式存在
- 文件以树形目录进行组织.
- 较为复杂, 扩展性相比现在新增的对象存储较差一些.(各公司自研的应该还好)
- 文件系统的复杂性导致可扩展性未能跟上互联网的高速发展, 极大简化了的对象存储及时填补了空确.
- 不过因为对象存储缺乏树状结构, 也不支持原子重命名操作, 跟文件系统差异较大.
关键优化问题
存储大量小文件时, 一次文件多次读取元数据信息的"长尾效应"
- 比如
Haystack
通过合并多个小文件成一个大文件, 通过减少文件数量的方式解决
文件系统架构对比[^3]
MPI 并行文件系统
JuiceFS
TODO:JuiceFS 只需要专注元数据的管理,也大大降低了元数据服务的复杂度(GFS 和 MooseFS 的 master 要同时解决元数据的存储和数据块的健康管理)。
盘古
NebulasFS(360)
- master和datanode
多文件系统关联
- 挂载时
- 分配一个新的inode指向新的文件系统的data block.
挂载完成后,将在/proc/self/{mounts,mountstats,mountinfo}这三个文件中写入挂载记录和相关的挂载信息,并会将/proc/self/mounts中的信息同步到/etc/mtab文件中,当然,如果挂载时加了-n参数,将不会同步到/etc/mtab。[^3]
FUSE
- 用户-内核通信协议
- splice
- 零拷贝技术
- 内核FUSE队列
- 缓存写回
实现
文件系统布局
引导块
超级块
- 文件系统的大小
- 文件系统中的数据块数
- 指示文件系统状态的标志
- 分配组大小
空闲空间块
- bitmap位图
- bit vector 位向量
- 链表 #### 碎片 #### inode ### 分配 思想
- 有效利用文件空间
- 快速访问文件 #### 连续分配
- CD-ROM
- 满的时候, 标记-1之类的. #### 链表分配
- 随机访问难 #### 使用内存表进行链表分配
- 引入索引的概念
- FAT(File Application Table)
- 整个链表都在内存中, 表占用空间较大(1T盘 1KB的块, 需要至少3-4个字节的管理, 需要3GB左右的内存)
- 满的时候, 直接链表最后断开就行了. #### inode
- 只有文件打开时, inode才在内存中, 解决FAT的问题
- inode在初始化文件系统时就给定了, 多大的块设置一个inode.
- 128字节整数倍
- /etc/mk2fs.conf
- inode_ratio, 多大的块分配一个inode号
- ext4预留了inode用于比如/proc/, lost+found
- 问题:
- 文件知道自己的inode之后, inode存储了什么东西,可以让他找到对应的所有块? [^3]
- inode中保存了块的文件指针
- ext2/ext3 最多15个指针, 前12个直接寻址, 第13-15个分别一级,二级,三级间接寻址
- 一个4K block可以放4096/4=1024个指针. 即1024^3+ 1024^2 + 1024^1+12 = 1.1G个块指针, * 4K= 4T左右的大小
- ext4 使用了extent的方案
- 满的时候, 最后一个磁盘地址不指向数据块, 而是指向一个包含额外磁盘块地址的地址 ### 目录的实现 ### 共享文件
- 文件知道自己的inode之后, inode存储了什么东西,可以让他找到对应的所有块? [^3]
- 有向无环图
- 硬链接
- 软链接
- 问题:
- 复制时如果不对符号链接进行区分, 会带来重复写入问题
日志结构文件系统(Log structured File System, LFS)
- 性能问题
- 背景:
- 不断增长的内存
- 顺序I/O强于随机I/O
- 现有低效率的文件系统
- 文件系统不支持RAID(虚拟化)
- 由于Page cache存在, 读性能基本不是问题
- 数据结构
- Inode
- Inode Map
- Segment
- Segment Usage Table ### 日志文件系统
- 防止掉电/崩溃问题
- 幂等性
- 引入原子事务
虚拟文件系统
- VFS
- 对用户进程的上层接口POSIX接口
- 下层接口, 各文件系统提供的.
- vnode
文件系统的管理和优化
磁盘空间管理
- 分段管理
- 分页管理 #### 块大小 #### 记录空闲块
- 位图(bitmap)
- 磁盘块链表
- 问题
- 在内存中保留一个半满的指针块, 这样既处理文件的创建和删除, 又不会为空闲表进行磁盘IO
- 磁盘处理一些列临时文件, 不需要进行任何磁盘IO
磁盘配额
- 硬限制
- 软限制
- 用于实现警告和用户权限控制的计数 ### 文件系统备份
- 场景
- 从灾害中恢复
- 从错误的操作中恢复
- 删除->回收站
- 设计
- 备份整个文件系统还是只备份一部分文件
- 增量转储
- 压缩
- 备份过程中出错, 压缩文件是直接损坏还是可以纠正?
- 正在使用的文件系统如何备份
- 设置时间点? 瞬时快照
物理转储和逻辑转储
- 物理转储
- 全量备份
- 坏块转储
- 逻辑转储
- 维持一个
inode
为索引的bitmap
, 修改过的文件被标记
- 维持一个
- 空洞问题的处理 ### 文件系统的一致性
- 因为系统调用并不是原子事务的, 写操作复杂, 完全存在不一致可能性.
- fsck
- sfc(windows)
- 块的一致性检查
- 文件的一致性检查
- 空闲块和已使用的块的表的对照
- 块丢失(missing block)
- 块重复
- 空闲表中重复
- 直接标记就行
- 不同文件使用了这个块
- 分配一个磁盘快, 把他插入到文件里, 比如文本文件打开中, 出现掉电, 中间插入乱码是不是就是这个做法?
- 已使用和空闲表中均出现(就是删除过程只执行了一半)
- 应该也是优先分配一个新块, 插入到文件里
- 空闲表中重复
- 检查目录系统, 维护计数器表
- 检查inode数量与目录的关系
文件系统性能
高速缓存
- block cache
- buffer cache
- 逻辑上属于磁盘, 实际通过内存来提供支持
- 页面置换算法
块提前读
- 考虑顺序读取, 进行预读取
- 好像在磁盘驱动那层也有这个设计? 所以其实多层都提供了这样的功能?
减少磁盘臂运动
- 块簇
- 以连续块簇来跟踪磁盘存储区.
- 分配时尽量分配在同一个柱面上
- 解决一个连续内容被分配在2个柱面上带来的寻道次数翻倍
- 读文件至少2次磁盘访问
- 访问inode
- 访问块
- 原本inode放在磁盘开始位置, 所以inode到块的平均距离是柱面组的一半
- 改到中间
磁盘碎片整理
- 删除文件, 回收磁盘块.
- defrag, 移动文件, 使空闲块连续分布
- ext2/ext3选择磁盘块的方式, 导致不怎么需要处理磁盘碎片整理[^2]
- 日文件系统分配策略上并不完全连续分布
- ext的延迟写入技术
- 主要是FAT
文件
文件命名
- 名字
- unix 大小写敏感
- MS-DOS 大小写不敏感
- 扩展名
- unix 无感知
- 但是目前是不是也像window一样了
- windows 能够在操作系统层面设置不同扩展名对应的程序
- unix 无感知
文件结构
目前都是字节序列, 用使用者自己进行定位. 这样的话其实和操作裸块也有点像吧? 的确
不知道有没有采用树等结构化关系的, 感觉更适合更上层的应用自己抉择.
文件类型
- 文件
- 由内核处理
- 目录
- 字符特殊文件
- 串行I/O设备
- 块特殊文件
- 由设备驱动程序处理
操作系统至少能识别自己的可执行文件. 所以文件系统和操作系统还是有一些绑定关系的?
文件访问
文件属性
文件操作
- create
- delete
- open
- close
- read
- write
- append
- seed
- get attributes
- set attributes
- rename
目录
一级目录系统
- 根目录
层次文件目录
路径名
目录操作
- create
- delete
- opendir
- closedir
- readdir
- rename
- link
- unlink