linux-0.11学习笔记(一)——从加电到main执行前

来源:互联网 发布:語音英文软件 编辑:程序博客网 时间:2024/06/15 09:52

1、关于linux-0.11从加电到main函数前

在加电后,计算机执行过的程序有bios、bootsect.s、setup.s和head.s,下面分析下各段程序都做了什么,到main函数执行前内存中是个什么状况,还需要做些什么,希望能有个总体的分析。


① 首先,开机加电后硬件从0xFFFF0处开始执行,也就是bios程序的入口,在bios 程序中bios在内存最开始的位置(即:0x00000)用1KB的内存空间(0x00000—0x003FF) 构建中断向量表(这是实模式下状态),并在紧挨着它的位置用256字节的内存空间构 建bios数据区(0x00400—0x004FF),在大约56KB后的位置(0x0E2CE)加载8KB左 右与中断向量表相关的中断服务程序(BIOS自身在0xFE000—0xFFFFF)。


② 然后在bios执行到加载系统时,由bios产生0x19中断,中断服务程序将第一扇区 bootsect加载进内存(在0X07C00处),然后bios中断服务程序执行完返回,貌似bootsect 得不到执行,我觉得可以这样设计,就是在中断服务程序执行完时通过跳转直接开始从 0X07C00处开始执行bootsect代码,bootsect将要开始内存规划。


③ bootsect上来先把自己复制到INITSEG(0x90000)起始位置,然后将ds、es、ss 等都设置为0x9000,设置栈指针sp为0xFF00,这样整个的在INITSEG起始的位置构 建了一个属于自己的王国,不过没有响应中断的能力,所以不能奈何bios,然后加载 setup.s和system模块,执行完后跳转到setup.s执行,但是我有个问题就是为什么不把bootsect.s和setup.s一起加载在0x07C00位置处呢,并且两段代码总共才5*512B,末地址在0x08600处,不会覆盖到bios的中断服务程序(0x0E2CE-0xFFFE),然后直接在0x90000处加载机器系统数据,在0x9FF00处建立栈顶,好像有点问题就是setup.s在移动system模块时会被覆盖,所以setup.s还是必须在0x90200处,这样设计才好


④ Setup的重要作用是开启32位保护模式,并建立保护模式下的一些要素,首先,趁 现在中断还是开着的加载机器系统数据到0x90000也就是原来的bootsect位置,一旦中 断关闭了就读不了了,读完之后开闭中断,马上要开启32位模式,开着中断会很麻烦 的,关中断后bios起不到什么作用了(bios所有的工作已经全部做完了),然后就直接 把bios占用的那段内存给覆盖了,然后开启A20地址建立GDT和IDT,总之,setup 主要目的就是建立32位保护模式,让head.s得以执行,因为head.s是32位下的汇编, 并且采用的是AT&T汇编,调用jmpi 0,8进入head.s执行,注意到head.s与前面两段程序不同的是,它采用的是AT&T(他下面有个贝尔实验室,而UNIX正是贝尔实验室搞出来的)汇编,所有我猜测head.s及c语言内嵌的汇编都是采用AT&T汇编格式,正是为了与UNIX兼容,因为linux也是原因minix兼容UNIX,这样做也可以使用UNIX下面的一些软件


⑤ head.s代码量为25KB+184B,head.s上来就把几个段选择子置为0x10,给各段指 定段基址和段限长,并且基地址都在0x00000处,然后开始加载栈

lss _stack_start,%esp //这里stack_start结构体在sched.c中定义包括long *a和short b,分别是&user_stack[PAGE_SIZE>>2](user_stack数据结构的最末位置指针)和0x10,这条指令将指针*a赋给esp(即栈顶指针为&user_stack[PAGE_SIZE>>2]),将b赋值给ss(即段选择子为0x10),栈加载完后开始加载IDT和GDT(这里的GDT和IDT为兼容16M内存特意改了的)setup_idt:lea ignore_int,%edx  //将ignore_int (系统中断服务函数,在系统出错的时候调用,里面还调用了个printk函数,看来是要panic的)的有效地址(偏移值0x00005428)写入edx 寄存器movl $0x00080000,%eax  //将选择符0x0008 置入eax 的高16 位中。movw %dx,%ax /* selector = 0x0008 = cs *///偏移值的低16 位置入eax 的低16 位中。此时eax 含有//门描述符低4 字节的值。movw $0x8E00,%dx  /* interrupt gate - dpl=0, present *///此时edx 含有门描述符高4 字节的值。lea _idt,%edi  //将中断描述符表地址传给edi,idt在head.h中定义,typedef struct desc_struct {//描述符结构体,长度8个字节,unsigned long a,b;} desc_table[256];extern unsigned long pg_dir[1024];extern desc_table idt,gdt;//256*8B(2KB)大小的描述符表,但是我不知道他是怎么控制idt首地址在0x054b8的,反正在msp430中全局变量的起始地址始终是在0x200处,并且全局变量的先后顺序会因为变量类型不同而不同,同时,全局变量值也会在0x1100处依次存放,即使程序中改变全局变量的值这里的值始终不变(猜测用于全局变量初始化),在编译时编译器会自动增加cstart_init_copy函数将全局变量地址拷贝到寄存器中mov $256,%ecxrp_sidt:movl %eax,(%edi) //循环256次,依次将中断门描述符存入表中。movl %edx,4(%edi)addl $8,%edi //edi 指向表中下一项。dec %ecxjne rp_sidtlidt idt_descr  //加载中断描述符表寄存器值。ret