磁盘分区原理

上一小节,我们学习了磁盘的内部结构,掌握了磁头、磁道、扇区等概念。

现代磁盘以扇区为基本存储和读写单位,采用 LBA 线性寻址机制,每个扇区都有一个线性序号作为地址。因此,磁盘可以简单看作是一个由一系列扇区组成的存储阵列。

我们知道,磁盘是支持分区的,分区可大可小。那么,磁盘分区是怎么实现的呢?一个分区都包含哪些扇区,又是怎么决定的呢?哪些 Linux 命令可以用来对磁盘进行分区呢?本文带领大家一探究竟!

磁盘分区原理

按照惯例,每块磁盘的首个扇区保存着一个特殊的 主引导记录master boot record ),内容由 3 部分组成:

  • 引导代码区 ,保存可以直接被 CPU 执行的代码指令,用于启动引导,总共 446 字节;
  • 磁盘分区表 ,保存磁盘的分区信息,共 4 条记录,每条 16 字节,共 64 字节;
  • 有效标志 ,占 2 个字节,通常为 0x550xaa ,表示主引导记录有效;

主分区

磁盘分区表有 4 条记录,这意味着一个磁盘可以被划分为 4 个分区,这就是所谓的 主分区main partition )。

每条分区记录保存一个分区的基本信息,相关字段依次是:

  • 分区状态 ,占 1 字节;
  • 起始扇区CHS坐标 ,占 3 字节,现已弃用;
  • 分区类型 ,占 1 字节;
  • 结束扇区CHS坐标 ,占 3 字节,现已弃用;
  • 起始扇区号 ,即该分区在磁盘中的起始位置,占 4 字节;
  • 总扇区数 ,即该分区的大小,占 4 字节;

换句话讲,组成磁盘分区的扇区是连续的,起始扇区号 记录起始位置,总扇区数 记录扇区个数。

以上图为例,根据分区表我们知道磁盘被划分为 4 个分区。其中,第一个分区的起始扇区为 2048 ,包含 2097152 个扇区,见磁盘绿色区域。因此,该分区总容量为 $512 \times 2097152$ 字节,也就是 1GB

扩展分区

由于 MBR 分区表只有 4 个条目,意味着一个磁盘最多只能划分为 4 个主分区,这肯定有不够用的时候。

如果想将磁盘划分为更多分区,可以将主分区标记为 扩展分区extended partition ),再将它进一步划分为若干 逻辑分区logical partition )。

这又是怎么实现的呢?答案是—— 扩展启动记录extended boot record ),简称 EBR

分区类型如果被设置为 0x05 表示这个分区是一个扩展分区,分区的首个扇区会保存一条扩展启动记录。这个记录的字段结构跟主引导记录类似,也包含一个分区表,进一步将扩展分区划分为逻辑分区。

扩展分区表结构跟主分区表一样,同样是 4 条,只是用法稍有不同。扩展分区表通常只用前 2 条,一条描述当前逻辑分区的信息,另一条则指明下一个逻辑分区 EBR 记录的位置。

如上图,这是一个扩展分区,它作为整体由 MBR 中的分区表描述,但又通过 EBR 分为 4 个逻辑分区。扩展分区的首个扇区保存 EBR 记录,EBR 分区表第一项描述第一个逻辑分区,另一项描述下一个分区。

因此,扩展分区中的每个逻辑分区前面都有一个 EBR 记录,它可以看作一个链表节点。通过一个 EBR 节点,我们可以找到对应的逻辑分区,以及下一个 EBR 节点。因此,逻辑分区个数没有数量限制,可以按照需要任意划分。

  • 主引导记录分区表先将磁盘分为 主分区 ,最多 4 个;
  • 主分区 可以被标注为 扩展分区
  • 扩展引导记录进一步将 扩展分区 划分为 逻辑分区 ,个数不限;

Linux分区操作

那么,用什么工具来给磁盘分区呢?接下来,以 Linux 系统为例,讲解磁盘分区的基本操作。

块设备文件

Linux 系统,每个磁盘都有一个 设备文件device file)与之对应,位于 /dev 目录下。设备文件可分为两种: 字符设备char device )和 块设备block device )。磁盘为块设备,其设备文件名通常为 sdasdb 等。

我有一台 VirtualBox 虚拟机,给它加了一块额外的虚拟硬盘,启动后就可以看到它的设备文件:

1
2
$ ls /dev/sd*
/dev/sda  /dev/sda1  /dev/sda2  /dev/sdb

sdasdb 分别是系统的第一和第二块磁盘,第一块是原来就有的,第二块是我后来加的。

查看分区信息

Linux 系统一般执行 fdisk 来操作磁盘分区,通过设备文件来指定要操作的硬盘。第一块磁盘为系统盘,装系统时已经做好分区,我们执行 fdisk 来查看分区信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
$ sudo fdisk -l /dev/sda
Disk /dev/sda: 10 GiB, 10737418240 bytes, 20971520 sectors
Disk model: VBOX HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 3F3307E6-C4C2-4B3C-86D7-F838A037E7D6

Device     Start      End  Sectors Size Type
/dev/sda1   2048     4095     2048   1M BIOS boot
/dev/sda2   4096 20969471 20965376  10G Linux filesystem

fdisk 通过设备文件 /dev/sda 来读写磁盘,因此需要超级用户权限。

可以看到,第一块磁盘分为两个分区,第一个大小为 1MBBIOS 引导时用的;第二个大小是 10GB ,也就是安装这个 Linux 系统的分区。注意到,每个分区也都有个设备文件与之对应,名字为磁盘设备名加上序号,依次是 sda1sda2

调整分区

那么,怎么调整磁盘的分区呢?我们以 sdb 为例,讲解如何给新磁盘分区。

首先,执行 fdisk 命令并指定磁盘的设备文件(不加 -l 选项),进入交互界面:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
fasion@u2004 [ ~ ]  ➜ sudo fdisk /dev/sdb

Welcome to fdisk (util-linux 2.34).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0xd8ea500a.

Command (m for help):

进入交互界面后,可以执行很多子命令。其中,子命令 m 可以查看帮助,敲入 m 并按回车:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Command (m for help): m

Help:

  DOS (MBR)
   a   toggle a bootable flag
   b   edit nested BSD disklabel
   c   toggle the dos compatibility flag

  Generic
   d   delete a partition
   F   list free unpartitioned space
   l   list known partition types
   n   add a new partition
   p   print the partition table
   t   change a partition type
   v   verify the partition table
   i   print information about a partition

  Misc
   m   print this menu
   u   change display/entry units
   x   extra functionality (experts only)

  Script
   I   load disk layout from sfdisk script file
   O   dump disk layout to sfdisk script file

  Save & Exit
   w   write table to disk and exit
   q   quit without saving changes

  Create a new label
   g   create a new empty GPT partition table
   G   create a new empty SGI (IRIX) partition table
   o   create a new empty DOS partition table
   s   create a new empty Sun partition table

查看分区信息

子命令 p 可以查看磁盘当前分区信息,新磁盘 sdb 上还没有任何分区:

1
2
3
4
5
6
7
8
Command (m for help): p
Disk /dev/sdb: 5 GiB, 5368709120 bytes, 10485760 sectors
Disk model: VBOX HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xd8ea500a

新建空分区表

先执行子命令 o 创建一个空的分区表( MBR ):

1
2
Command (m for help): o
Created a new DOS disklabel with disk identifier 0xd2a1c801.

创建主分区

执行子命令 n 可以添加一个新分区,我们先添加一个大小为 1GB 的主分区:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Command (m for help): n
Partition type
   p   primary (0 primary, 0 extended, 4 free)
   e   extended (container for logical partitions)
Select (default p):

Using default response p.
Partition number (1-4, default 1):
First sector (2048-10485759, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-10485759, default 10485759): +1G

Created a new partition 1 of type 'Linux' and of size 1 GiB.

创建逻辑分区

现在尝试建一个 2G 的扩展分区,分区类型为 e 表示扩展分区 :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
Command (m for help): n
Partition type
   p   primary (1 primary, 0 extended, 3 free)
   e   extended (container for logical partitions)
Select (default p): e
Partition number (2-4, default 2):
First sector (2099200-10485759, default 2099200):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2099200-10485759, default 10485759): +2G

Created a new partition 2 of type 'Extended' and of size 2 GiB.

然后再将其分为两个 1G 的逻辑分区,分区类型选 l 表示逻辑分区:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Command (m for help): n
Partition type
   p   primary (1 primary, 1 extended, 2 free)
   l   logical (numbered from 5)
Select (default p): l

Adding logical partition 5
First sector (2101248-6293503, default 2101248):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2101248-6293503, default 6293503): +1G

Created a new partition 5 of type 'Linux' and of size 1 GiB.

重复这个操作可以将扩展分区中的剩余空间建成另一个 1G 的逻辑分区:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Command (m for help): n
Partition type
   p   primary (1 primary, 1 extended, 2 free)
   l   logical (numbered from 5)
Select (default p): l

Adding logical partition 6
First sector (4200448-6293503, default 4200448):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (4200448-6293503, default 6293503):

Created a new partition 6 of type 'Linux' and of size 1022 MiB.

现在查看分区表可以看到我们建好的扩展分区,序号为 2 ;以及它包含的 2 个逻辑分区,序号分别是 56

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
Command (m for help): p
Disk /dev/sdb: 5 GiB, 5368709120 bytes, 10485760 sectors
Disk model: VBOX HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x7487e2a0

Device     Boot   Start     End Sectors  Size Id Type
/dev/sdb1          2048 2099199 2097152    1G 83 Linux
/dev/sdb2       2099200 6293503 4194304    2G  5 Extended
/dev/sdb5       2101248 4198399 2097152    1G 83 Linux
/dev/sdb6       4200448 6293503 2093056 1022M 83 Linux

小结

  • 一个磁盘最多可以划分为 4 个主分区,由主引导记录分区表决定;
  • 主分区可以设为扩展分区,再分为若干逻辑分区,由扩展引导记录实现;
  • Linux 下每个磁盘都有一个设备文件与之对应,例如 sdasdb 等;
  • 磁盘的每个分区也都有一个设备文件与之对应,为磁盘设备文件名加上序号,例如 sdb1sdb2 等;
  • 通过设备文件可以直接读写一个磁盘,或者一个磁盘分区的扇区;

小菜自制文件系统】系列文章首发于公众号【小菜学编程】,敬请关注:

【自制文件系统】系列文章首发于公众号【小菜学编程】,敬请关注: