Linux 之从硬件硬盘到文件系统的全面过渡

发布于:2025-09-08 ⋅ 阅读:(26) ⋅ 点赞:(0)

前提引入

文件=内容+属性,这是从单个文件的角度。但是有很多文件,我们可以在宏观上把文件分为被打开的文件(在前面基础IO讲过)和没有被打开的文件;而被打开的文件在内存中方便管理,没有被打开的文件在磁盘里。

没有打来的文件肯定是很多的,那这么多的文件在磁盘中怎么被我们找到呢?从现阶段的认知,文件是一种目录结构,目录结构是树状的,需要路径(绝对、相对),文件存到磁盘上,最基本的诉求就是:就是被找到。而研究上面这些需求,要完成以特定的结构组织管理文件和帮我们找到文件就是文件系统做的事情!!!

1. 理解硬件

磁盘-服务器-机柜-机房

机械磁盘是计算机中唯一的一个机械设备
磁盘--- 外设
容量大,价格便宜

磁盘物理结构

磁盘存储结构

磁道是同心圆

扇区:是磁盘存储数据的基本单位,512字节,块设备

三片六面,六个磁头,磁头在传动臂的带动下,共进退!!!

磁盘写入的时候,是向柱面进行批量写入的!!!

如何定位一个扇区呢?
可以先定位磁头(header)
确定磁头要访问哪一个柱面(磁道)(cylinder)
定位一个扇区(sector)
文件 = 内容+属性 都是数据,无非就是占据那几个扇区的问题!能定位一个扇区了,能不能定位多个扇区呢?能
扇区是从磁盘读出和写入信息的最小单位,通常大小为 512 字节。
磁头(head)数:每个盘片一般有上下两面,分别对应1个磁头,共2个磁头
磁道(track)数:磁道是从盘片外圈往内圈编号0磁道,1磁道...,靠近主轴的同心圆用于停靠磁头,不存储数据
柱面(cylinder)数:磁道构成柱面,数量上等同于磁道个数
扇区(sector)数:每个磁道都被切分成很多扇形区域,每道的扇区数量相同
圆盘(platter)数:就是盘片的数量
磁盘容量=磁头数 × 磁道(柱面)数 × 每道扇区数 × 每扇区字节数
细节:传动臂上的磁头是共进退的(
柱面(cylinder),磁头(head),扇区(sector),显然可以定位数据了,这就是数据定位(寻址)方式之一,CHS寻址方式。
📌 CHS寻址
对早期的磁盘非常有效,知道用哪个磁头,读取哪个柱面上的第几扇区就可以读到数据了。
但是CHS模式支持的硬盘容量有限,因为系统用8bit来存储磁头地址,用10bit来存储柱面地址,用6bit来存储扇区地址,而一个扇区共有512Byte,这样使用CHS寻址一块硬盘最大容量
为256 * 1024 * 63 * 512B = 8064 MB(1MB = 1048576B)(若按1MB=1000000B来算就是
8.4GB)

磁盘的逻辑结构

理解过程

磁带上面可以存储数据,我们可以把磁带“拉直”,形成线性结构。
卷起来,就是同心圆,拉出来,就是一个线性结构!!!

那么磁盘本质上虽然是硬质的,但是逻辑上我们可以把磁盘想象成为卷在一起的磁带,那么磁盘的逻辑存储结构我们也可以类似于:

这样每一个扇区,就有了一个线性地址(其实就是数组下标),这种地址叫做LBA。

真实过程

一个细节:传动臂上的磁头是共进退的

柱面是一个逻辑上的概念,其实就是每一面上,相同半径的磁道逻辑上构成柱面。
所以,磁盘物理上分了很多面,但是在我们看来,逻辑上,磁盘整体是由“柱面”卷起来的。

磁盘的真实情况是:
磁道:
某一盘面的某一个磁道展开:
即:一维数组
柱面:
整个磁盘所有盘面的同一个磁道,即柱面展开:

柱面上的每个磁道,扇区个数是一样的,可以理解成二维数组。
整个盘:

整个磁盘不就是多张二维的扇区数组表(三维数组?)
所有,寻址一个扇区:先找到哪一个柱面
(Cylinder) ,在确定柱面内哪一个磁道(其实就是磁头位置, Head),在确定扇区(Sector),所以就有了CHS。
之前学过C/C++的数组,在我们看来,其实全部都是一维数组:

所以,每一个扇区都有一个下标,我们叫做LBA(Logical Block Address)地址,其实就是线性地址。所以怎么计算得到这个LBA地址呢?
LBA,1000,CHS 必须要! LBA地址转成CHS地址,CHS如何转换成为LBA地址。
OS只需要使用LBA就可以了!!
LBA地址转成CHS地址,CHS如何转换成为LBA地址。谁做啊??磁盘自己来做!固件(硬件电路,伺服系统)

CHS && LBA地址

CHS转成LBA:
磁头数*每磁道扇区数 = 单个柱面的扇区总数
LBA = 柱面号C*单个柱面的扇区总数 + 磁头号H*每磁道扇区数 + 扇区号S - 1
即:LBA = 柱面号C*(磁头数*每磁道扇区数) + 磁头号H*每磁道扇区数 + 扇区号S - 1
扇区号通常是从1开始的,而在LBA中,地址是从0开始的
柱面和磁道都是从0开始编号的
总柱面,磁道个数,扇区总数等信息,在磁盘内部会自动维护,上层开机的时候,会获取到这些参
数。
LBA转成CHS:
柱面号C = LBA // (磁头数*每磁道扇区数)【就是单个柱面的扇区总数】
磁头号H = (LBA % (磁头数*每磁道扇区数)) // 每磁道扇区数
扇区号S = (LBA % 每磁道扇区数) + 1
"//": 表示除取整
所以:从此往后,在磁盘使用者看来,根本就不关心CHS地址,而是直接使用LBA地址,磁盘内部自己转换。所以:
从现在开始,磁盘就是一个 元素为扇区 的一维数组,数组的下标就是每一个扇区的LBA地址。OS使用磁盘,就可以用一个数字访问磁盘扇区了。

2. 引入文件系统

引入"块概念"

其实硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,其实是不会一个个扇区地读取,这样 效率太低,而是一次性连续读取多个扇区,即一次性读取一个”块”(block)。 硬盘的每个分区是被划分为一个个的”块”。一个”块”的大小是由格式化的时候确定的,并且不可 以更改,最常见的是4KB,即连续八个扇区组成一个”块”。”块”是文件存取的最小单位。

注意:

• 磁盘就是一个三维数组,我们把它看待成为一个"一维数组",数组下标就是LBA,每个元素都是扇 区

• 每个扇区都有LBA,那么8个扇区一个块,每一个块的地址我们也能算出来。

• 知道LBA:块号=LBA/8 • 知道块号:LAB=块号*8+n.(n是块内第几个扇区)

引入"分区"概念

其实磁盘是可以被分成多个分区(partition)的,以Windows观点来看,你可能会有一块磁盘并且将 它分区成C,D,E盘。那个C,D,E就是分区。分区从实质上说就是对硬盘的一种格式化。
但是Linux的设备 都是以文件形式存在,那是怎么分区的呢?
柱面是分区的最小单位,我们可以利用参考柱面号码的方式来进行分区,其本质就是设置每个区的起始柱面和结束柱面号码。 此时我们可以将硬盘上的柱面(分区)进行平铺,将其想象成一个大的平面,如下图所示:

注意:
柱面大小一致,扇区个位一致,那么其实只要知道每个分区的起始和结束柱面号,知道每
一个柱面多少个扇区,那么该分区多大,其实和解释LBA是多少也就清楚了.

引入"inode"概念

之前我们说过 文件=数据+属性 ,我们使用ls -l 的时候看到的除了看到文件名,还能看到文件元数据(属性)。

每行包含7列:
模式
硬链接数
文件所有者
大小
最后修改时间
文件名
ls -l读取存储在磁盘上的文件信息,然后显示出来
其实这个信息除了通过这种方式来读取,还有一个stat命令能够看到更多信息
hu@hcss-ecs-6579:~/test$ stat code.c
  File: code.c
  Size: 1744      	Blocks: 8          IO Block: 4096   regular file
Device: fc01h/64513d	Inode: 547045      Links: 1
Access: (0664/-rw-rw-r--)  Uid: ( 1002/      hu)   Gid: ( 1002/      hu)
Access: 2025-08-13 16:25:18.011548344 +0800
Modify: 2025-08-13 16:25:18.011548344 +0800
Change: 2025-08-13 16:25:18.015548373 +0800
 Birth: 2025-08-13 16:25:18.011548344 +0800
hu@hcss-ecs-6579:~/test$ 
到这我们要思考一个问题,文件数据都储存在”块”中,那么很显然,我们还必须找到一个地方储存
文件的元信息(属性信息),比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为”索引节点”。
每一个文件都有对应的inode,里面包含了与该文件有关的一些信息。为了能解释清楚inode,我们需要是深入了解一下文件系统。
注意:
Linux下文件的存储是属性和内容分离存储的
Linux下,保存文件属性的集合叫做inode,一个文件,一个inode,inode内有一个唯一的标识符,叫做inode号
所以一个文件的属性inode长什么样子呢?
struct ext2_inode {
__le16 i_mode; /* File mode */
__le16 i_uid; /* Low 16 bits of Owner Uid */
__le32 i_size; /* Size in bytes */
__le32 i_atime; /* Access time */
__le32 i_ctime; /* Creation time */
__le32 i_mtime; /* Modification time */
__le32 i_dtime; /* Deletion Time */
__le16 i_gid; /* Low 16 bits of Group Id */
__le16 i_links_count; /* Links count */
__le32 i_blocks; /* Blocks count */
__le32 i_flags; /* File flags */
union {
struct {
__le32 l_i_reserved1;
} linux1;
struct {
__le32 h_i_translator;
} hurd1;
struct {
__le32 m_i_reserved1;
} masix1;
} osd1; /* OS dependent 1 */
__le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
__le32 i_generation; /* File version (for NFS) */
__le32 i_file_acl; /* File ACL */
__le32 i_dir_acl; /* Directory ACL */
__le32 i_faddr; /* Fragment address */
union {
struct {
__u8 l_i_frag; /* Fragment number */
__u8 l_i_fsize; /* Fragment size */
__u16 i_pad1;

__le16 l_i_uid_high; /* these 2 fields */
__le16 l_i_gid_high; /* were reserved2[0] */
__u32 l_i_reserved2;
} linux2;
struct {
__u8 h_i_frag; /* Fragment number */
__u8 h_i_fsize; /* Fragment size */
__le16 h_i_mode_high;
__le16 h_i_uid_high;
__le16 h_i_gid_high;
__le32 h_i_author;
} hurd2;
struct {
__u8 m_i_frag; /* Fragment number */
__u8 m_i_fsize; /* Fragment size */
__u16 m_pad1;
__u32 m_i_reserved2[2];
} masix2;
} osd2; /* OS dependent 2 */
};
/*
* Constants relative to the data blocks
*/
#define EXT2_NDIR_BLOCKS 12
#define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1)
备注:EXT2_N_BLOCKS = 15
再次注意:
文件名属性并未纳入到inode数据结构内部
inode的大小一般是128字节或者256,我们后面统一128字节
任何文件的内容大小可以不同,但是属性大小一定是相同的

结束语

 我们已经知道硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,读取的基本单位是”块”。“块”又是硬盘的每个分区下的结构,难道“块”是随意的在分区上排布的吗?那要怎么找到“块”呢?
还有就是上面提到的存储文件属性的inode,又是如何放置的呢?
⽂件系统就是为了组织管理这些的!!!
下节小编将对文件系统进行讲解,欢迎大佬们评论区发表见解!!!


网站公告

今日签到

点亮在社区的每一天
去签到