标题: Linux开机过程的分析(关于bootsect.S )
问天
元帅
Rank: 1


元帅勋章 终身成就勋章
UID 11493
精华 187
积分 34199
帖子 33353
威望 91
金币 13148
热心 2617
阅读权限 100
注册 2006-4-7
状态 离线
Linux开机过程的分析(关于bootsect.S )

发信人: huzq (你快乐所以我快乐), 信区: LINUX  
标 题: [转载]Linux开机过程的分析  
发信站: 武汉白云黄鹤站 (Sat Feb 10 14:37:30 2001), 站内信件  
 
这篇文章的目的,在将linuxkernel的boot部份做一个介绍,因为笔者觉得很少有这样的  
文章介绍一个作业系统最最开始的一步--把kernel本身载入至内存中,同时进行一些机  
器相关(machinedependent)的初始化工作,由于linux刚好使用的是大家最熟悉的386,  
486系列PC,所以在说明其程序流程时,也刚好可以对其相关的PC硬体架构做探讨,可以  
说是一举两得,不过,我必须假设读者对于组合语言及PC最基础的架构,如寄存器,分  
段,分页,中断服务等有大概的认识。  
  读者可在linuxsourcecode的/boot子目录下找到几个以.S作为副档名的组合语言档  
,本文要说明的即是其中的bootsect.S及setup.S两个档案,及尽量简单的说明其所牵涉  
的相关硬体部份。  
bootsect.S  
  这个程序是linuxkernel的第一个程序,包括了linux自己的bootstrap程序,但是在   
说明这个程序前,必须先说明一般IBMPC开机时的动作(此处的开机是指"打开PC的电源"  
):  
  一般PC在电源一开时,是由内存中地址FFFF:0000开始执行(这个地址一定在ROMBIO  
S中,ROMBIOS一般是在FEOOOh到FFFFFh中),而此处的内容则是一个jump指令,jump到另  
一个位于ROMBIOS中的位置,开始执行一系列的动作,包括了检查RAM,keyboard,显示  
器,软硬磁盘等等,这些动作是由系统测试码(systemtestcode)来执行的,随着制作BI  
OS厂商的不同而会有些许差异,但都是大同小异,读者可自行观察自家机器开机时,萤  
幕上所显示的检查讯息。  
  紧接着系统测试码之后,控制权会转移给ROM中的启动程序(ROMbootstraproutine)  
,这个程序会将磁盘上的零道零扇区读入内存中(这就是一般所谓的bootsector,如果你  
曾接触过电脑病毒,就大概听过它的大名),至于被读到内存的哪里呢?--绝对位置07C0  
:0000(即07C00h处),这是IBM系列PC的特性。而位在linux开机磁盘的bootsector上的正  
是linux的bootsect程序,也就是说,bootsect是第一个被读入内存中并执行的程序。现  
在,我们可以开始来看看到底bootsect做了什么。   


第一步  
  首先,bootsect将它"自己"从被ROMBIOS载入的绝对地址0x7C00处搬到0x90000处,  
然后利用一个jmpi(jumpindirectly)的指令,跳到新位置的jmpi的下一行去执行,关键  
的assemblycode如下:  
.  
(搬移bootsect本身)  
.  
.  
jmpigo,INITSEC  
go:  
.  
.  
.  
  表示将跳到CS为0x9000,IP为offset"go"的位置(CS:IP=0x9000:offsetgo),其中I  
NITSEC=0x9000定义于程序开头的部份,而go这个label则恰好是下一行指令所在的位置  
。  
第二步  
  接着,将其它segmentregisters包括DS,ES,SS都指向0x9000这个位置,与CS看齐  
。另外将SP及DX指向一任意位移地址(offset),这个地址等一下会用来存放磁盘参数表  
(diskpara-metertable)  
  提到磁盘参数表,就必须提到BIOS中断1Eh。先简单的介绍一下BIOS的中断服务:80  
x86将内存最低的256*4byte保留给256个中断向量(每个interruptvector大小为4byte,  
所以一共有256*4=1024byte),而其中的第1Eh个向量指向"磁盘参数表",这个表会告诉   
电脑如何去读取磁盘机,而我们所要做的事是搬移磁盘参数表到刚才所设定的任意地址  
。  
  接着,改变搬移来的参数表的参数,以符合我们的需要。再将中断向量1Eh指向我们  
所修改过的磁盘参数表,然后呼叫BIOSinterrupt的int13h(function0,即AH=0)重置磁  
盘控制卡及磁盘驱动器,之后磁盘机就会照我们的意思动作了。如果你曾trace过DOS的  
kernel,你会发现,上述的动作在DOS中也有类似的对应流程。  
现在让我们来看看关键的程序码:.  
.  
.  
push#0  
popfs  
movbx,#0x78  
.  
(使GS:SI=FS:BX,指向磁盘参数表,  
再将GS:SI所指地址的内容搬移6个  
word至ES:DI所指的地址)  
.  
.  
  此段程序是将FS:BX调整成0000:0078,接着再将GS:SI的内容设成与FS:BX相同,此  
处0x78h即为int1Eh的起始位置(7*16 8=120,(1*16 14)*4=120)。调整ES:DI为刚才所设  
定的任意地址,从GS:SI搬移6个word(即12byte)到ES:DI所指的位置,显然磁盘参数表的  
长度就是6个word,(不过事实上,磁盘参数表的确实长度是11个byte)。关于磁盘参数表   
,有兴趣的读者可自行参阅讲述BIOSinterruptservices的技术手册,会有详细的说明。  
 
  读者可以用debug自行观察自家机器上DOS的磁盘参数表的起始位置(即int1Eh的内容  
)。以下是笔者机器的情形(笔者使用的作业系统是MSDOS6.2):  
C:>debug  
-d0000:0000  
0000:00008A101601F4067000-1600CB04F4067000......p.......p.  
0000:0010F40670000301790E-43EB00F0EBEA00F0..p...y.C.......  
0000:002004108E340C118E34-5700CB046F00CB04...4...4W...o...  
0000:00308700CB0408079433-B700CB04F4067000.......3......p.  
0000:00400C01790E4DF800F0-41F800F0BA165F06..y.M...A....._.  
0000:005039E700F01B01790E-70118E341201790E9.....y.p..4..y.  
0000:006000E000F085175F06-6EFE00F0EE067000......_.n.....p.  
0000:007053FF00F0A4F000F0-220500003E4600C0S......."...>F..  
^^^^^^^^  
由上图中可知,在DOS中磁盘参数表的起始位置(int1Eh的内容)为0000:0522。接着观察  
DOS中位置0000:0522开始的11个byte,也就是磁盘参数表的内容  
C:>debug   
-d0000:0520l10  
0000:05204D53DF022502121B-FF54F60F08000000MS..%....T......  
^^^^^^^^^^^^^^^^^^^^^^  
此11byte即为磁盘参数表的内容(分别是byte00h到0Ah)  
  在程序中我们所更动的是第五个byte(byte04h),改为18h(在上图例子中为12h),这  
个byte的功能是定义磁轨上一个磁区的资料笔数。关键的程序码如下:  
.  
movb4(di),*18  
.  
   
第叁步  
  接着利用BIOS中断服务int13h的第0号功能,重置磁盘控制器,使得刚才的设定发挥  
功能。  
.  
.  
xorah,ah  
xordl,dl  
int0x13  
.  
.  
第四步  
  完成重置磁盘控制器之后,bootsect就从磁盘上读入紧邻着bootsect的setup程序,  
也就是以后将会介绍的setup.S,此读入动作是利用BIOS中断服务int13h的第2号功能。  
setup的image将会读入至程序所指定的内存绝对地址0x90200处,也就是在内存中紧邻着  
bootsect所在的位置。待setup的image读入内存后,利用BIOS中断服务int13h的第8号功  
能读取目前磁盘机的参数。      
第五步  
  再来,就要读入真正linux的kernel了,也就是你可以在linux的根目录下看到的"v  
mlinuz"。在读入前,将会先呼叫BIOS中断服务int10h的第3号功能,读取游标位置,之  
后再呼叫BIOS中断服务int10h的第13h号功能,在萤幕上输出字符串"Loading",这个字  
符串在bootlinux时都会首先被看到,相信大家应该觉得很眼熟吧。  
  linux的kernel将会被读入至内存绝对地址0x10000处,键关的程序码如下:  
.  
.  
movax,#SYSSEG  
moves,ax  
callread_it  
callkill_motor  
.  
.  
  其中SYSSEG于程序开头时定义为0x1000,先将ES内容设为0x1000,接着在read_it这  
个子程序便以ES为目的地的节地址,将kernel读入内存中,至于read_it子程序的详细内  
容笔者并不想一一介绍,不过聪明的读者们应该已经猜到,read_it一定又利用了BIOSi  
nt13h与磁盘有关的I/O中断服务了。  
  至于kill_motor子程序,它的功能在于停止软盘机的马达(各位聪明的读者会不会觉  
得这个子程序的名称取得颇为传神呢?),其程序码如下:  
.   


.  
kill_motor:  
pushdx  
movdx,#0x3f2  
xoral,al  
outb  
popdx  
ret  
.  
.  
  首先利用DX指定要输出的port,而03f2这个port则是代表了软盘控制器(floppydis  
kcontroller)的所在,再利用outb将资料送出,而我们送出的资料,当然就是归零过的  
AL了。如此一来,软盘的马达就停止了。  
第六步  
  接下来做的事是检查rootdevice,之后就仿照一开始的方法,利用indirectjump跳  

至刚刚已读入的setup部份,程序码如下:  
.  
.  
jmpi0,SETYPSEG  
  其中SETUPSEG已在先前定义为0x9020,所以CS:IP会设定为9020:0000,即跳到绝对  
地址为0x90200,也就是setup的起点。而bootsect也大功告成了。  
到此为止,内存的内容应该如下图所示:  
比较  
  把大家所熟知的MSDOS与linux的开机部份做个粗浅的比较,MSDOS由位于磁盘上boo  
tsector的boot程序负责把IO.SYS载入内存中,而IO.SYS则负有把DOS的kernel--MSDOS.  
SYS载入内存的重责大任。而linux则是由位于bootsector的bootsect程序负责把setup及      
linux的kernel载入内存中,再将控制权交给setup。  
  至于setup.S,就留到下一次再来讨论了。  
转自:动态网制作指南 www.knowsky.com

网友 问天 签名 - 网友社区 请您回个帖。谢谢
PR查询 免费域名 免费空间
顶部
[广告] 免费域名(Free Subdomain) 免费空间(Free hosting) PR查询(Google Pagerank)



当前时区 GMT+8, 现在时间是 2008-10-8 08:33
信产部ICP备案:京ICP备05066424号 北京市公安局网监备案:1101050648号

Powered by Discuz! 5.5.0
清除 Cookies - 联系我们 - 网友俱乐部 - Archiver - WAP