🔥个人主页🔥:孤寂大仙V
🌈收录专栏🌈:Linux
🌹往期回顾🌹:【Linux笔记】基础IO(下)
🔖流水不争,争的是滔滔不
文件=属性+内容。被打开的文件通过基础IO到内存中,没有被打开的文件在磁盘中。文件在磁盘上最基本的诉求就是被用户找到,这也是文件系统最基本的诉求。
一、从硬件开始理解
1.1 磁盘
基本结构
- 盘片:是存储数据的介质,一般由铝合金或玻璃等材料制成,表面涂有磁性材料。盘片通常有多个,它们叠放在一起,安装在主轴上,能够高速旋转。
- 磁头:负责读取和写入数据。每个盘片的上下两面都有一个磁头,磁头通过移动臂在盘片表面移动,寻找到正确的位置来进行数据的读写操作。磁头与盘片之间的距离非常小,通常只有几微米甚至更小,以实现高精度的数据读写。
- 电机:包括主轴电机和音圈电机。主轴电机用于驱动盘片以恒定的速度旋转,常见的转速有 5400 转 / 分钟、7200 转 / 分钟等。音圈电机则负责控制磁头的移动,使磁头能够快速、准确地定位到需要读写数据的位置。
- 控制电路:主要由硬盘控制器、缓存控制器等组成。硬盘控制器负责与计算机的其他部件进行通信,协调数据的- 传输和存储操作;缓存控制器管理硬盘的缓存,提高数据的读写性能。
- 缓存:是硬盘中的高速内存区域,用于临时存储即将被读写的数据。缓存的大小通常为几 MB 到几十 MB 不等,较大的缓存可以提高硬盘的数据读写速度和系统性能。
工作原理 - 数据写入:当计算机需要向硬盘写入数据时,操作系统会将数据发送到硬盘的缓存中。然后,磁头根据数据的存储地址,在盘片旋转的同时,通过电磁转换原理,将数据以磁性信号的形式记录在盘片的特定位置上。
- 数据读取:当计算机需要从硬盘读取数据时,磁头首先会根据数据的存储地址移动到相应的位置,然后随着盘片的旋转,磁头感应盘片上的磁性信号,并将其转换为电信号,再经过放大、解码等处理后,将数据传输到硬盘的缓存中,最后由缓存将数据发送给计算机的其他部件。
主要性能指标 - 容量:是指硬盘能够存储的数据量,常见的容量有 500GB、1TB、2TB 等。随着技术的发展,硬盘的容量也在不断增大。
- 转速:指盘片每分钟的旋转次数,转速越高,硬盘的数据传输速度就越快,寻道时间也越短。
- 平均寻道时间:是指磁头从一个位置移动到另一个位置并找到所需数据的平均时间,一般在几毫秒到十几毫秒之间。
- 数据传输率:分为内部数据传输率和外部数据传输率。内部数据传输率是指硬盘磁头与缓存之间的数据传输速度,它主要取决于硬盘的盘片转速和磁头的读写能力;外部数据传输率是指硬盘与计算机其他部件之间的数据传输速度,它主要取决于硬盘的接口类型和传输协议。
优缺点 - 优点:技术成熟,成本相对较低,容量大,适合大量数据的存储。
- 缺点:机械结构复杂,容易出现故障,抗震性能较差;读写速度相对较慢,尤其是随机读写性能不如固态硬盘。
机械硬盘在计算机存储领域曾经占据主导地位,虽然近年来随着固态硬盘的发展,其市场份额有所下降,但仍然广泛应用于个人电脑、服务器、监控设备等领域。
1.2磁盘的存储结构
扇区是磁盘存储数据的基本单位,是磁盘上的一个弧形区域。常见的扇区大小为 512 字节。扇区是数据在磁盘上存储的最小单元,计算机在向磁盘写入数据时,会将数据按扇区进行划分并存储;读取数据时,也以扇区为单位进行读取。
不同盘面(磁头)上相同位置的磁道组成一个柱面。在进行数据读写时,通常会按柱面、磁头、扇区的顺序来访问磁盘上的数据,这样可以充分利用磁盘的存储结构,提高数据访问的效率。
1.3 CHS地址定位
如何定位一个扇区呢?
- 先定位磁头(header)
- 确定磁头访问哪一个柱面(磁道)(cylinder)
- 定位一个扇区(sector)
文件=内容+属性 都是数据,就时占据几个扇区的问题,因为扇区是存储数据的基本单位。可以通过定位多个扇区的方式访问数据与存储数据。
磁头(head):每个盘片有上下两面,对应 2 个磁头,盘片数决定磁头总数,用于读写数据。
磁道(track):从盘片外圈往内圈编号,是一系列同心圆,靠近主轴的同心圆用于磁头停靠,不存数据。
柱面(cylinder):由不同盘面上相同位置的磁道构成,数量与磁道数相等。
扇区(sector):是磁盘读写信息的最小单位,通常为 512 字节,每个磁道切分成的扇区数量相同。
圆盘(platter):即盘片,其数量影响磁盘容量等性能。
磁盘容量:计算公式为磁盘容量 = 磁头数 × 磁道 (柱面) 数 × 每道扇区数 × 每扇区字节数。
CHS 寻址方式:通过柱面、磁头、扇区三个参数定位数据,是一种常见的数据定位方式。
1.4 磁盘的逻辑结构
磁带上存储数据是在直的磁带上,是线性结构。
磁盘物理存储虽然是以扇区为单位,且是曲面的。但我们可以把磁盘抽象逻辑上理解为卷起的磁带,那么磁盘的逻辑存储结构也可以理解为线性的。
这样的线性地址叫做LBA。
1.5 真实过程
上面说过,传动臂上的磁头是共进退的。
柱面是⼀个逻辑上的概念,其实就是每一面上,相同半径的磁道逻辑上构成柱面。所以,磁盘物理上分了很多面,但是在我们看来,逻辑上,磁盘整体是由“柱面”卷起来的。
磁道展开
柱面展开
整个磁盘
抽象为线性结构
每个扇区都有一个下标,我们叫做LBA地址,其实就是线性地址。
OS只需要LBA就可以了。
1.6 CHS 和 LBA 地址
CHS 转 LBA
- 首先计算单个柱面的扇区总数,即磁头数 × 每磁道扇区数。
- 计算公式为:LBA = 柱面号 C×(磁头数 × 每磁道扇区数)+ 磁头号 H× 每磁道扇区数 + 扇区号 S - 1。
- 注意扇区号从 1 开始,而 LBA 地址从 0 开始,柱面和磁道从 0 开始编号。
LBA 转 CHS
- 柱面号 C = LBA // (磁头数 × 每磁道扇区数)。
- 磁头号 H = (LBA % (磁头数 × 每磁道扇区数)) // 每磁道扇区数。
- 扇区号 S = (LBA % 每磁道扇区数) + 1。
- “//” 表示除取整。
实际应用
磁盘内部会自动维护总柱面、磁道个数、扇区总数等信息,上层开机时会获取这些参数。
对于磁盘使用者来说,通常直接使用 LBA 地址,磁盘内部会自行进行 CHS 与 LBA 的转换。可以将磁盘看作是一个元素为扇区的一维数组,数组下标就是每个扇区的 LBA 地址,操作系统使用磁盘时,可通过一个数字(LBA 地址)来访问磁盘扇区。
二、文件系统
2.1 '‘块’'概念
os文件系统访问磁盘,不以扇区为单位进行访问。而是以“块”为单位进行访问。“块”一般为8kb。
其实硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,其实是不会⼀个个扇区地读取,这样效率太低,而是⼀次性连续读取多个扇区,即⼀次性读取⼀个”块”(block)。
硬盘的每个分区是被划分为⼀个个的”块”。⼀个”块”的大小是由格式化的时候确定的,并且不可以更改,最常见的是4KB,即连续八个扇区组成⼀个 ”块”。”块”是文件存取的最小单位。
注意:
- 磁盘就是⼀个三维数组,我们把它看待成为⼀个"⼀维数组",数组下标就是LBA,每个元素都是扇区
- 每个扇区都有LBA,那么8个扇区⼀个块,每⼀个块的地址我们也能算出来。
- 知道LBA:块号 = LBA/8
- 知道块号:LAB=块号*8 + n. (n是块内第⼏个扇区)
2.2 分区概念
其实磁盘是可以被分成多个分区(partition)的,以Windows观点来看,你可能会有⼀块磁盘并且将它分区成C,D,E盘。那个C,D,E就是分区。分区从实质上说就是对硬盘的⼀种格式化。
Linux的设备都是以文件形式存在的。柱面是分区的最小单位,我们可以利用参考柱面号码的方式来进行分区,其本质就是设置每个区的起始柱面和结束柱面号码。 此时我们可以将硬盘上的柱面(分区)进行平铺,将其想象成⼀个大的平面
2.4 inode概念
之前我们说过 文件=数据+属性 ,我们使用 ls -l 的时候看到的除了看到文件名,还能看到文件元数据(属性)。
[root@localhost linux]# ls -l
总⽤量 12
-rwxr-xr-x. 1 root root 7438 "9⽉ 13 14:56" a.out
-rw-r--r--. 1 root root 654 "9⽉ 13 14:56" test.c
到这我们要思考⼀个问题,文件数据都储存在”块”中,那么很显然,我们还必须找到⼀个地方储存文件的元信息(属性信息),比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode,中文译名为”索引节点”。
每⼀个文件都有对应的inode,里面包含了与该文件有关的⼀些信息。为了能解释清楚inode,我们需要是深入了解⼀下文件系统。
/*
* Structure of an inode on the disk
*/
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)