磁盘组成

  • 圆形的盘片(主要记录数据的部分);
    • 扇区(Sector)为最小的物理储存单位,且依据磁盘设计的不同,目前主要有 512Bytes 与 4K 两种格式;
    • 将扇区组成一个圆,那就是柱面(Cylinder);
    • 早期的分区主要以柱面为最小分区单位,现在的分区通常使用扇区为最小分区单位;
    • 磁盘分区表主要有两种格式,一种是限制较多的 MBR 分区表,一种是限制较少的 GPT 分区表。
      • MBR 分区表中,第一个扇区最重要,里面有:
        • 主要开机区(Master boot record, MBR),占有 446 Bytes
        • 分区表(partition table),占有 64 Bytes。
      • GPT 分区表除了分区数量扩充较多之外,支持的磁盘容量也可以超过 2TB。
  • 机械手臂,与在机械手臂上的磁头(可读写盘片上的数据);
  • 主轴马达,可以转动盘片,让机械手臂的磁头在盘片上读写数据。

MSDOS(MBR)分区表

早期为了兼容windows的磁盘,所以支持MBR(Master Boot Record)的方式处理开机管理程序与分区表。这个扇区大小为512Bytes,其中主要开机记录区(安装开机管理程序的地方)大小为446Bytes,分区表(记录整颗硬盘的状态)大小为64Bytes。

磁盘分区表的作用示意图

如上图所示为分区表的记录区段示意图,我们假设硬盘只有400个柱面,共分区成为四个分区,第四个分区所在为第301到400号柱面的范围。 当你的操作系统为Windows时,那么第一到第四个分区的代号应该就是C, D, E, F。当你有数据要写入F盘时, 你的数据会被写入这颗磁盘的301~400号柱面之间的意思。

由于分区表只有64 Bytes,最多只能容纳四笔分区的记录, 这四个分区的记录被称为主要(Primary)或延伸(Extended)分区。那么当我们要分割出四个以上的分区时,就需要从延伸分区入手。延伸分区只能有一个,其目的是使用额外的扇区来记录分区信息,延伸分区本身并不能被拿来格式化。

如下图所示:硬盘的四个分区记录区仅使用到两个,P1为主要分区,而P2则为延伸分区,可从延伸分区继续分出五个分区, 这五个由延伸分区继续切出来的分区,就被称为逻辑分区(logical partition),可以使用的柱面范围就是延伸分区所设置的范围。逻辑分区的设备ID是从5开始,前面4个ID是保留给主要分区和延伸分区使用的。

磁盘分区表的作用示意图

GPT分区表(GUID partition table)

MBR的一个扇区仅512Bytes,GPT的扇区可达到4k。为了相容于所有的磁盘,因此在扇区的定义上面, 大多会使用所谓的逻辑区块位址(Logical Block Address, LBA)来处理。GPT 将磁盘所有区块以此 LBA(默认为 512Bytes) 来规划,而第一个 LBA 称为 LBA0 (从 0 开始编号)。

与 MBR 仅使用第一个 512Bytes 区块来纪录不同, GPT 使用了 34 个 LBA 区块来纪录分区信息。同时与MBR失去第一扇区就会引起分区故障不同, GPT 除了前面 34 个 LBA 之外,整个磁盘的最后 33 个 LBA 也拿来作为另一个备份,结构图如下所示:

GPT 分区表的结构示意图
  • LBA0 (MBR 相容区块):与MBR相似,存储了开机管理程序。而在原本的分区表的纪录区内,这个相容模式仅放入一个特殊标志的分区,用来表示此磁盘为 GPT 格式
  • LBA1 (GPT 表头纪录):纪录了分区表本身的位置与大小,同时纪录了备份用的 GPT 分区 (最后34个LAB区块)放置的的位置,同时放置了分区表的检验机制码 (CRC32),操作系统可以根据这个检验码来判断 GPT 是否正确。若有错误,还可以通过这个纪录区来取得备份的 GPT(磁盘最后的那个备份区块) 来恢复 GPT 的正常运行。
  • LBA2-33 (实际纪录分区信息处):从 LBA2 区块开始,每个 LBA 都可以纪录 4 笔分区纪录。

GPT 分区已经没有所谓的主、延伸、逻辑分区的概念,既然每笔纪录都可以独立存在, 当然每个都可以视为是主分区,每一个分区都可以拿来格式化使用。

BIOS 搭配 MBR/GPT 的开机流程

BIOS是一个写入到主板上的一个固件(写入到硬件上的软件),在开机的时候会主动执行的第一个程序,它会去分析计算机里有哪些存储设备。

以硬盘为例,BIOS会依据使用者的设置去取得能够开机的硬盘, 并且到该硬盘里面去读取第一个扇区的MBR位置。 MBR这个仅有446 Bytes的硬盘容量里面会放置最基本的开机管理程序, 此时BIOS就功成圆满,而接下来就是MBR内的开机管理程序的工作了,开机管理程序主要是载入核心文件。

如果分区表为 GPT 格式的话,那么 BIOS 也能够从 LBA0 的 MBR 相容区块读取第一阶段的开机管理程序码, 如果开机管理程序能够认识 GPT 的话,那么使用 BIOS 同样可以读取到正确的操作系统核心。换句话说, 如果开机管理程序不懂 GPT ,例如 Windows XP 的环境,那自然就无法读取核心文件,开机就会失败。

由于 LBA0 仅提供第一阶段的开机管理程序码,因此如果你使用类似grub的开机管理程序的话,那么就得要额外分区出一个“ BIOS boot ”的分区, 这个分区才能够放置其他开机过程所需的程序码。在 CentOS 当中,这个分区通常占用 2MB 左右而已

整合起来开机流程到操作系统之间的动作应该是这样的:

  1. BIOS:开机主动执行的固件,会认识第一个可开机的设备;
  2. MBR:第一个可开机设备的第一个扇区内的主要开机记录区块,内含开机管理程序;
  3. 开机管理程序(boot loader):一支可读取核心文件来执行的软件;
    1. 提供菜单:使用者可以选择不同的开机项目,这也是多重开机的重要功能;
    2. 载入核心文件:直接指向可开机的程序区段来开始操作系统;
    3. 转交其他loader:将开机管理功能转交给其他loader负责
  4. 核心文件:开始操作系统的功能

流程中的开机管理程序功能支持多系统,开机管理程序除了安装在MBR之外,还可安装在每个分区的开机扇区。如下图所示,MBR的开机管理程序提供两个菜单,菜单一(M1)可以直接载入Windows的核心文件来开机; 菜单二(M2)则是将开机管理工作交给第二个分区的开机扇区(boot sector)。当使用者在开机的时候选择菜单二时, 那么整个开机管理工作就会交给第二分区的开机管理程序了。 当第二个开机管理程序启动后,该开机管理程序内(上图中)仅有一个开机菜单,因此就能够使用Linux的核心文件来开机啰。 这就是多重开机的工作情况。

开机管理程序的工作执行示意图

日常操作中也会如上图所示,安装系统时会选择先安装Windows再安装其它系统,产生这样的行为是因为Windows在安装的时候,他的安装程序会主动的覆盖掉原有的MBR以及自己所在分区的开机扇区,这样原先安装好的Linux项目就会消失无法选择,但Linux系统还在,那么可以利用Linux的救援模式来恢复MBR。

索引式文件系统

文件系统是操作系统中负责管理持久数据的子系统,说简单点,就是负责把用户的文件存到磁盘硬件中,因为即使计算机断电了,磁盘里的数据并不会丢失,所以可以持久化的保存文件。文件系统的基本数据单位是文件,它的目的是对磁盘上的文件进行组织管理,那组织的方式不同,就会形成不同的文件系统。

因为每种操作系统所设置的文件属性/权限并不相同, 为了存放这些文件所需的数据,因此就需要将分区进行格式化,以成为操作系统能够利用的“文件系统格式(filesystem)”。

传统的磁盘与文件系统之应用中,一个分区就是只能够被格式化成为一个文件系统,所以一个 filesystem 就是一个 partition。但是由于新技术的利用,LVM与软件磁盘阵列(software raid), 这些技术可以将一个分区格式化为多个文件系统(例如LVM),也能够将多个分区合成一个文件系统(LVM, RAID)。所以说,目前在格式化时已经不再说成针对 partition 来格式化了, 一个可被挂载的数据为一个文件系统而不是一个分区。

格式化后的文件系统分为以下几大存储区域:

  • superblock:记录此 filesystem 的整体信息,包括inode/block的总量、使用量、剩余量, 以及文件系统的格式与相关信息等;
  • inode:记录文件的属性,一个文件占用一个inode,同时记录此文件的数据所在的 block 号码;
  • block:实际记录文件的内容,若文件太大时,会占用多个 block 。

每个文件都会占用一个inode,而inode内置存放文件数据的block号码,所以可以理解成找到文件的inode信息就可以读取到数据了。

如下图所示,文件系统先格式化出 inode 与 block 的区块,假设某一个文件的属性与权限数据是放置到 inode 4 号而这个 inode 记录了文件数据的实际放置点为 2, 7, 13, 15 这四个 block 号码,此时我们的操作系统就能够据此来排列磁盘的读取顺序,将四个 block 内容读出来。

inode/block 数据存取示意图

这种数据存取的方法我们称为索引式文件系统(indexed allocation)。

EXT2文件系统

Ext2 文件系统在格式化的时候基本上是区分为多个区块群组 (block group) 的,每个区块群组都有独立的 inode/block/superblock 系统。ext2格式化后如下所示 :

ext2文件系统示意图
  • Boot Sector:安装开机管理程序
  • Data Block:存放文件内容数据
  • Inode Table:存放文件属性以及该文件实际数据的block
  • Superblock:记录整个文件系统的信息
  • Filesystem Description:描述每个 block group 的开始与结束的 block 号码,以及说明每个区段 (superblock, bitmap, inodemap, data block) 分别介于哪一个 block 号码之间
  • block bitmap:记录的是使用与未使用的 block信息
  • inode bitmap:记录使用与未使用的 inode信息

文件系统与目录树的关系

在 Linux 下的文件系统创建一个目录时,文件系统会分配一个 inode 与至少一块 block 给该目录。其中,inode 记录该目录的相关权限与属性,并可记录分配到的那块 block 号码; 而 block 则是记录在这个目录下的文件名与该文件名占用的 inode 号码数据。也就是说目录所占用的 block 内容在记录如下的信息:

目录占用的 block 记录的数据示意图

 在目录下面的文件数如果太多而导致一个 block 无法容纳的下所有的文件名与 inode 对照表时,Linux 会给予该目录多一个 block 来继续记录相关的数据。

当我们在 Linux 下的ext2创建一个一般文件时,ext2会分配一个 inode 与相对于该文件大小的 block 数量给该文件。假设一个 block 为 4 KBytes ,创建一个 100 KBytes 的文件,那么 linux 将分配一个 inode 与 25 个 block 来储存该文件。 但由于 inode 仅有 12 个直接指向,还要多一个 block 来作为区块号码的记录。

# ll -di / /etc /etc/passwd
      64 dr-xr-xr-x. 17 root root  224 Oct 23 01:13 /
67160129 drwxr-xr-x. 75 root root 8192 Oct 30 09:40 /etc
67556680 -rw-r--r--.  1 root root  798 Oct 23 01:13 /etc/passwd

读取/etc/passwd的流程:

  1. / 的 inode:通过挂载点的信息找到inode号码为64的根目录inode,inode规范的权限可以读取该 block 的内容(rx) ;
  2. / 的 block:经过上个步骤取得 block 的号码,并找到该内容有 etc/ 目录的 inode 号码 (67160129);
  3. etc/ 的 inode:读取67160129号inode得知root具有 r 与 x 的权限,因此可以读取etc/的block内容;
  4. etc/ 的 block:经过上个步骤取得block号码,并找到该内容有passwd文件的inode号码 (67556680);
  5. passwd的inode:读取67556680号inode得知root具有 r 的权限,因此可以读取passwd的block内容;
  6. passwd的block:最后将该block内容的数据读出来。

新建文件的流程:

  1. 确定使用者对于新增文件的目录是否具有 w 与 x 的权限;
  2. 根据inode bitmap找到没有使用的inode号码,并将新文件的权限/属性写入;
  3. 根据block bitmap找到没有使用中的block号码,并将实际的数据写入block中,且更新inode的block指向数据;
  4. 将刚刚写入的inode与block数据同步更新inode bitmap 与block bitmap,并更新superblock的内容。

 inode table与data block称为数据存放区域,其他例如superblock、block bitmap于inode bitmap等区段就被称为metadata(中介数据)。 

非同步处理

当系统载入一个文件到内存后,如果该文件没有被更动过,则在内存区段的文件数据会被设置为干净(clean)的。 但如果内存中的文件数据被更改过了(例如你用vim去编辑过这个文件),此时该内存中的数据会被设置为脏的 (Dirty)。此时所有的动作都还在内存中执行,并没有写入到磁盘中。系统会不定时的将内存中设置为“Dirty”的数据写回磁盘,以保持磁盘与内存数据的一致性。

文件系统与内存的关联影响:

  • 系统会将常用的文件数据放置到内存的缓冲区,以加速文件系统的读/写,可加速系统性能;
  • 可使用 sync 来强迫内存中设置为 Dirty 的文件回写到磁盘中;
  • 正常关机时,关机指令会主动调用 sync 来将内存的数据回写入磁盘内;
  • 不正常关机时(如跳电、死机或其他不明原因),由于数据尚未回写到磁盘内, 重新开机后可能会花很多时间在进行磁盘检验,甚至可能导致文件系统的损毁(非磁盘损毁)。

VFS( Virtual Filesystem Switch )

文件系统的种类众多,而操作系统希望对用户提供一个统一的接口,于是在用户层与文件系统层引入了中间层,这个中间层就称为虚拟文件系统(VFS)。

假设你的/使用的是/dev/hda1,用ext3,而/home使用/dev/hda2,用reiserfs,那么你取用home目录的文件时,不需用文件系统的模块来读取,通过VFS的功能来管理所有的filesystem, 省去我们需要自行设置读取文件系统的定义。

VFS 文件系统的示意图

XFS 文件系统

EXT家族文件系统支持度最广,但格式化慢。xfs也是一个日志系统,用于大容量磁盘以及高性能文件系统。

xfs文件系统组成:

  • 数据区 (data section):数据区就跟ext家族一样,包括inode/data block/superblock等数据,都放置在这个区块。 这个数据区与ext家族的block group类似,也是分为多个储存区群组 (allocation groups) 来分别放置文件系统所需要的数据。 每个储存区群组都包含了 (1)整个文件系统的 superblock、(2)剩余空间的管理机制、 (3)inode的分配与追踪。此外inode与block都是系统需要用到时才动态配置产生,所以格式化动作快。
  • 系统活动登录区 (log section):用来纪录文件系统的变化,类似于日志区。文件的变化会在这里纪录下来,直到该变化完整的写入到数据区后, 该笔纪录才会被终结。如果文件系统因为某些缘故 (如停电关机) 而损毁时,系统会拿这个登录区块来进行检验,看看系统挂掉之前,文件系统正在运行什么动作,借以快速修复文件系统。
  • 实时运行区 (realtime section):当有文件要被创建时,xfs会在这个区段里面找一个到数个的extent区块,将文件放置在这个区块内,等到分配完毕后,再写入到data section的inode与block 去。 这个extent区块的大小得要在格式化的时候就先指定,最小值是4K最大可到1G。一般非磁盘阵列的磁盘默认为64K容量,而具有类似磁盘阵列的stripe情况下,则建议extent设置为与stripe一样大较佳。这个extent最好不要乱动,因为可能会影响到实体磁盘的性能。

观察xfs系统数据

# df -T /boot/
Filesystem     Type 1K-blocks   Used Available Use% Mounted on
/dev/sda1      xfs    1038336 153452    884884  15% /boot
# xfs_info /dev/sda1
meta-data=/dev/sda1              isize=512    agcount=4, agsize=65536 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=0 spinodes=0
data     =                       bsize=4096   blocks=262144, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
log      =internal               bsize=4096   blocks=2560, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
  • 第 1 行里面的 isize 指的是 inode 的容量,每个有 512Bytes 这么大。至于 agcount 则是储存区群组 (allocation group) 的个数,共有 4 个, agsize 则是指每个储存区群组具有 65536 个 block 。
  • 第 2 行里面 sectsz 指的是逻辑扇区 (sector) 的容量设置为 512Bytes。
  • 第 4 行里面的 bsize 指的是 block 的容量,每个 block 为 4K 的意思,共有 262144 个 block 在这个文件系统内。
  • 第 5 行里面的 sunit 与 swidth 与磁盘阵列的 stripe 相关性较高。由于我们并没有使用磁盘阵列,因此 sunit 与 extent 就没有额外的指定特别的值。
  • 第 7 行里面的 internal 指的是这个登录区的位置在文件系统内,而不是外部设备的意思。且占用了 4K * 2560 个 block,总共约 10M 的容量。
  • 第 9 行里面的 realtime 区域,里面的 extent 容量为 4K。不过目前没有使用。