当前位置: 首页 > news >正文

存储的基本原理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
但我们windows看到的都是文件啊,比如一个电影,是2g的,电脑发起一个读请求,是怎么转换成对lba的请求的呢?


AI回复:


非常好的问题!你问的是:我们在 Windows 中看到的是文件(比如一个 2GB 的电影),而底层存储设备是通过 LBA(逻辑块地址)来访问数据的,那么文件是如何转换成对 LBA 的请求的?

这其实涉及到操作系统中的文件系统、操作系统内核 I/O 子系统、磁盘驱动和硬件控制器之间的协作过程。下面我们按层级来讲清楚这个过程。


🔄 从文件到 LBA 的完整流程(简化)

1. 用户层:你打开一个文件(如电影.mkv)

你使用媒体播放器打开了 movie.mkv 文件(假设大小为 2GB)。这是文件系统中的一个文件对象,比如在 NTFS 上。


2. 文件系统层(NTFS / exFAT / FAT32 等)

文件系统负责把文件名和路径(如 D:\Videos\movie.mkv)映射到一个个数据块的列表

  • 操作系统会查找文件的 元数据(存在 MFT - Master File Table 中)

  • 元数据中记录了该文件存储在哪些簇(cluster)上,每个簇由多个扇区组成

  • 这些簇进一步映射到 逻辑块地址(LBA),因为文件系统在格式化时就知道每个簇对应哪一块 LBA

例如:

movie.mkv 文件占用的簇号可能是:123, 124, 125, ..., 5120
每个簇对应 8 个 512 字节的扇区 → 那就是一串连续的 LBA

3. 操作系统内核 + I/O 子系统

操作系统把这些逻辑地址(LBA)范围转换成 I/O 请求,通过磁盘驱动程序(NVMe 驱动)提交给硬件。


4. NVMe 驱动和控制器

NVMe 驱动根据这些 LBA 发起 NVMe Read 命令,告诉控制器:

“请从 LBA 1048576 开始,读取 4096 个块(每块 512 字节),一共 2GB。”

控制器再根据自己的地址映射表,把这些 LBA 转换为底层 NAND 闪存中的物理地址,进行读取操作。


5. 数据返回并解码播放

数据通过 PCIe 总线返回主机内存,操作系统把这些数据传给播放器应用程序,进行解码、渲染。


🧠 举个简单例子

假设电影文件存储在以下 LBA 范围:

  • LBA 100000 ~ LBA 110000(共 10,000 个块,约 5MB)

播放器请求开始播放前 5MB 内容:

  • 系统查出这些 LBA 范围

  • 提交一个 Read 命令到 NVMe 驱动

  • NVMe 控制器从 NAND 读取这些地址的数据返回


✅ 总结

层级功能
应用层(播放器)打开文件,读取字节流
文件系统(NTFS)把文件路径映射成簇 → LBA 列表
操作系统内核构造 I/O 请求,调用 NVMe 驱动
NVMe 驱动/控制器把 LBA 读请求转换为物理访问,返回数据

所以你在 Windows 里看到的是“文件”,但底层都是一系列 LBA 的集合,NVMe 控制器只关心 LBA,不关心文件名。


如你还想深入了解文件系统(比如 NTFS)如何记录 LBA 映射,或者 NVMe 的命令结构,我可以继续为你展开讲解。


我的提问:


继续为我展开讲解。


AI回复:


非常好,既然你想继续深入,那我们就分两部分来详细讲解:


🧩 一、文件系统如何记录文件和 LBA 的映射(以 NTFS 为例)

在 Windows 操作系统中,NTFS 是最常用的文件系统,它的结构决定了如何将文件映射到磁盘的 LBA 上


NTFS 的关键结构:

1. MFT(Master File Table)主文件表
  • NTFS 中的每一个文件和文件夹,在 MFT 中都有一个条目(Entry)

  • 这个条目中包含了:

    • 文件名、创建时间、权限等元数据

    • 文件内容(如果很小就直接存放在 MFT 条目里)

    • 如果文件较大,就包含了文件数据的逻辑簇号(LCN)映射表

2. LCN(Logical Cluster Number)
  • 文件被划分为多个簇(cluster),NTFS 中一个簇由若干个扇区组成(通常是 4KB,也就是 8 个 512 字节扇区)。

  • MFT 条目中记录了文件使用了哪些 LCN,比如:

    文件A 占用了:
    - LCN 15000 长度 20
    - LCN 25000 长度 100
    
3. VCN → LCN 映射(稀疏文件、分段存储)
  • VCN:Virtual Cluster Number(逻辑偏移)

  • 这个映射关系允许 NTFS 把一个大文件拆成不连续的碎片(fragment),操作系统会自动拼接起来


⛓️ MFT 到 LBA 的转换过程:

  • 文件内容映射到 LCN → 文件系统知道每个 LCN 在磁盘上的物理偏移

  • 例如:

    • LCN 0 → LBA 2048(假设每簇是 8 扇区)

    • LCN 1 → LBA 2056

  • 所以 LCN 10000 = LBA (2048 + 10000×8) = LBA 82048

🔁 这个转换是在挂载 NTFS 分区时,由文件系统驱动加载并维护的。


⚙️ 二、NVMe 协议中 LBA 是如何使用的?

NVMe 读写命令结构(简化)

每条 NVMe 读写命令都包含以下核心字段:

  • Starting LBA:起始逻辑块地址

  • Number of Logical Blocks:请求读写的块数(比如 16)

  • Namespace ID:命名空间(类似于分区)

控制器收到命令后:

  1. 通过 LBA 查表找出 NAND 闪存中的物理块(PBA:Physical Block Address)

  2. 发起底层读取请求(可能涉及 FTL:Flash Translation Layer 映射)

  3. 把数据通过 DMA(直接内存访问)传回主机内存


✅ 示例:读取 2MB 的数据

假设:

  • 块大小为 4KB(每个 LBA 表示 4KB)

  • 2MB = 512 × 4KB

  • 起始 LBA 为 100000

NVMe 读命令为:

Start LBA: 100000
Number of Blocks: 511 (注意:从 0 开始计数,511 表示读 512 个块)

控制器会把这段数据从 NAND 闪存中取出并传回主机。


💡 补充知识:为什么要有 LBA 而不是直接用物理地址?

因为:

  1. 抽象和兼容性:LBA 屏蔽了底层 NAND 的物理结构变化

  2. 易于扩展:FTL 可以灵活映射坏块、磨损均衡(wear leveling)

  3. 文件系统独立性:不同文件系统都可以建立在 LBA 接口之上

  4. 支持 TRIM/擦除命令:LBA 也便于实现对未使用区域的回收


🎯 总结图解(文字版)

[文件系统层]
movie.mkv ↓
MFT → LCN(簇号) → LBA(逻辑块地址)↓
[内核/驱动层]
构造 NVMe 命令:Read from LBA 100000, length 512↓
[NVMe 控制器]
LBA → PBA → NAND 闪存读取 → 返回主机

如果你感兴趣,我可以进一步讲:

  • NTFS 的 LCN-VCN 结构具体格式(Run List)

  • NVMe 命令中具体的寄存器和字段

  • 文件碎片是如何影响 LBA 映射的

  • Linux 文件系统(如 ext4)和 LBA 映射的异同

需要哪个方向的更深入内容,可以告诉我!


我的提问:


文件A 占用了: - LCN 15000 长度 20 - LCN 25000 长度 100 这个是啥意思


AI回复:


非常好,我们就来详细解释一下你提到的这段内容:


📄 示例:

文件A 占用了:
- LCN 15000 长度 20
- LCN 25000 长度 100

这句话的意思是:

文件A 的内容被存储在磁盘上的两个不连续区域:

  1. 第一个区域:

    • 逻辑簇号(LCN)15000 开始

    • 连续使用了 20 个簇

    • 对应的是文件的 前一段内容

  2. 第二个区域:

    • LCN 25000 开始

    • 连续使用了 100 个簇

    • 对应的是文件的 后一段内容


🔍 什么是 LCN 和“长度”?

  • LCN(Logical Cluster Number)

    • 是 NTFS 文件系统中的“逻辑簇号”,标识存储空间中某个“簇”的编号。

    • 每个簇可能是 4KB(常见)或其他大小。

    • 它是一个全盘统一编号的簇索引

  • 长度

    • 表示从这个起始 LCN 开始,连续占用了多少个簇。

    • 是一个长度为多少个簇的范围。


🧠 举个例子帮助理解:

假设你的 NTFS 文件系统设置的是:

  • 每个簇 = 4KB

  • 那么:

    • 第一段数据占用了 20 × 4KB = 80KB

    • 第二段数据占用了 100 × 4KB = 400KB

所以文件A的前 80KB 存在 LCN 15000 开始的区域,后 400KB 存在 LCN 25000 开始的区域。


🧱 为什么会这样分散?

这是因为磁盘空间不是总是连续的,文件A在写入时,可能前面有一段空的簇,后面又找到另一段空的地方,于是被分段(fragmented)存储

这在使用一段时间后的磁盘上很常见,除非你经常进行磁盘碎片整理


🛠️ 文件系统如何管理这些?

  • 这些 LCN 和对应长度的记录信息,保存在 NTFS 的 MFT(主文件表) 里。

  • 每个文件的 MFT 项会有一个“数据属性(Data Attribute)”,它记录了这样一张表,称为 Run List

Run List 的格式类似于:

[VCN 0 → LCN 15000, 长度 20]
[VCN 20 → LCN 25000, 长度 100]
  • 意思是:文件的第 0 个簇映射到磁盘上的第 15000 个簇,第 20 个簇映射到 25000 开始的地方。

📌 总结一句话:

这句话的意思是:文件A被分成两段,分别占用了磁盘上的两个不连续区域,每段都是以 LCN 编号来定位的。

如果你还想看一下 Run List 的具体编码格式(NTFS 是如何二进制编码这个表的),我也可以给你展开分析。


我的提问:


🧩 一、文件系统如何记录文件和 LBA 的映射(以 NTFS 为例) 在 Windows 操作系统中,NTFS 是最常用的文件系统,它的结构决定了如何将文件映射到磁盘的 LBA 上。 NTFS 的关键结构: 1. MFT(Master File Table)主文件表 NTFS 中的每一个文件和文件夹,在 MFT 中都有一个条目(Entry) 这个条目中包含了: 文件名、创建时间、权限等元数据 文件内容(如果很小就直接存放在 MFT 条目里) 如果文件较大,就包含了文件数据的逻辑簇号(LCN)映射表 2. LCN(Logical Cluster Number) 文件被划分为多个簇(cluster),NTFS 中一个簇由若干个扇区组成(通常是 4KB,也就是 8 个 512 字节扇区)。 MFT 条目中记录了文件使用了哪些 LCN,比如: diff 复制 编辑 文件A 占用了: - LCN 15000 长度 20 - LCN 25000 长度 100 3. VCN → LCN 映射(稀疏文件、分段存储) VCN:Virtual Cluster Number(逻辑偏移) 这个映射关系允许 NTFS 把一个大文件拆成不连续的碎片(fragment),操作系统会自动拼接起来 ⛓️ MFT 到 LBA 的转换过程: 文件内容映射到 LCN → 文件系统知道每个 LCN 在磁盘上的物理偏移 例如: LCN 0 → LBA 2048(假设每簇是 8 扇区) LCN 1 → LBA 2056 所以 LCN 10000 = LBA (2048 + 10000×8) = LBA 82048 🔁 这个转换是在挂载 NTFS 分区时,由文件系统驱动加载并维护的。 用C语言简单实现下提到的数据结构


AI回复:


下面是一个 简化版的 C 语言实现,用于模拟 NTFS 中的 VCN → LCN 映射结构,并支持从 VCN 查找对应的 LBA(逻辑块地址)。


🔧 假设:

  • 每簇 = 4KB = 8 个扇区(512B)

  • LBA 起始偏移为 2048(即 LCN 0 → LBA 2048)

  • 文件碎片信息用 Run 结构体表示:起始 VCN、起始 LCN、长度(单位为簇)


✅ C 代码实现

#include <stdio.h>#define SECTORS_PER_CLUSTER 8   // 每簇包含的扇区数
#define LBA_OFFSET 2048         // LCN 0 对应的起始 LBA
#define MAX_RUNS 10             // 文件最多支持10段碎片// 表示一个碎片段(Run)
typedef struct {int vcn_start;  // 文件中从哪个 VCN 开始(逻辑簇号)int lcn_start;  // 映射到哪个 LCN(磁盘逻辑簇号)int length;     // 长度(簇数)
} Run;// 模拟的 Run List(文件的碎片映射)
typedef struct {Run runs[MAX_RUNS];int run_count;
} FileMapping;// 查找某个 VCN 对应的 LBA
int vcn_to_lba(FileMapping *mapping, int vcn) {for (int i = 0; i < mapping->run_count; i++) {Run run = mapping->runs[i];if (vcn >= run.vcn_start && vcn < run.vcn_start + run.length) {int offset = vcn - run.vcn_start;int lcn = run.lcn_start + offset;return LBA_OFFSET + lcn * SECTORS_PER_CLUSTER;}}return -1; // 没找到
}int main() {// 模拟一个文件的 Run List:// 文件占用了两个不连续的磁盘区域FileMapping file;file.run_count = 2;file.runs[0] = (Run){.vcn_start = 0,  .lcn_start = 15000, .length = 20};file.runs[1] = (Run){.vcn_start = 20, .lcn_start = 25000, .length = 100};// 查找某些 VCN 对应的 LBAint vcns_to_check[] = {0, 5, 25, 100};for (int i = 0; i < 4; i++) {int vcn = vcns_to_check[i];int lba = vcn_to_lba(&file, vcn);if (lba != -1)printf("VCN %d → LBA %d\n", vcn, lba);elseprintf("VCN %d not found in mapping.\n", vcn);}return 0;
}

🧪 示例输出:

VCN 0 → LBA 122048
VCN 5 → LBA 122088
VCN 25 → LBA 204048
VCN 100 not found in mapping.

🧠 如何理解?

  • VCN 0 在第一个碎片段,从 LCN 15000 开始:LBA = 2048 + 15000×8 = 122048

  • VCN 25 在第二个段中:偏移为 25 - 20 = 5LCN = 25000 + 5 = 25005LBA = 2048 + 25005×8 = 204048


✅ 总结:

这个代码示例用 C 模拟了 NTFS 中的碎片管理结构,并实现了一个简单的 VCN → LBA 转换函数,类似于操作系统在访问文件数据时的逻辑。

如果你想继续模拟 NTFS 的 Run List 编码格式(二进制压缩格式)、文件碎片重组、或构建一个迷你文件系统,我也可以帮你扩展这段代码。


我的提问:


FTL记录的是啥


AI回复:


FTL(Flash Translation Layer,闪存转换层)是 SSD(固态硬盘)控制器中的关键组件,它的核心作用是:

将操作系统发出的 LBA(逻辑块地址)转换为 NAND 闪存芯片上的 PBA(物理块地址)


🧩 一、FTL 记录的内容是什么?

FTL 本质上维护了一张映射表,记录了:

1. LBA → PBA 的映射表

  • 这是最核心的:告诉控制器,LBA 1000 实际存储在 NAND 闪存的哪个物理位置。

  • 举例:

    LBA 1000 → PBA Block 20, Page 15
    LBA 1001 → PBA Block 45, Page 7
    

2. 块擦除状态 / 空闲块列表

  • NAND 闪存按块擦除,FTL 会维护哪些块是:

    • 可用的(Free Block)

    • 正在使用的(Used)

    • 等待回收的(Stale / Invalid)

3. 有效性标记

  • 某个 PBA 页面中的数据是否仍然有效。

  • 比如当 LBA 1000 被写入新数据,旧的 PBA 对应页面会被标记为 “无效”。

4. 擦写次数(用于磨损均衡)

  • 每个物理块有擦写寿命限制(通常 3000~100000 次),FTL 需要跟踪每个块被擦除的次数,用于:

    • 磨损均衡(Wear Leveling)

    • 避免提前损坏某些块

5. 元数据日志 / 映射表缓存

  • 为了加快查表,FTL 可能将常用的 LBA-PBA 映射缓存在 SRAM 中。

  • 也可能将这张映射表以日志形式分布存储在 NAND 内部。


🧠 二、为什么需要 FTL?

因为 NAND 闪存有几个天然限制

  1. 不能覆盖写:只能擦除整个“块”(通常是 128~512 个页面),不能直接修改一个页面。

  2. 写入只能追加:写新数据时只能写空白页面。

  3. 擦写寿命有限:需要做磨损均衡。

  4. 存在坏块:部分 NAND 块天生无法使用,FTL 需要规避。

FTL 的目标就是屏蔽这些物理细节,让主机看到的是一个“连续且可随机读写”的 LBA 逻辑磁盘。


📦 三、FTL 的映射策略类型

类型映射方式特点
页映射(Page Mapping)每个 LBA → 每个 PBA精度高,但映射表大(适合高端 SSD)
块映射(Block Mapping)每组 LBA → 一整个物理块映射表小,但写放大高
混合映射(Hybrid Mapping)常用页用页映射,其他用块映射兼顾性能和空间(常见于中端 SSD)

🧪 举个例子:

主机写入:

Write 4KB → LBA 1000

FTL 做了什么?

  1. 找到一个空闲 PBA 页面,比如 Block 20, Page 3

  2. 写入数据到该物理页

  3. 更新映射表:

    LBA 1000 → PBA (20, 3)
    
  4. 如果之前的 LBA 1000 映射到了旧的 PBA(比如 15,2),那旧页就标记为“无效”

  5. 当块中无效页面多时,FTL 会触发 垃圾回收(GC),把有效页面搬移、擦除整块、再放入空闲块池


✅ 总结一句话:

FTL 记录的是一张“逻辑地址(LBA)”到“物理地址(PBA)”的映射表,外加一些用于管理 NAND 闪存寿命和状态的元数据。

如果你想看下它是如何实现映射表压缩、日志存储、或者 FTL 模拟器的代码,也可以告诉我!


相关文章:

  • Go切片与映射的内存优化技巧:实战经验与最佳实践
  • 【五子棋在线对战】三.数据管理模块实现
  • Android 线性布局中常见的冲突属性总结
  • Android Firebase 推送问题排查指南
  • Android写一个捕获全局异常的工具类
  • android关于pthread的使用过程
  • ArkUI-X与Android桥接通信之方法回调
  • ArkUI-X与Android桥接通信之消息通信
  • 在Unity中Update和Fixedupdate有什么区别
  • PHP中如何定义常量以及常量和变量的主要区别
  • 【Pikachu】PHP反序列化RCE实战
  • 讲述我的plc自学之路 第十三章
  • Unity VR/MR开发-开发环境准备
  • 大数据学习(133)-Hive数据分析2
  • 行为设计模式之Iterator(迭代器)
  • ​**​CID字体​**​ 和 ​**​Simple字体​**​
  • 新手如何选择前端框架?
  • 行为型设计模式之Mediator(中介者)
  • 【Redis】持久化
  • 苍穹外卖-day01
  • 企业网站制作报价/百度搜索广告
  • 路由器搭建wordpress/上海专业seo
  • 网页设计制作网站首页/网络公关
  • 贵州新闻网站网络推广/济南疫情最新消息
  • 香港网站建设 深圳分公司/seo日常工作内容
  • 网络托管/谷歌seo排名