操作系统真象还原前7章总结

才读了一小半, 但也感觉需要总结一下, 真希望能看完, 真希望. 但真没这个信心.

mbr.S

上卷行(以达到清屏的目的) BIOS调用
写显存, 显示一些字符
读loader(读硬盘, 且是从2号扇区开始, 因为0被mbr占了)
跳转到loader

loader.S

通过BIOS调用获取物理内存(0x15的0x0000e820号调用)
进入保护模式(3步,A20,lgdt,cr0寄存器)
读入磁盘上kernel(从9开始,200)
填写页表, 填写的一级页表项包括0和3GB开始的, 一直到1022, 还有1023. 其中0和768的低1M(本来一个一级页表项对应的是4M)对应的都是二级页表起始, 为此二级页表填了一页的1/4
1023对应的项(也就是它的二级页表所在的地址)就是一级页表的起始地址, 这样就有办法访问到一级页表的任何一项
769到1022的情况是, 分配了二级页表, 但是二级页表没有填内容

由于填写了页表, 之前的gdt就需要改改了, 以及gdtr存的也需要改, 代码段和数据段是不需要改的, 本来就是平坦模型. 需要改的是显存, 需要把它弄成3G以上的那个内存空间(0xc0000000). 关于页表位置也是同理. 关于页表位置见内存布局.

打开分页的3步: cr3存页表地址, cr0的最后一位

之前把内核读到0x70000, 现在需要把它移到合适的位置, 也就是根据elf头部信息把它的各个segment移到头部信息中的只是信息. 然后跳转到代码段的位置(0x1500)

用汇编写了一个打印函数, 可以打印单个字符, 字符串, 还有数, 便于内核使用

之前说跳转到内核, 但是内核到底干了啥? 初始化中断和PIT(计数器, 控制时钟中断的频率).
啥是初始化中断, 其实就是填写IDT, 中断描述符表, 中段描述符最重要的是地址, 选择子就是之前gdt中code段的索引. 大家都一样, 不同的是处理程序的地址, 用了非常巧妙的做法. 汇编中非常巧妙地用宏构建了一个数组. global这个数组的起始地址. 这样c就知道怎么填了.
希望中断处理程序用c写, 这里又非常巧妙地, 用idt_table数组保存函数地址, 汇编直接call idt_table起始地址+4*中断号即可(注意是4而不是8, 我们习惯了指针是8位, 是因为gcc默认用64位, 但本书中全部是16位或者32位的). 这个函数表, 起始都存的是打印函数, 打印名字, 为此它需要一个参数就是中断号.

初始化8253计数器, 其实是为了提高时钟中断的频率. 所做的事就是写2个端口, 第一次写端口用于确定工作模式, 有3个参数, 第二次写端口则是决定时钟中断频率.

但这里有非常重要的一点, 那就是, 在初始化结束后, 是有while(1); 死循环的, 否则内核结束了工作, OS就结束了, 还能干点啥?
所以目前, OS一般来说在一个无意义的死循环中, 如果有中断且CPU愿意响应, CPU就执行中断程序, 再回到这个死循环中去.

页表的内存布局

loader是从0x900开始, 不能被覆盖, 因为有GDT, 大概容纳160左右个entry
内核代码段是从0x1500开始, 可以覆盖MBR
内核文件是0x70000(初始读硬件给的缓冲位置)

磁盘扇区的规划:

0 mbr
从2开始, loader
9开始, 内核mbr.S

原文地址:https://www.cnblogs.com/Tokubara/p/14452259.html