哈工大操作系统学习L3 之操作系统启动

操作系统在硬盘上,而程序存在内存里。要进行“取指执行”就必须先将OS从磁盘上载入到内存里。这一部分工作由开机时候的引导扇区的bootsect.s完成。
复习bootsect.s: 首先读入setup,然后出现logo,然后调用BIOS13号中断把system部分也读进内存中,这时bootsect就完成了它的历史使命。然后进行下一步的“取指执行”。这将从setup开始。接下来我们继续学习setup模块。

setup 模块 (setup.s)

setup将完成OS启动前的设置
开机主要完成两个工作,读入操作系统,以及完成初始化。

start: mov ax,#INITSEG    mov ds,ax      mov ah,#0x03
     xor bh,bh            int 0x10//取光标位置dx   
     mov [0],dx //取出光标位置(包括其他硬件参数) 到0x90000处
     mov ah,#0x88         int 0x15    mov [2],ax ...//扩展内存大小,(读入内存大小,以便管理内存),SYSSEG = 0x1000
     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   //将system模块 移到0地址
     movsw
     jmp do_move

在这里插入图片描述
操作系统用类似表的数据结构进行存储和管理。
进入保护模式后,jmpi 0,8,(ip->0, cs->8) cs左移4位+ip做多达到1M,要想访问4G的内存,就要切换到32位模式(保护模式),前面的16位模式的访问已经不能满足需求了。 16位与32位的根本区别就是CPU中的解释程序不一样(寻址方式不一样)。 16位的解释方法: ip->0, cs->8。

将setup移到0地址处

但0地址处是有重要内容的
以后不调用int指令了吗?
因为操作系统要让硬件进入保护模式了… 保护模式下int n和cs:ip解释不再和实模式一样

end_move: mov ax,#SETUPSEG mov ds,ax
lidt idt_48 lgdt gdt_48//设置保护模式下的中断和寻址 进入保护模式的命令...
idt_48:.word 0 .word 0,0 //保护模式中断函数表
gdt_48:.word 0x800 .word 512+gdt,0x9 
gdt: .word 0,0,0,0
.word 0x07FF, 0x0000, 0x9A00, 0x00C0
.word 0x07FF, 0x0000, 0x9200, 0x00C0 //64位,8

在这里插入图片描述

保护模式下的地址翻译和中断处理

保护模式下的地址翻译

在这里插入图片描述
gdt的作用:(硬,快)
cs: 选择子,选择表中的一项,产生基址。再加上偏移,产生32位

t是table,所以实模式 下:cs左移4+ip。保护模 式下:根据cs查表+ip

保护模式下中断处理函数入口

在这里插入图片描述
idt的作用:

t仍是table,仿照gdt, 通过int n的n进行查表

进入保护模式

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

cr0一个非常酷的寄存器

在这里插入图片描述

empty_8042:
.word 0x00eb,0x00eb
in al,#0x64
test al,#2
jnz empty_8042
ret

jmpi 0,8 //cs=8用来查gdt

jmpi 0,8 //gdt中的8

在这里插入图片描述

跳到system模块执行

system模块(目标代码)中的第一部分代码? head.s
system由许多文件编译而成,为什么是head.s?
linux/Makefile :控制整个操作系统执行(树状结构,后跟)

disk: Image
   dd bs=8192 if=Image of=/dev/PS0 
Image: boot/bootsect boot/setup **tools/system** tools/build
   tools/build boot/bootsect boot/setup tools/system > Image
tools/system: boot/head.o init/main.o $(DRIVERS)** $(LD) boot/head.o**   init/main.o $(DRIVERS)-o tools/system

if=input file
/dev/PS0是软驱A

head.s //一段在保护模式下运行的代码

setup是进入保护模式,head是进入之后的初始化
注意:32位汇编代码,与16位不同

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 
//idt_48:.word 0 word 0,0
//_idt: .fill 256,8,0
call setup_gdt  
xorl %eax,%eax
1:incl %eax
movl %eax,0x000000 cmpl %eax,0x100000
je 1b //0地址处和1M地址处相同(A20没开启),就死循环,开启A20才能开始访问4G
jmp after_page_tables //页表,什么东东?
setup_idt: lea ignore_int,%edx 
movl $0x00080000,%eax movw %dx,%ax
lea _idt,%edi movl %eax,(%edi)
struct{long *a; short b;}stack_start
={&user_stack[PAGE_SIZE>>2],0x10};


关于汇编…head.s的汇编和前面不一样

as86汇编:能产生16位代码的Intel 8086(386)汇编

mov ax, cs //csàax, 目标操作数在前

GNU as汇编:产生32位代码,使用AT&T系统V语法

AT&T美国电话电报公司, 包含贝尔实验室等,1983年AT&T UNIX支持 组发布了系统V

movl var, %eax//(var)à%eax
movb -4(%ebp), %al //取出一字节

内嵌汇编,gcc编译x.c会产生中间 结果as汇编文件x.s

__asm__(“汇编语句”
: 输出
: 输入
: 破坏部分描述);

__asm__(“movb 
%%fs:%2, %%al”
:=a”(_res)
:0(seg),”m”(*(addr))
)
  1. 0或空表示使用与 相应输出一样的 寄存器
  2. a表示使用eax, 并编号%0
  3. %2表示addr,m 表示使用内存

after_page_tables //设置了页表之后

setup是进入保护模式,head是进入之后的初始化

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

从汇编跳转到C函数(C语言中的调用采用栈的结构)

简单的几句程序,控制流却很复杂

在这里插入图片描述

  1. setup_paging执行ret后? 会执行函数main()
  2. 进入main()后的栈为0,0,0,L6
  3. main()函数的三个参数是0,0,0
  4. main()函数返回时进入L6,死循环… 所以main()函数不会被返回

进入main函数

在init/main.c中

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();} // 表明不会被执行
}

在这里插入图片描述
三个参数分别是envp,argv,argc,但此处main并没使用。此处的main只保留传统main的 形式和命名。
main的工作就是xx_init: 内存、中断、设备、 时钟、CPU等内容的初始化…

看一看mem_init…

在linux/mm/memory.c中


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;// 每次4K 页
while(end_mem -- > 0)
mem_map[i++] = 0; }

在这里插入图片描述
所谓内存初始化就是建立如上图所示的表格(一个称为mem_map的 表格…), 上面橙色部分表示已经被使用的(OS部分),下面绿色部分表示未被使用部分(从90002开始传入)
总结:bootsect,setup,head,main,mem_map;主要完成两个工作:将OS读入内存以及完成初始化(OS 管理硬件,需要对硬件建立数据结构,返回硬件参数)

发布了16 篇原创文章 · 获赞 0 · 访问量 314

猜你喜欢

转载自blog.csdn.net/qq_43156233/article/details/103885068