知识大全 自己动手写操作系统(三)

Posted 寄存器

篇首语:人生在勤,不索何获本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 自己动手写操作系统(三)相关的知识,希望对你有一定的参考价值。

    在上两期中(自己动手写操作系统 ) 我向大家讲述了如何使用Linux提供的开发工具在软盘的启动扇区写一些代码 以及如何调用BIOS的问题 现在 这个操作系统已经越来越接近当年Linus Torvalds的那个具有 历史意义 的Linux内核了 因此 要马上把这个系统切换到保护模式之下     什么是保护模式    自从 年推出第一个微处理器以来 Intel处理器就在不断地更新换代 从 到 奔腾 奔腾Ⅱ 奔腾 等 其体系结构也在不断变化 以后 提供了一些新的功能 弥补了 的一些缺陷 这其中包括内存保护 多任务及使用 KB以上的内存等 并仍然保持和 家族的兼容性 也就是说 仍然具备了 和 的所有功能 但是在功能上有了很大的增强 早期的处理器是工作在实模式之下的 以后引入了保护模式 而在 以后保护模式又进行了很大的改进 在 中 保护模式为程序员提供了更好的保护 提供了更多的内存 事实上 保护模式的目的不是为了保护程序 而是要保护程序以外的所有程序(包括操作系统)     简言之 保护模式是处理器的一种最自然的模式 在这种模式下 处理器的所有指令及体系结构的所有特色都是可用的 并且能够达到最高的性能     保护模式和实模式    从表面上看 保护模式和实模式并没有太大的区别 二者都使用了内存段 中断和设备驱动来处理硬件 但二者有很多不同之处 我们知道 在实模式中内存被划分成段 每个段的大小为 KB 而这样的段地址可以用 位来表示 内存段的处理是通过和段寄存器相关联的内部机制来处理的 这些段寄存器(CS DS SS和ES)的内容形成了物理地址的一部分 具体来说 最终的物理地址是由 位的段地址和 位的段内偏移地址组成的 用公式表示为     物理地址=左移 位的段地址+偏移地址     在保护模式下 段是通过一系列被称之为 描述符表 的表所定义的 段寄存器存储的是指向这些表的指针 用于定义内存段的表有两种 全局描述符表(GDT)和局部描述符表(LDT) GDT是一个段描述符数组 其中包含所有应用程序都可以使用的基本描述符 在实模式中 段长是固定的(为 KB) 而在保护模式中 段长是可变的 其最大可达 GB LDT也是段描述符的一个数组 与GDT不同 LDT是一个段 其中存放的是局部的 不需要全局共享的段描述符 每一个操作系统都必须定义一个GDT 而每一个正在运行的任务都会有一个相应的LDT 每一个描述符的长度是 个字节 格式如图 所示 当段寄存器被加载的时候 段基地址就会从相应的表入口获得 描述符的内容会被存储在一个程序员不可见的影像寄存器(shadow register)之中 以便下一次同一个段可以使用该信息而不用每次都到表中提取 物理地址由 位或者 位的偏移加上影像寄存器中的基址组成 实模式和保护模式的不同可以从图 和图 中很清楚地看出来      图 实模式的寻址     图 保护模式下的寻址    图 段描述俯的格式    此外 还有一个中断描述符表(IDT) 这些中断描述符会告诉处理器到那里可以找到中断处理程序 和实模式一样 每一个中断都有一个入口 但是这些入口的格式却完全不同 因为在切换到保护模式的过程中没有使用到IDT 所以在此就不多做介绍了     进入保护模式     有 个 位控制寄存器 名字分别为CR CR CR 和CR CR 是保留在未来处理器中使用的 在 中没有定义 CR 包含系统的控制标志 用于控制处理器的操作模式和状态 CR 和CR 是用于控制分页机制的 在此 我们关注的是CR 寄存器的PE位控制 它负责实模式和保护模式之间的切换 当PE= 时 说明处理器运行于保护模式之下 其采用的段机制和前面所述的相应内容对应 如果PE= 那么处理器就工作在实模式之下     切换到保护模式 实际就是把PE位置为 为了把系统切换到保护模式 还要做一些其它的事情 程序必须要对系统的段寄存器和控制寄存器进行初始化 把PE位置 后 还要执行跳转指令 过程简述如下      创建GDT表;      通过置PE位为 进入保护模式;      执行跳转以清除在实模式下读取的任何指令     下面使用代码来实现这个切换过程     需要的东西    ◆ 一张空白软盘     ◆ NASM编译器     下面是整个程序的源代码      x c ; 起始地址是 : c   jmp short begin_boot  ; 跳过其它的数据 跳转到引导程序的开始处  bootmesg db Our OS boot sector loading   pm_mesg db Switching to protected mode   dw  ; 每一扇区的字节数  db  ; 每一簇的扇区数  dw  ; 保留的扇区号  db     dw x e     dw x b     db x f     dw     dw     dw  ; 读写扇区号  dw  ; 隐藏扇区号  print_mesg :  mov ah x  ; 使用中断 h的功能 在屏幕上写一个字符串  mov al x  ; 决定调用函数后光标所处的位置  mov bx x   ; 设置显示属性  mov cx x  ; 在此字符串长度为   mov dx x   ; 光标的起始行和列  int x  ; 调用BIOS的中断 h  ret ; 返回调用程序  get_key :  mov ah x    int x  ; Get_key使用中断 h的功能 读取下一个字符  ret  clrscr :  mov ax x  ; 使用中断 h的功能 实现卷屏 如果al= 则清屏  mov cx x  ; 清屏  mov dx x f ; 卷屏至   mov bh  ; 使用颜色 来填充  int x  ; 调用 h中断  ret  begin_boot :  call clrscr  ; 先清屏  mov bp bootmesg ; 提供串地址  call print_mesg ; 输出信息  call get_key  ; 等待用户按下任一键  bits   call clrscr ; 清屏  mov ax xb  ; 使gs指向显示内存  mov gs ax ; 在实模式下显示一个棕色的A  mov word [gs: ] x  ; 显示  call get_key ; 调用Get_key等待用户按下任一键  mov bp pm_mesg ; 设置串指针  call print_mesg ; 调用print_mesg子程序  call get_key ; 等待按键  call clrscr ; 清屏  cli ; 关中断  lgdt[gdtr] ; 加载GDT   mov eax cr     or al x  ; 设置保护模式位  mov cr eax ; 将更改后的字送至控制寄存器中  jmp codesel:go_pm  bits   go_pm :   mov ax datasel    mov ds ax ; 初始化ds和es 使其指向数据段  mov es ax   mov ax videosel ; 初始化gs 使其指向显示内存  mov gs ax   mov word [gs: ] x ; 在保护模式下显示一个白色的字符A  spin : jmp spin ; 循环  bits   gdtr :  dw gdt_end gdt  ; gdt的长度  dd gdt ; gdt的物理地址  gdt  nullsel equ $ gdt ; $指向当前位置 所以nullsel = h  gdt  ; 空描述符  dd       dd  ; 所有的段描述符都是 位的  codesel equ $ gdt ; 这是 h也就是gdt的第二个描述符  code_gdt       dw x ffff ; 段描述符的界限是 Gb  dw x      db x         db x a     db x cf     db x     datasel equ $ gdt     data_gdt       dw x ffff       dw x       db x         db x   db x cf  db x   videosel equ $ gdt     dw       dw x  ; 基址是 xb   db x b  db x       db x       db x   gdt_end  times ($ $$) db    dw x aa      把上面的代码存在一个名为abc asm的文件之中 使用命令nasm abc asm 将得出一个名为abc的文件 然后插入软盘 输入命令 dd if=abc of=/dev/fd 该命令将把文件abc写入到软盘的第一扇区之中 然后重新启动系统 就会看到如下的信息     *Our os booting   * A (棕色)   * Switching to protected mode   * A (白色)    对代码的解释    上面给出了所有的代码 下面我对上述代码做一些解释     ◆ 使用的函数     下面是代码中一些函数的说明     print_mesg 该子程序使用了BIOS中断 h的功能 h 即向屏幕写一字符串 属性控制是通过向一些寄存器中送入不同的值来实现的 中断 h是用于各种字符串操作 我们把子功能号 h送到ah中 用于指明要打印一个字符串 al寄存器中的 说明了光标返回的起始位置 表示调用函数后光标返回到下一行的行首 如果al为 则表示光标位于最后一个字符处     显存被分成了几页 在同一时 cha138/Article/program/Oracle/201311/16515

相关参考