IBM的LPI复习资料之LPI101-Topic102:Linux安装和包管理(1)硬盘分区

摘要

学习如何为Linux系统设计硬盘分区布局。本文用来备考LPI 101 或者仅仅是为了兴趣。

概述

在本文学习为Linux系统设计硬盘分区布局,具体说来是:
  • 为文件系统或者交换空间分配单独的分区
  • 对于系统的不同应用设计不同的分区布局
  • 理解逻辑卷管理(LVM)的基本特征
本文对应LPI考试的102.1目标,权重为2。

注意:本文关注的是分区布局的设计计划,具体的实现步骤参考Topic 104。

注意:本文包含了LPI101 2012年更新的目标。除此之外,我们还增加了逻辑卷管理的基本知识,以及传统的基于MBR的硬盘的分区表细节和GPT的基本知识。本文实验平台为64位的Fedora 16系统。

文件系统概述

一个Linux的文件系统包含以目录组织的很多文件,这些文件通常存放在硬盘或者其他块设备上。像其他系统一样,目录可以包含其他的子目录。与Windows系统不同的是,Linux文件系统是一个从/开始的单树结构,而Windows为每个分区分配不同的驱动器标识符。

既然文件系统是一个单树结构,你可能想知道为什么硬盘分区还很重要。实际上,每一个块设备,如硬盘分区、CD-ROM、或者软盘,都有一个文件系统。之所以看到的是一个单树文件系统,是因为这些不同设备上的文件系统都被挂载在这个单一树的不同点上,这个点叫做挂载点。

通常,内核会挂载一个硬盘分区上的文件系统到/上,然后你可以挂载其他的硬盘分区到/boot,/tmp,或者/home。你也可以挂载CD-ROM到/media/cdrom1,挂载一个软盘到/mnt/floppy。你也可以挂载其他Os上的网络文件系统,如NFS。还有很多其他类型的挂载。既然挂载过程实际上挂载的是设备上的文件系统,所以也通常简称为“挂载设备”。

现在,假设你刚刚挂载了根文件系统,并且想挂载一个CD-ROM,/dev/sr0,到挂载点/media/cdrom。在挂载之前,这个挂载点必须存在。当挂载完毕后,CD-ROM上的文件和目录会变成/media/cdrom目录下的子目录和文件。/media/cdrom下原来的文件和目录将会不再可见,尽管这些文件和目录仍然存在于包含/media/cdrom这个挂载点的设备上。当CD-ROM取消挂载后,这些原来的文件和目录又重新变得可见。为了避免这个问题,不要把文件放到一个准备作为挂载点的目录下。

下表显示了FHS标准要求的根目录下的子目录结构。
目录名 目录下文件
bin 必备的命令文件
boot 启动加载器的文件
dev 设备文件
etc 系统配置文件
lib 共享库文件和内核模块文件
media 移动介质挂载点
mnt 临时文件系统挂载点
opt 附加的应用程序和软件包
sbin 系统命令文件
srv 系统服务的数据
tmp 临时文件
usr 用户安装的文件
var 经常变动的文件

分区

第一个SCSI驱动器通常是/dev/sda。在老的Linux系统上,第一个IDE硬盘是/dev/sda。在同时有串口硬盘和传统IDE硬盘的系统上,Ide设备通常被命名为/dev/hda,串口硬盘被命名为/dev/sda。在较新的系统上,所有的IDE设备都被叫做/dev/sda,/dev/sdb等等。IDE设备命名之所以发生变化,是因为热插拔系统的缘故,它最开始支持USB设备。热插拔允许你随时插入一个设备并立刻使用。现在,热插拔系统被用来管理所有设备,不管这个设备是内建的还是在系统启动后插入的。

传统上,一个硬盘被格式化成512字节大小的扇区。同一个盘面上不用移动磁头就可以读取的所有扇区组成一个磁道,所有盘面对应的磁道组成一个柱面。现在有的硬盘的扇区是4KB,如果一个文件系统仍然假设扇区大小是512字节的话,当分区不是在以4KB为边界的扇区开始时,系统性能将会受损。
(译注:关于这部分是传统的硬盘知识,没有一对一翻译)。

现在硬盘的物理结构往往不再是传统的扇区、磁道、柱面,而是采用了一种叫做LBA(逻辑块寻址)的方式。今天使用的大硬盘采用了LBA48标准,使用48位来表示扇区号。一种叫做GPT的新格式也被用在大硬盘上,以取代传统的MBR格式。GPT默认支持多达128个分区。

一个硬盘上的空间被划分为分区。分区之间不能有重叠。没有分配给分区的空间叫做空闲空间。分区的名字类似/dev/hda1,/dev/hda2,/dev/sda1等等。在不使用热插拔的系统上,IDE设备最多有63个分区。支持热插拔的SCSI硬盘、USB硬盘、IDE硬盘的分区不能超过15个分区。一个分区通常包含整数个柱面(也许不是传统的柱面)。

如果两个分区工具程序对硬盘的特征理解不同,那么很有可能会发生一个硬盘分区程序创建的分区在另一个分区工具下不能使用或报错。这种问题经常会在不同系统间移动硬盘时发生,特别是两个系统的BIOS不兼容。

在Linux系统中,可以使用parted或者fdisk工具来查看硬盘的标称大小。老的Linux系统中还可以通过/proc文件系统查看这些信息,一般是文件/proc/ide/hda/geometry,这个文件在新的Linux系统中不再存在。下表显示了怎样利用fdisk命令来查看硬盘的分区信息。显示或者操纵分区表,需要root权限。
Listing 1. Hard disk geometry
ian@attic4:~$ fdisk -v
fdisk (util-linux-ng 2.16)
ian@attic4:~$ sudo fdisk /dev/sdb
[sudo] password for ian: 

The number of cylinders for this disk is set to 30401.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
   (e.g., DOS FDISK, OS/2 FDISK)

Command (m for help): p

Disk /dev/sdb: 250.1 GB, 250059350016 bytes
255 heads, 63 sectors/track, 30401 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x000404d6

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1               1          25      200781   83  Linux
/dev/sdb2              26       12965   103940550   83  Linux
/dev/sdb3           12966       30401   140054670   83  Linux

Command (m for help): 


注意:fdisk给出了一个警告,说柱面的数量超过了1024。第1024个柱面在一些老的系统中很重要,因为这些系统中的BIOS只能启动位于前1024柱面的分区。这种限制只存在于不支持LBA的老的BIOS和老的启动加载器中,在现代的机器中,这种问题不再存在,但是你还是要注意到这种限制的存在。

你可以通过-u选项或者在交互模式下使用u命令来让fdisk显示的大小单位从柱面变为扇区。parted命令支持很多不同的单位。如下表:
ian@attic4:~$ sudo parted /dev/sdb
[sudo] password for ian: 
GNU Parted 1.8.8.1.159-1e0e
Using /dev/sdb
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted) help u                                                          
  unit UNIT                                set the default unit to UNIT

	UNIT is one of: s, B, kB, MB, GB, TB, compact, cyl, chs, %, kiB, MiB,
        GiB, TiB
(parted) p                                                                
Model: ATA HDT722525DLA380 (scsi)
Disk /dev/sdb: 250GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start   End    Size   Type     File system  Flags
 1      32.3kB  206MB  206MB  primary  ext3
 2      206MB   107GB  106GB  primary  ext4
 3      107GB   250GB  143GB  primary  ext3

(parted) u s                                                              
(parted) p                                                                
Model: ATA HDT722525DLA380 (scsi)
Disk /dev/sdb: 488397168s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start       End         Size        Type     File system  Flags
 1      63s         401624s     401562s     primary  ext3
 2      401625s     208282724s  207881100s  primary  ext4
 3      208282725s  488392064s  280109340s  primary  ext3

(parted) u chs                                                            
(parted) p                                                                
Model: ATA HDT722525DLA380 (scsi)
Disk /dev/sdb: 30401,80,62
Sector size (logical/physical): 512B/512B
BIOS cylinder,head,sector geometry: 30401,255,63.  Each cylinder is 8225kB.
Partition Table: msdos

Number  Start      End           Type     File system  Flags
 1      0,1,0      24,254,62     primary  ext3
 2      25,0,0     12964,254,62  primary  ext4
 3      12965,0,0  30400,254,62  primary  ext3

(parted)
注意: parted和fdisk在显示分区的起始和终止柱面时数据不同,这是因为fdisk从1开始计数,而parte从0开始计数柱面。下表显示了fdisk和parted其实对分区的理解是相同的。

Listing 3. Checking start and end sector numbers
ian@attic4:~$ sudo fdisk -ul /dev/sdb

Disk /dev/sdb: 250.1 GB, 250059350016 bytes
255 heads, 63 sectors/track, 30401 cylinders, total 488397168 sectors
Units = sectors of 1 * 512 = 512 bytes
Disk identifier: 0x000404d6

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1              63      401624      200781   83  Linux
/dev/sdb2          401625   208282724   103940550   83  Linux
/dev/sdb3       208282725   488392064   140054670   83  Linux
ian@attic4:~$ echo $(( 208282725 / 255 / 63 ))
12965



分区类型

IDE硬盘又三种分区类型:主分区、逻辑分区、扩展分区。分区表存在MBR中。MBR是硬盘的第一个扇区,所以分区表就不可能太大,这种结构限制了主分区的个数最多是4.当需要4个以上分区时,其中的一个主分区就要充当扩展分区。一个扩展分区是一个或多个逻辑分区的容器。通过这种方式,就可以在一个MBR格式的硬盘上分出超过4个的分区。

MBR的布局也限制了硬盘的最大大小为2TB左右。新的GPT布局解决了这个问题,同时也解决了分区个数少的问题。一个使用GPT格式化的硬盘,默认就支持128个主分区,并且不需要扩展分区和逻辑分区。 更多关于MBR和GPT内部的信息,请参考: MBR,EBR,GPT和LVM内部结构

一个扩展分区仅仅是一个或者多个逻辑分区的容器。这种分区策略最初被MS DOS和PC DOS使用,并且允许PC硬盘被DOS, Windows和Linux系统使用。一个硬盘可能只有一个扩展分区,数据存储在扩展分区内的逻辑分区中。在创建逻辑分区之前不能够存储数据到扩展分区中。


Linux把主分区或者扩展分区编号为1到4.所以,dev/sda可能有4个主分区,/dev/sda1,/dev/sda2, /dev/sda3, /dev/sda4。或者也可能只有一个主分区/dev/sda1和一个扩展分区/dev/sda2。如果有逻辑分区,那么逻辑分区的编号从5开始,所以第一个逻辑分区的编号是/dev/sda5,即使没有主分区,只有一个扩展分区/dev/sda1。所以,如果你需要4个以上分区,那么就需要为扩展分区保留一个主分区号。尽管Linux内核使用热插拔理论上能支持15个IDE分区,但是你也许不能创建这么多。如果你使用超过12个分区,那么要好好检查是否系统能正常工作。

上面例子中的硬盘有3个主分区,并且都被格式化为Linux使用。其中两个分区被格式化为Ext3文件系统,另一个被格式化为Ext4文件系统。下表显示了Ubunto9.10 系统下一个一个内部硬盘以及Fedora12 系统中一个通过USB挂载的硬盘的信息。注意不同的文件系统类型,以及parted可以使用命令行参数代替交互模式的命令。

Listing 4. Displaying the partition table with parted
ian@attic4:~$ sudo parted /dev/sda u s p
[sudo] password for ian: 
Model: ATA WDC WD6401AALS-0 (scsi)
Disk /dev/sda: 1250263728s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start       End          Size         Type      File system     Flags
 1      63s         2040254s     2040192s     primary   ext3
 2      2040255s    22523129s    20482875s    primary   linux-swap(v1)
 4      22523130s   1250258624s  1227735495s  extended                  boot
 5      22523193s   167397299s   144874107s   logical   ext3
 6      167397363s  310761359s   143363997s   logical   ext3
 7      310761423s  455442749s   144681327s   logical   ext3
 8      455442813s  600092009s   144649197s   logical   ext3
[root@echidna ~]# parted /dev/sdc p
Model: WD My Book (scsi)
Disk /dev/sdc: 750GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start   End    Size    Type      File system  Flags
 1      32.3kB  135GB  135GB   primary   fat32        lba
 2      135GB   750GB  616GB   extended
 5      135GB   292GB  157GB   logical   ext3
 6      292GB   479GB  187GB   logical   ext3
 7      479GB   555GB  76.5GB  logical   ext3
 8      555GB   750GB  195GB   logical   ext3


fdisk命令不支持GPT格式化的硬盘。可以使用parted,gparted或者gdisk工具代替。
警告: 如果使用gdisk作用于一个MBR格式的硬盘,那么它会提供一个转换到GPT的提示,倍加小心!!

逻辑卷管理器

既然你已经理解了不同的分区类型,你可能想知道如果没有计划好分区的大小会怎样。 怎样扩展或者缩小一个分区?如果你需要的空间超过了一个硬盘的大小该怎么办?进入“逻辑卷管理器”。

使用LVM,你可以对磁盘空间管理进行抽象,这样一个单一的文件系统可以跨越多个硬盘或分区,你也可以很容易地为文件系统增加或减少空间。现在的版本是lvm2,它兼容过去的lvm(有时叫做lvm1)。
LVM通过如下管理硬盘空间:
  • 物理卷
  • 卷组
  • 逻辑卷
一个物理卷可以是整个硬盘也可以是硬盘的一个分区。尽管LVM可以使用一个没有经过分区的硬盘,但是这通常不是一个好主意。
一个卷组是一个或多个物理卷的集合。一个卷组的空间被当作一个大的硬盘来管理,即使卷组下的卷可能跨越了多个硬盘或分区。底下的物理卷可以大小不同,也可以位于不同类型的硬盘上。

一个逻辑卷在某种意义上类似于一个物理的GPT或者MBR分区,它是使用文件系统类型(如ext4或XFS)格式化的空间单位,然后就可以挂载到Linux文件系统树上了。一个逻辑卷位于一个卷组中。

可以这样想象,PV是组成一个叫做VG的虚拟硬盘的物理空间单元。然后这个虚拟的硬盘(VG)又被分区为多个LV以便供文件系统使用。

在一个VG里,你使用extents来管理空间。默认的extent大小是4MB,通常这足够了。如果你使用大的extent,注意VG中的所有PV必须使用相同大小的extent单位。当你分配或者调整LV的大小,分配单元是extent的大小。所以默认情况下,LV的大小是4MB的整数倍,所以扩张或缩小LV时,必须也是extent的整数倍。

LVM最后一个谜团就是设备映射。这是Linux内核的一部分,它为像LVM或者软Raid这样的虚拟设备提供了基础平台。

LVM所使用的命令通常在lvm2包中,你可以在命令行运行很多命令,也可以运行lvm命令进入一个特殊的shell,在这里可以运行不同的LVM命令。下表显示了lvm命令和lvmshell下可以执行的各种命令。

Listing 5. The lvm command and its subcommands
[root@echidna ~]# lvm
lvm> help
  Available lvm commands:
  Use 'lvm help <command>' for more information
   
  dumpconfig      Dump active configuration
  formats         List available metadata formats
  help            Display help for commands
  lvchange        Change the attributes of logical volume(s)
  lvconvert       Change logical volume layout
  lvcreate        Create a logical volume
  lvdisplay       Display information about a logical volume
  lvextend        Add space to a logical volume
  lvmchange       With the device mapper, this is obsolete and does nothing.
  lvmdiskscan     List devices that may be used as physical volumes
  lvmsadc         Collect activity data
  lvmsar          Create activity report
  lvreduce        Reduce the size of a logical volume
  lvremove        Remove logical volume(s) from the system
  lvrename        Rename a logical volume
  lvresize        Resize a logical volume
  lvs             Display information about logical volumes
  lvscan          List all logical volumes in all volume groups
  pvchange        Change attributes of physical volume(s)
  pvresize        Resize physical volume(s)
  pvck            Check the consistency of physical volume(s)
  pvcreate        Initialize physical volume(s) for use by LVM
  pvdata          Display the on-disk metadata for physical volume(s)
  pvdisplay       Display various attributes of physical volume(s)
  pvmove          Move extents from one physical volume to another
  pvremove        Remove LVM label(s) from physical volume(s)
  pvs             Display information about physical volumes
  pvscan          List all physical volumes
  segtypes        List available segment types
  vgcfgbackup     Backup volume group configuration(s)
  vgcfgrestore    Restore volume group configuration
  vgchange        Change volume group attributes
  vgck            Check the consistency of volume group(s)
  vgconvert       Change volume group metadata format
  vgcreate        Create a volume group
  vgdisplay       Display volume group information
  vgexport        Unregister volume group(s) from the system
  vgextend        Add physical volumes to a volume group
  vgimport        Register exported volume group with system
  vgmerge         Merge volume groups
  vgmknodes       Create the special files for volume group devices in /dev
  vgreduce        Remove physical volume(s) from a volume group
  vgremove        Remove volume group(s)
  vgrename        Rename a volume group
  vgs             Display information about volume groups
  vgscan          Search for all volume groups
  vgsplit         Move physical volumes into a new or existing volume group
  version         Display software and driver version information
lvm> quit
  Exiting.


为了给你一个LVM操作的简明例子,我已经创建好了一个GPT分区/dev/sdc5和一个MBR分区/dev/sdd1。可以使用pvscan命令查看:
Listing 6. Displaying your physical volumes
    
[root@echidna ~]# pvscan
  PV /dev/sdc5                      lvm2 [146.43 GiB]
  PV /dev/sdd1                      lvm2 [232.88 GiB]
  Total: 2 [379.32 GiB] / in use: 0 [0   ] / in no VG: 2 [379.32 GiB]


现在,你将使用vgcreate命令来窜改建一个卷组来包含这两个PV,然后使用lvcreate命令来创建一个空间大于任意一个PV的LV。最后,你会把新建的LV格式化为ext4,然后把它挂载到/mnt/lvdemo下。如下表:

Listing 7. Creating a volume group and a logical volume
    
[root@echidna ~]# vgcreate demo-vg /dev/sdc5 /dev/sdd1
  Volume group "demo-vg" successfully created
[root@echidna ~]# lvcreate -L 300G -n demo-lv demo-vg
  Logical volume "demo-lv" created
[root@echidna ~]# lvscan
  ACTIVE            '/dev/demo-vg/demo-lv' [300.00 GiB] inherit
[root@echidna ~]# mkfs -t ext4 /dev/demo-vg/demo-lv
mke2fs 1.41.14 (22-Dec-2010)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
19660800 inodes, 78643200 blocks
3932160 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=4294967296
2400 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks: 
	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208, 
	4096000, 7962624, 11239424, 20480000, 23887872, 71663616

Writing inode tables: done                            
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 36 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.
[root@echidna ~]# mount /dev/demo-vg/demo-lv /mnt/lvdemo
[root@echidna ~]# df -h /mnt/lvdemo/
Filesystem                     Size  Used Avail Use% Mounted on
/dev/mapper/demo--vg-demo--lv  296G  191M  281G   1% /mnt/lvdemo


注意:
  1. 像mkfs和mount这样的文件系统命令使用像/dev/卷组名/逻辑卷名 的格式来访问逻辑卷。
  2. 默认情况下,当你创建一个LV后,它会立即变成活动的,如表中lvscan(译注:原文此处有错误,在此改正)命令的输出所示。如果LV存在一个可移除的硬盘上,再从系统中移除硬盘之前,需要先使用lvchange命令让LV处于非活动状态。
想更深入的学习LVM的知识,请参考文章: “逻辑卷管理"

MBR,EBR,GPT和LVM内幕

在学习分配磁盘空间之前,绕道看看MBR,EBR,GPT和LVM分区表内幕来强化这些概念。注意:LPI考试不要求你掌握这些,所以如果你时间紧或者对内幕不感兴趣可以直接跳到”分配磁盘空间“那一节。

MBR

主引导区是硬盘的第一个扇区。MBR中包含有启动代码,也可能还有其他信息,然后是64字节的分区表信息,最后是2个字节的启动标识符。64字节的分区表有4个16字节大小的项,分区表起始于446(0x1BE)处,下表显示了每一个16字节项。
Table 2. Partition table entry format
Offset (hex) Length Description
0h 1 状态。80h表示活动(可启动)分区。.
1h 3 分区第一扇区所在的CHS地址
4h 1 分区类型
5h 3 分区最后一个扇区的CHS地址
8h 4 分区第一扇区的LBA地址
Ch 4 分区占用的扇区数量

看一个实际的例子。根用户可以使用dd命令读取硬盘上的扇区。下表显示了打印出来的/dev/sda的MBR的前510个字节。然后利用tail来选择最后64个字节(也就是分区表了)并用16进制显示。

Listing 8. Displaying the partition table on /dev/sda

[root@echidna ~]# dd if=/dev/sda bs=510 count=1 2>/dev/null|tail -c 64 |hexdump -C
00000000  80 01 01 00 07 fe ff ff  3f 00 00 00 98 66 b9 08  |........?....f..|
00000010  00 fe ff ff 83 fe ff ff  61 5c 39 09 21 c7 17 00  |........a\9.!...|
00000020  00 fe ff ff 05 fe ff ff  82 23 51 09 85 ab 68 66  |.........#Q...hf|
00000030  00 fe ff ff 82 fe ff ff  d7 66 b9 08 8a f5 7f 00  |.........f......|
00000040

注意到第一个记录的状态是80h,表示一个启动分区,分区类型是07h,表示是一个NTFS分区。其余的分区类型分别是83h(Linux),05h(扩展分区),82h(交换分区)。所以该硬盘有三个主分区和一个扩展分区。所有的CHS地址都是feffff,在LBA硬盘上,这是标准值。LBA起始扇区地址和扇区总数要作为小端法的整数来解释,所以9866b908代表08b96698h。所以可以看出,第一个分区(NTFS分区)起始于63(00003f)扇区,共占用146368152(08b96698h)个扇区。因此,fdisk会显示结束扇区的编号为 63+146368152=146368214。

注意:

  1. MBR中的分区表不包含逻辑分区的任何信息。一会我们会介绍逻辑分区。
  2. 如果你的硬盘(老式硬盘)使用CHS而不是LBA,那么需要根据CHS来计算绝对扇区地址,除了计算方法外,原理完全相同,这里不再累述。
Listing 9. Fdisk output for /dev/sda
[root@echidna ~]# fdisk -l /dev/sda

Disk /dev/sda: 1000.2 GB, 1000204886016 bytes
255 heads, 63 sectors/track, 121601 cylinders, total 1953525168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x000de20f

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *          63   146368214    73184076    7  HPFS/NTFS/exFAT
/dev/sda2       154754145   156312449      779152+  83  Linux
/dev/sda3       156312450  1874448134   859067842+   5  Extended
/dev/sda4       146368215   154754144     4192965   82  Linux swap / Solaris
/dev/sda5       156312513   336031604    89859546   83  Linux
/dev/sda6       336031668   636880859   150424596   83  Linux
/dev/sda7       636880923   865983824   114551451   83  Linux
/dev/sda8       865983888   949891319    41953716   83  Linux
/dev/sda9       949891383   954003959     2056288+   b  W95 FAT32
/dev/sda10      954007552  1187801087   116896768   83  Linux
/dev/sda11     1187803136  1229760511    20978688   83  Linux
/dev/sda12     1229762560  1874446335   322341888   83  Linux

Partition table entries are not in disk order


可以看出,/dev/sda1的最后一个扇区确实是146368214。
注意上表中给出警告说,分区表的项目没有按顺序排列。这是因为/dev/sda4,这个Linux交换分区物理上在/dev/sda3(扩展分区)的前面。这不是一个错误,但是像fdisk以及一些其他工具提供选项来按照顺序重写分区表。

扩展启动记录(EBR)

在你继续研究扩展分区之前,快速看一下图像工具gparted的输出结果,它会给出一个图形化的分区布局展示,并显示出扩展分区的容器本质。
下图显示了gparted命令显示的/dev/sda分区信息。像刚刚说的,这个磁盘有三个主分区(/dev/sda1,/dev/sda4, /dev/sda2)以及一个扩展分区(/dev/sda3)。扩展分区包含逻辑分区/dev/sda5到/dev/sda12。图中,扩展分区表示为蓝色虚线的框架,浅绿色的框表示逻辑分区。



前面已经说过,MBR中不包含逻辑分区的信息。它只是定义了一个容器(扩展分区),利用这个容器去查找其他逻辑分区。那么究竟是如何工作的呢?
一个扩展分区包含的逻辑分区数目没有硬性限制,也没有固定大小的逻辑分区表。实际上,每一个逻辑分区都对应有一个扩展启动记录(EBR)。与MBR类似,EBR也是512个字节大小,其中的分区表也是起始于446(1BEh)处。在EBR的分区表中,只有两个项被利用。第一个项定义了本分区的起始扇区和大小,第二项定义了到下一个逻辑分区的偏移量。对于最后一个逻辑分区来说,第二项总是0.

为了方便转换小端法的十六进制数字到十进制数字的转换,我们创建了一个bash函数,用来显示EBR的两个项。然后利用hexdump的字符串格式化功能来用10进制而不是16进制显示LBA起始扇区和扇区总数。下表是函数showebr的定义:

Listing 10. Function to display an EBR
showebr () 
{ 
    dd if=$1 skip=$2 bs=512 count=1 2> /dev/null | tail -c 66 | 
     hexdump -n 32 -e '"%07.7_ax  " 8/1 "%2.2x " 2/4 " %12d" "\n"'
}


第一个EBR就是扩展分区的第一个扇区。所以使用前面表中的结果来调用showebr函数。下面是输出结果:

Listing 11. The first EBR on /dev/sda
[root@echidna ~]# showebr /dev/sda 156312450
0000000  00 fe ff ff 83 fe ff ff           63    179719092
0000010  00 fe ff ff 05 fe ff ff    179719155    300849255


EBR的第一项显示分区/dev/sda5起始于此EBR后面的63个扇区,分区大小是179719092个扇区。第二项显示,下一个EBR位于此EBR后面179719155扇区处,count的意思是,下一个EBR到下一个分区结束扇区的距离。下表显示了/dev/sda扩展分区以及逻辑分区信息。/dev/sda6的大小是636880859-336031668+1=300849192。考虑到300849255,你可以计算出/dev/sda6的起始扇区是300849255-300849192=63.这对于很多系统来说是一个常见的偏移量,包括Windows XP。

Listing 12. Extended and logical partitions on /dev/sda
dev/sda3       156312450  1874448134   859067842+   5  Extended
/dev/sda5       156312513   336031604    89859546   83  Linux
/dev/sda6       336031668   636880859   150424596   83  Linux
/dev/sda7       636880923   865983824   114551451   83  Linux
/dev/sda8       865983888   949891319    41953716   83  Linux
/dev/sda9       949891383   954003959     2056288+   b  W95 FAT32
/dev/sda10      954007552  1187801087   116896768   83  Linux
/dev/sda11     1187803136  1229760511    20978688   83  Linux
/dev/sda12     1229762560  1874446335   322341888   83  Linux


利用你刚刚学到的知识,以及showebr函数和一些shell算术逻辑,你可以现在便利整个EBR链了。如下表:

Listing 13. Walking the chain of logical partitions on /dev/sda
[root@echidna ~]# showebr /dev/sda 156312450
0000000  00 fe ff ff 83 fe ff ff           63    179719092
0000010  00 fe ff ff 05 fe ff ff    179719155    300849255
[root@echidna ~]# showebr /dev/sda $(( 156312450 + 179719155 ))
0000000  00 fe ff ff 83 fe ff ff           63    300849192
0000010  00 fe ff ff 05 fe ff ff    480568410    229102965
[root@echidna ~]# showebr /dev/sda $(( 156312450 + 480568410 ))
0000000  00 fe ff ff 83 fe ff ff           63    229102902
0000010  00 fe ff ff 05 fe ff ff    709671375     83907495
[root@echidna ~]# showebr /dev/sda $(( 156312450 + 709671375 ))
0000000  00 fe ff ff 83 fe ff ff           63     83907432
0000010  00 fe ff ff 05 fe ff ff    793578870      4112640
[root@echidna ~]# showebr /dev/sda $(( 156312450 + 793578870 ))
0000000  00 fe ff ff 0b fe ff ff           63      4112577
0000010  00 fe ff ff 05 fe ff ff    797691510    233797128
[root@echidna ~]# showebr /dev/sda $(( 156312450 + 797691510 ))
0000000  00 fe ff ff 83 fe ff ff         3592    233793536
0000010  00 fe ff ff 05 fe ff ff   1031488638     41959424
[root@echidna ~]# showebr /dev/sda $(( 156312450 + 1031488638 ))
0000000  00 fe ff ff 83 fe ff ff         2048     41957376
0000010  00 fe ff ff 05 fe ff ff   1073448062    644685824
[root@echidna ~]# showebr /dev/sda $(( 156312450 + 1073448062 ))
0000000  00 fe ff ff 83 fe ff ff         2048    644683776
0000010  00 00 00 00 00 00 00 00            0            0



注意有一个分区的偏移量为3592.这对英语/dev/sda9和/dev/sda10之间存在1.75MB的空闲空间。还要注意/dev/sda12起始于2048而不是63。你可能在扇区大小是2048字节大小的硬盘或者是固态盘上发现这个。对于这些硬盘来说,扇区对齐非常重要。

GUID分区表(GPT)

前面我说过MBR布局的硬盘,如果扇区是512字节的话,其最大容量是2TB。随着大于2TB硬盘的出现,一种新的叫做GPT的布局被设计出来。这种格式可以用于小硬盘,对于大硬盘是必须的。硬盘格式化后,一个叫做保护的MBR出现在通常MBR出现的地方,这对于不能识别GPT的系统来说,会把整个硬盘当成是未知类型分区(EEh),分区大小就是整个硬盘的大小或者MBR支持的最大大小。

最近的Linux发行版包含了格式化GPT以及从GPT硬盘启动的工具,如gdisk(对应fdisk),parted,gparted, grub2,以及最新版本的GRUB。

最近的Linux系统支持从GPT分区启动,使用传统的BIOS或者新的EFI(扩展固件借口)。最近的64位Windows也支持使用EFI从GPT分区启动。

GPT分区包含一个GPT头以及GPT分区矩阵。GPT分区矩阵包含至少128个项,所以一个GPT硬盘可以支持最少128个分区。GPT头在GPT分区的第一个扇区,接下来就是GPT矩阵。GPT头和GPT矩阵都会在分区的尾部重复。为了进一步保护,GPT头还包含了GPT矩阵的32位CRC校验值。

下表显示了gdisk命令作用于一个包含5个主分区的移动硬盘(/dev/sdac)的输出结果。

Listing 14. Output from gdisk for /dev/sdc
[root@echidna ~]# gdisk -l /dev/sdc
GPT fdisk (gdisk) version 0.8.4

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/sdc: 3906963456 sectors, 1.8 TiB
Logical sector size: 512 bytes
Disk identifier (GUID): 7E637BAC-33FC-46D3-9860-6A300CDD0E5F
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 3906963422
Partitions will be aligned on 2048-sector boundaries
Total free space is 1361893309 sectors (649.4 GiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048       773580799   368.9 GiB   0700  Ian's partition 1
   2       773580800      1387724799   292.8 GiB   0700  Ian's partition 2
   3      1387724800      1831110655   211.4 GiB   0700  
   4      1831110656      2237978623   194.0 GiB   0700  
   5      2237978624      2545072127   146.4 GiB   8E00  


你可以在”Make the most of large drives with GPT and Linux"这篇文章中找到更多的GPT的信息。

为物理和逻辑卷管理一定要进行分区

我们说过,LVM可以使用没有任何分区的硬盘(裸盘)。处于和GPT使用保护MBR同样的原因,你应该在你的裸盘上创建一个分区,然后再在这个分区上创建PV,而不要依赖LVM去把一个裸盘定义为PV。这样,其他不能使用LVM的操作系统会意识到这个硬盘有一个未知分区,而不是认为这个硬盘没有格式化。

LVM为了管理抽象层,显然需要比原始MBR分区表更多地信息。除了建议你为PV创建一个分区,我们不会进一步查看LVM使用的盘上的数据结构。要想学习LVM的更多知识,参考文章“逻辑卷管理”

分配硬盘空间

前面说过,Linux文件系统是一个以/为根的单树。很显然,软盘或者CD-ROM里的数据必须要挂载。但是为什么要把硬盘上的数据进行分割呢?如下是分割文件系统的几个原因:
  • 启动文件。启动时,一些文件必须能被BIOS或者启动加载器读取。
  • 多个硬盘。典型的,每一个硬盘都会被分成一个或多个分区,每一个都需要挂载到文件系统树上。
  • 共享文件。多个操作系统可以共享静态文件,如可执行文件。动态文件,如用户家目录或者邮箱文件也可能需要共享,这样用户可以登入任何一台机器使用相同的家目录和邮箱。
  • 潜在的溢出。如果一个文件系统的使用率达到了100%,把这部分和系统启动需要的文件分割开存放是个好注意。
  • 磁盘配额。限制用户或用户组可以使用的磁盘空间大小。
  • 只读加载。在日志文件系统出现之前,系统崩溃后恢复一个文件系统总是会花费很长时间。因此,你可以把不怎么需要变动的文件系统(比如可执行程序目录)只读加载。这样就可以减少系统崩溃后的磁盘检查时间。
除了这些应用,你也需要考虑在硬盘上分配出一块交换空间。对于一个Linux系统,这通常是一个或者几个必须的分区。

做出选择

假设你在建立一个至少一个硬盘的系统,并且你需要从硬盘启动系统。(本文不针对从LAN启动或者使用LiveCD/DVD的无盘工作站)。尽管以后可能改变分区的大小,但是通常会非常费力。所以,事先选择好是很重要的。让我们开始吧。

你首先要考虑的是,你的系统可以被启动。一些老的机器有BIOS只能启动位于1024柱面内的扇区的限制。如果你使用这样的机器,你必须创建一个boot分区来存放系统启动必须的文件。一旦系统被加载,Linux系统会接管磁盘的操作,1024柱面的限制也不再存在。如果你需要创建启动分区,大概100MB大小就足够了。


你下一个需要考虑的可能是需要的交换空间。与内存价格相比,交换空间是一个慢速的内存。曾经的一个说法是,交换空间的大小等于内存。今天,你可能想把交换空间配成内存的1到2倍大小,这样就可以运行很多大的程序而不至于超出内存限制。切换大程序可能是很慢的,但是在一个给定的时间里,你可能只运行一个这样的程序。

对于一个内存很小的系统来说,一个大的交换空间也是建议的。对于服务器,你可能需要的交换空间为内存的一半大小,除非你运行一个有特殊要求的程序。任何时候,你都应该检测服务器内存的使用情况来确定增加物理内存或者进行多服务器负载均衡。对于服务器,太多的交换空间不好。也可以使用交换文件,但是一个专门的交换分区性能更好。

现在,我们来到了一个岔路口。个人工作站的用途相对于服务器来说非常不确定。我的建议,特别是对新用户来说,是为大多数的标准目录(如/usr, /opt, /var等等。)建立单独的大分区。这对于那些不知道接下来会安装什么软件的新手来说非常有用。一个运行图形桌面和一定数量开发工具的工作站至少需要5GB空间,这还不算用户的数据。一些大的开发工具可能需要更多的空间。我通常为一个操作系统分配40GB-60GB的空间,剩下的空间用来加载其他的操作系统。

服务器的负载是比较固定的,如果一个文件系统的空间被用完了将会是一场灾难。所以,通常需要创建多个分区,跨越多个硬盘,可能使用硬Raid或软Riad,或者是逻辑卷组。

你也要考虑一个特定文件系统的负载,以及这个文件系统是否与其他系统共享还是仅仅被一个系统使用。你需要使用经验、计划工具、增长评估来为你的系统分配空间。

无论是配置工作站还是服务器,你都需要一些系统特定地文件部署到本地硬盘上。典型地,这包括/etc/系统配置目录,/boot启动文件目录,/sbin启动和系统修复,/root根用户的家目录,/var/lock锁定文件目录,/var/run系统运行信息,/var/log系统日志。其他文件系统,如/home家目录,/user,/opt,/var/mail或者/var/spool/news可能是单独的分区,或者是网络挂载的,这是根据你的安装需要和偏好来设定的。

原创内容(一个分析MBR及跟踪EBR的C程序)

程序源码

头文件:mbr.h
/****************************************************
 *
 * MBR 物理结构
 *
 * *************************************************/
#ifndef MBR_H
#define MBR_H

/* 分区表项结构 */
typedef struct _PARTION 
{
    unsigned char state;             //状态
    unsigned char chs_frist[3];      //第一扇区的CHS地址
    unsigned char type;          //分区的类型
    unsigned char chs_last[3];       //最后一个扇区的CHS地址
    unsigned char lba_first[4];      //第一个扇区的LBA地址
    unsigned char sector_count[4];       //分区占用的扇区数量
}__attribute__((packed)) PARTION;

/* 分区表结构 */
typedef struct _PTABLE
{
    PARTION p[4];   
} __attribute__((packed)) PTABLE;

/* MBR结构 */
typedef struct _MBR
{
    unsigned char boot[446];    //MBR开始的加载程序 
    PTABLE pt;                  //分区表    
    unsigned char active[2];    //分区活动标记
} __attribute__((packed)) MBR, *PMBR;

#endif

实现文件mbr.cpp
/*************************************************
 *
 * MBR读取分析
 *
 * ToDo: 追踪逻辑分区EBR
 * **********************************************/


#include "mbr.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


struct partion_info
{
    unsigned int start_sector;
    unsigned int end_sector;
    char state[16];  /*状态描述*/
    char type[16];   /*分区类型*/
};


/*计算lba的地址*/
static unsigned int cal_lba(void* pstart)
{
    /* mbr中LBA地址是小端法存储的 */
    unsigned int result = (*(unsigned char*)pstart)  +
                          (*((unsigned char*)pstart+1)) * 16*16 +
                          (*((unsigned char*)pstart+2)) * 16*16*16*16 +
                          (*((unsigned char*)pstart+3)) * 16*16*16*16*16*16;
    return result;
}


/* 获取MBR信息  */
static void get_mbr_info(MBR* pmbr, struct partion_info pinfo[4])
{
    int i=0;
    for(i=0; i<4; i++) {
        PARTION p = pmbr->pt.p[i];


        if(p.state==0x80) {
            strcpy(pinfo[i].state, "可启动");
        } else {
            strcpy(pinfo[i].state, "不可启动");
        }
        switch(p.type) {
            case 0x7: strcpy(pinfo[i].type, "NTFS"); break;
            case 0x83: strcpy(pinfo[i].type, "Linux"); break;
            case 0x82: strcpy(pinfo[i].type, "Swap"); break;
            case 0x05: strcpy(pinfo[i].type, "Extend"); break;
            default: strcpy(pinfo[i].type, "Unknown"); break;
        }


        pinfo[i].start_sector = cal_lba(p.lba_first);
        pinfo[i].end_sector = pinfo[i].start_sector + cal_lba(p.sector_count) - 1;
    }
}
/* 追踪扩展分区 */
static int get_ebr_info(struct partion_info extend)
{
    MBR ebr;
    struct partion_info pi[4];
    memset(&ebr, 0, sizeof(ebr));
    memset(pi, 0, sizeof(pi));


    FILE *fp = fopen("/dev/sda", "r");
    if(fp==NULL) {
        printf("打开设备出错\n");
        return -2;
    }
    int n=0;
    if(0!=fseek(fp, (extend.start_sector)*512L, 0)) {
        printf("移动文件指针失败\n");
        fclose(fp);
        return -3;
    }


    unsigned long addr_ebr1 = ftell(fp); //记录下当前的位置


    if(512 != (n=fread(&ebr, 1, 512, fp))) {
        printf("实际读取%d, 读取MBR出错\n", n);
        fclose(fp);
        return -1;
    }


    int pid = 5;
    while(1) {
        get_mbr_info(&ebr, pi); //对于EBR,只有两个项, 其中的偏移量是相对于扩展分区的,而不是整个硬盘的,这里进行转换
        printf("%-20d %-20s %-20s %-20d %-20d %-20d\n",
                pid++, pi[0].state, pi[0].type,
                extend.start_sector + pi[0].start_sector,
                extend.start_sector + pi[0].end_sector,
                (pi[0].end_sector-pi[0].start_sector+1)/2/1024);


        //判断是否是最后一个逻辑分区
        if(pi[1].start_sector==0) {
            break;
        }
        fseek(fp, addr_ebr1 + pi[1].start_sector*512L, 0);
        memset(&ebr, 0, sizeof(ebr));
        memset(pi, 0, sizeof(pi));
        fread(&ebr, 1, 512, fp);
    }


    fclose(fp);
    return 0;
}


int main(int argc, char** argv)
{
    MBR mbr;
    struct partion_info pinfos[4];
    memset(&mbr, 0, sizeof(mbr));
    memset(&pinfos, 0, sizeof(sizeof(pinfos)));
    FILE *fp = fopen("/dev/sda", "r");
    if(fp==NULL) {
        printf("打开设备出错\n");
        return -2;
    }
    int n=0;
    if(512 != (n=fread(&mbr, 1, 512, fp))) {
        printf("实际读取%d, 读取MBR出错\n", n);
        return -1;
    }
    fclose(fp);
    get_mbr_info(&mbr, pinfos);
    int i=0;
    printf("%-20s %-20s %-20s %-20s %-20s %-20s\n",
            "分区", "状态", "文件系统", "起始扇区(从0计数)", "终止扇区(从0计数)", "分区大小(MB)");


    for(i=0; i<4; i++) {
        printf("%-20d %-20s %-20s %-20d %-20d %-20d\n",
                i+1, pinfos[i].state, pinfos[i].type, pinfos[i].start_sector, pinfos[i].end_sector,
                (pinfos[i].end_sector-pinfos[i].start_sector+1)/2/1024);
    }
    for(i=0; i<4; i++) {
        if(0==strcmp(pinfos[i].type,"Extend")){
            get_ebr_info(pinfos[i]);
        }
    }
    return 0;
}

运行结果:



注意事项:

(1)MBR,EBR里的起始扇区都是从0开始编号的,也可以说是代表偏移量

(2)逻辑分区的EBR的第一项中的起始扇区是以扩展分区的头为基础算起的,而主分区是以整个硬盘的头也就是(MBR)算起的

(3)逻辑分区的EBR的第二项中的下一个EBR的偏移量都是以扩展分区为头算起的,而不是以前面相邻的EBR。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
项目:使用AngularJs编写的简单 益智游戏(附源代码)  这是一个简单的 javascript 项目。这是一个拼图游戏,也含一个填字游戏。这个游戏玩起来很棒。有两个不同的版本可以玩这个游戏。你也可以玩填字游戏。 关于游戏 这款游戏的玩法很简单。如上所述,它含拼图和填字游戏。您可以通过移动图像来玩滑动拼图。您还可以选择要在滑动面板中拥有的列数和网格数。 另一个是填字游戏。在这里你只需要找到浏览器左侧提到的那些单词。 要运行此游戏,您需要在系统上安装浏览器。下载并在代码编辑器中打开此项目。然后有一个 index.html 文件可供您修改。在命令提示符中运行该文件,或者您可以直接运行索引文件。使用 Google Chrome 或 FireFox 可获得更好的用户体验。此外,这是一款多人游戏,双方玩家都是人类。 这个游戏含很多 JavaScript 验证。这个游戏很有趣,如果你能用一点 CSS 修改它,那就更好了。 总的来说,这个项目使用了很多 javascript 和 javascript 库。如果你可以添加一些具有不同颜色选项的级别,那么你一定可以利用其库来提高你的 javascript 技能。 演示: 该项目为国外大神项目,可以作为毕业设计的项目,也可以作为大作业项目,不用担心代码重复,设计重复等,如果需要对项目进行修改,需要具备一定基础知识。 注意:如果装有360等杀毒软件,可能会出现误报的情况,源码本身并无病毒,使用源码时可以关闭360,或者添加信任。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值