操作系统的启动过程

Posted 天赋不够努力来凑

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了操作系统的启动过程相关的知识,希望对你有一定的参考价值。

操作系统在开机前是储存在磁盘中的,以下会依次介绍操作系统从磁盘读入再到操作系统初始化的过程。(以X86为例)

一、操作系统读入

1、boot扇区的读入
(一)、打开电源,CPU处于实模式
(二)、CS:IP指向0xFFFF0(ROM Bios映射区)
(三)、BIOS执行自检,检查磁盘、显示器等设备
(四)、BIOS读入磁盘0磁道0扇区的bootsect.s代码存于0x7c00处,一共512字节
(五)、CS:IP指向0x7c00

2、执行bootsect模块
bootsect.s代码(只列出重要部分)

mov ax, #BOOTSEG mov ds, ax//BOOTSEG=0x07c0
mov ax, #INITSEG mov es, ax//INITSEG=0x9000
mov cx, #256 
sub si, si sub di,di
rep movw 
jmpi go, INITSEG

该代码主要操作是把0x7c00处的那512字节移动到0x90000处,然后将CS:IP指向0X90000,为什么要做此移动操作呢?后面再讲

go: mov ax,cs //cs=0x9000
mov ds,ax mov es,ax mov ss,ax mov sp,#0xff00
load_setup: //载入setup模块
mov dx,#0x0000 //dh=磁头号,dl=驱动器号
mov cx,#0x0002 //ch=柱面号,cl=开始扇区(从第二扇区开始)
mov bx,#0x0200 //es:bx为将setup扇区内容读入的内存地址
mov ax,#0x0200+SETUPLEN //ah=0x02(读磁盘),al=磁盘数量(SETUPLEN=4)
int 0x13 //BIOS中断
jnc ok_load_setup
mov dx,#0x0000
mov ax,#0x0000 //复位
int 0x13
j load_setup //重读

此代码利用BIOS读取磁盘中断将磁盘的setup的四个扇区读入到内存0x90200处

Ok_load_setup: //载入setup模块
mov dl,#0x00 mov ax,#0x0800 //ah=8获得磁盘参数
int 0x13 mov ch,#0x00 mov sectors,cx
mov ah,#0x03 xor bh,bh int 0x10 //读光标
mov cx,#24//字符显示长度
mov bx,#0x0007
mov bp,#msg1 mov ax,#1301 int 0x10 //显示字符,就像windows开机时的“欢迎使用”字符
mov ax,#SYSSEG //SYSSEG=0x1000 
mov es,ax 
call read_it //读入system模块
jmpi 0,SETUPSEG//CS:IP指向0x90200,开始执行setup模块
msg1:.byte 13,10//显示字符的信息
.ascii “Loading system...//可更改双引号内的字符
.byte 13,10,13,10

读入system扇区内容

read_it: mov ax,es cmp ax,#ENDSEG jb ok1_read 
ret
ok1_read:
mov ax,sectors 
sub ax,sread //sread是当前磁道已读扇区数,ax未读扇区数
call read_track //读磁道...

bootsect.s将setup扇区和sysytem扇区读入内存并在屏幕上显示字符,任务就完成了,转入执行setup模块,所以可以看出bootsect主要是起着引导作用,所以bootsect叫引导扇区,将其他扇区读入

3、执行setup模块

start: mov ax,#INITSEG mov ds,ax mov ah,#0x03
xor bh,bh int 0x10//取光标位置dx mov [0],dx
mov ah,#0x88 int 0x15 mov [2],ax ...
cli ///不允许中断
mov ax,#0x0000 cld
do_move: mov es,ax add ax,#0x1000
cmp ax,#0x9000 jz end_move
mov ds,ax sub di,di
sub si,si
mov cx,#0x8000
rep
movsw
jmp do_move 

该代码重要部分是利用15号中断获取扩展内存数并储存在内存0x90002中,然后将system模块移动到0地址处。这里就要讲到为什么之前要将bootsect模块从0x7c00处移动到0x90000处了,因为system模块移动到0地址它的长度是超过0x7c00的,所以需要提前为system模块腾出位置。

call empty_8042 mov al,#0xD1 out #0x64,al
//8042是键盘控制器,其输出端口P2用来控制A20地址线
call empty_8042 mov al,#0xDF out #0x60,al
//选通A20地址线 call empty_8042
初始化8259(中断控制) //一段非常机械化的程序
mov ax,#0x0001 mov cr0,ax
jmpi 0,8

将计算机从实模式设置为保护模式,最后跳转到system模块
何为保护模式?
16位机一开始为实模式时能通过cs<<4+ip寻址20位,也就是1M,转换成保护模式能寻址32位地址,也就是4G

如何开启保护模式
有一个寄存器cr0,PE=1时启动保护模式

保护模式如何寻址
根据CS(选择子)来进行查gdt表再加ip给出地址,CS就相当于查表时的下标

4、执行system模块
system模块第一个文件是head.s
head.s

stratup_32: movl $0x10,%eax mov %ax,%ds mov %ax,%es
mov %as,%fs mov %as,%gs //指向gdt的0x10项(数据段)
lss _stack_start,%esp //设置栈(系统栈)
call setup_idt
call setup_gdt
xorl %eax,%eax
1:incl %eax
movl %eax,0x000000 cmpl %eax,0x100000
je 1b //0地址处和1M地址处相同(A20没开启),就死循环
jmp after_page_tables //页表,什么东东?
setup_idt: lea ignore_int,%edx 
movl $0x00080000,%eax movw %dx,%ax
lea _idt,%edi movl %eax,(%edi)

该代码初始化gdt表和idt表,之前setup模块只是建立的临时表,然后跳转到after_page_tables代码部分,还有要注意的是因为是32位寻址,所以汇编是32位汇编。

after_page_tables:
pushl $0 pushl $0 pushl $0 pushl $L6
pushl $_main jmp set_paging 
L6: jmp L6
setup_paging: 设置页表 ret

往栈里依次放入0、0、0、L6、main(),setup_paging执行ret后会执行main()函数,mian()函数执行完返回时进入L6,进入死循环。

执行main()

void main(void)
 mem_init();
trap_init();
blk_dev_init();
chr_dev_init();
tty_init();
time_init();
sched_init();
buffer_init();
hd_init();
floppy_init();
sti();
move_to_user_mode();
if(!fork())init();

main的工作就是xx_init: 内存、中断、设备、
时钟、CPU等内容的初始化…main()是一个永远不会退出的程序

mem_init()

void mem_init(long start_mem,long end_mem)
 
int i;
for(i=0; i<PAGING_PAGES; i++)
mem_map[i] = USED;
i = MAP_NR(start_mem);
end_mem -= start_mem;
end_mem >>= 12;
while(end_mem -- > 0)
mem_map[i++] = 0; 

mem_init()起着初始化内存作用,建立一个mem_map数组记录内存情况,数组内容为0就表示未使用,那些为100的区域则是操作系统代码,表的长度取决于mem_end,这参数来自setup模块0x90002处的扩展内存数。

以上是关于操作系统的启动过程的主要内容,如果未能解决你的问题,请参考以下文章

Linux系统管理-磁盘管理1

自动化kickstart安装linux系统

硬盘磁头坏了有哪些表现

centos 启动过程

学习记录011-磁盘结构和文件系统

磁盘读写的时间花费和调度算法(操作系统)