日期:2014-05-16  浏览次数:20799 次

linux启动过程浅析(2)

在本文的第一篇中,我主要对bootsect.s进行了讲述. 在第二部分中,我将对setup.s进行描述,我将其视为是Linux启动的第二步骤.
操作系统的启动过程是一个漫长而有序的过程,各个阶段都有其不同的作用. boot;setup;init虽然看似很接近,但是却是完全不同的过程.他们各司其职,按部就班.boot比较准确的翻译应该是引导,而setup的翻译则是设置或者建立.这听上去可能有点微妙,不要怪我咬文嚼字,请看官少安毋躁,等你看完了setup.s的过程后,你就会有深刻的体会了.

这是本文的第二部分,我将接续第一部分的内容,直接从bootsect运行完毕,跳转到setup.s开始.
在开始之前,让我们先看看现在计算机处在什么样的状态.下图是一个假想的内存空间,当bootsect运行完毕后,计算机的物理内存分配情况如图所示:
内核的system模块现在处于0x10000起始的内存段中,bootsect.s的二进制码处于0x90000起始的内存段中,而现在即将运行的setup.s则被load在0x90200的内存段中. 需要说明的是,下图的运行结果是在0.11版本的Linux运行完毕之后的结果,如2.6这样的高版本,system模块可能已经相当大了,如果放在0x10000位置上,从0x10000到0x90000的内存空间已经容不下它.在这种情况下,system模块会被load至0x100000起始的地址空间中,这就是所谓的"高地址".当前的程序计数器指向0x90200位置上,既从setup.s开始运行.
+--------+
||
| |
| |
| |<---------0x100000 "高地址"
| |
| |
| |
|---------|
| |
| setup|
| |
|---------|<----0x90200 (程序计数器位置)
|bootsect|
|----------|<----0x90000
||
| |
| |
| |
| |
|---------|
| |
| S |
| y |
| s|
| t |
| e|
| m |
| 模块 |
| |
| |
|--------|<----0x10000 "低地址"
| |
| |
| |
+--------+<----0x00000

现在,让我们来看看setup.s究竟做了些什么工作.一下分析还是基于0.11版本的Linux为主,古老的版本更能清晰的展现出操作系统的原理.而如果直接拿高版本进行分析,反而经常会陷入其他方面的纠缠.
由于接下来的代码都会显得比较长,不会象bootsect.s那样简短(否则也不能称之为操作系统了,^_^).我将把一个文件分段讲述:

首先可以看到的是:
INITSEG = 0x9000! we move boot here - out of the way
SYSSEG = 0x1000! system loaded at 0x10000 (65536).
SETUPSEG = 0x9020! this is the current segment
这些符号定义你应该非常熟悉了,只需要使他们与bootsect.s中定义的一致就可以了. 在2.6中,他们都引用于同一个宏定义,更加确定了他们的一致性.

然后使程序的起始.应该说,bootsect.s运行完毕并且跳转的目的,就是下面的start:
entry start
start:

! ok, the read went well so we get current cursor position and save it for
! posterity.

!!下面几行代码的意义使使用0x10中断请求(int 0x10),ah=0x03,读取当前光标位置.
!!并且把结果存放在0x90000处.
movax,#INITSEG! this is done in bootsect already, but...
movds,ax
movah,#0x03! read cursor pos
xorbh,bh
int0x10! save it in known place, con_init fetches
mov[0],dx! it from 0x90000.!!注意,在bootsect中,基地址已经设置为0x9000.
!!所以起始使写倒了0x90000处,覆盖了原先的bootsect.s程序.
!!使用同样的方法取得内存大小.
! Get memory size (extended mem, kB)

movah,#0x88
int0x15
mov[2],ax

!!取显卡信息,呵呵,现在是不是觉得很无聊了.
! Get video-card data:

movah,#0x0f
int0x10
mov[4],bx! bh = display page
mov[6],ax! al = video mode, ah = window width

!!...... 建议跳过此断,接下来的工作都是重复的,只为了取得机器参数.
!!写程序有时就是体力劳动.
! check for EGA/VGA and some config parameters

movah,#0x12
movbl,#0x10
int0x10
mov[8],ax
mov[10],bx
mov[12],cx

! Get hd0 data

movax,#0x0000
movds,ax
ldssi,[4*0x41]
movax,#INITSEG
moves,ax
movdi,#0x0080
movcx,#0x10
rep
movsb

! Get hd1 data

movax,#0x0000
movds,ax
ldssi,[4*0x46]
movax,#INITSEG
moves,ax
movdi,#0x0090
movcx,#0x10
rep
movsb

! Check that there IS a hd1 :-)

movax,#0x01500
movdl,#0x81
int0x13
jcno_disk1
cmpah,#3
jeis_disk1
no_disk1:
movax,#INITSEG
moves,ax
movdi,#0x0090
movcx,#0x10
movax,#0x00
rep
stosb
is_disk1:

! now we want to move to protected mode ...

cli! no interrupts allowed !

! first we move the system to it's rightful place

movax,#0x0000
cld! 'direction'=0, movs moves forward
这整整一段起始有些沉闷. 就是使用BIOS中断请求,读取机器的各种参数,并且放到目标地址上去.

取完了信息,现在开始更有趣的工作:
! now we want to move to protected mode ...

cli! no interrupts allowed ! !!把中断禁了!

! first we move the system to it's rightful place
!!开始把system整块地往低位迁移,移动倒0x0000地址起始处地内存区域.
!!就是把整个system模块向低位移动了0x10000的距离.
movax,#0x0000
cld! 'direction'=0, movs moves forward
do_move:
moves,ax! destination segment
addax,#0x1000
cmpax,#0x9000
jzend_move
movds,ax! sou