从双击文件夹到数据落盘:一篇说清 IO、存储、硬盘和文件系
我们写代码、存照片、下电影,天天都在跟这几样东西打交道:IO(数据怎么进进出出)、存储(数据存在哪)、硬盘(最终的仓库)、文件系统(仓库的管理员)。很多人对这些概念是模糊的,感觉它们混在一起。今天就把这层窗户纸捅破重点把文件系统讲明白
一、存储金字塔:速度、容量、价格不可兼得
先建立一个基本认知:计算机里的存储是分级的,越往上越快、越贵、越少
| 层级 | 代表 | 特点 |
|---|---|---|
| 寄存器 | CPU 内部 | 纳秒级,容量极小 |
| 缓存(Cache) | L1/L2/L3 | 微秒级,MB 级别 |
| 内存(RAM) | 内存条 | 几十纳秒,GB 级别,断电数据消失 |
| 硬盘/SSD | 机械盘、固态盘 | 毫秒级,TB 级别,断电数据还在 |
核心结论:内存是"临时工",硬盘是"永久档案室"。程序运行时数据在内存里飞,但要想不丢,最终都得落盘。
二、硬盘:机械盘 vs 固态盘,区别到底在哪
硬盘是最终存数据的地方,现在分两种:
1. 机械硬盘(HDD)
里面是真有马达和磁头的,像唱片机一样。磁头要移动到对应的磁道,等盘片旋转到对应位置,才能读写。
寻道时间:磁头移动找磁道(几毫秒)
旋转延迟:等盘片转过来(几毫秒)
顺序读写快:磁头不用大挪移,一路读过去
随机读写慢:磁头来回跑,性能暴跌
2. 固态硬盘(SSD)
没有机械结构,全是闪存芯片(NAND)。靠电信号读写,所以:
随机读写极快:不用等磁头,直接定位
顺序读写也快:比 HDD 快一个数量级
怕写坏:闪存单元擦写次数有限,所以有磨损均衡算法
有写入放大:小文件频繁写入会影响寿命
一句话:HDD 像图书馆找书(要走路),SSD 像电子书直接跳转。现在买电脑,系统盘无脑上 SSD。
三、IO:程序怎么跟硬盘打交道?
IO(Input/Output)就是数据的输入输出。程序想读硬盘里的文件,不是直接伸手就抓的,要经过好几层:
程序 → 系统调用(read/write)→ 内核页缓存 → 文件系统 → 硬盘驱动 → 硬盘控制器 → 物理硬盘
这里提几个实际开发常听见的词:
BIO(阻塞 IO):你发起一个读请求,干等着数据回来,期间啥也干不了。简单但低效。
NIO(非阻塞/多路复用):你发起请求,不傻等,先去干别的,数据好了通知你。Netty、Redis 都用这套。
AIO(异步 IO):更彻底,内核帮你把数据搬好了再叫你,程序全程不操心。
日常开发记住:硬盘是慢设备,程序跟硬盘打交道,尽量批量、顺序、减少次数,别一个字节一个字节地折腾。
四、文件系统:硬盘上的"翻译官"(重点)
:文件系统到底是什么?
1. 没有文件系统,硬盘就是一块裸砖
硬盘出厂时就是一大块连续的空间,它只认识"从第 1000 块到第 2000 块写点数据"。它不认识什么叫"我的简历.docx",也不认识什么叫"用户头像文件夹"。
文件系统就是中间的翻译官。它把硬盘这个"裸砖"组织成我们熟悉的"文件"和"文件夹",负责:
哪个文件存在哪几个块上
怎么找文件
怎么分配空间
怎么保证写一半断电不丢数据
2. 文件系统的核心三件套
① inode(文件的身份证)
每个文件(和目录)都有一个 inode。你可以把它理解为文件的"档案袋",里面记录了:
文件大小(年龄)
存在硬盘哪几个块上(块指针)(地址)
创建时间、修改时间(出生日期)
权限(谁可读可写)
所有者(名字)
注意:inode 里不存文件名。文件名存在目录里
② data block(数据块)
这是真正存文件内容的地方。比如你一个 1MB 的文件,文件系统把它切成一个个 4KB 的小块,分散存在硬盘的不同位置(也可能是连续的),然后在 inode 里记下这些块的地址。
③ superblock(文件系统的户口本)
存在硬盘最开头,记录整个文件系统的全局信息:
硬盘总容量
还剩多少块可用
每个块多大
inode 表在哪
如果 superblock 坏了,整个分区就挂了。所以通常会有备份。
3. 文件到底怎么存在硬盘上
这是个经典问题,文件系统有三种分配策略:
| 方式 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 连续分配 | 一个文件占连续的一块地 | 读写快,一次定位 | 容易产生碎片,删了文件留下空洞 |
| 链式分配 | 每个块末尾指向下一个块地址 | 没有碎片,利用率高 | 找第 100 块要从头遍历 100 次,慢 |
| 索引分配 | inode 里存一张"索引表",指向所有块 | 兼顾速度和空间,支持随机访问 | 大文件需要多级索引 |
现代文件系统(EXT4、NTFS 等)用的都是索引分配,通常采用"多级索引":小文件直接存在 inode 里(快),大文件用间接指针指向更多的块。
4. 目录到底是什么
目录不是容器,它本质上也是一个特殊的文件。只不过它存的内容不是你的照片或代码,而是一张映射表:
文件名 → inode 号 文件名 → inode 号 ...所以你"打开文件夹"时,系统其实是读取这个目录文件的内容,拿到一堆 inode 号,再去查每个 inode 对应的文件信息(大小、类型、权限等),最后展示给你。
这也解释了为什么"硬链接"不占用额外空间:两个文件名指向同一个 inode,就是同一个文件的两个门牌号。
5. 日志(Journal):防止断电丢数据的保险
文件系统最怕什么?写一半断电。
比如你要修改一个文件,正常流程是:
分配新块
写新数据
更新 inode 指向新块
更新目录
如果第 3 步做完,第 4 步没做就断电了,文件系统就懵了:inode 说数据在新块,但目录可能还指向旧块。
日志文件系统(如 EXT3/EXT4、NTFS)的做法是:先在一个专门的"日志区"写下"我要做这三件事",做完一件勾掉一件。就算断电了,重启后看日志,没做完的要么重做,要么回滚,保证文件系统结构一致性。
注意:日志保证的是文件系统本身不崩,不保证你刚才写的文件内容一定完整(那得应用层自己处理)。
6. 常见文件系统速览
| 文件系统 | 常用系统 | 特点 |
|---|---|---|
| EXT4 | Linux | 主流,支持大文件、 extents 分配、日志 |
| XFS | Linux 服务器 | 适合大文件、高并发,删大量小文件慢 |
| NTFS | Windows | 支持权限、压缩、加密、日志 |
| APFS | macOS | 针对 SSD 优化,支持快照、克隆 |
| FAT32/exFAT | U 盘/跨平台 | 兼容性好,但无日志、无权限,不适合系统盘 |
7. 页缓存(Page Cache):给硬盘加了个"内存加速器"
程序读文件时,数据其实不是直接从硬盘读到程序的。而是:
硬盘 → 内核页缓存(内存里)→ 程序
第一次读慢(走硬盘),但如果这块数据还在内存里,下次读就直接从内存拿,快几十倍。写操作也一样,先写进页缓存,内核找个合适的时机再刷到硬盘。
你可以用free -m看 Linux 内存,里面 "buff/cache" 就是这个。它自动管理,不用你操心,但要知道它的存在——内存不够时系统会优先回收页缓存。
五、总结:一条线串起来
把这些串成一条故事线,你就全明白了:
程序要存一个文件,发起 IO 请求;
请求经过VFS(虚拟文件系统)层,找到对应的文件系统;
文件系统查inode,知道数据该存在哪几个data block;
如果数据还在页缓存里,直接读写内存;不在的话,去找硬盘;
硬盘(HDD 或 SSD)把数据落到物理介质;
文件系统写日志保证一致性,superblock更新全局状态。
