编辑
2025-07-23
个人笔记
00

目录

设备和驱动程序
Everything is a File
文件:有 “名字” 的数据对象
文件描述符
I/O 设备
总线
今天获得 “CPU 直连” 的标准设备
应用程序访问设备
配置设备
两种实现方法
存储设备原理
从1Bit到1TB
解决方法:按块访问
透明抽象的代价
Linux Bio
最后的版本答案——电存储
文件系统API
存储设备的抽象
链接
Linux系统文件元数据
文件系统实现
LVM
FAT
目录树
inode

设备和驱动程序

Everything is a File

文件:有 “名字” 的数据对象

  • 字节流 (终端,random)
  • 字节序列 (普通文件)

文件描述符

  • 指向操作系统对象的 “指针”
    • Everything is a file
    • 通过指针可以访问 “一切”
  • 对象的访问都需要指针
    • open, close, read/write (解引用), lseek (指针内赋值/运算), dup (指针间赋值)

I/O 设备

I/O 设备 = 一个能与 CPU 交换数据的接口/控制器

  • 就是 “几组约定好功能的线” (寄存器)
    • 通过握手信号从线上读出/写入数据
  • 给寄存器 “赋予” 一个内存地址 (Address Decoder)
    • CPU 可以直接使用指令 (in/out/MMIO) 和设备交换数据
    • 是的,就这么简单

总线

提供设备的虚拟化注册转发

  • 把收到的地址 (总线地址) 和数据转发到相应的设备上
  • 例子: port I/O 的端口就是总线上的地址
    • IBM PC 的 CPU 其实只看到这一个 I/O 设备

今天获得 “CPU 直连” 的标准设备

  • 接口
    • 75W 供电
      • 所以我们需要 6-pin, 8-pin 的额外供电
  • 数据传输
    • PCIe 6.0 x16 带宽达到 128GB/s
      • 于是我们有了 800Gbps 的网卡 😂
    • 总线自带 DMA (专门执行 memcpy 的处理器)
  • 中断管理
    • 将设备中断转发到操作系统 (Message-signaled Interrupts)

应用程序访问设备

一个设备会被多个程序使用,而设备的使用是基于上面的寄存器进行交互,如果不加以管理是非常混乱的。

通过操作系统虚拟化设备 => Everything is a File

源自于Everything is a File,设备被虚拟化为Unix上/dev/中的文件,而这些文件就是设备上的寄存器

驱动程序(Drive),把系统调用 “翻译” 成与设备能听懂的数据(就是一段普通的内核代码)

配置设备

设备交互的复杂不在于如何使用,而在于配置设备

设备不仅仅是数据,还有配置

两种实现方法

  • 控制作为数据流的一部分 (ANSI Escape Code)
  • 提供一个新的接口 (request-response)

存储设备原理

一个能反复改写的状态

  • 寻址+电路改写

经过这么多存储媒介的更迭,但是存储设备的抽象缺没有变更

从1Bit到1TB

  • 寻址能力的代价
    • 磁盘:位置划分 + 扇区头
    • 电路:行 (字线) 和列 (位线) 选通信号 这些都会消耗额外的资源 (面积)

解决方法:按块访问

  • “一块” 可以共享 metadata
    • 物理分割、Erase 信号、纠错码……
    • 磁盘是 struct block disk[NUM_BLOCKS]
      • Block 是读/写的最小单位
      • Block devices 块设备 (ls -l /dev/sd*)

透明抽象的代价

  • 不经意间的读/写放大 (read/write amplifications)
  • 存储设备在实际执行读写操作时,处理的数据量超过用户实际请求的数据量
    • 随机读/写一个 byte,都会导致大量数据传输
    • 文件系统的实现应该能够感知 “块” 的概念

Linux Bio

Linux中通过Linux BIO系统实现块设备管理

  • 上层 (进程、文件系统……) 可以任意提交请求
  • 下层 (Bio + Driver) 负责调度 ![[assets/持久化/file-20250606140816646.png]]

最后的版本答案——电存储

![[assets/持久化/file-20250606140442119.png]]

文件系统API

存储设备的抽象

磁盘 = 块(字节)序列

文件,虚拟的磁盘 怎么管理文件?

  • 树状分层索引:信息的局部性
  • 路径 => 结构化查询 => 智能检索

链接

  • 硬(Hard)链接
    • 同一个文件可以被重复“引用”,链接
  • 软(Symbolic)连接
    • “ 跳转提示 ”

Linux系统文件元数据

  • Type:d (directory), l (link), p (pipe), c (char), b (block)

  • Mode(权限)rwx (user, group, other)

    • 例子:0o755 = rwx (111) r-x (101) r-x (101)
  • Links(硬连接):引用计数 (硬链接,包括目录)

  • Extended Attributes (xattr) 更多的元数据

    • 每个文件可以维护一个任意的 key-value dictionary
    c
    ssize_t fgetxattr( int fd, const char *name, void value[.size], size_t size ); int fsetxattr( int fd, const char *name, const void value[.size], size_t size, int flags );

联合挂载

将硬盘设备虚拟化,得到了文件系统 将文件目录虚拟化,得到了容器、快照 在后面关于容器的课程会详细讨论......


文件系统实现

文件系统实现就是在块设备(硬盘)上实现一个数据结构,来对数据进行管理

通过文件系统,映射到磁盘上对应的数据,从而进行管理

LVM

传统意义上,磁盘的虚拟化 => 文件系统 现在通常是,使用LVM(Logical Volume Manager,逻辑卷管理器) ![[assets/持久化/file-20250625141445505.png]] LVM是一种块设备虚拟化技术,它允许操作系统将多个物理存储设备组合成一个或多个逻辑卷,从而提供更灵活和高效的存储管理。 LVM的架构:

  1. 物理层
  2. 物理卷
  3. 卷组
  4. 逻辑卷
  5. 文件系统
  • 物理硬盘首先被Linux挂载为/dev/sda/dev/sdb,将物理硬盘变为Linux上可用的资源
  • 然后LVM将挂载的硬盘,纳入并初始化设备为物理卷,也就是LVM系统中的数据结构
  • 卷组(VG) 将多个物理卷(PV)组合成一个统一的存储资源池,作为LVM的核心管理单元
  • 基于卷组的资源,LVM构建出逻辑设备——逻辑卷,并将逻辑卷挂载到Linux上,作为一个标准的块设备/dev/vg_name/lv_name

不过这部分内容不是这里的重点,这里主要探讨如何实现文件系统

FAT

文件是由多个块设备中的块组成的,多个块按一定的顺序排列,就组成的文件

于是乎文件在块设备的存储问题,变成了如何管理块设备中的块关系

  • 两种设计

    • 在每个数据块后放置指针
      • 数据大小不规则,要读文件最后一位就得全读一遍(链表)
    • ==将指针集中存放在文件系统的某个区域==
      • 局部性好,随机读取更佳
      • 索引损坏会导致全部数据丢死
  • FAT File Allocation Table: 集中保存所有指针

目录树

目录就是个普通文件

  • 但在 metadata 里标记一下 (mode = directory)
    • 操作系统把数据理解成 struct dirent[]
    • Quiz: 为什么不把元数据 (大小、文件名、……) 保存在文件的头部?
  • DOS: “8 + 3” 文件名 “AUTOEXEC.BAT”
    • Linux: /etc/xdg/autostart/
    • Windows: shell

inode

联合数据结构 ext2