Chinaunix首页 | 论坛 | 博客
  • 博客访问: 499562
  • 博文数量: 176
  • 博客积分: 4045
  • 博客等级: 上校
  • 技术积分: 2491
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-19 11:23
文章分类

全部博文(176)

文章存档

2011年(7)

2009年(12)

2008年(157)

我的朋友

分类:

2008-05-27 10:25:19

原文地址:http://www.ibm.com/developerworks/cn/linux/sdk/install/part3/

分区功能和RAID、LVM支持

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

将打印机的版面设置成横向打印模式

将此页作为电子邮件发送

将此页作为电子邮件发送


级别: 初级

于辰涛 (scu_yct@263.net), 软件工程师, 联想(北京)电脑公司

2001 年 8 月 01 日

本文是构造Linux的图形化安装程序系列文章的第三部分,作者将从基本的分区表结构开始,介绍分区功能的实现和如何支持RAID、LVM这些高级功能。

分区功能对于Linux图形化安装程序来说,是一个非常重要的功能。它的基本功能是自动识别硬盘,并在硬盘上创建ext2类型的分区和交换分区。用户将此分区作为根分区,在其上进行Linux系统的安装。

对于一个功能较全的安装程序,它还应该具备创建其他类型的分区的能力,包括reiserfs、fat等等,创建软件RAID分区,创建逻辑卷管理分区的能 力。以及具有过滤合法安装点,根据选择的分区类型决定最合适挂接点的能力。分区能够动态的删除和修改,分区操作能够恢复到分区操作之前具体内容的状态。

这个部分介绍的主要内容包括代码都是基于HappyLinux的安装程序,所有功能全部在HappyLinux3.0的安装程序中实现。您可以在安装盘的/HappyLinux/happyinst/usr/bin/perl-install目录下获得安装程序源码。

在图形化安装程序中,硬盘分区的一般流程是:



图 1-1

安装程序中与分区功能密切相关的模块包括:

diskdrake.pm
图形化的分区处理模块,它是分区功能的主控模块,处理与用户的交互,进行分区操作的合法性检查。

diskdrake.rc
分区操作窗口的资源文件,主要描述不同分区类型的表述。

fs.pm
完成挂接和格式化分区的操作,同时也生成/etc/fstab文件。

fsedit.pm
获得硬盘分区结构,完成添加、删除、修改分区的操作。

lvm.pm
提供创建、修改和获取逻辑卷管理分区的模块。

partition_table.pm
对分区表操作的逻辑副本进行操作的模块,包括分区操作的合法性判断、设置分区对齐、读取主分区和扩展分区、类型判断的函数。

partition_table_dos.pm
对dos类型的分区表进行操作的模块。

partition_table_raw.pm
直接存取硬盘分区表读取/写入分区信息。

raid.pm
对软件raid设备进行处理的模块。





回页首


硬盘主分区由240字节的硬盘主引导记录(0000H-00EFH),64字节的硬盘分区表(01BEH-01FDH)以及最后两个字节的自举记录有效标志等三部分组成。主引导记录块的布局:

硬盘0柱面0磁头1扇区

主引导记录代码区
第一个分区表
第二个分区表
第三个分区表
第四个分区表
主引导记录有效标志55H,AAH

硬盘分区各个字段的意义是:

字节位移长度(字节)字段含义
01 活动分区指示符。该值为80H,表示可自举分区(仅有一个);改值为00H,表示不可自举分区。该字节也称为自举标志。
11起始磁头号
21起始扇区号(低6位)和起始柱面号的高2位
31起始柱面号的低8位
41分区上的系统标志。
该值为01H,表示采用12位FAT格式的DOS分区。
该值为04H,表示采用16位FAT格式的DOS分区。
该值为07H,表示采用NTFS分区。
该值为0BH,表示采用WIN98的FAT32格式的分区。
该值为0CH,表示采用WIN98的FAT32格式的分区,支持LBA方式。
该值为0FH,表示采用WIN95的扩展分区,支持LBA方式。
该值为82H,表示采用LINUX SWAP格式的分区。
该值为83H,表示采用LINUX EXT2格式的分区。
该值为8EH,表示采用LVM格式的分区。
该值为FDH,表示采用软件RAID格式的分区。
51结束磁头号
61结束扇区号(低6位)和结束柱面号的高2位
71结束柱面号的低8位
84相对扇区号
124该分区所用的扇区号

硬盘分区是以链接表的形式存在的,在每个硬盘上都存在一个主分区描述块,它可以描述四个分区,每个硬盘最多只存在四个主分区,其他的分区则为扩展分区。每个扩展分区是通过链接字段联结在一起。



图 1-2

在Linux系统下,主分区使用的设备别名是从/dev/hda1到/dev/hda4,其后从/dev/hda5开始的分区,都代表扩展分区。

  • 读入分区表
    #- 以下代码读入整个硬盘分区表形成分区的描述结构。
    sub read($;$) {
    my ($hd, $clearall) = @_;
    if ($clearall) {
    partition_table_raw::zero_MBR_and_dirty($hd);
    return 1;
    }
    #- 读入主分区表,检测这分区表上扩展分区数,如大于1,则出错。
    my $pt = read_one($hd, 0) or return 0;
    #- 主分区描述数组存入primary变量中。
    $hd->{primary} = $pt;
    undef $hd->{extended};
    #- 校验主分区,校验包括:分区是否重叠,是否存在未知空洞。
    verifyPrimary($pt);
    #- 如果存在扩展分区,则读入扩展分区,这时要检测扩展分区是否重叠,是否存在循环联结。
    eval {
    $pt->{extended} and read_extended($hd, $pt->{extended}) || return 0;
    };
    #- 对读出的分区指定其设备号,主分区/dev/hda1(/dev/sda1)到/dev/hda4(/dev/sda4)。
    assign_device_numbers($hd);
    #- 除去扩展分区中的空连接。
    remove_empty_extended($hd);
    1;
    }


  • 将分区操作写入分区表
    #- 此操作只是根据用户的操作写分区表,包括写入分区大小分区类型等信息。
    sub write($) {
    my ($hd) = @_;
    $hd->{isDirty} or return;
    #- 设置引导标志
    for ($hd->{primary}{raw}) {
    (grep { $_->{local_start} = $_->{start}; $_->{active} ||= 0 } @$_) or $_->[0]{active} = 0x80;
    }
    #- 校验分区,校验包括:分区是否重叠,是否存在未知空洞。
    verifyParts($hd);
    #- 写入分区表
    $hd->write(0, $hd->{primary}{raw}, $hd->{primary}{info}) or die "writing of partition table failed";
    $hd->{isDirty} = 0;
    $hd->{hasBeenDirty} = 1;
    if ($hd->{needKernelReread}) {
    sync();
    $hd->kernel_read;
    $hd->{needKernelReread} = 0;
    }
    }






回页首


在分区操作结束之后,为了在其上进行安装,还需要在分区上创建文件系统。安装程序是使用系统命令,比如mkdosfs,mke2fs,mkreiserfs来创建文件系统。

sub format_ext2($@) {
my ($dev, @options) = @_;
$dev =~ m,(rd|ida|cciss)/, and push @options, qw(-b 4096 -R stride=16);
push @options, qw(-b 1024 -O none) if arch() =~ /alpha/;
#- 调用mke2fs创建ext2文件系统
run_program::run("mke2fs", @options, devices::make($dev)) or die _("%s formatting of %s failed", "ext2", $dev);
}
sub format_dos($@) {
my ($dev, @options) = @_;
#- 调用mkdosfs创建fat文件系统
run_program::run("mkdosfs", @options, devices::make($dev)) or die _("%s formatting of %s failed", "dos", $dev);
}

创建Reiserfs文件系统

sub format_reiserfs($@) {
my ($dev, @options) = @_;
#- 调用mkreiserfs创建Reiserfs文件系统
run_program::run("mkreiserfs", "-f", @options, devices::make($dev)) or die _("%s formatting of %s failed", "reiserfs", $dev);
}

在使用Reiserfs文件系统作为系统的根分区时,系统在启动的过程中需要加载模块reiserfs.o。RedHat 7.0以下的版本则不支持Reiserfs文件系统,所以很多基于RedHat的发行版本也不支持Reiserfs文件系统。如果您要在这样的系统上加入 reiserfs支持,除了装载Reiserfs对应的rpm包之外,还要在生成初始启动镜像(initrd)时,在linuxrc中加载 Reiserfs模块。

这需要在/sbin/mkinitrd文件中加入下列语句:

# 对于/etc/fstab中挂接的文件系统,如果它的类型不是ext2,那么必须加载设备模块。

fs=$(awk '$2 == "/" {print $3 }' /etc/fstab)

[ -n "$fs" -a "$fs" != "ext2" ] && findmodule $fs





回页首


一般而言,磁盘分区的大小是固定的,它要求用户在安装系统时对分区空间的使用有大概的了解。在用户用尽了分区上所有的空间时,则要求重新分区或者移走一部分文件。

LVM (Logical Volume Management)是逻辑卷管理的缩写。它的出现将物理磁盘分割成一些逻辑单位,来自于不同磁盘的分区能组成一个逻辑卷。此外,在需要时分区能被动态 的加入和删除。举例来说,如果你有一个8GB的磁盘,其上有一个2GB的分区/usr,它的空间已经耗尽了。如果您要扩大/usr分区的话,必须首先创建 一个更大的分区,然后将/usr的所有内容都拷贝到此分区中,改变/etc/fstab文件,重新启动。但是如果在系统中使用了LVM的话,你只需使用 LVM中的命令,就可以简单的增大/usr。

LVM的分区方式对于需要大数据量存储的分区进行管理,可以极大的减轻管理员的负担。而且,在LVM和RAID设备结合使用之后,可以构造出一种灵活而且高效的存储方案。

后面在建立LVM类型的分区时,会用到一些术语,下面先对这些术语进行介绍。

  • 物理卷(PV)
    物理卷仅仅是进行了LVM初始化的物理分区,以使得LVM管理程序能识别这个分区。
  • 物理范围(PE)
    物理范围是一些大数据块,通常有几兆字节。
  • 卷组(VG)
    一个VG可由多个PE组成。一个VG可由几个分区组成,它包含的PE由这些分区提供。在某种意义上说,我们可以认为VG就是一个硬盘设备。
  • 逻辑卷(LV)
    逻辑卷是最终用户访问的部分,它用于存储数据。在某种意义上说,我们可以认为LV就是一个逻辑分区。其上可以创建任何类型的分区,包括EXT2,ReiserFS,NTFS等等。在访问时,它和正常的磁盘分区一样。
  • 逻辑范围(LE)
    每个逻辑卷被分割成为数据块。

    为了使LVM在系统中能够正常工作,那么在定制内核的时候,要将LVM支持作为模块形式定制。然后,为了创建和管理LVM逻辑分区,您还需要安装 。

    步骤如下:

    1. 使用fdisk命令设置分区/dev/hda3和/dev/hdc3类型为0x8e。
    2. 创建物理卷。

      pvcreate /dev/hda3
      pvcreate /dev/hdc3

    3. 创建新卷组newvg,它包含/dev/hda3和/dev/hdc3分区,卷组的大小是两个分区容量的总和。在成功创建此卷组之后会在/dev目录下形成目录/dev/newvg,在其后生成的逻辑卷设备文件都保存在此目录下。

      vgcreate newvg /dev/hda3
      vgcreate newvg /dev/hdc3

    4. 创建新的逻辑卷

      lvcreate -L1500 -nnewlv newvg
      创建一个1500MB线性逻辑卷,同时这条命令也创建逻辑卷对应的块设备文件/dev/newvg/newlv。
      lvcreate -i2 -I4 -l1500 -notherlv newvg
      以交错块(stripe)为2,块大小为4 KB创建另一个逻辑卷,对应的设备别名是/dev/newvg/otherlv。

    5. 创建文件系统

      mke2fs /dev/newvg/newlv
      在设备/dev/newvg/newlv上创建ext2文件系统,除了ext2文件系统之外,您还可以创建Reiserfs等多种文件系统。

    6. 激活lvm逻辑卷

      vgchange -a y
      在使用卷组之前,控制卷组对内核的可见性。这条命令激活系统中所有已知的卷组。在删除一个卷组之前,最好先使用vgchange -a n禁用卷组。

  • 读入LVM分区信息
    sub get_lvs {
    my ($lvm) = @_;
    my @fstabs;
    my $start = 0;

    @fstabs = ();
    $lvm->{primary}{normal} = ();
    undef $lvm->{primary}{normal};
    #- 使用vgdisplay -v -D lvmname获得所有卷组。
    foreach (map { /^LV Name\s+(\S+)/ ? $1 : () } `vgdisplay -v -D $lvm->{LVMname}`) {
    #- 使用lvdisplay -D -c lv获得逻辑卷的信息。
    my @lvinfo = split(':', `lvdisplay -D -c $_`);
    my $size = $lvinfo[6];
    my $type = -e $_ && fsedit::typeOfPart($_);
    my %fstab;
    $fstab{device} = $_;
    $fstab{type} = $type || 0x83;
    $fstab{size} = $size;
    $fstab{isFormatted} = $type;
    $fstab{number} = $lvinfo[4];
    $fstab{start} = $start;
    $fstab{lvm} = $_;
    $start += $size;
    push @fstabs, \%fstab;
    }
    $lvm->{primary}{normal} = \@fstabs;
    }


  • 将LVM分区操作写入磁盘
    #- 对已经删除的lvm分区,则除去其上的逻辑卷,然后将其除去。
    #- 若只有部分逻辑卷被除去,则删除这部分逻辑卷
    foreach my $lv (@{$o->{lvms}}) {
    if ($lv->{isRemove}) {
    #- 除去所有的逻辑卷
    foreach (fsedit::get_fstab($lv)) {
    lvm::lv_delete($_);
    }
    #- 清空主分区描述结构
    splice @{$lv->{primary}{normal}}, 0;
    #- 清除卷组
    lvm::vg_destroy($lv);
    } else {
    foreach (fsedit::get_fstab($lv)) {
    lvm::lv_delete($_) if ($_->{isRemove});
    }
    }
    }

    foreach my $lv (@lvm) {
    #- 对现在存在的lvm分区,如果是新创建的,则创建新的卷组
    #- 否则,找出新添加的卷组,并将其加入已存在的卷组中
    if ($lv->{isCreate}) {
    lvm::vg_add($_) foreach (@{$lv->{disks}});
    } else {
    lvm::vg_add($_) foreach (grep { $_->{isNewPV} } @{$lv->{disks}});
    }
    #- 对于卷组上存在的逻辑卷,如果是新创建的,则调用lv_create创建。
    my @fstab = get_all_fstab($lv);
    foreach (@fstab) {
    lvm::lv_create($lv, $_) if ($_->{isCreate});
    }
    #- 重新创建卷组的主分区结构,设置安装点和格式化标志
    lvm::get_lvs($lv);
    foreach my $f (fsedit::get_fstab($lv)) {
    foreach (@fstab) {
    if ($_->{type} && $f->{device} eq $_->{device}) {
    $f->{mntpoint} = $_->{mntpoint};
    $f->{toFormat} = $_->{toFormat};
    }
    }
    }
    }
    #- 生成/etc/lvmtab描述文件,同时激活卷组
    if (@lvm) {
    run_program::run('vgscan') if !-e '/etc/lvmtab';
    run_program::run('vgchange', '-a', 'y');
    }

    为了在系统启动时能够加载lvm-mod.o模块,扫描物理卷,生成正确的描述文件/etc/lvmtab,同时激活LVM分区,又由于基于RedHat 的发布缺省情况下是不支持LVM的分区方式,您还需要修改初始启动脚本文件/etc/rc.d/rc.sysinit文件,在其中加入如下语句:

    # 创建/etc/lvmtab文件
    if [ -x /sbin/vgscan ]; then
    /sbin/vgscan > /dev/null 2>&1
    fi
    # 激活lvm分区,如果不进行激活操作,lvm分区无法正常工作
    if [ -x /sbin/vgchange -a -f /etc/lvmtab ]; then
    action "Setting up LVM:" /sbin/vgchange -a y
    fi





回页首


软件Raid分区支持是指Linux系统不借助任何硬件支持,在普通ide或scsi硬盘上实现Raid功能。这首先需要内核的支持,其次还需要安装raidtools包。

  1. Linear模式

    两个或者多个磁盘被组合成一个物理设备,这些磁盘是叠加使用的。也就是说,RAID设备会先装满第一个磁盘,然后第二个磁盘,以此类推。每个磁盘的大小不必相同。

    使用此模式Raid设备不提供冗余信息。如果一个磁盘崩溃了,你将会丢失所有的数据。对于单个读写操作,读写性能都没有任何提升,但是当多个用户共享RAID设备时,如果用户访问的文件在不同的磁盘上,那可能会使访问性能更高。

  2. RAID-0模式

    这种模式也被称为交错(Stripe)模式。RAID0要求磁盘(分区)具有近似的大小。它的所有磁盘读/写操作时并行完成的。如果其中一个分区比另外一个分区大得多,那么在使用这些空间时,并行操作无法完成,导致性能下降。

    这种模式也不存在数据冗余。如果一个磁盘损坏,将导致所有数据丢失。但是,在此种模式下,因为并行存取读写性能会提升。这是使用RAID0的主要原因。

  3. RAID-1模式

    此种模式具有数据冗余性。RAID-1缺省时,使用两块磁盘,同时也可以指定多块备用盘。这个模式下的两个硬盘互为镜像。这就要求这两个磁盘的大小一致,否则RAID设备的大小等于最小的磁盘。

    在任何一个磁盘崩溃之后,数据都会保持完整并可立即进行重建。在此种方式下,读性能会得到很大的提升,但是写磁盘的性能保持不变或者有所下降。

  4. RAID-4模式

    这种模式不是经常使用,用于三个或者多个磁盘上。这种模式采用数据块存储方式,将校验数据单独存在一个磁盘上,写数据到其他磁盘时采用和RAID-0相似的方式。因为一个磁盘用于奇偶校验,所以RAID阵列的大小为(N-1)*S(S为阵列中最小的磁盘尺寸)。

    在一个磁盘失效之后,奇偶信息会用于重构所有数据。但如果有两个磁盘崩溃,所有数据将会丢失。但是奇偶信息保存在一个磁盘上,每次数据更新都会访问这张磁盘,因此这张磁盘会成为瓶颈。

  5. RAID-5模式

    在需要组合大量的物理磁盘时,这是最有用的RAID方式。它也用在三个或者多个磁盘上,RAID阵列的大小为(N-1)*S。此种模式采用数据块存储方式,将校验数据分布保存在各个磁盘上,避免了RAID-4的瓶颈问题。

    如果一块磁盘崩溃,在存在备用盘的情况下,重构立即开始。但是如果有两个磁盘崩溃,所有数据将会丢失。在此种方式下读写的性能都将有所提升。

在Linux系统下为了安装RAID设备,必须首先安装raidtools工具,创建/etc/raidtab文件,描述创建的RAID阵列的类别。然后使用mkraid命令创建RAID分区。

mkraid /dev/md0
这条命令初始化RAID阵列,写永久超级块并且启动阵列。然后察看/proc/mdstat,应该见到阵列正在运行。最后,可以创建文件系统,并在fstab中挂接它。

下面是配置不同RAID模式时,raidtab文件的内容:

  1. 线性模式
    raiddev /dev/md0
    raid-level linear
    nr-raid-disks 2
    chunk-size 32
    persistent-superblock 1
    device /dev/hdb6
    raid-disk 0
    device /dev/hdc5
    raid-disk 1

  2. RAID-0模式
    raiddev /dev/md0
    raid-level 0
    nr-raid-disks 2
    persistent-superblock 1
    chunk-size 4
    device /dev/hdb6
    raid-disk 0
    device /dev/hdc5
    raid-disk 1

  3. RAID-1
    raiddev /dev/md0
    raid-level 1
    nr-raid-disks 2
    nr-spare-disks 0
    chunk-size 4
    persistent-superblock 1
    device /dev/hdb6
    raid-disk 0
    device /dev/hdc5
    raid-disk 1

    若有备用盘,可以增加它们到设备规范的尾部,例如:
    device /dev/hdd5
    spare-disk 0
    spare-disk的设定值应该与nr-spare-disks项的设置一致。
    mkraid /dev/md0启动镜像初始化(镜像重构)。重构过程使用空闲的I/O带宽,重构过程是透明的,重构时仍能使用这个设备。在重构运行时,格式化、挂接和使用设备都可以使用。
  4. Raid-4
    raiddev /dev/md0
    raid-level 4
    nr-raid-disks 4
    nr-spare-disks 0
    persistent-superblock 1
    chunk-size 32
    device /dev/hdb1
    raid-disk 0
    device /dev/hdc1
    raid-disk 1
    device /dev/hdd1
    raid-disk 2
    device /dev/hde1
    raid-disk 3

    若有备用磁盘,添加
    device /dev/hdf1
    spare-disk 0
    mkraid /dev/md0进行初始化。使用带有特殊选项的mke2fs进行格式化。
    mke2fs -b 4096 -R stride=8 /dev/md0
  5. Raid-5
    raiddev /dev/md0
    raid-level 5
    nr-raid-disks 7
    nr-spare-disks 0
    persistent-superblock 1
    parity-algorithm left-symmetric
    chunk-size 32
    device /dev/hda3
    raid-disk 0
    device /dev/hdb1
    raid-disk 1
    device /dev/hdc1
    raid-disk 2
    device /dev/hdd1
    raid-disk 3
    device /dev/hde1
    raid-disk 4
    device /dev/hdf1
    raid-disk 5
    device /dev/hdg1
    raid-disk 6

    运行mkraid /dev/md0,使用带有特殊选项的mke2fs格式化阵列。例如:
    mke2fs -b 4096 -R stride=8 /dev/md0
    stride=8表示raid块为8个文件系统块大小,即为32KB。

在创建了正确的RAID描述文件,并使用mke2fs文件创建了正确的文件系统之后,您还需要执行命令:
raidstart /dev/md0

启动RAID设备。在执行此命令之后,内核的RAID子系统就自动地在后台开始工作了。在修改RAID设备对应的分区时,为了保证数据不发生错误,必须先用
raidstop /dev/md0

停止此设备,然后才能进行修改。

#- 下面的代码是安装程序中创建raid设备的模块。
#- 根据raid设备结构,创建/etc/raidtab文件和软Raid分区:
sub make
{
my ($raid) = @_;
isMDRAID($_) and make($raid, $_) foreach @{$raid->{disks}};
#- 创建raid设备
my $dev = devices::make($raid->{device});
#- 根据raid级别,加载对应的设备模块
eval { modules::load(module($raid)) };
run_program::run("raidstop", $dev);
#- 创建/etc/raidtab文件和软Raid分区
&write($raid, "/etc/raidtab");
run_program::run("mkraid", "--really-force", $dev) or die
$::isStandalone ? _("mkraid failed (maybe raidtools are missing?)") : _("mkraid failed");
}

在RAID设备作为系统的根分区安装之后,为了使系统能够正常启动,整个的系统环境还要做到自动检测RAID设备并正确挂接分区。这需要下列步骤:

  1. 定制的系统内核支持raid设备自动检测。也就是说,在编译内核时选择:
    Multi-device support (RAID and LVM)
    [*] Multiple devices driver support (RAID and LVM)
    <*> RAID support

    将设备模块md.o打入内核。如果对此设备的支持选择了内核模块的方式,那么软件RAID分区无法作为根分区启动系统。这主要是因为RAID设备需要在启 动之初对硬盘进行读写,以决定RAID分区的位置,类型等参数。如果不将md.o置入内核,仅选择模块方式,那么它将从初始内存镜像中进行加载,但是加载 过后无法完成上述的信息读取过程。
  2. 用带有persistent-superblock的选项创建RAID设备。
  3. RAID的设备分区类型必须设为0xFD。
  4. 正确的配置RAID阵列,并在RAID系统上建立了正确的文件系统。
  5. 必须创建初始启动镜像,这样保证在启动时能够加载raid设备模块。
    mkinitrd --with=raid5 raid-ramdisk 2.4.3
  6. 纠正/etc/fstab文件,使/dev/md0为根文件系统对应的设备文件,并使引导时所用的分区包含在fstab中。



于辰涛,联想(北京)电脑公司软件工程师。目前主要从事Linux系统安装程序的开发工作,主要研究兴趣是操作系统的工作机制和开发底层系统程序。您可以通过电子邮件


阅读(402) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~