【Linux】Ext系列文件系统(上)
一、理解硬件
1.1 磁盘、服务器、机柜、机房
- 机械磁盘是计算机中唯一一个机械设备
- 磁盘 --- 外设
- 慢、容量大、价格便宜
磁盘:磁盘是计算机存储数据的基本硬件设备,通常分为机械硬盘(HDD)和固态硬盘(SSD)。磁盘直接安装在服务器或存储设备中,用于存储操作系统、应用程序和用户数据。磁盘的性能和容量直接影响服务器的数据处理能力。
服务器:服务器是一种高性能计算机,用于处理数据、运行应用程序和提供服务。服务器通常包含多个磁盘、CPU、内存和网络接口,能够执行复杂的计算任务。服务器可以独立运行,也可以集群化部署以提高性能和可靠性。
机柜:机柜是用于集中安装和管理服务器的物理框架,通常为标准19英寸宽度。机柜提供电源分配、网络布线、散热和安全管理功能,能够容纳多台服务器、存储设备及网络设备。机柜的设计便于设备的集中维护和扩展。
机房:机房是专门用于存放和管理机柜、服务器及其他IT设备的基础设施空间。机房配备UPS电源、空调系统、消防设施和监控系统,确保设备在适宜的环境中运行。机房的规模可以从小型企业机房到大型数据中心不等。
它们之间的关系:磁盘安装在服务器中,服务器安装在机柜中,机柜部署在机房内。这种层级关系确保了硬件资源的集中管理和高效利用。机房作为顶层设施,为整个IT基础设施提供稳定的运行环境。
1.2 磁盘的物理结构
1.3 磁盘的存储结构
扇面:磁盘存储数据的基本单位,512字节,块设备。
如何定位一个扇区呢?
- 可以先定位磁头(head)
- 确定磁头要访问哪一个柱面(磁道)(cylinder)
- 定位一个扇区(sector)
- CHS地址定位
文件 = 内容 + 属性 都是数据,无非就是占据哪几个扇区的问题!能定位一个扇区了,那能不能定位多个扇区?
- 扇区是从磁盘读取和写入信息的最小单位,通常大小为512字节。
- 磁头(head)数:每个盘片一般由上下两面,分别对应一个磁头,共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)
1.4 磁盘的逻辑结构
1.4.1 理解过程
磁带上面可以存储数据,我们可以把磁带“拉直”,形成线性结构。
那么磁盘本质上虽然是硬质的,但是逻辑上我们可以把磁盘想象为卷在一起的磁带,那么磁盘的逻辑存储结构我们也可以类似于:
这样每一个扇区都有一个线性地址(其实就是数组下标),这种地址叫做LBA。
1.4.2 真实过程
一个细节:传动臂上的磁头是共进退的。
柱面是一个逻辑上的概念,其实就是每一面上,相同半径的磁道逻辑上构成柱面。
所以,磁盘物理上分了很多面,但是在我们看来,逻辑上,磁盘整体是由“柱面”卷起来的。
所以,磁盘的真实情况是:
磁道:
某一盘面的某一磁道展开:
即:一维数组
柱面:
整个磁盘的所有盘面的同一个磁道,即柱面展开:
- 柱面上的每个磁道,扇区的个数是相同的。
- 即:二维数组
整盘:
柱面n等等。
整个磁盘不就是多张二维扇区数组表(三维数组)。
所以,寻址一个扇区:先找到哪一个柱面(cylinder),在确定柱面内哪一个磁道(其实就是磁头的位置,Head),在确定扇区(sector),所以就有了CHS。
我们之前学过C/C++数组,在我们看来,其实全部都是一维数组:
所以,每个扇区都有一个下标,我们叫做LBA(Logical Block Address)地址,其实就是线性地址。那么怎么计算得到这个LBA地址呢?
1.5 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.1 引入“块”概念
其实硬盘是典型的“块”设备,操作系统读取硬盘数据的时候,其实是不会一个个扇区读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个“块”(block)。
硬盘的每个分区被划分为一个个的“块”。一个“块”的大小是由格式化的时候决定的,并且不可以更改,常见的是4KB,即连续八个扇区组成一个“块”。“块”是文件存取的最小单位。
注意:
- 磁盘就是一个三维数组,我们把它看待成一个一维数组,数组下标就是LBA,每个元素都是扇区。
- 每个扇区都有LBA,那么8个扇区一个块,每一个块的地址我们也能算出来。
- 知道LBA,块号 = LBA / 8
- 知道块号,LBA = 块号 * 8 + n(n是块内第几个扇区)
2.2 引入“分区”概念
其实磁盘是可以被分成多个分区的,以Windows观点来看,你可能会有一块磁盘并且将它分区成C,D,E盘。那个C,D,E就是分区。分区从实质上说就是对硬盘的一种格式化,但是Linux的设备都是以文件形式存在,那是怎么分区呢?
柱面是分区的最小单位,我们可以利用参考柱面号码的方式进行分区,其本质就是设置每个区的起始柱面和结束柱面的号码。此时我们可以将磁盘上的柱面(分区)进行平铺,将其想象成一个大的平面,如下图所示:
注意:
柱面大小一致,扇区个位一致,那么其实只要知道每个分区的起始和结束柱面号,知道每一个柱面多少个扇区,那么该分区多大,其实和解释LBA是多少也就清楚了。
2.3 引入“inode”概念
之前我们说过 文件 = 内容 + 属性,我们使用 ls -l 的时候看到的除了文件名,还能看到文件的元数据(属性)。
每行包含7列:模式、硬链接数、文件所有者、组、大小、最后修改时间、文件名。
ls -l 读取存储在磁盘上的文件信息,然后显示出来。
其实这个信息除了这种方式来读取,还有一个 stat 命令能看到更多信息
到这里我们要思考一个问题,文件数据都存储在“块”中,那么很显然,我们必须找到一个地方存储文件的元数据(属性),比如文件的创建者、文件的创建日期、文件的大小等等。这种存储文件元数据的区域就叫做inode,中文译名为“索引节点”。
每一个文件都有对应的inode,里面包含了于该文件有关的一些信息。为了能够解释清楚inode,我们需要深入了解一下文件系统。
注意:
- Linux下文件的存储是属性和内容分离存储的。
- Linux下,保存文件属性的集合叫做inode,一个文件,一个inode,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)备注:EXT2_N_BLOCKS = 15
注意:
- 文件名属性并未纳入到inode数据结构内部。
- inode的大小一般是128字节或者256字节,我们后面统一128字节。
- 任何文件的内容大小可以不同,但是属性大小一定是相同的。