gvim 发表于 2008-06-30 16:04

FreeBSD的磁盘分区

上面雨丝风片结合bochs的汇编调试器详细描述了mbr的引导过程。作为上文的延续,这里我和大家一起用基于实验的手法观察PC机上FreeBSD的分区。

FreeBSD在PC结构的计算机上,有两种同时使用的,也是十分容易混淆的分区概念:一是DOS分区,一是BSD分区。本文的重点在帮助大家准确理解这两种分区。

为了理解分区,我们最好的插手阶段是FreeBSD的引导过程。简单的说,完整的FreeBSD的引导分为四部分:第一部分是DOS分区的mbr引导,FreeBSD将之称为boot0;第二部分是FreeBSD分区的引导,FreeBSD将之称为boot1;然后是FreeBSD所谓的boot2引导,最后是loader引导。

整个过程如下图1示
图1:引导过程简图

其中mbr引导是与FreeBSD无关的,也就是说我们可以使用很多不同的引导工具进行mbr引导,最著名的恐怕就是grub了。后面三个引导阶段是FreeBSD自有的,这是FreeBSD称之为三部曲的引导,本文仅解析磁盘分区,并不深入FreeBSD的引导,因此关于boot的详情可以参见man 8 boot。

第一部分:DOS分区

那么,奇怪的是为什么在讨论FreeBSD分区的时候,我们需要首先面对的却是DOS分区呢?原因在于,PC机上的FreeBSD运行在基于IA32的硬件之上,并且在设计时就考虑到和同一个磁盘上的其他系统共存的用户需求,比如Microsoft Windows。所以,FreeBSD不得不建立在DOS分区之上的。当然运行在非IA32体系的计算机上的FreeBSD系统并不一定使用DOS分区。

首先我们需要一个实验环境来观察后面的操作:操作系统FreeBSD7.0-release,虚拟机bochs2.3.7,虚拟磁盘大小10G,分为两个区:一个分区1G为FAT分区,另一个分区9G为BSD分区。创建虚拟磁盘的操作显示如图2:

图2: 实验用虚拟硬盘


图2告诉我们的最重要的信息是total sectors=20971440,而我们在安装*BSD时经常看到的cylinder=20805,heads=16等信息在现在较新的磁盘上已经不再重要,至于原因我们放在下面讲述。我们还可以看出这个磁盘的扇区大小为(10239.96MB/20971440扇区=512字节)。

空白磁盘添加了保留引导区之后的第一次划分光秃秃的如下图3:

图3: 磁盘的第一次划分


这时磁盘上并没有任何数据,这63个扇区的划分直到后面写入磁盘信息时才会正式建立,我们这里引用这些分区是为了文章描述的逻辑性。

OK,接下来我们正常安装FreeBSD,直到下一步,如图4:(同样的信息我们也可以在系统安装完成之后使用fdisk命令查看)

图4中显示了设备名:ad0,整个磁盘扇区大小:20971440,还有三个看起来像分区的条目。呃,这里用了“看起来像”,是因为从扇区0到扇区62结束的第一个条目,其实不是一个分区,它是磁盘的保留块,至于为什么要63个扇区而不是我们经常讲的mbr 1个扇区?原因在于磁盘开始的63个扇区保留给了mbr和扩展分区。而为什么保留的扇区是63而不是64,65呢?呃,因为一个柱面包含了63个扇区:) 第0个扇区确实是我们关心的mbr,而第1—62扇区,是为扩展分区保留的空间,它可以不使用。如果是这样,那么1—62扇区可以存放扩展引导代码,也可以放数据。

进一步划分:一个分区1G为FAT分区,从63扇区开始到2096639扇区结束,共(2096639−63+1) = 2096577个扇区,共2096577S · 512B = 1023KB = 1G。这个分区的分区号为Subtype = 6。另一个分区9G为FreeBSD分区,从2096640扇区开始到20971439扇区结束,共18874800个扇区,分区号为Subtype = 165。

图4: 划分磁盘DOS分区


图4中显示的Subtype也就是我们通常说的分区类型,我们会在后面的mbr实例中看到这个类型号。然而,分区类型的使用只与操作系统有关。系统挂载时,OS可以使用,也可以不使用这个类型决定目标分区系统的类型。

接下来的安装中,用户可以选择FreeBSD安装到mbr的操作类型,如图5所示。

图5: mbr引导程序选择


BootMgr对应/boot/boot0(代码在src/boot/i386/boot0/),Standard对应/boot/mbr(代码在src/boot/i386/mbr/),None当然就是不改变当前PC机安装的mbr。那么问题是:FreeBSD为什么要分两种类型的mbr?其实boot0和mbr都是安装在PC机的mbr上的,完成相同的引导功能,然而它们之间的区别在于boot0提供一种简单的分区选择功能。当系统启动时,boot0会根据分区表的内容自动检测系统上存在的磁盘分区,然后在一定计时范围内给出一个选择让用户自行选择boot0下一步引导的磁盘分区。这时可以看见屏幕上出现的所有分区列表,和它对应的选择键:F1, F2, F3等等。用户如果在这期间选择了一个分区进行引导,那么boot0自动将用户选择的分区激活为活动分区并进行加载。当计时超出之后,boot0将默认选择分区表中记录的活动分区进行引导。这样一来,如果上一次用户选择了某个分区进行引导,那么下一次PC机启动的时候,默认的启动分区就是上一次用户选择的引导分区。

/boot/mbr的功能就简单的多,只需要找到分区表里面的活动分区进行加载就完成任务。具体的代码分析参见雨丝风片的FreeBSD7.0 /sys/boot/i386/mbr/mbr.s源代码分析。

需要注意的是:我们这里仅仅只是引用文件系统上的对应特定文件,这并不意味着系统引导之时,就是执行的这些文件。也就是说:FreeBSD在引导过程中执行的boot0(mbr) 和后面讲述的boot1 都不是直接执行文件系统上的代码。因为在引导之时,FreeBSD的文件系统结构尚未在内存中建立。解决的办法是通过bsdlabel, boot0cfg等工具,FreeBSD直接将/boot/boot0(/boot/mbr) 和/boot/boot1 安装到磁盘的对应扇区上(需要root权限)。

图6: mbr结构,图片来自wiki

图6和图7显示了mbr的结构和分区表结构,下面祭起我们的照妖镜 -- bochs,观察有关分区的系统活动。

图7: mbr分区结构,图片来自wiki


中间插一句(等会还要继续按照我们的系统,呵呵),在整个系统安装完成之后,我们通过在bochs引导初期下断点的方式,取得图8所示的整个512字节mbr反汇编:

图8: mbr中的分区表


mbr信息我们也可以通过dd if=/dev/ad0 of=mbr.bin bs=512 count=1得到(为什么是/dev/ad0在后面解释)。我们最关心的是446—509字节表示的mbr分区表内容,每个条目16字节,共4个条目。446—462字节描述了第一个DOS分区,463—479描述了第二个DOS分区。

0x7DBE开始的16个字节依次是00 010100 06 0fffff 3f000000 c1fd1f00(注意,这里反汇编显示的是intel的little-ending序)按照图7的分区表格式,我们知道这个分区不是引导分区(0x0),分区类型是0x6 (图4中的Subtype域,下同),起始LBA在第0x3f=63 扇区,分区大小0x1ffdc1=2096577 扇区,这是一个FAT分区。继续0x7DCE开始的第二个分区,依次是80 ffffff a5 0fffff 00fe1f00 b0012001 按照图7的分区格式,这个分区是引导分区(0x80),分区类型是0xa5 (表示FreeBSD分区),起始LBA在第0x1ffe00=2096640扇区,分区大小0x12001b0=18874800 扇区,这是一个FreeBSD分区。

呵呵,是不是和我们在图4中看到的一样呢?至此,FreeBSD引导过程的DOS引导部分完成。

总结一下目前为止的DOS分区的情况,图9

图9: 简明DOS分区结构


整个第一个磁盘在FreeBSD里面表示为/dev/ad0,如果有第二个磁盘,则命名为/dev/ad1,依次类推。磁盘上的DOS分区在FreeBSD里面表示为Slice,因此第一个磁盘/dev/ad0上的第一个Slice为/dev/ad0s1,第二个Slice为/dev/ad0s2。正如9所示,/dev/ad0s1在mbr里面被表示为FAT分区,从63号扇区开始到2096639扇区结束;/dev/ad0s2在mbr里面被表示为FreeBSD分区,跨越剩下的磁盘空间。

最后做个简单的加法:
/dev/ad0s2 = /dev/ad0 + 2099640 。
这意味着
dd if=/dev/ad0s2 of=xxx bs=512 count=1

dd if=/dev/ad0 of=yyy bs=512 count=1 skip=2099640
完成相同的功能。

[ 本帖最后由 gvim 于 2008-6-30 16:55 编辑 ]

gvim 发表于 2008-06-30 16:04

接上


第二部分:FreeBSD分区

我们继续前面的安装过程,进行到下步图10:
图10: BSD分区


这里我们同样看到了/dev/ad0s1和/dev/ad0s2的区分信息。我们在FreeBSD中分三个分区,第一个128M作为swap,接着是1G的/根,最后剩下的都分给/usr。默认剩下的操作步骤,就可以将FreeBSD系统安装到bochs虚拟机中,下面的实验操作(以及上面提及的相关操作都可以在完整系统中实验了)。

那么BSD分区结构又是什么样的呢,它在磁盘上的布局又是怎样?

首先源代码src/sys/sys/disklabel.h中的struct disklabel结构告诉了我们关于BSD分区结构的答案(分区类型号也记录在这个文件里)。

简单起见,我们只引用本文关心的结构域和分区类型:
#define MAXPARTITIONS 8
struct disklabel {
      u_int32_t d_magic; /* the magic number */
      char d_typename; /* type name, e.g. "eagle" */
      struct partition { /* the partition table */
                u_int32_t p_size; /* number of sectors in partition */
                u_int32_t p_offset; /* starting sector */
                u_int8_t p_fstype; /* filesystem type, see below */
      } d_partitions; /* actually may be more */
};

#define FS_UNUSED       0               /* unused */
#define FS_SWAP         1               /* swap */
#define FS_V6         2               /* Sixth Edition */
#define FS_V7         3               /* Seventh Edition */
#define FS_SYSV         4               /* System V */
#define FS_V71K         5               /* V7 with 1K blocks (4.1, 2.9) */
#define FS_V8         6               /* Eighth Edition, 4K blocks */
#define FS_BSDFFS       7               /* 4.2BSD fast filesystem */
#define FS_MSDOS      8               /* MSDOS filesystem */
#define FS_BSDLFS       9               /* 4.4BSD log-structured filesystem */
#define FS_OTHER      10            /* in use, but unknown/unsupported */
#define FS_HPFS         11            /* OS/2 high-performance filesystem */
#define FS_ISO9660      12            /* ISO 9660, normally CD-ROM */
#define FS_BOOT         13            /* partition contains bootstrap */
#define FS_VINUM      14            /* Vinum drive */
#define FS_RAID         15            /* RAIDFrame drive */
#define FS_JFS2         21            /* IBM JFS2 */



d_magic表示分区的幻数,BSD的分区幻数表示为:0x82564557。d_packname是分区所在的磁盘标识符,也就是引用它的设备名。d_partitions中的p_size代表分区长度,p_offset代表起始扇区号,p_fstype表示分区类型。

FreeBSD系统正常安装之后,通过bsdlabel命令我们看到下面的BSD分区结构图11:

图11: BSD分区结构


那么BSD分区是如何在磁盘上分布的呢?图12显示出一个最一般的FreeBSD分区的磁盘分布。

图12: BSD分区磁盘分布


注意,这里的扇区号是相对/dev/ad0s2的扇区号。因此0,1,. . .,15,16等相对扇区需要在整个磁盘上引用的话,需要加上/dev/ad0s2的绝对起始扇区,也就是2096640。

简单解释一下这个图:在我们的实例系统里,FreeBSD分区的前128个扇区(64k)作为FreeBSD本身的引导区,后面接着是文件系统超级块,柱面组信息等等,这里就不详述了。其中第二个扇区开始的sizeof struct disklabel = 276个字节存放的是BSD分区表信息,这是我们这里的重点。围绕第二个扇区的其余十五个扇区,是FreeBSD的引导代码。。。(可恶,怎么又出来一个引导说-_-;),在图12中的boot1和boot2都是FreeBSD的引导代码,至于它们怎么一起工作,这里也不再关注(以后专文呈诉)。

为什么强调“我们的实例系统里”?因为FreeBSD的引导扇区并不固定为64K,还有其他尺寸可以选择,而我使用的刚好是最通常的情况:64K。

前面我们成功的从mbr里面找到了FreeBSD分区的起始扇区号:2096640。/boot/mbr读取2096640扇区的内容,正式进入FreeBSD系统的引导三部曲(man 8 boot)。

那么2096640扇区存放的是什么让我们感兴趣的东西呢?很容易从man 8 boot中找到信息:FreeBSD分区的第一个扇区存放的是boot1,图12中的第一部分。可以通过命令
dd if=/dev/ad0s2 of=boot1.bin bs=512 count=1得到这个引导块。

紧接着的磁盘块是我们的目标,呃,很简单,用命令dd if=/dev/ad0s2 of=bsdlabel.bin bs=512 count=1 skip=1 就取得了。用十六进制编辑器查看bsdlabel.bin这个文件的前276 个字节:

0000000057 45 56 82 05 00 00 0061 64 30 73 32 00 00 00|WEV.....ad0s2...|
0000001000 00 00 00 00 00 00 0000 00 00 00 00 00 00 00|................|
0000002000 00 00 00 00 00 00 0000 02 00 00 3f 00 00 00|............?...|
0000003010 00 00 00 45 51 00 00f0 03 00 00 b0 ff 3f 01|....EQ........?.|
0000004000 00 00 00 00 00 00 0010 0e 01 00 00 00 00 00|................|
0000005000 00 00 00 00 00 00 0000 00 00 00 00 00 00 00|................|
0000008000 00 00 00 57 45 56 82f3 96 08 00 00 20 00 00|....WEV...... ..|
0000009000 00 00 00 00 00 20 0000 fe 23 00 00 08 00 00|...... ...#.....|
000000a007 08 88 6f 00 00 04 0000 fe 1f 00 00 00 00 00|...o............|
000000b001 00 00 00 b0 01 20 0100 fe 1f 00 00 00 00 00|...... .........|
000000c000 00 00 00 b0 01 fc 0000 fe 43 00 00 08 00 00|..........C.....|
000000d007 08 88 6f 00 00 00 0000 00 00 00 00 00 00 00|...o............|
000000e000 00 00 00 00 00 00 0000 00 00 00 00 00 00 00|................|
0000011000 00 00 00

结合struct disklabel的结构域,我们清楚的看到:d_magic = 0x82564557,d_typename = da0s2,第一个BSD分区的分区信息从0x94字节偏移开始共16字节。前面8个字节是p_size = 0x200000 = 2097152个扇区,p_offset = 0x23fe00 = 2358784号扇区,p_fstype = 0x07。

咦,怎么回事,offset居然跑那么远?嗯,不要忘记了还要算上/dev/ad0s2的绝对偏移:2096640 + 262144 = 2358784。这样就对了,我们在图11中看到这个分区就是分区'a',在FreeBSD里面表示“/” 根分区,表示为/dev/ad0s2a,由于这个条目在bsdlabel中是第一个条目,所以就是'a'咯。类型号0x07经过查表,知道这个分区表示FS_BSDFFS。

然后下面的第二个条目,类推当然就是'b':/dev/ad0s2b,大小为0x40000 = 262144 个扇区,绝对偏移为0x1ffe00 = 2096640号扇区。相对于/dev/ad0s2的启示扇区,这个相对偏移当然就是0(图11中的offset域)。类型号0x01,表示FS_SWAP。

下面的第三个和第四个条目,可以用相同的方法推知。需要特别说明的是,'c' 在BSD分区里面表示整个Slice。

bingo!通过我们的分析,得到了和图11完全一致的结果:)

最后,说了这么多,终于可以总结DOS分区和BSD分区的关系:

图13: 磁盘上的分区总览


[ 本帖最后由 gvim 于 2008-7-1 09:50 编辑 ]

FinalBSD 发表于 2008-06-30 17:12

不错,最近越玩越高深了啊:mrgreen:

大大狗 发表于 2008-06-30 17:20

继续学习

感谢分享

enjoyo 发表于 2008-06-30 17:35

好文,支持GVIM老兄

gvim 发表于 2008-06-30 17:35

对了,忘了补一句“cylinder=20805,heads=16等信息在现在较新的磁盘上已经不再重要”,意味着现在较新的磁盘使用的线性扇区寻址,大概来说就是LBA(Logic Block Address),不再用这些柱面,磁头等东西寻址啦。
忘说了:luya: :luya: :luya:

[ 本帖最后由 gvim 于 2008-6-30 17:36 编辑 ]

qdmacat 发表于 2008-07-01 09:31

非常感谢!希望BSD版多出这样的好贴:mrgreen:

雨丝风片 发表于 2008-07-01 11:08

经典好文,看完之后对freebsd硬盘分区的概念清晰多了。:mrgreen:

congli 发表于 2008-07-01 11:09

好贴!支持!!:em17:

剑心通明 发表于 2008-07-01 11:10

不错不错,收藏了
页: [1] 2 3 4
查看完整版本: FreeBSD的磁盘分区