Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Co-authored-by: LoveCatC <3306702538@qq.com>
  • Loading branch information
everything411 and LoveCatc committed Oct 25, 2024
1 parent 44567e3 commit c1d7e32
Show file tree
Hide file tree
Showing 19 changed files with 1,828 additions and 0 deletions.
193 changes: 193 additions & 0 deletions docs/2019-01-01-FileSystem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# FileSystem && Partition

> :fontawesome-regular-face-grin: everything411 and LoveCatC
>
> :material-clock-edit-outline: 2019年1月1日 0:00:00
## File System

> In computing, a **file system** or **filesystem** controls how data is stored and retrieved.
>
> Without a file system, information placed in a storage medium would be one large body of data with no way to tell where one piece of information stops and the next begins. By separating the data into pieces and giving each piece a name, the information is easily isolated and identified. Each group of data is called a "file". The structure and logic rules used to manage the groups of information and their names is called a "file system".
在PC中,常用的文件系统包括Windows下的NTFS、FAT、exFAT等。到了Linux下,个人用户最常用的要数ext文件系统。



### FAT

FAT得名于它的文件分配表(File Allocation Tables)方式,即在存储空间最开始有个大表,记录所有的可用块的使用链,并标记空闲块和坏块。它最早是由Bill Gates同Marc Mcdonald一起发明。FAT经历了FAT8、FAT12、FAT16、FAT32时代,目前FAT32还在使用中。FAT后面的数字就代表着分配表中,表示下个链的位数(即簇号)。

FAT在它问世的那个年代有着相当先进的一面。但同时,FAT的弊端是根本性的。首先,FAT需要对FAT表进行大量读写操作,导致物理部分极易损坏;同时,读写操作一旦经历意外(如断电)就会造成FAT表出错,从而在逻辑层面上也使得FAT的健壮性很差。更致命的是,FAT表的大小是有限的,从而FAT支持的空间大小也有限。后来的历史证明,FAT支持的空间大小根本跟不上存储设备的发展。使用过FAT32的人都会有这种困扰,就是FAT32格式的文件系统不支持4G以上的文件,而这个问题是FAT这一文件系统无法避免的。

FAT有过辉煌的一段时期。然而现在,FAT已经落后于现代文件系统。但它仍有用武之地。这就是UEFI标准中,规定的EFI分区(ESP)采用FAT格式。并且这一标准要求在移动介质上,同样以FAT作为ESP的File System。这也是为什么**在制作系统启动盘时,要将U盘首先格式化为FAT32格式。**

> 现在网上有一些教程,声称“格式化U盘为NTFS格式同样可以使用UEFI启动”。事实上,真正制作了就会发现,本质上这种制作,是划分U盘中的一部分区域作为ESP(FAT)用以引导,将剩下的部分格式化为exFAT或NTFS来存储需要的系统镜像。在启动时,通过ESP中的特殊引导程序来跳转到系统镜像并进行安装。


### exFAT

exFAT是Extended File Allocation Table的缩写。从它的名字就可以看出,exFAT和FAT有着千丝万缕的联系。exFAT是微软在2006年推出的,“专门为闪存优化的”,FAT的一种替代品。

相比于FAT,exFAT最大支持1EB的文件大小,目前来讲对于大容量文件的存储完全没有问题。相较于对于支持硬盘分区大小的提升、单目录下支持文件数目的提升,令用户最欣喜的,还是终于不用受FAT32的单文件4GB大小的限制了。在exFAT发布之后,许多闪存设备默认都会采用exFAT格式。并且Mac和Linux目前也都支持exFAT。



### NTFS

NTFS(New Technology File System)是Windows NT Family的默认文件系统。作为File System的集大成之作,NTFS集成了典型File System的绝大多数优点。它具有日志,没有文件大小的限制,有良好的安全性能,支持超长文件名,支持文件压缩,并且在服务器文件管理上独具特色,总之NTFS有着优越的性能,让它成为综合性上当前最优秀的文件系统之一。

NTFS由微软主持开发,目前在Linux上也有第三方驱动提供对NTFS的读写支持。然而Mac OS原生只提供对NTFS的读权限而没有写权限。想要更改只能借助第三方软件。



### 总结

FAT在这三种File System中是诞生最早的。一路走来,到今天FAT依然有其用武之地,靠的就是其强大的兼容性。在几乎所有的平台上,都能够使用FAT。但它的弊端也很明显,不支持4G以上的文件注定了它无法走的更远。exFAT作为FAT的一种替代品,在闪存设备上体现出了相当的优越性,并且Windows、Linux、Mac OS也都支持。然而它最大的软肋在于不支持日志。并且传统硬盘有的不能格式化为exFAT。这都使得很多时候exFAT不能成为理想的选择。而NTFS在各方面都很优秀。然而NTFS有一个特性是会详细记录磁盘的读写操作。对于目前应用越来越广的Flash存储而言,过多的读写操作给介质带来很大的负担。因而微软才会将exFAT称作“Optimized for Flash”。



---



## Disk Partition



### 简介



让我们先从HDD讲起。

在早期,磁盘的地址管理策略被称为Cylinder-Head-Sector,即柱面、磁头和扇区。通过这三个要素,来描述一个存储地址。

```Linux
> fdisk -l
Disk /dev/cciss/c0d0: 146.7 GB, 146778685440 bytes
255 heads, 63 sectors/track, 17844 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
```

可以看到,硬盘的容量就是17844(Cylinder)* 255(head)* 63(Sector)=146.7G。

这里,**Sector是硬盘的最小存储单位**。大小为512Bytes。Sector是物理层面上的一个概念,是硬盘生产出来时就定下的,无法更改。最早时,硬盘的Sector一般是512Bytes。随着硬盘存储空间的扩大,目前也有Sector大小为4096Bytes的硬盘,也就是常说的4K扇区。

有了Sector的概念,再来考虑块(Block)。对于OS,或者File System来说(以下统称OS),单独的Sector实在太小,一个个读取过于缓慢。这时自然就产生了同时操作多个Sector的想法。而**Block,实际上就是由整数个(通常是2的n次方)的块组成**。对于OS来说,**对于文件的操作是以块(Block)为单位进行的。**换句话说,在OS中,**Block是文件存取的最小单位。**

```Linux
> df -T
/dev/cciss/c0d0p5 ext3 112738028 81733116 25185772 77% /
> tune2fs -l /dev/cciss/c0d0p5 | grep "Block size"
Block size: 4096
```

可以看到,在这一个Linux OS下,使用的File System是ext3,其Block大小是4096,也就是说,一个Block由8个Sector组成。

同时,很显然的,Block是建立在OS/File System基础上的,也就是说它是一个逻辑概念。

> 通常,在Windows下Block称为簇,Linux等Unix-Like的OS中将Block称为块。二者其实指的都是Block这个概念。
由于OS存储文件的最小单位是Block。这意味着多小的文件,只要存储,必定占据一个Block。同时,不同OS、不同File System,对于Block的大小支持都不同。



回到地址管理策略上来。CHS策略的弊端是显然的。它只能够应用于HDD(或者说,当时意义上的硬盘,因为那时还没有SSD)而无法应用于磁带等其他存储介质。并且由于机械结构上的限制,CHS表示的硬盘空间大小也是有限的。后来就有了LBA(Logical Block Addressing,逻辑扇区地址)。LBA为每个扇区分配逻辑地址。同时,为了与CHS兼容,LBA会通过算法同样得到一个C-H-S式的地址。只是,当然,这个CHS Address也是逻辑上的而非物理上的。

具体的转换规则可以参考[Wiki-LBA](<https://en.wikipedia.org/wiki/Logical_block_addressing>)



### 硬盘分区

硬盘分区,顾名思义,就是将硬盘划分成几个部分供不同用途,这同样是一个逻辑上的概念。在不同的分区表策略中(马上就会提到),有不同的划分分区方式。到了9102年,虽然还是会有人理所应当的认为不需要分区,但对于现代PC用户来说,想要使用一块硬盘,不分区是不可能的。最基本的事实就是——如果想让一个OS使用一块(部分)硬盘,就必须为这块硬盘(的某部分)分区并指定文件系统。这样之前提到的块才有实现基础。



### MBR

MBR是Master Boot Record的缩写,意为主分区引导记录。在MBR出现前,就已经有了LBA。因而MBR的实现也是在LBA基础上的。

在MBR分区下,每个分区的第一个Sector称为引导扇区,而整个硬盘的第一个扇区称为主引导扇区。主引导扇区也是主分区的第一个扇区。

主引导分区的512Bytes分配如下:

| 项目 | 大小 |
| -------------------- | --------- |
| Boot Loader(Code) | 446 Bytes |
| Disk Partition Table | 64 Bytes |
| Magic Number | 2 Bytes |

其中,开始的Boot Loader完全是以代码形式存在的,负责引导进入OS。

第二部分的DPT对于理解MBR的分区比较关键。这一部分储存着分区信息。事实上,DPT存储的64Bytes中划分为4 * 16。一个典型的16Bytes信息为:

`80 01 01 00, 0B FE BF FC, 3F 00 00 00, 7E 86 BB 00`

这段16Bytes的信息中记录着分区开始、结束的CHS信息,使用的File System以及总扇区数等信息。像这样,在DPT中可以储存4个分区。这四个分区就是人们熟知的MBR下的四个主分区。

最后的相当于是一个Validation Check。只有BIOS读到了这个Magic Number才会继续进行引导,否则会报错,相当于没有找到MBR的主引导分区。

而扩展分区和逻辑分区是如何实现的呢?由于主引导记录的DPT中至多只能储存4个分区信息,这时,为了有更多的分区,就将4个分区划分成3P+E的形式,最后的这个E分区就是扩展分区。在这个分区里只记录一份磁盘分区信息(也就是说,扩展分区无法直接用于存储)。在这份磁盘分区信息中记录的分区,就是逻辑分区。

表面上,MBR只能有四个分区的问题得到了解决。事实上,还有一个更大的问题。之前提过,一个分区的信息只能由16Bytes来表示。在分区信息中表示分区大小,是通过描述总扇区数实现的。而描述总扇区数的信息,事实上在上面的实例中,就是最后的4个Byte:`7E 86 BB 00`。在这种情况下,分区大小就等于总扇区数 * 扇区大小(512Bytes)。然而,这种方式表达的最大扇区数就是`FF FF FF FF`,折合过来就是2TB。换言之,MBR无法支持2TB以上的分区。这在现代是非常致命的。

### GPT

GPT是在UEFI的规范下研制的。它的全称是Globally Unique Identifier Partition Table,也叫GUID分区表。

GPT的大概结构如下:

| 项目 | 扇区 |
| ---------------------- | ------------ |
| PMBR | LBA0 |
| GPT HDR | LBA1 |
| Partition Table | LBA2~LBA34 |
| Partition ||
| Partition Table Backup | LBA-2~LBA-34 |
| GPT HDR Backup | LBA-1 |



第一项是PMBR,这里P为Protective之意。为了兼容不支持GPT的分区工具,PMBR可以像在Legacy BIOS下一样被操作并启动。而支持GPT的启动则会在读取到PMBR后跳过这部分,去到GPT分区表头读取信息。

第二部分就是GPT HDR,即GPT表头。在这部分包含了Partition Table中的各分区及其大小、硬盘的容量大小以及一系列信息。

> GPT HDR具体包括了:
>
> - Signature的ASCII码表示
> - UEFI及GPT的版本号
> - GPT HDR的大小
> - GPT的CRC校验。UEFI启动可以根据这个校验值判断GPT HDR是否出错。如果出错则从硬盘尾部的Backup中恢复。如果备份的CRC校验也表明有误,那么硬盘就不可使用(逻辑层面)。
> - GPT HDR的LBA
> - GPT HDR Backup的LBA
> - 可用分区(Partition)的首个LBA(就是分区表的最后一个LBA+1)
> - 可用分区的最后LBA
> - GPT Partition起始LBA
> - GPT Partition总项数
> - GPT Partition中单个分区表大小
> - GPT Partition的CRC校验
> - 硬盘的GUID
第三部分是分区表。每个分区表都包含分区类型的GUID、分区自身的GUID、分区起止LBA、分区属性(只读等)、分区名。

> 微软对分区属性做了划分。包括:
>
> - 系统分区
> - EFI隐藏分区
> - Legacy BIOS的可引导分区
> - 只读分区
> - (另一个分区的)副本
> - 隐藏分区
> - 不分配驱动器号


Partition就是用户实际使用的分区了。硬盘尾端的两个Backup前文已经有所介绍。



以上。
80 changes: 80 additions & 0 deletions docs/2019-01-01-Legacy_BIOS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
## Legacy BIOS

> :fontawesome-regular-face-grin: everything411 and LoveCatC
>
> :material-clock-edit-outline: 2019年1月1日 0:00:00
## BIOS简介

BIOS是Basic Input Output System的缩写。顾名思义,BIOS是一个底端程序。大体来说,BIOS负责完成计算机硬件自检、CMOS设置、引导操作系统的启动、提供硬件I/O以及硬件中断。

最早的BIOS是IBM工程师由汇编语言写成的,并且只能用于特定的计算机。然而计算机发展到今天,BIOS早已不再是那么简单。



## 为什么要有BIOS

X86和ARM对于理解BIOS这一概念很重要。为什么要提ARM和X86呢?因为只有X86平台才有BIOS这一说法。这主要是因为对于X86平台用户来说,客制化程度很高。换言之,X86的整个产业链是相对开放的,OS厂商(OSV)会发布操作系统,芯片厂商提供CPU,主板厂商(OEM)提供主板,等等。而形成鲜明对比的是ARM平台下,基本是由品牌商整合产品,形成相对封闭的产业链。或者说,定制化程度极高。二者的差别就在于此。举个栗子,在PC上,如果Windows蓝屏了用户会吐槽巨硬,Ubuntu的Nvidia显卡驱动出了问题用户会问候F**K Nvidia,SSD出问题会大骂无良厂商……而使用MAC的时候,不论是系统崩溃,或是硬件问题,用户第一时间都会想到Apple背锅而不是去找上游厂商。

说了这么多,想要说明的,其实就是这样一个问题:X86平台上的操作系统往往要面对成千上万千奇百怪的硬件搭配,要想让操作系统等软件服务于如此多的硬件,就必须要一个抽象层来封装这种硬件上的差别,并且为软件层/操作系统提供这种接口。于是**BIOS(Basic Input Output System)**就应运而生。而BIOS的意义所就是初始化硬件和提供硬件的抽象软件接口。

> ARM体系没有BIOS,但其当然也要初始化主板的相关硬件。这一般是通过BSP(Board Support Package,板级支持包)完成的。但前文已经提过,ARM架构下的产品,硬件基本都是定制化的,在初始化时预先就知道了有哪些硬件需要初始化,相应的,BSP几乎就只需要“照方抓药”。而X86系统的硬件配置情况在开机时是未知的。需要通过*探测(Probe)、Training(内存和PCIe)和枚举(即插即用设备)*进行分析,最终才能得出硬件的配置信息。得出配置信息之后,BIOS还要对这些硬件进行软件抽象,并将抽象出的接口提供给操作系统(OS)。




## 开机时发生了什么

当用户按下计算机电源开关的那一刹,电源首先开始向主板和其他硬件供电,但这时的电压并不稳定。早期,主板上还存在北桥时,由北桥向CPU发出RESET信号使CPU初始化,电压稳定后即撤去RESET信号,CPU的初始化也就完成。现在主板只有一个南桥,CPU的初始化过程实际上是由CPU自身调节稳定电压实现的。总之,经历了这样一个电压稳定的过程后,CPU会在BIOS保留的内存地址处执行一个跳转到**系统BIOS**起始处的指令,POST自检即将开始。

> **TIPS**
>
> 1. 早期计算机上是有Reset物理按钮的,按下Reset会重启机器。松开Reset按钮时,RESET信号也就随之撤去。
> 2. 内存地址FFFF:0000H上存储了一条跳转指令。CPU初始化完成后读取这条指令,然后跳转到BIOS起始处,BIOS开始运行。


**系统BIOS**代码进行的第一步是POST自检(Power-On Self Test,加电后自检)。POST首先会进行**关键部件检测**。更确切的说,这个测试主要检测这些关键硬件是否存在、是否正常工作。一般关键部件检测会检查CPU、ROM、64KB RAM。

> **TIPS**
>
> 完整的关键硬件监测大致顺序是:CPU-ROM-BIOS-System Clock-DMA-64KB RAM-IRQ。 其中,System Clock、DMA、IRQ更多算是处理机制。有兴趣可以自行了解。
需要注意的是,在关键硬件的监测过程中,显卡事实上是没有初始化的,所以更不用提在屏幕上显示错误信息了。在这一步出问题能够作为反馈的,一般只有主板上的报警扬声器(**不是声卡!**),或是主板上带有的指示灯/LED显示板。



关键部件检测完成后就轮到了显卡。系统BIOS查找并读取、调用相应地址上的**显卡BIOS**代码,开始显卡的初始化。显卡初始化的过程中,大多数显卡都会在屏幕上打印一些初始化信息。但这个画面基本是**一闪而过**的。

> **TIPS**
>
> 显卡BIOS对应的起始地址一般在C0000H处。


接下来系统BIOS会像初始化显卡一样,读取其他硬件的BIOS代码并完成这些硬件的初始化。

初始化完成后,**系统BIOS**会显示有关自身的信息,包括类型、版本、序列号等。

接下来系统BIOS会继续进行硬件的检测和配置。比较重要的包括检测、显示CPU的类型、频率,测试RAM,配置即插即用设备等。

这些都完成后,大多数BIOS会清屏并在屏幕上方显示硬件自检后各硬件的参数和信息。

到这里,可以说POST就结束了。



接下来,系统BIOS**可能**需要更新ESCD(Extended System Configuration Data,扩展系统配置数据)。这是系统BIOS和操作系统OS交换硬件配置信息的一种手段。这些数据被存储在CMOS中。一般来说,ESCD只在硬件信息发生改动后进行更新。也存在一些特殊情况,操作系统使用了与系统BIOS不同的ESCD写入格式,这就使得每次系统BIOS更新后,OS启动时又会重写ESCD,下次再由系统BIOS重写…如此陷入到循环中。



BIOS的最后一项工作就是移交控制权给下一阶段的启动程序。但,移交的前提是,“知道移交给谁”。这时首先要确定下一阶段的启动程序在哪个设备中。这时就到了一个熟悉的选项——**Boot Sequence (启动顺序)**。BIOS会按照启动顺序,将控制权移交给第一个设备。



BIOS的使命,到此就告一段落。当然,即使是到OS启动之后,现代的BIOS依然有其用武之地。但那就牵扯到更多的历史和标准规范了。



以上介绍的加电-POST-引导在很长的时间内都是一种主流解决方案。然而,随着时代的发展,一种新的启动方式——UEFI启动出现了。UEFI启动比上述BIOS启动方案有很多优势。为了区分,上面这种BIOS启动方式也被称作Legacy BIOS。
Loading

0 comments on commit c1d7e32

Please sign in to comment.