目录
前言
Part1. 磁盘硬件底层:CHS 与 LBA 寻址
Part1.1. 磁盘硬件结构
Part1.2. LBA:线性块寻址
Part1.3. 块--操作系统的IO单元
Part2. 磁盘分区--分治管理空间
Part3. 文件的两大组成:内容 + 元信息
Part4. VFS虚拟文件系统:向上统一抽象
Part4.1. 进程描述符
Part4.2. struct file 打开文件对象
Part4.3. struct dentry 目录项
Part4.4. struct inode 索引节点
Part4.5. super_block 超级块
Part4.6. 底层文件系统驱动
Part5. fsync
Part6. 概念总结
Part7. 结语
前言
Linux下一切皆文件,对于文件与储存文件的磁盘的认识是非常重要的,接下来来跟随小编的视角来看看吧。
let's go!!!!!!!!
Part1. 磁盘硬件底层:CHS 与 LBA 寻址
Part1.1. 磁盘硬件结构
机械硬盘由盘片、磁头、磁道、扇区构成:
• 盘片:圆形金属薄片,上下两面都可以存储数据;每一面有一个对应的磁头(head),负责读写。
• 磁道(track):盘片上一圈一圈同心圆。磁头不动、盘片旋转,磁头就沿着磁道读写。
• 扇区(sector):磁道上切分出最小读写单元,标准扇区大小 512字节。磁盘硬件最小读写单位是扇区,一次IO最少读/写一个扇区。
CHS 寻址(Cylinder-Head-Sector,柱面-磁头-扇区),是老式硬盘寻址方式:• C:柱面(Cylinder):所有盘片同一半径位置的磁道,合起来称为一个柱面
• H:磁头(Head):选择哪个盘面
• S:扇区(Sector):该磁道上第几个扇区
磁盘总容量计算公式:
磁盘容量 = 柱面数 × 磁头数 × 每磁道扇区数 × 扇区字节数Part1.2. LBA:线性块寻址
CHS是三维坐标,不方便操作系统管理。现代硬盘对外统一抽象成一维连续编号,这个编号就是LBA(Logical Block Address,逻辑块地址)。
把磁盘所有扇区从头到尾排成一条长数组,LBA编号从0开始,每一个LBA编号对应一个物理扇区。LBA ↔ CHS 转换公式
设:
• a = 每个柱面包含的扇区总数(磁头数 × 每磁道扇区数)
• b = 每条磁道的扇区数量
1. C=LRA/a 柱面号
2. H=(LBA%a)/b 磁头号
3. S=(LBA%a)%b 扇区号(注意S从1开始计数,不是0)
核心理解:LBA就是把三维磁盘空间拍平成一维数组,操作系统只需要给硬盘下发LBA编号,硬盘固件内部自动转换成CHS,不需要OS关心磁头、柱面细节。
写入磁盘:把512字节数据 + LBA编号交给磁盘控制器,硬件内部完成CHS转换,找到对应的扇区完成写入。Part1.3. 块--操作系统的IO单元
硬件最小单元是扇区(512B),但操作系统不会每次只读写1个扇区,效率太低。
Linux引入块(block):文件系统层面的最小读写单位。• 常见块大小:4KB,等于连续8个扇区(8 × 512B = 4KB)
•块号 ×8 + (0~7),就可以定位这个块内部对应的LBA扇区。
重点考点:扇区是硬件最小读写单位;块是Linux文件系统最小IO单位。
Linux对文件的所有读写,全部转化成对块的读写。
Part2. 磁盘分区--分治管理空间
一块大容量磁盘,不会直接裸给文件系统使用,会做分区。
举例:800GB磁盘,可以划分成300G、300G、200G三个分区。
操作系统内部维护一张分区表,记录每个分区的start起始LBA、end结束LBA。
分区本质:对磁盘连续LBA空间做切分,隔离不同文件系统,便于管理。
分区是硬件/磁盘层面划分;分区内部,再去部署文件系统(ext4)。
Part3. 文件的两大组成:内容 + 元信息
Linux里一句话:文件 = 文件内容数据 + 文件属性元信息,两者分开存储
1.文件内容:就是你写入的文本、二进制数据,放在数据块中;
2.文件元信息:文件大小、权限rwx、uid/gid、时间戳、数据块指针,这些全部存在 inode
inode核心概念
• inode本质:一个结构体,固定大小,ext4默认128字节。
• 一个文件,对应唯一一个inode。inode不保存文件名!文件名保存在目录项dentry里面。
• inode里面存放:指向磁盘上数据块的指针,用来找到存放文件内容的那些块。
面试高频:删除文件,本质是删除目录项的文件名链接,inode引用计数减一;引用计数归零,才会释放inode和数据块。
区分两个容易混淆结构体:• struct inode:磁盘文件的元信息,描述磁盘上的文件本身,所有进程共享;
• struct file:进程打开文件之后,在内核中创建的对象,描述本次打开的文件实例(包含文件偏移量、读写标志),不同进程打开同一个文件,会生成多个独立struct file。
Part4. VFS虚拟文件系统:向上统一抽象
VFS的目标:给上层用户/进程提供一套统一的文件操作接口(open/read/write),屏蔽底层差异(ext4、xfs、ntfs、设备文件)。不管什么文件系统,上层调用接口完全一致。
整条链路:进程 → fd → struct file → dentry → inode → super_block → 磁盘Part4.1. 进程描述符
每个进程都维护一个files_struct,里面存放fd_array[]文件描述符数组,也就是我们熟悉的fd。
stdin(0)、stdout(1)、stderr(2)都在这里。fd本质就是数组下标。Part4.2. struct file 打开文件对象
进程调用open成功后,内核创建struct file,里面保存:当前文件读写偏移量f_pos、文件操作函数指针表file_operations、引用计数,并且关联目录项dentry。
同一个文件,多个进程多次open,会生成多个独立struct file,各自拥有独立的文件偏移。Part4.3. struct dentry 目录项
dentry保存文件名,以及指向inode的指针。目录也是文件,目录文件里面存放一堆dentry,记录这个目录下所有文件/子目录名字,以及对应的inode编号。
Part4.4. struct inode 索引节点
对应磁盘上文件的元数据。inode_operations 是inode层面操作函数集(创建、删除、查找)。
一个inode对应磁盘上真实文件。Part4.5. super_block 超级块
一个分区,一个文件系统,对应一个超级块super block。
超级块记录整个文件系统全局信息:总块数、空闲块、inode总数、空闲inode,以及文件系统类型(ext4)。
super_operations 是超级块操作函数,挂载、卸载文件系统时使用。Part4.6. 底层文件系统驱动
VFS只是抽象层,真正的实现由ext4/xfs等文件系统驱动实现,把VFS的接口翻译成对块设备的读写。
一句话串起全链路
用户态进程调用open → 内核分配fd,创建struct file → 路径解析,查找目录项dentry → dentry找到磁盘inode → 加载inode到内存,关联超级块super_block。
read/write的时候,通过inode里的块指针找到磁盘数据块,完成读写。
Part5. fsync
int fsync(int fd);缓存机制
Linux读写文件,会经过页缓存(page cache)。
我们write写入数据,不会立刻写到磁盘,先写到内存页缓存,返回成功。内核后台线程pdflush会异步刷盘。
问题:断电时,内存数据丢失,造成文件损坏。
fsync(fd)作用:强制把fd对应的文件,所有缓存数据、文件元信息,一次性同步刷入磁盘,调用返回时磁盘已经持久化完成。
区分:• fsync:刷数据 + inode元信息;
• fdatasync:只刷文件内容数据,不强制刷元信息,速度更快。
Part6. 概念总结
1. 扇区:磁盘硬件最小读写单元,512字节;块:OS文件系统最小IO单元,4KB。
2. LBA:一维线性扇区编号,现代硬盘标准寻址;CHS是老式三维寻址。
3. 分区:切分磁盘LBA空间,分区表记录起止位置。
4. 文件 = 数据块(内容)+ inode(元信息);文件名保存在dentry目录项。
5. struct file:进程打开文件实例;inode:磁盘上文件本体。
6. VFS虚拟文件系统:统一接口,屏蔽不同底层文件系统差异。
7. page cache页缓存:write先写内存,fsync强制落盘持久化。
Part7. 结语
这篇文章我们知道了文件与磁盘的联系,接下来,小编还会带来更多Linux相关的知识,敬请期待~
最后,祝大家可以:春风得意马蹄疾,一日看尽长安花!
最后的最后,要是觉得本文还可以的话,可以点点赞,关注小编一波,谢谢大家!~