笔记----从MBR到OS到application

    这个工程包含三段代码,一是引导代码,负责引导OS,二是OS,负责引导app,三是app。最后,OS会在app返回之后hlt。


    下面阐述每段代码的结构。
    1. 引导代码:
         1) 流程:进入保护模式-->从硬盘中将OS代码读入内存-->为OS将要使用的段"注册"段描述符-->跳转到OS
         2) 子程序:
                 i. read_hard_disk_0:    
                          ;从硬盘读取一个逻辑扇区
                          ;EAX=逻辑扇区号
                          ;DS:EBX=目标缓冲区地址
                          ;返回:EBX=EBX+512
                 ii. make_gdt_descriptor:
                          ;构造描述符
                          ;输入:EAX=线性基地址
                          ;      EBX=段界限
                          ;      ECX=属性(各属性位都在原始
                          ;      位置,其它没用到的位置0)
                          ;返回:EDX:EAX=完整的描述符
    2. OS
        1) 头部
         操作系统的头部包含三部分信息,一是内核长度,用于引导时确定读取的扇区数量;二是段起始点偏移,用于向GDT中"注册"段描述符前计算段基地址和段界限(具体的说,内核基地址加上这些段起始点偏移就是段基地址,而下一个段的起始点偏移减去本段起始点偏移就能得到本段大小);三是程序入口点,供mbr代码跳转,其中选择子用equ定义好,与start一起在编译阶段作为常量数据写入。
              #00 doubleWord         core_length                ;内核长度
              #04 doubleWord         sys_routine_seg            ;例程段的起始点位移
              #08 doubleWord         core_data_seg              ;核心数据段的起始点位移
              #0c doubleWord         core_code_seg              ;核心代码段起始点位移
              #10 doubleWord + word  start + core_code_seg_sel  ;入口点:4字节偏移+2字节选择子
        2) 数据段
        3) 代码段
              i.  重定位子程序
                  load_relocate_program:      
                      ;加载并重定位用户程序
                      ;输入:ESI=起始逻辑扇区号
                      ;返回:AX=指向用户程序头部的选择子
                  重定位子程序要做两项工作,一是从硬盘将用户程序读入内存,二是为用户程序的运行准备环境,包括GDT的更新和OS例程入口的映射。该程序的工作流程简述如下。
                  首先,读取用户程序的头部到内核数据段的预定区域。程序的头部包含两部分信息,一是入口点。二是和段相关的信息,包括各段相对于整个程序的偏移、各段的大小、用户程序希望得到的栈的大小,有了这些信息,OS就可以为程序刷新GDT,申请栈空间,将CPU控制权交给用户程序。三是程序要使用的系统例程。这些例程由内核提供,常驻于内核的例程代码段,可以由OS和用户程序调用(远call)。为了让OS和用户程序更好的隔离,即允许用户程序方便的利用过程名而不是过程入口地址调用过程,OS进行查表映射操作,将用户程序头部的过程名映射为入口地址,回写入用户程序头部。所谓“内核数据段的预定区域”指内核数据段的一块2KB的缓冲区,用以缓存用户程序头部信息。
                  第二,OS从用户程序头部读取用户程序大小,并且向上扩充为512的倍数。
                  第三,OS申请足够大的内存用以承载用户程序,让bs指向申请到的内存起始点。
                  第四,OS根据用户程序大小,计算需要读取的扇区数。
                  第五,OS循环读取完整的用户程序到内存里,存在之前申请到的内存里。
                  第六,OS根据头部信息为用户程序安装段描述符,并将段选择子回写到头部信息中。
                  第七,OS根据头部信息申请栈空间,为栈段注册段描述符,并回写到头部。
                  第八,OS重定位用户程序要使用的OS例程。用户程序将使用到的系统例程名字登记在头部,OS依据这些信息查找内核数据段中的映射表,将系统例程的名字映射到系统例程的入口地址,并且回写到用户程序的头部,覆盖其内的例程名称。在内核中提供的例程中有一个"TerminateProgram",这个例程的入口指向OS代码中所谓的"返回点",也就是jmp跳转用户程序之后的位置。用户程序可以调用(jmp)TerminateProgram将CPU控制权交还给OS。
                  经过上述复杂而繁琐的操作之后,重定位子程序load_relocate_program为用户程序的运行准备好了环境,并且让AX填充入一个选择子,指向用户程序头部段(用户程序头部信息自成一段)。
              ii. 流程
                  显示处理器品牌信息-->调用load_relocate_program加载并重定位app-->保护堆栈指针-->跳转到app-->从app返回后修正DS和栈(包括SS和SP)-->hlt。这个地方要留意对栈的保护。当CPU从内核跳转到应用程序后,相应的栈段(ss与sp所决定)应该从内核的栈段(0x7c00开始的4KB,向低地之方向拓展,由mbr代码初始化)转换成应用程序的栈段(由OS动态分配)。相应的ss和sp的更新由应用程序执行,而从应用程序返回后,相应的更新则应由OS尽早完成。在进入应用程序之前,为了完成对ss和sp的保护,OS可以将ss和sp缓存入OS数据段的指定位置,待应用程序返回后重新取出、恢复。
        3) 系统公共例程
              这是一些子程序,用于被OS和app调用。
              i.   put_string:        
                     ;显示0终止的字符串并移动光标
                     ;输入:DS:EBX=串地址
              ii.  put_char:  
                     ;在当前光标处显示一个字符,并推进
                     ;光标。仅用于段内调用
                     ;输入:CL=字符ASCII码
              iii. read_hard_disk_0:  
                     ;从硬盘读取一个逻辑扇区
                     ;EAX=逻辑扇区号
                     ;DS:EBX=目标缓冲区地址
                     ;返回:EBX=EBX+512
              iv.  put_hex_dword:                  
                     ;在当前光标处以十六进制形式显示
                     ;一个双字并推进光标
                     ;输入:EDX=要转换并显示的数字
                     ;输出:无
              v.   allocate_memory:    
                     ;分配内存
                     ;输入:ECX=希望分配的字节数
                     ;输出:ECX=起始线性地址
              vi.  set_up_gdt_descriptor:    
                     ;在GDT内安装一个新的描述符
                     ;输入:EDX:EAX=描述符
                     ;输出:CX=描述符的选择子
              vii. make_seg_descriptor:          
                     ;构造存储器和系统的段描述符
                     ;输入:EAX=线性基地址
                     ;      EBX=段界限
                     ;      ECX=属性。各属性位都在原始
                     ;          位置,无关的位清零
                     ;返回:EDX:EAX=描述符
    3.app
          1) 头部段
              app的头部段非常重要。如果用一句话概括,这个段的作用就是:负责OS和app之间的通信。首先,app在编译阶段将头部信息填充完毕,这些信息将帮助OS给本app准备运行环境。所谓准备运行环境包括代码装载、内存申请、gdt安装、OS例程入口映射。其次,当OS一切就绪,希望将有关该app的运行环境的信息告知该app时,就将信息回写入头部段。回写的信息包括头部段、栈段、代码段、数据段的选择子。头部段包括下面两部分。
              i.
                 #0x00   doubleWord   program_length    程序总长度     
                 #0x04   doubleWord   head_len                头部长度
                 #0x08   doubleWord   stack_seg              栈段选择子
                 #0x0c   doubleWord   stack_len                程序建议的堆栈大小
                 #0x10   doubleWord   prgentry                   程序入口
                 #0x14   doubleWord   code_seg               代码段位置
                 #0x18   doubleWord   code_len                代码段长度
                 #0x1c   doubleWord   data_seg                数据段位置
                 #0x20   doubleWord   data_len                 数据段长度 
              ii. 符号地址检索表
                 该表存储app要使用的OS例程的名称,比如@PrintString。OS在跳转入app之前会将这些名称映射成相应的入口地址。
           2) 数据段
              1024字节的缓冲区 + message。其中1024字节缓冲区用于缓存从硬盘中读取的数据。
           3) 代码段
              流程:
              更新ds,ss,sp --> 显示一些信息 --> 调用TerminateProgram返回OS
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值