Windows 引导过程
Windows 内核中的各个组件和各种机制在起作用以前,必须首先被初始化。此初始化工作是在系统引导时完成的。当用户打开计算机的电源开关时,计算机便开始运行,但操作系统并不立即获得控制权,而是BIOS 代码首先获得控制,它执行必要的硬件检测工作,并允许用户通过一些功能键来配置当前系统中的硬件设置,甚至诊断硬件问题,然后才将控制权交给操作系统。
1.1 内核加载
在Intel x86 系统上,Windows 操作系统获得控制首先从硬盘的主引导记录(MBR,Master Boot Record)开始,Windows Setup 程序在安装Windows 时填充MBR(其他的磁盘管理程序也可能填充MBR)。MBR 包含代码和数据,其代码称为引导代码,在系统引导时首先获得控制;MBR 中的数据是一张分区表,指定了每个分区在磁盘上的位置和大小,以及分区的类型。当MBR 中的引导代码被执行时,它检查分区表中的每一个分区,若找到一个已被标记为可引导的分区(称为引导分区),则将该分区的第一个扇区(称为引导扇区)读到内存中。由于分区表包含了每一个分区的磁盘位置,所以,引导扇区的位置很容易被确定。然后MBR 的代码将控制权交给引导扇区中的代码。
;此处代码摘自NT4代码的\\private\\ntos\\boot\\bootcode\\x86mboot.asm
relocated_org equ 0600h
buildtime_org equ 0100h
org_delta equ (relocated_org - buildtime_org)
_data segment public
assume cs:_data,ds:_data
org buildtime_org
; 这段代码读出位于主引导记录末尾的分区表,找到标志为可引导的分区,把它的引导扇区拷贝到内存中并执行
start:
cli ;开始的时候并没有中断
xor ax,ax
mov ss,ax
mov sp,7c00h ;位于地址0:7c00处的新堆栈
mov si,sp ; 0:7c00为标准引导地址
push ax
pop es
push ax
sti ;允许中断
cld
mov di,relocated_org mov cx,100h
rep movsw
;重定位到地址 0000:0600,跳到这里从分区表中读取可引导分区的入口,把引导分区拷贝到内存的标准引导地址(0000:7C00)
; jmp entry2 + org_delta
db 0eah
dw $+4+org_delta,0
entry2:
mov si,(offset tab) + org_delta ;表示分区表
mov bl,4 ;分区表项的个数
next:
cmp byte ptr[si],80h ;判断是否是可以引导的入口
je boot ;yes
cmp byte ptr[si],0 ;再次判断是否为0
jne bad ;不是,只有 x\或者x\是有效的
add si,16 ;执行到下一个入口点
dec bl
jnz next
int 18h ;未检测到可引导的入口,返回
boot:
mov dx,[si] ;引导开始处
mov cx,[si+2]
mov bp,si ;保存表入口地址并传给分区引导记录
next1:
add si,16 ;下一个表项
dec bl ;表项数目递减
jz tabok
cmp byte ptr[si],0 ;所有剩余的表入口都要从0开始
je next1 ;满足上述判断条件
bad:
mov si,(offset m1) + org_delta ;无法找到一个从0开始的表项入口,该表为坏表
msg:
lodsb ;获取显示信息的字符
cmp al,0
je hold
push si
mov bx,7
mov ah,14
int 10h ;显示信息
pop si
jmp msg ;循环打印完整信息
hold: jmp hold ;此处自旋,不做任何事
tabok:
mov di,5 ;计数值
rdboot:
mov bx,7c00h ;读取系统引导记录的位置
mov ax,0201h ;读取一个扇区
push di
int 13h ; 获取引导记录
pop di
jnc goboot ;成功得到引导记录,交与控制权
xor ax,ax ;出现错误
int 13h ;重新校准
dec di ;递减计数值
jnz rdboot ;只要计数值仍大于0,就继续尝试
mov si,(offset m2) + org_delta ;所有的入口都已检测完毕,错误无法避免
jmp msg ;跳转到显示错误信息
goboot:
mov si,(offset m3) + org_delta
mov di,07dfeh
cmp word ptr [di],0aa55h ;判断引导记录是否有效
jne msg ;无效,则显示无效的系统引导记录信息
mov si,bp ;有效,则将分区表入口地址传给它
db 0eah
dw 7c00h,0
include x86mboot.msg
org 2beh;此处显示了主引导记录的结构
tab: ;partition table
dw 0,0 ;partition 1 begin
dw 0,0 ;partition 1 end
dw 0,0 ;partition 1 relative sector (low, high parts)
dw 0,0 ;partition 1 # of sectors (low, high parts)
dw 0,0 ;partition 2 begin
dw 0,0 ;partition 2 end
dw 0,0 ;partition 2 relative sector
dw 0,0 ;partition 2 # of sectors
dw 0,0 ;partition 3 begin
dw 0,0 ;partition 3 end
dw 0,0 ;partition 3 relative sector
dw 0,0 ;partition 3 # of sectors
dw 0,0 ;partition 4 begin
dw 0,0 ;partition 4 end
dw 0,0 ;partition 4 relative sector
dw 0,0 ;partition 4 # of sectors
signa db 55h,0aah ;引导区有效签名值
_data ends
end start
Windows Setup 程序在确定了要将Windows 系统安装到哪个分区中以后,除了可能会写入MBR 以外,还会写入该分区的引导扇区。所以,严格意义上讲,Windows 操作系统的真正入口点应该是引导扇区中的代码。引导分区必须被格式化成Windows 所支持的文件系统,典型的文件系统格式是NTFS 和FAT,其中NTFS 是Windows NT 的原生文件系统,而FAT 则是从MS-DOS 时代继承和发展过来的。
引导扇区中的代码随硬盘文件系统格式的不同而有所不同,其职责是,给Windows提供有关该硬盘上卷的结构和格式方面的信息,并且从该卷的根目录中读入Windows 的加载程序,即ntldr 文件;然后将控制权交给ntldr 的入口函数。为了能够从根目录中读入加载程序,引导扇区包含了能理解文件系统结构和读取文件的代码,这通常只是文件系统极其简单的一部分功能,而并非完整的实现。尽管引导扇区的职责相对简单,但是单个扇区(512 B)的代码和数据往往不足以完成其功能,为此,Windows 的做法是,让引导扇区中的代码读入其他扇区的数据,然后跳转到下一个扇区的代码区。这样就可以不受单个引导扇区长度的限制,这种做法相当于将第一个引导扇区当做一个加载器(loader),而真正完成引导扇区功能的扇区随后被加载进来并执行。这一过程对于MBR 是透明的,从而保持良好的兼容性。
; 此处代码摘自NT4代码的\\private\\ntos\\boot\\bootcode\\ntfs\\i386\\ntfsboot.asm
MASM equ 1
.xlist
.286
A_DEFINED EQU 1
include ntfs.inc
DoubleWord struc
lsw dw ?
msw dw ?
DoubleWord ends
; 下面的代码显示了几个引导加载器使用的不同区段,最开始的两个分别是引导扇区最先加载的位置以及之后重定位的位置
; 第三个则是NTLDR加载的静态地址
BootSeg segment at 07c0h ; ROM 起先加载的位置.
BootSeg ends
NewSeg segment at 0d00h ; 重定位的位置.
NewSeg ends
LdrSeg segment at 2000h ; 将要在地址 2000:0000处加载加载器
LdrSeg ends