系統的引導過程和磁盤分區信息
在PC機上,最初的啟動由BIOS完成。當開機自檢(Power-On Self Test,POST)結束時, BIOS嘗試讀入磁盤的第一個扇區,把它看作引導扇區。由於大多數BIOS不提供SCSI支持,若要從SCSI磁盤啟動,SCSI適配器要提供他自己的 BIOS。如果什麼都找不到,老的BIOS會啟動內置的ROM BASIC,或直接打印“NO ROM-BASIC”。
操作系統的啟動分幾步完成。由於引導扇區比較小,通常它主要任務是讀入第二個loader,第二個loader再讀入第三個loader,直到整個操作系統被完全讀入。
硬盤的引導區:
代碼:
OFFSET
0x000 JMP xx Near jump into the program code
0x003 Disk parameters
0x03E Program code loading the DOS kernel
0x1FE 0xAA55 Magic number for BIOS
可見,引導區的結構相對比較簡單。它的長度總是512字節。重要的是引導區從0開始,以BIOS的0x1FE處的0xAA55結束。
從軟盤啟動比較簡單,因為只有一個引導扇區:第一個扇區。硬盤則困難一些,它被分成很多分區。但是,BIOS根本不管分區信息,它象對待軟盤一樣對待硬盤,仍讀入第一個分區,叫作:master boot record。(MBR)。所以MBR也應該和上面介紹的結構一樣,在MBR的最後部分,有分區表。如下圖:
代碼:
OFFSET Length
0x000 0x1BE code loading and starting the boot sector of the active
partitian
0x1BE 0x010 partition1
0x1CE 0x010 partition2
0x1DE 0x010 partition3
0x1EE 0x010 partition4
0x1FE 0x0012 0xAA55 Disk parameters
從0x1BE處(即第462字節處)開始,是一個個主分區信息。每個分區信息占16字節,結構如下:
1 BOOT Boot flag: 0=not active ,0x80 active
1 HD Begin:head number
2 SEC CYL Begin:sector and cylinder number of boot sector
1 SYS System Code:0x83 Linux , 0x82 linux swap etc。
1 HD End:head number
2 SEC CYL End: sector and cylinder number of boot sector
4 low byte high byte Relative sector number of start sector
4 low byte high byte Number of sectors in the partition
所以硬盤可以有4個主分區:primary prititions。假如它們不夠用,可以設置所謂的擴展分區。擴展分區包含至少一個邏輯分區。擴展分區的第一個扇區結構類似MBR,它的分區表的第一表項對應第一個邏輯分區。如果存在第二個邏輯分區,那麼分區表的第二個表項就包含了一個指針。這個指針指向第一個邏輯分區後面的一個地址,這個地址包含一個分區表,該分區表的第一表項對應第二個邏輯分區。這樣就組成一個鏈表,從而擴展分區可以有任意多的邏輯分區。
每一個主分區和擴展區都包含一個引導扇區。系統只能從這幾個地方之一啟動。BOOT標志決定哪個區被引導。
MBR的代碼要作以下的操作:
1:確定活動分區。
2:使用BIOS,將活躍分區的啟動扇區讀入。
3:跳到啟動扇區的0位置。
MBR的空間足夠完成這些工作。如上所述,每個分區理論上包含一個引導扇區,而且,存在的第二個硬盤也包含和第一個類似的結構。MBR完全可以容納一個復雜的引導程序。即所謂的boot manager,動態的決定活動分區。Linux為我們提供了lilo及grub等工具來管理啟動各啟動項。
下面這段C程序可以用來檢測磁盤分區信息以驗證上述理論的正確性。
代碼:
/* marco corvi <[email protected]>
* @date feb 2003
*
* \brief Read disk partition table
* modified by zhoulifa <[email protected] http://zhoulifa.bokee.com
* 周立發 Linux愛好者 Linux知識傳播者 SOHO族 開發者
*/
#include <stdio.h> // printf
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
int
main(int argc, char ** argv )
{
int fd;
unsigned char mbs[512]; // master boot sector
unsigned char * pp; // partition pointer
int head, sect, cyl;
unsigned int sector;
int n;
if (argc <= 1) {
fprintf(stderr, "Usage: %s <device>\n", argv[0] );
exit(1);
}
fd = open( argv[1], O_RDONLY );
if ( fd < 0 ) {
fprintf(stderr, "Error: unable to open device %s\n", argv[1] );
perror("");
exit(1);
}
n = read( fd, mbs, 512);
close(fd);
if ( n < 512 ) {
fprintf(stderr, "Error: short read %d\n", n );
exit(1);
}
pp = mbs + 0x1BE;
for (n=0; n<4; n++) {
printf("PARTITION %d\n", n );
printf("Boot flag %2x\n", pp[0] );
printf("System flag %2x\n", pp[4] );
// C/H/S as 10+8+6
if(argc == 3) {
head = pp[1];
sect = pp[2]; sect = (sect<<2) (pp[3]>>6);
cyl = pp[3] & 0x3f;
/*
cyl = pp[1] >> 2;
head = ((pp[1] & 0x03)<<6) (pp[2]>>2);
sect = pp[2] & 0x03;
sect = (sect << 8) pp[3];
head = pp[1];
sect = pp[3]>>6;
sect = (sect<<8) pp[2];
cyl = pp[3] & 0x3f;
*/
printf("Start Cylinder %d Head %d Sector %d \n", cyl, head, sect );
head = pp[5];
sect = pp[6]; sect = (sect<<2) (pp[7]>>6);
cyl = pp[7] & 0x3f;
cyl = pp[5];
printf("End Cylinder %d Head %d Sector %d \n", cyl, head, sect );
}
sector = pp[8] + (1<<8)*pp[9] + (1<<16)*pp[10] + (1<<24)*pp[11];
printf("Sector number %u\n", sector);
sector = pp[12] + (1<<8)*pp[13] + (1<<16)*pp[14] + (1<<24)*pp[15];
printf("Number of sectors %u\n", sector);
pp += 0x10;
}
}
編譯此程序後可以直接運行,如果沒有任何參數,會從/dev/partitions文件裡讀取相應信息。如果有參數,則第一個參數指明要查看的設備。
參考了 http://www.linuxdby.com/articlesdisplay.PHP?newsid=75 及 marco_corvi的“Linux kernel programming by example”,並在Red Hat Linux 7.2系統上驗證通過。