分區功能對于Linux圖形化安裝程序來說,是一個非常重要的功能。它的基本功能是自動識別硬盤,并在硬盤上創建ext2類型的分區和交換分區。用戶將此分區作為根分區,在其上進行Linux系統的安裝。
對于一個功能較全的安裝程序,它還應該具備創建其他類型的分區的能力,包括reiserfs、fat等等,創建軟件RAID分區,創建邏輯卷管理分區的能力。以及具有過濾合法安裝點,根據選擇的分區類型決定最合適掛接點的能力。分區能夠動態的刪除和修改,分區操作能夠恢復到分區操作之前具體內容的狀態。
這個部分介紹的主要內容包括代碼都是基于HappyLinux的安裝程序,所有功能全部在HappyLinux3.0的安裝程序中實現。您可以在安裝盤的/HappyLinux/happyinst/usr/bin/perl-install目錄下獲得安裝程序源碼。
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設備進行處理的模塊。
2 基本分區功能
硬盤主分區由240字節的硬盤主引導記錄(0000H-00EFH),64字節的硬盤分區表(01BEH-01FDH)以及最后兩個字節的自舉記錄有效標志等三部分組成。主引導記錄塊的布局:
硬盤0柱面0磁頭1扇區
主引導記錄代碼區
第一個分區表
第二個分區表
第三個分區表
第四個分區表
主引導記錄有效標志55H,AAH
表 1-1
硬盤分區各個字段的意義是:
字節位移 長度(字節) 字段含義
0 1 活動分區指示符。該值為80H,表示可自舉分區(僅有一個);改值為00H,表示不可自舉分區。該字節也稱為自舉標志。
1 1 起始磁頭號
2 1 起始扇區號(低6位)和起始柱面號的高2位
3 1 起始柱面號的低8位
4 1 分區上的系統標志。
該值為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格式的分區。
5 1 結束磁頭號
6 1 結束扇區號(低6位)和結束柱面號的高2位
7 1 結束柱面號的低8位
8 4 相對扇區號
12 4 該分區所用的扇區號
表 1-2
硬盤分區是以鏈接表的形式存在的,在每個硬盤上都存在一個主分區描述塊,它可以描述四個分區,每個硬盤最多只存在四個主分區,其他的分區則為擴展分區。每個擴展分區是通過鏈接字段聯結在一起。

主分區使用的設備別名是從/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;
}
}
3 創建文件系統
在分區操作結束之后,為了在其上進行安裝,還需要在分區上創建文件系統。安裝程序是使用系統命令,比如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
4 支持LVM類型的分區
一般而言,磁盤分區的大小是固定的,它要求用戶在安裝系統時對分區空間的使用有大概的了解。在用戶用盡了分區上所有的空間時,則要求重新分區或者移走一部分文件。
LVM(Logical Volume Management)是邏輯卷管理的縮寫。它的出現將物理磁盤分割成一些邏輯單位,來自于不同磁盤的分區能組成一個邏輯卷。此外,在需要時分區能被動態的加入和刪除。舉例來說,如果你有一個8GB的磁盤,其上有一個2GB的分區/usr,它的空間已經耗盡了。如果您要擴大/usr分區的話,必須首先創建一個更大的分區,然后將/usr的所有內容都拷貝到此分區中,改變/etc/fstab文件,重新啟動。但是如果在系統中使用了LVM的話,你只需使用 LVM中的命令,就可以簡單的增大/usr。
LVM的分區方式對于需要大數據量存儲的分區進行管理,可以極大的減輕管理員的負擔。而且,在LVM和RAID設備結合使用之后,可以構造出一種靈活而且高效的存儲方案。
4.1 LVM的基本概念
后面在建立LVM類型的分區時,會用到一些術語,下面先對這些術語進行介紹。
物理卷(PV)
物理卷僅僅是進行了LVM初始化的物理分區,以使得LVM管理程序能識別這個分區。
物理范圍(PE)
物理范圍是一些大數據塊,通常有幾兆字節。
卷組(VG)
一個VG可由多個PE組成。一個VG可由幾個分區組成,它包含的PE由這些分區提供。在某種意義上說,我們可以認為VG就是一個硬盤設備。
邏輯卷(LV)
邏輯卷是最終用戶訪問的部分,它用于存儲數據。在某種意義上說,我們可以認為LV就是一個邏輯分區。其上可以創建任何類型的分區,包括EXT2,ReiserFS,NTFS等等。在訪問時,它和正常的磁盤分區一樣。
邏輯范圍(LE)
每個邏輯卷被分割成為數據塊。
為了使LVM在系統中能夠正常工作,那么在定制內核的時候,要將LVM支持作為模塊形式定制。然后,為了創建和管理LVM邏輯分區,您還需要安裝LVM包。
步驟如下:
使用fdisk命令設置分區/dev/hda3和/dev/hdc3類型為0x8e。
創建物理卷。
pvcreate /dev/hda3
pvcreate /dev/hdc3
創建新卷組newvg,它包含/dev/hda3和/dev/hdc3分區,卷組的大小是兩個分區容量的總和。在成功創建此卷組之后會在/dev目錄下形成目錄/dev/newvg,在其后生成的邏輯卷設備文件都保存在此目錄下。
vgcreate newvg /dev/hda3
vgcreate newvg /dev/hdc3
創建新的邏輯卷
lvcreate -L1500 -nnewlv newvg
創建一個1500MB線性邏輯卷,同時這條命令也創建邏輯卷對應的塊設備文件/dev/newvg/newlv。
lvcreate -i2 -I4 -l1500 -notherlv newvg
以交錯塊(stripe)為2,塊大小為4 KB創建另一個邏輯卷,對應的設備別名是/dev/newvg/otherlv。
創建文件系統
mke2fs /dev/newvg/newlv
在設備/dev/newvg/newlv上創建ext2文件系統,除了ext2文件系統之外,您還可以創建Reiserfs等多種文件系統。
激活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
5 支持軟Raid分區
軟件Raid分區支持是指Linux系統不借助任何硬件支持,在普通ide或scsi硬盤上實現Raid功能。這首先需要內核的支持,其次還需要安裝raidtools包。
5.1 Raid設備概念
Linear模式
兩個或者多個磁盤被組合成一個物理設備,這些磁盤是疊加使用的。也就是說,RAID設備會先裝滿第一個磁盤,然后第二個磁盤,以此類推。每個磁盤的大小不必相同。
使用此模式Raid設備不提供冗余信息。如果一個磁盤崩潰了,你將會丟失所有的數據。對于單個讀寫操作,讀寫性能都沒有任何提升,但是當多個用戶共享RAID設備時,如果用戶訪問的文件在不同的磁盤上,那可能會使訪問性能更高。
RAID-0模式
這種模式也被稱為交錯(Stripe)模式。RAID0要求磁盤(分區)具有近似的大小。它的所有磁盤讀/寫操作時并行完成的。如果其中一個分區比另外一個分區大得多,那么在使用這些空間時,并行操作無法完成,導致性能下降。
這種模式也不存在數據冗余。如果一個磁盤損壞,將導致所有數據丟失。但是,在此種模式下,因為并行存取讀寫性能會提升。這是使用RAID0的主要原因。
RAID-1模式
此種模式具有數據冗余性。RAID-1缺省時,使用兩塊磁盤,同時也可以指定多塊備用盤。這個模式下的兩個硬盤互為鏡像。這就要求這兩個磁盤的大小一致,否則RAID設備的大小等于最小的磁盤。
在任何一個磁盤崩潰之后,數據都會保持完整并可立即進行重建。在此種方式下,讀性能會得到很大的提升,但是寫磁盤的性能保持不變或者有所下降。
RAID-4模式
這種模式不是經常使用,用于三個或者多個磁盤上。這種模式采用數據塊存儲方式,將校驗數據單獨存在一個磁盤上,寫數據到其他磁盤時采用和RAID-0相似的方式。因為一個磁盤用于奇偶校驗,所以RAID陣列的大小為(N-1)*S(S為陣列中最小的磁盤尺寸)。
在一個磁盤失效之后,奇偶信息會用于重構所有數據。但如果有兩個磁盤崩潰,所有數據將會丟失。但是奇偶信息保存在一個磁盤上,每次數據更新都會訪問這張磁盤,因此這張磁盤會成為瓶頸。
RAID-5模式
在需要組合大量的物理磁盤時,這是最有用的RAID方式。它也用在三個或者多個磁盤上,RAID陣列的大小為(N-1)*S。此種模式采用數據塊存儲方式,將校驗數據分布保存在各個磁盤上,避免了RAID-4的瓶頸問題。
如果一塊磁盤崩潰,在存在備用盤的情況下,重構立即開始。但是如果有兩個磁盤崩潰,所有數據將會丟失。在此種方式下讀寫的性能都將有所提升。
5.2 Raid設備安裝
在Linux系統下為了安裝RAID設備,必須首先安裝raidtools工具,創建/etc/raidtab文件,描述創建的RAID陣列的類別。然后使用mkraid命令創建RAID分區。
mkraid /dev/md0
這條命令初始化RAID陣列,寫永久超級塊并且啟動陣列。然后察看/proc/mdstat,應該見到陣列正在運行。最后,可以創建文件系統,并在fstab中掛接它。
下面是配置不同RAID模式時,raidtab文件的內容:
線性模式 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
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
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帶寬,重構過程是透明的,重構時仍能使用這個設備。在重構運行時,格式化、掛接和使用設備都可以使用。
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
Raid-5 raiddev /dev/md0
raid-level 5
nr-raid-disks 7
nr-spare-disks 0
persistent-superblock 1
parity-algorithmleft-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。
5.3 啟動Raid設備
在創建了正確的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設備并正確掛接分區。這需要下列步驟:
定制的系統內核支持raid設備自動檢測。也就是說,在編譯內核時選擇: Multi-device support (RAID and LVM)
[*] Multiple devices driver support (RAID and LVM)
<*>RAID support
將設備模塊md.o打入內核。如果對此設備的支持選擇了內核模塊的方式,那么軟件RAID分區無法作為根分區啟動系統。這主要是因為RAID設備需要在啟動之初對硬盤進行讀寫,以決定RAID分區的位置,類型等參數。如果不將md.o置入內核,僅選擇模塊方式,那么它將從初始內存鏡像中進行加載,但是加載過后無法完成上述的信息讀取過程。
用帶有persistent-superblock的選項創建RAID設備。
RAID的設備分區類型必須設為0xFD。
正確的配置RAID陣列,并在RAID系統上建立了正確的文件系統。
必須創建初始啟動鏡像,這樣保證在啟動時能夠加載raid設備模塊。
mkinitrd --with=raid5 raid-ramdisk 2.4.3
糾正/etc/fstab文件,使/dev/md0為根文件系統對應的設備文件,并使引導時所用的分區包含在fstab中。