分享
 
 
 

系统引导过程分析与编制

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

读了2004年《程序员》杂志中的《程序员田园》板块中相关的文章之后,对此颇有感触。因此也想对此作一番小小的研究。

其实说穿了,系统引导很简单:

1、 启动电源之后,硬件完成它所应完成的工作;

2、 BIOS按照CMOS中保存的设置,按照启动顺序读取启动盘中的0柱面0磁道1扇区,将512字节装载到内存的0:7C00处并运行,并把硬件的控制权交给软件;判断一个扇区是否是引导扇区的依据是它是否以055AA结束;

3、 这512字节引导记录的任务就是完成必需的初始化工作,然后装载、运行操作系统的内核。

分析完原理之后,让我们来动动手,作一番尝试。

该过程所需要的工具如下:

1、 NASM或MASM汇编语言编译器,我选择NASM;自从接触到这个Open Source的编译器以后,我就把MASM扔在一旁了。其主要原因,是因为它可以编写跨平台的程序。下载连接:http://sourceforge.net/projects/nasm

2、 一个虚拟机软件,VMWare或者VirtualPC均可。用虚拟机做实验,可免去一次又一次重启的麻烦,也可以保护自己的爱机;

3、 Ultra Edit + ASM语法高亮显示包。用汇编语言写程序,没有什么好的IDE,这个我认为是最完美的组合了。也有人推荐用Edit Plus的,但由于个人喜好的原因,我一直使用这个。http://www.ultraedit.com,词库文件可以免费下载。

先来做一个最简单的测试,它完成的任务仅仅是引导系统后在屏幕上显示红色的’A’:

打开Ultra Edit,输入如下代码:

ORG 7C00H ;设置程序装载时的偏移量

MOV AH,0 ;调用BIOS中断,设置显示模式

MOV AL,1

INT 10H

MOV AH,09H ;调用BIOS中断,显示字符

MOV AL,'A'

MOV BH,0

MOV BL,4

MOV CX,1

INT 10H

JMP $ ;停止程序运行

DW 0AA55H ;扇区结束标志

然后编译这段程序,nasm boot.asm –f bin –o boot.bin。

-f开关符是指定输出方式为flat,-o是指定输出文件。Flat方式不会为源程序添加任何其它信息,只是照翻源程序,将其翻译成机器码。不过NASM默认的输出方式就是flat,所以这个开关符也可以省略。

因为其不足512字节,所以我用UltraEdit的Hex编辑模式将其打开,插入489个字节,将其凑满512字节。

接下来的任务就是准备一张软盘,用绝对扇区读写工具将其写入磁盘;幸好,手头的MINIX源码光盘中有这么一个工具,可以将其编译一下拿来用,源代码如下:

/* fdvol.c - A stripped down version of the MINIX vol program. It takes

* a file and breaks into into floppy-sized chunks, writing each one raw

* to a floppy.

*

* Usage: fdvol file drive KB [slow]

*

* Examples: fdvol 1440 a: foo.taz # 1.4 MB floppy a:

* fdvol 1200 b: foo.taz # 1.2 MB floppy b:

* fdvol slow 360 a: foo.taz # old machine

* fdvol 1440 a: foo bar # concatenate

*

* The optional 'slow' parameter forces the program to write in units of 3

* sectors. Folk tradition has it that this works around some buggy BIOSes.

*

* This code borrows heavily from Mark Becker's RaWrite program.

*/

#include <sys/types.h>

#include <fcntl.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <ctype.h>

#define TEST 0

#if !TEST

#include <alloc.h>

#include <bios.h>

#include <dir.h>

#include <dos.h>

#include <io.h>

#endif

#define FALSE 0

#define TRUE (!FALSE)

#define SECTORSIZE 512

#define RESET 0

#define LAST 1

#define READ 2

#define WRITE 3

#define VERIFY 4

#define FORMAT 5

int done;

char buffer[18*SECTORSIZE]; /* do I/O in units of up to 18 sectors */

char testbuf[SECTORSIZE]; /* to do a test read of the first sector */

int handler(void)

{

/* Catch CTRL-C and CTRL-Break. */

done = 1;

return(0);

}

void msg(char (*s))

{

/* Print an error message and quit. */

fprintf(stderr, "%s\n", s);

_exit(1);

}

void Error(int status, int cyl, int head, int sector)

{

/* Identify the error code with a real error message. */

fprintf(stderr, "\nError occured while writing cyl %d, head=%d, sector=%d\n", cyl,head,sector+1);

switch (status) {

case 0x00: msg("Operation Successful");

break;

case 0x01: msg("Bad command");

break;

case 0x02: msg("Address mark not found");

break;

case 0x03: msg("Attempt to write on write-protected disk");

break;

case 0x04: msg("Sector not found");

break;

case 0x05: msg("Reset failed (hard disk)");

break;

case 0x06: msg("Disk changed since last operation");

break;

case 0x07: msg("Drive parameter activity failed");

break;

case 0x08: msg("DMA overrun");

break;

case 0x09: msg("Attempt to DMA across 64K boundary");

break;

case 0x0A: msg("Bad sector detected");

break;

case 0x0B: msg("Bad track detected");

break;

case 0x0C: msg("Unsupported track");

break;

case 0x10: msg("Bad CRC/ECC on disk read");

break;

case 0x11: msg("CRC/ECC corrected data error");

break;

case 0x20: msg("Controller has failed");

break;

case 0x40: msg("Seek operation failed");

break;

case 0x80: msg("Attachment failed to respond");

break;

case 0xAA: msg("Drive not ready (hard disk only");

break;

case 0xBB: msg("Undefined error occurred (hard disk only)");

break;

case 0xCC: msg("Write fault occurred");

break;

case 0xE0: msg("Status error");

break;

case 0xFF: msg("Sense operation failed");

break;

}

exit(1);

}

void main(int argc, char *argv[])

{

int disknr = 1, fdin, count, drive, head, cyl, status, sector;

int max_cyl, chunk, nsectors, ct;

long offset, drive_size, r, cyl_size;

char *p, c;

int slow;

int kbsize;

char **files;

int nfiles, i;

#if !TEST

/* Catch breaks. */

ctrlbrk(handler);

#endif

#if 0 /* Do we have to clear the screen? */

fprintf(stderr, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");

#endif

if (argc > 1 && strcmp(argv[1], "slow") == 0) { /* Lousy BIOS? */

slow = 1;

argc--;

argv++;

} else {

slow = 0;

}

/* Check the arguments for validity. */

if (argc < 4)

msg("Usage: fdvol [slow] #kilobytes drive-letter file1 [file2 ...]");

kbsize = atoi(argv[1]);

p = argv[2];

c = *p;

if (c == 'a' || c == 'A')

drive = 0;

else if (c == 'b' || c == 'B')

drive = 1;

else

msg("fdvol: Second parameter must be drive, either a: or b:");

files = argv + 3;

nfiles = argc - 3;

switch(kbsize) {

case 360:

cyl_size = 9*2*SECTORSIZE; /* bytes/cylinder */

max_cyl = 39; /* zero-based counting */

drive_size = cyl_size * (max_cyl+1);

chunk = (!slow ? 9 * SECTORSIZE : 3 * SECTORSIZE);

nsectors = chunk/SECTORSIZE;

break;

case 720:

cyl_size = 9*2*SECTORSIZE; /* bytes/cylinder */

max_cyl = 79; /* zero-based counting */

drive_size = cyl_size * (max_cyl+1);

chunk = (!slow ? 9 * SECTORSIZE : 3 * SECTORSIZE);

nsectors = chunk/SECTORSIZE;

break;

case 1200:

cyl_size = 15*2*SECTORSIZE; /* bytes/cylinder */

max_cyl = 79; /* zero-based counting */

drive_size = cyl_size * (max_cyl+1);

chunk = (!slow ? 15 * SECTORSIZE : 3 * SECTORSIZE);

nsectors = chunk/SECTORSIZE;

break;

case 1440:

cyl_size = 18*2*SECTORSIZE; /* bytes/cylinder */

max_cyl = 79; /* zero-based counting */

drive_size = cyl_size * (max_cyl+1);

chunk = (!slow ? 18 * SECTORSIZE : 3 * SECTORSIZE);

nsectors = chunk/SECTORSIZE;

break;

default:

msg("fdvol: First parameter must be one of: 360, 720, 1200, or 1440");

}

#if !TEST

biosdisk(RESET, drive, 0, 0, 0, 0, testbuf);

#endif

/*

* Start writing data to diskette until there is no more data to write.

* Optionally read and write in units of 3 sectors. Folk tradition says

* that this makes fewer buggy BIOSes unhappy than doing a whole track at a

* time.

*/

offset = 0;

i = 0;

fdin = -1;

while(1) {

if (done > 0) {

if (done == 1) msg("User abort");

#if !TEST

biosdisk(READ, drive, 0, 0, 1, 1, testbuf); /* Retract head */

#endif

fprintf(stderr, "Done. \n");

exit(done == 1 ? 1 : 0);

}

/* Until a chunk is read. */

count = 0;

while (count < chunk) {

if (fdin == -1) { /* open next file */

#if !TEST

_fmode = O_BINARY;

#endif

fdin = open(files[i], O_RDONLY);

if (fdin < 0) {

perror(files[i]);

exit(1);

}

}

/* read from file */

ct = read(fdin, buffer + count, chunk - count);

if (ct < 0) {

perror(files[i]);

exit(1);

}

if (ct == 0) { /* end of file */

close(fdin);

fdin = -1;

/* choose next file */

if (++i >= nfiles) break; /* no more files */

}

count += ct;

}

if (count == 0) { /* absolute EOF */

done = 2;

continue;

}

if (count < chunk) { /* pad last track */

/* Pad out buffer with zeroes. */

p = &buffer[count];

while (p < &buffer[chunk]) *p++ = 0;

done = 2;

}

r = offset % drive_size;

if (r == 0) {

/* An integral number of diskettes have been filled. Prompt. */

fprintf(stderr, "Please insert formatted diskette #%d in drive %c, then hit Enter%c\n", disknr, c, 7);

disknr++;

#if !TEST

while(bioskey(1) == 0) ; /* wait for input */

if ((bioskey(0) & 0x7F) == 3) exit(1); /* CTRL-C */

biosdisk(READ, drive, 0, 0, 1, 1, testbuf); /* get it going */

#endif

}

/* Compute cyl, head, sector. */

cyl = r/cyl_size;

r -= cyl * cyl_size;

head = (r < cyl_size/2 ? 0 : 1);

r -= head * cyl_size/2;

sector = r/SECTORSIZE;

fprintf(stderr, "Track: %2d Head: %d Sector: %2d File offset: %ld\r",

cyl, head, sector+1,offset);

#if !TEST

status = biosdisk(WRITE, drive, head, cyl, sector+1, nsectors, buffer);

if (status != 0) Error(status, cyl, head, sector);

#else

write(1, buffer, chunk);

#endif

offset += chunk;

}

}

然后,用这个工具将刚才编译成功的bin文件写入软盘就成功了。J

如果要对其进行更深入的研究,可以参阅相关书籍。

http://sourceforge.net/上面搜索NASM的时候,我偶然看到了一个FDOS的项目,可以免费获得。它是“在一张磁盘上的DOS系统”,支持常用DOS命令、FAT12、TSR等。它的文档中有很详细的引导记录说明,我将在下一篇中对其源代码作一些剖析。

另外,网上也能找到MSDOS系统的汇编源代码,有兴趣的同志也可以拿来研究一下。当然,拿Linux的引导代码来研究也未尝不可,但刚开始的时候,不应好高骛远,打击自己的信心,还是从简单的开始吧。J

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有